libertyos_kernel 0.14.1

The kernel for LibertyOS.
// cmos.rs

use core::hint::spin_loop;
use bit_field::BitField;
use x86_64::instructions::{interrupts, port::Port};

#[repr(u8)]
enum Reg
{
	Second = 0x00,
	Minute = 0x02,
	Hour = 0x04,
	Day = 0x07,
	Month = 0x08,
	Year = 0x09,
	A = 0x0A,
	B = 0x0B,
	C = 0x0C,
}

#[repr(u8)]
enum Intr
{
	Periodic = 1 << 6,
	Alarm = 1 << 5,
	Update = 1 << 4,
}

pub struct CMOS
{
	address: Port<u8>,
	data: Port<u8>,
}

#[derive(Debug, PartialEq)]
pub struct RTC
{
	pub day: u8,
	pub hour: u8,
	pub minute: u8,
	pub month: u8,
	pub second: u8,
	pub year: u16,
}

impl CMOS
{
	pub fn new() -> Self
	{
		CMOS
		{
			address: Port::new(0x70),
			data: Port::new(0x71),
		}
	}

	pub fn rtc(&mut self) -> RTC
	{
		let mut rtc;
		loop
		{
			self.waitfor_update();
			rtc = self.nocheck_rtc();
			self.waitfor_update();
			if rtc == self.nocheck_rtc()
			{
				break;
			}
		}

		let b = self.readreg(Reg::B);
		if b & 0x04 == 0
		{
			rtc.day = (rtc.day & 0x0F) + ((rtc.day / 16) * 10);
			rtc.hour = ((rtc.hour & 0x0F) + (((rtc.hour & 0x70) / 16) * 10)) | (rtc.hour & 0x80);
			rtc.minute = (rtc.minute & 0x0F) + ((rtc.minute / 16) * 10);
			rtc.month = (rtc.month & 0x0F) + ((rtc.month / 16) * 10);
			rtc.second = (rtc.second & 0x0F) + ((rtc.second / 16) * 10);
			rtc.year = (rtc.year & 0x0F) + ((rtc.year / 16) * 10);
		}

		if (b & 0x02 == 0) && (rtc.hour & 0x80 == 0)
		{
			rtc.hour = ((rtc.hour & 0x7F) + 12) % 24;
		}

		rtc.year += 2000;
		rtc
	}

	fn nocheck_rtc(&mut self) -> RTC
	{
		RTC
		{
			day: self.readreg(Reg::Day),
			hour: self.readreg(Reg::Hour),
			minute: self.readreg(Reg::Minute),
			month: self.readreg(Reg::Month),
			second: self.readreg(Reg::Second),
			year: self.readreg(Reg::Year) as u16,
		}
	}

	fn enable_intr(&mut self, intr: Intr)
	{
		interrupts::without_interrupts(||
		{
			self.disablenmi();
			unsafe
			{
				self.address.write(Reg::B as u8);
				let prev = self.data.read();
				self.address.write(Reg::B as u8);
				self.data.write(prev | intr as u8);
			}
			self.enablenmi();
			self.notify_intrend();
		});
	}

	pub fn enable_periodintr(&mut self)
	{
		self.enable_intr(Intr::Periodic);
	}

	pub fn set_periodintr_rate(&mut self, rate: u8)
	{
		interrupts::without_interrupts(||
		{
			self.disablenmi();
			unsafe
			{
				self.address.write(Reg::A as u8);
				let prev = self.data.read();
				self.address.write(Reg::A as u8);
				self.data.write((prev & 0xF0) | rate);
			}
			self.enablenmi();
			self.notify_intrend();
		});
	}

	fn waitfor_update(&mut self)
	{
		while self.updating()
		{
			spin_loop();
		}
	}

	fn updating(&mut self) -> bool
	{
		unsafe
		{
			self.address.write(Reg::A as u8);
			self.data.read().get_bit(7)
		}
	}

	fn readreg(&mut self, reg: Reg) -> u8
	{
		unsafe
		{
			self.address.write(reg as u8);
			self.data.read()
		}
	}

	pub fn enable_alarmintr(&mut self)
	{
		self.enable_intr(Intr::Alarm);
	}

	pub fn enable_updateintr(&mut self)
	{
		self.enable_intr(Intr::Update);
	}

	pub fn notify_intrend(&mut self)
	{
		unsafe
		{
			self.address.write(Reg::C as u8);
			self.data.read();
		}
	}

	fn enablenmi(&mut self)
	{
		unsafe
		{
			let prev = self.address.read();
			self.address.write(prev & 0x7F);
		}
	}

	fn disablenmi(&mut self)
	{
		unsafe
		{
			let prev = self.address.read();
			self.address.write(prev | 0x80);
		}
	}
}