/* タイマ関係 */

#include "bootpack.h"

struct timers *timers;		/* 管理用 */
extern struct timer *ttimer;

void pit_init(void)
{
	outb(PIT_CTRL, 0x34);
	outb(PIT_CNT0, 0x9c);	/* 10msごとに割り込む */
	outb(PIT_CNT0, 0x2e);

	timers->count = 0;
	timers->next = 0xffffffff;
	return;
}

void timers_init(void)
{
	struct timer *t;
	int i;

	timers = (struct timers *) memory_alloc(mem, sizeof(struct timers));
	for(i = 0; i < TIMER_MAX; i++) {
		timers->timer[i].flag = TIMER_NONE;
	}

	/* 番兵 */
	t = timer_alloc();
	t->flag = TIMER_USING;
	t->tout = 0xffffffff;
	t->next = 0;
	timers->t = t;
	timers->next = 0xffffffff;
	return;
}

void timer_ihandle(int *esp)
{
	struct timer *timer;
	char ts = 0;

	outb(PIC0_OCW2, 0x60);	/* IRQ-00受付完了をPIC0に通知 */
	timers->count++;
	if(timers->next > timers->count) {	/* タイムアウトが存在しない */
		return;
	}
	timer = timers->t;
	for(;;) {
		if(timer->tout > timers->count) {	/* タイムアウトはもうない */
			break;
		}
		timer->flag &= ~TIMER_USING;
		timer->flag |= TIMER_ALLOC;
		if(timer != ttimer) {
			fifo_put(timer->fifo, timer->data);
		} else {
			ts = 1;
		}
		timer = timer->next;
	}
	/* タイムアウトしたのでずらす */
	timers->t = timer;
	timers->next = timer->tout;
	if(ts != 0) {
		task_switch();
	}
	return;
}

struct timer *timer_alloc(void)
{
	int i;

	for(i = 0; i < TIMER_MAX; i++) {
		if(timers->timer[i].flag == TIMER_NONE) {
			timers->timer[i].flag = TIMER_ALLOC;
			return &timers->timer[i];
		}
	}
	return 0;
}

void timer_free(struct timer *timer)
{
	timer->flag = TIMER_NONE;
	return;
}

void timer_init(struct timer *timer, struct fifo *fifo, int data)
{
	timer->fifo = fifo;
	timer->data = data;
	return;
}

void timer_set(struct timer *timer, unsigned int tout)
{
	struct timer *t, *s;
	int e;

	timer->flag &= ~TIMER_ALLOC;
	timer->flag |= TIMER_USING;
	timer->tout = timers->count + tout;
	e = leflags();
	cli();

	t = timers->t;
	if(timer->tout <= t->tout) {	/* 先頭に入れる */
		timers->t = timer;
		timer->next = t;
		timers->next = timer->tout;
		seflags(e);
		return;
	}
	for(;;) {	/* 入れる場所を検索 */
		s = t;
		t = t->next;
		if(timer->tout <= t->tout) {	/* 間に入る */
			s->next = timer;
			timer->next = t;
			seflags(e);
			return;
		}
	}
}

void timer_cancel_auto(struct fifo *fifo)
{
	struct timer *t;
	int e, i;

	e = leflags();
	cli();
	for(i = 0; i < TIMER_MAX; i++) {
		t = &timers->timer[i];
		if((t->flag & TIMER_ACANCEL) != 0 && t->fifo == fifo) {
			timer_cancel(t);
			timer_free(t);
		}
	}
	seflags(e);
	return;
}

int timer_cancel(struct timer *timer)
{
	struct timer *t;
	int e;

	e = leflags();
	cli();
	if((timer->flag & TIMER_USING) != 0) {
		if(timer == timers->t) {	/* 先頭の場合 */
			t = timer->next;
			timers->t = t;
			timers->next = t->tout;
		} else {
			t = timers->t;
			for(;;) {
				if(t->next == timer) {
					break;
				}
				t = t->next;
			}
			t->next = timer->next;
		}
		timer->flag &= ~TIMER_USING;
		timer->flag |= TIMER_ALLOC;
		seflags(e);
		return 1;
	}
	seflags(e);
	return 0;
}

void timer_sleep(int time)
{
	struct task *task = task_now();
	struct fifo f;
	struct timer *timer;
	int fbuf[8], i;

	/* FIFOとタイマの初期化 */
	fifo_init(&f, task, fbuf, 8);

	timer = timer_alloc();
	timer_init(timer, &f, 1);
	timer_set(timer, time);

	for(;;) {
		cli();
		if(fifo_stat(&f) == 0) {
			task_sleep(task);
			sti();
		} else {
			i = fifo_get(&f);
			sti();

			if(i == 1) {
				timer_free(timer);
				return;
			}
		}
	}
}

