141 lines
4.5 KiB
C
141 lines
4.5 KiB
C
#include "rtc.h"
|
|
|
|
// flag variable used to indicate whether the RTC interrupt
|
|
// has occurred
|
|
static volatile int rtc_interrupt_occurred;
|
|
static uint16_t rtc_freq;
|
|
|
|
/* void rtc_init()
|
|
* @effects: Put RTC into working state
|
|
* @description: Prepare the RTC for generating interrupts at specified interval
|
|
*/
|
|
void rtc_init() {
|
|
cli();
|
|
// From https://wiki.osdev.org/RTC
|
|
outb(RTC_REG_B, RTC_PORT_CMD); // select register B, and disable NMI
|
|
char prev = inb(RTC_PORT_DATA); // read the current value of register B
|
|
outb(RTC_REG_B, RTC_PORT_CMD); // set the index again (a read will reset the index to register D)
|
|
outb(prev | 0x40, RTC_PORT_DATA); // write the previous value ORed with 0x40. This turns on bit 6 of register B
|
|
rtc_freq = 1024;
|
|
enable_irq(RTC_IRQ);
|
|
sti();
|
|
}
|
|
|
|
/* uint8_t rtc_freq_to_config(uint16_t freq)
|
|
* @input: freq - the desired frequency for RTC to send interrupts.
|
|
* can only be powers to 2, from 2 Hz to 1024 Hz.
|
|
* @output: ret val - the data to be written into RTC register
|
|
* to make it send interrupts at *freq* Hz.
|
|
* @description: Generates the data to set RTC into desired frequency.
|
|
* The formula is (16 - log_2(freq)), where 2 <= freq <= 1024.
|
|
* Read more at https://wiki.osdev.org/RTC
|
|
*/
|
|
uint8_t rtc_freq_to_config(uint16_t freq) {
|
|
switch(freq) {
|
|
case 1024: return 0x06; // All calculated with retval = (16 - log_2(freq)),
|
|
case 512: return 0x07; // as stated above.
|
|
case 256: return 0x08; // As there's no ready to use log implementation,
|
|
case 128: return 0x09; // I'll just list out all possibilities.
|
|
case 64: return 0x0A;
|
|
case 32: return 0x0B;
|
|
case 16: return 0x0C;
|
|
case 8: return 0x0D;
|
|
case 4: return 0x0E;
|
|
case 2: return 0x0F;
|
|
default: return 0x0F; // If the input freq is invalid, return 2 Hz.
|
|
}
|
|
}
|
|
|
|
/* void rtc_set_freq(uint16_t freq)
|
|
* @input: freq - the desired frequency for RTC to send interrupts.
|
|
* can only be powers to 2, from 2 Hz to 1024 Hz.
|
|
* @effects: RTC set to desired frequency.
|
|
* @description: As stated above. Read more at https://wiki.osdev.org/RTC
|
|
*/
|
|
void rtc_set_freq(uint16_t freq) {
|
|
uint8_t new_freq = rtc_freq_to_config(freq); // Convert freq to RTC command
|
|
uint8_t old_freq;
|
|
cli(); // Enter critical section
|
|
outb(RTC_REG_A, RTC_PORT_CMD); // Read https://wiki.osdev.org/RTC for why
|
|
old_freq = inb(RTC_PORT_DATA); // we're doing this
|
|
outb(RTC_REG_A, RTC_PORT_CMD);
|
|
outb((old_freq & 0xF0) | new_freq, RTC_PORT_DATA);
|
|
rtc_freq = new_freq;
|
|
sti(); // Quit critical section
|
|
}
|
|
|
|
/* void rtc_interrupt()
|
|
* @description: function to be called when RTC generates an interrupt.
|
|
*/
|
|
void rtc_interrupt() {
|
|
//test_interrupts();
|
|
printf("%d ", rtc_freq);
|
|
// set rtc_interrupt_occurred flag to 1
|
|
rtc_interrupt_occurred = 1;
|
|
// Read from RTC register C, so it can keep sending interrupts
|
|
outb(RTC_REG_C, RTC_PORT_CMD); // select register C
|
|
inb(RTC_PORT_DATA); // just throw away contents
|
|
|
|
send_eoi(RTC_IRQ); // And we're done
|
|
}
|
|
|
|
|
|
/* RTC Driver */
|
|
/* int32_t rtc_open(const uint8_t* filename)
|
|
* @input: filename - ignored
|
|
* @output: 0 (SUCCESS)
|
|
* @description: initialize RTC, set freq to 2 Hz
|
|
*/
|
|
int32_t rtc_open(const uint8_t* filename)
|
|
{
|
|
// set the RTC frequency to 2 Hz
|
|
rtc_set_freq(2);
|
|
rtc_freq = 2;
|
|
return 0;
|
|
}
|
|
|
|
/* int32_t rtc_read(int32_t fd, void* buf, int32_t nbytes)
|
|
* @input: all ignored
|
|
* @output: 0 (SUCCESS)
|
|
* @description: wait until the next RTC tick.
|
|
*/
|
|
int32_t rtc_read(int32_t fd, void* buf, int32_t nbytes)
|
|
{
|
|
rtc_interrupt_occurred = 0;
|
|
while (rtc_interrupt_occurred != 1) {}
|
|
return 0;
|
|
}
|
|
|
|
/* int32_t rtc_write(int32_t fd, void* buf, int32_t nbytes)
|
|
* @input: buf - value of new frequency for RTC
|
|
* @output: 0 (SUCCESS) / -1 (FAIL)
|
|
* @description: set the frequency of interrupts by RTC
|
|
*/
|
|
int32_t rtc_write(int32_t fd, const void* buf, int32_t nbytes)
|
|
{
|
|
// invalid input
|
|
if (buf == NULL)
|
|
{
|
|
return -1;
|
|
}
|
|
// enter critical section
|
|
cli();
|
|
// change the frequency of RTC
|
|
rtc_set_freq(*(uint16_t *)buf);
|
|
rtc_freq = *(uint16_t *)buf;
|
|
sti();
|
|
// quit critical section
|
|
return 0;
|
|
}
|
|
|
|
/* int32_t rtc_close(int32_t fd)
|
|
* @input: fd - ignored
|
|
* @output: 0 (SUCCESS)
|
|
* @description: close RTC, currently does nothing
|
|
*/
|
|
int32_t rtc_close(int32_t fd)
|
|
{
|
|
// there is nothing to do with syscall close() to RTC
|
|
return 0;
|
|
}
|