BarryServer : Git

All the code for all my projects
// BarryServer : Git / Orion / blob / 7ae31b03c38925f5d527e6303765925586731209 / task / time.c

// Related

Orion

Barry Importing existing Orion kernel d41a53c (2 years, 4 months ago)
/*
 * This file controls the system clock and contains the functions related to
 * getting and setting the time from various sources.  It keeps a monotonic
 * clock internally, but can also make use of the tasks' clocks, and the RTC.
 */

#include <stdint.h>
#include <sys/times.h>
#include <errno.h>
#include "task.h"
#include "../proc/proc.h"
#include "../io.h"

#define RTC_SECONDS 0x00
#define RTC_MINUTES 0x02
#define RTC_HOURS   0x04
#define RTC_WEEKDAY 0x06
#define RTC_DAY     0x07
#define RTC_MONTH   0x08
#define RTC_YEAR    0x09
#define RTC_CENTURY 0x32

#define LEAP_YEAR(x) ((((x % 4) == 0) && ((x % 100) != 0)) || ((x % 400) == 0))

extern TaskQueue readyQueue;

uint32_t monotonicClock = 0, millis = 0;
uint8_t slice[MAX_CPUS] = {0};
TaskQueue sleepQueue;

/* Read an RTC register */
static uint8_t
read_rtc(uint8_t index)
{
	outb(0x70, 0x80 | index);
	io_wait();
	return inb(0x71);
}

/* Timer interrupt */
void
timer_handler(InterruptFrame *frame)
{
	/* Monotonic clock */
	millis++;
	if (millis == 1000) {
		monotonicClock++;
		millis = 0;
	}

	/* Sleeping processes */
	Task *proc = sleepQueue.start, *prev = NULL;
	for (proc = sleepQueue.start; proc; prev = proc, proc = proc->next) {
		if (proc->sleepExpiry > (monotonicClock * 1000) + millis)
			break;
		if (prev)
			prev->next = proc->next;
		if (sleepQueue.start == proc)
			sleepQueue.start = proc->next;
		if (sleepQueue.end == proc)
			sleepQueue.end = NULL;
		add_to_queue(&readyQueue, proc);
	}

	/* Account timeslice */
	slice[CPUID]++;
	if (!current)
		return;

	/* Account task time */
	if (in_syscall())
		current->systime++;
	else
		current->usertime++;

	/* Call scheduler */
	if (slice[CPUID] < current->priority)
		return;
	slice[CPUID] = 0;
	schedule();
}

/* Sleep for a specified time (milliseconds) */
int
sleep(uint32_t ms)
{
	current->sleepExpiry = (monotonicClock * 1000) + millis + ms;

	/* Add to sorted sleep TaskQueue */
	TaskQueue *q = &sleepQueue;
	Task *task, *prev = NULL;
	acquire(&q->lock);
	if (!q->start) {
		q->start = current;
		q->end = current;
		current->next = NULL;
	} else {
		for (task = q->start; task; prev = task, task = task->next)
			if (task->sleepExpiry > current->sleepExpiry)
				break;
		if (!prev) {
			current->next = q->start;
			q->start = current;
		} else {
			current->next = task;
			prev->next = current;
		}
	}
	release(&q->lock);

	block_task(SLEEP);
	return 0;
}

/* Get epoch time */
time_t
time(time_t *tloc)
{
	if (!verify_access(tloc, sizeof(time_t), PROT_WRITE))
		return -EFAULT;

	/* Length of months for normal and leap years */
	const uint16_t monthLen[2][12] = {
		{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
		{31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
	};

	uint16_t i;
	uint8_t second, minute, hour, day, month, year, cent;
	time_t time = 0;
	uint8_t years = 0, leapYears = 0;

	second = read_rtc(RTC_SECONDS);
	minute = read_rtc(RTC_MINUTES);
	hour   = read_rtc(RTC_HOURS);
	day    = read_rtc(RTC_DAY);
	month  = read_rtc(RTC_MONTH);
	year   = read_rtc(RTC_YEAR);
	cent   = read_rtc(RTC_CENTURY);

	second = (second & 0x0F) + ((second        / 16) * 10);
	minute = (minute & 0x0F) + ((minute        / 16) * 10);
	hour   = ((hour  & 0x0F) + (((hour & 0x70) / 16) * 10)) | (hour & 0x80);
	day    = (day    & 0x0F) + ((day           / 16) * 10);
	month  = (month  & 0x0F) + ((month         / 16) * 10);
	year   = (year   & 0x0F) + ((year          / 16) * 10);
	cent   = (cent   & 0x0F) + ((cent          / 16) * 10);

	for (i = 1970; i < (cent * 100) + year; i++)
		if (LEAP_YEAR(i))
			leapYears++;
		else
			years++;
	time += ((years * 365) + (leapYears * 366)) * 86400;

	for (i = 0; i < month - 1; i++)
		time += monthLen[LEAP_YEAR(year)][i] * 86400;

	time += (day - 1) * 86400;
	time += hour * 3600;
	time += minute * 60;
	time += second;

	if (tloc)
		*tloc = time;

	return time;
}

/* Get process times */
clock_t
times(Times *buf)
{
	if (!verify_access(buf, sizeof(Times), PROT_WRITE))
		return -EFAULT;
	if (buf) {
		buf->utime = current->usertime;
		buf->stime = current->systime;
		buf->cutime = current->usertime;
		buf->cstime = current->systime;
	}
	return (monotonicClock * 1000) + millis;
}