Timer re-worked!
authorMinep <zelong56@gmail.com>
Sat, 12 Mar 2022 18:55:50 +0000 (18:55 +0000)
committerMinep <zelong56@gmail.com>
Sat, 12 Mar 2022 18:55:50 +0000 (18:55 +0000)
Renamings some header files.
A linked list implementation stolen from Linux.

20 files changed:
lunaix-os/arch/x86/hhk.c
lunaix-os/hal/acpi/acpi.c
lunaix-os/hal/apic.c
lunaix-os/hal/rtc.c
lunaix-os/includes/hal/apic.h
lunaix-os/includes/hal/rtc.h
lunaix-os/includes/lunaix/common.h [new file with mode: 0644]
lunaix-os/includes/lunaix/constants.h [deleted file]
lunaix-os/includes/lunaix/ds/llist.h [new file with mode: 0644]
lunaix-os/includes/lunaix/mm/page.h
lunaix-os/includes/lunaix/time.h [new file with mode: 0644]
lunaix-os/includes/lunaix/timer.h [new file with mode: 0644]
lunaix-os/kernel/asm/x86/interrupts.c
lunaix-os/kernel/k_init.c
lunaix-os/kernel/k_main.c
lunaix-os/kernel/kprintf.c
lunaix-os/kernel/mm/kalloc.c
lunaix-os/kernel/mm/pmm.c
lunaix-os/kernel/timer.c [new file with mode: 0644]
lunaix-os/kernel/tty/tty.c

index ed3709f0e44800a8f3f316da8a850cbbeb1b06a5..c6797c892d0aef826d7534f855340cf429345481 100644 (file)
@@ -1,7 +1,7 @@
 #include <arch/x86/boot/multiboot.h>
 #include <arch/x86/idt.h>
 #include <lunaix/mm/page.h>
-#include <lunaix/constants.h>
+#include <lunaix/common.h>
 
 #define PT_ADDR(ptd, pt_index)                       ((ptd_t*)ptd + (pt_index + 1) * 1024)
 #define SET_PDE(ptd, pde_index, pde)                 *((ptd_t*)ptd + pde_index) = pde;
index 47812197bf954cf70fde28aa67c2c4d1848c9504..a37466a91f0671e3588dede7ef3402e482b9069f 100644 (file)
@@ -8,7 +8,7 @@
 
 #include "parser/madt_parser.h"
 
-acpi_context* toc = NULL;
+static acpi_context* toc = NULL;
 
 LOG_MODULE("ACPI")
 
@@ -51,7 +51,7 @@ acpi_init(multiboot_info_t* mb_info)
     kprintf(KINFO "OEM: %s\n", toc->oem_id);
     kprintf(KINFO "IOAPIC address: %p\n", toc->madt.ioapic->ioapic_addr);
     kprintf(KINFO "APIC address: %p\n", toc->madt.apic_addr);
-    
+
     for (size_t i = 0; i < 24; i++) {
         acpi_intso_t* intso = toc->madt.irq_exception[i];
         if (!intso)
index c74d3f91b38425033c494e5d931967ebf0edbd8a..32bfed531429633fc226a06eaa0dd9a85829b551 100644 (file)
@@ -27,7 +27,7 @@ void
 apic_setup_lvts();
 
 void
-init_apic()
+apic_init()
 {
     // ensure that external interrupt is disabled
     cpu_disable_interrupt();
@@ -77,9 +77,6 @@ init_apic()
     // install our handler for spurious interrupt.
     spiv = (spiv & ~0xff) | APIC_SPIV_APIC_ENABLE  | APIC_SPIV_IV;
     apic_write_reg(APIC_SPIVR, spiv);
-
-    // setup timer and performing calibrations
-    apic_setup_timer();
 }
 
 #define LVT_ENTRY_LINT0(vector)           (LVT_DELIVERY_FIXED | vector)
@@ -89,7 +86,6 @@ init_apic()
 // LINT#1 *must* be edge trigged (Intel manual vol3. 10-14)
 #define LVT_ENTRY_LINT1                   (LVT_DELIVERY_NMI | LVT_MASKED | LVT_TRIGGER_EDGE)
 #define LVT_ENTRY_ERROR(vector)           (LVT_DELIVERY_FIXED | vector)
-#define LVT_ENTRY_TIMER(vector, mode)     (LVT_DELIVERY_FIXED | mode | vector)
 
 void
 apic_setup_lvts()
@@ -98,124 +94,3 @@ apic_setup_lvts()
     apic_write_reg(APIC_LVT_LINT1, LVT_ENTRY_LINT1);
     apic_write_reg(APIC_LVT_ERROR, LVT_ENTRY_ERROR(APIC_ERROR_IV));
 }
-
-void
-temp_intr_routine_rtc_tick(const isr_param* param);
-
-void
-temp_intr_routine_apic_timer(const isr_param* param);
-
-void
-test_timer(const isr_param* param);
-
-uint32_t apic_timer_base_freq = 0;
-
-// Don't optimize them! Took me an half hour to figure that out...
-
-volatile uint32_t rtc_counter = 0;
-volatile uint8_t apic_timer_done = 0;
-
-#define APIC_CALIBRATION_CONST  0x100000
-
-void
-apic_setup_timer()
-{
-    cpu_disable_interrupt();
-    
-    // Setup APIC timer
-
-    // Setup a one-shot timer, we will use this to measure the bus speed. So we can
-    //   then calibrate apic timer to work at *nearly* accurate hz
-    apic_write_reg(APIC_TIMER_LVT,  LVT_ENTRY_TIMER(APIC_TIMER_IV, LVT_TIMER_ONESHOT));
-    
-    // Set divider to 64
-    apic_write_reg(APIC_TIMER_DCR, APIC_TIMER_DIV64);
-
-    /*
-        Timer calibration process - measure the APIC timer base frequency
-
-         step 1: setup a temporary isr for RTC timer which trigger at each tick (1024Hz)
-         step 2: setup a temporary isr for #APIC_TIMER_IV
-         step 3: setup the divider, APIC_TIMER_DCR
-         step 4: Startup RTC timer
-         step 5: Write a large value, v, to APIC_TIMER_ICR to start APIC timer 
-                 (this must be followed immediately after step 4)
-         step 6: issue a write to EOI and clean up.
-
-        When the APIC ICR counting down to 0 #APIC_TIMER_IV triggered, save the rtc timer's 
-        counter, k, and disable RTC timer immediately (although the RTC interrupts should be 
-        blocked by local APIC as we are currently busy on handling #APIC_TIMER_IV)
-
-        So the apic timer frequency F_apic in Hz can be calculate as
-            v / F_apic = k / 1024
-            =>  F_apic = v / k * 1024
-
-    */
-
-    apic_timer_base_freq = 0;
-    rtc_counter = 0;
-    apic_timer_done = 0;
-
-    intr_subscribe(APIC_TIMER_IV, temp_intr_routine_apic_timer);
-    intr_subscribe(RTC_TIMER_IV, temp_intr_routine_rtc_tick);
-
-        
-    rtc_enable_timer();                                         // start RTC timer
-    apic_write_reg(APIC_TIMER_ICR, APIC_CALIBRATION_CONST);     // start APIC timer
-    
-    // enable interrupt, just for our RTC start ticking!
-    cpu_enable_interrupt();
-
-    wait_until(apic_timer_done);
-    
-    // cpu_disable_interrupt();
-
-    assert_msg(apic_timer_base_freq, "Fail to initialize timer");
-
-    kprintf(KINFO "Timer base frequency: %u Hz\n", apic_timer_base_freq);
-
-    // cleanup
-    intr_unsubscribe(APIC_TIMER_IV, temp_intr_routine_apic_timer);
-    intr_unsubscribe(RTC_TIMER_IV, temp_intr_routine_rtc_tick);
-
-    // TODO: now setup timer with our custom frequency which we can derived from the base frequency
-    //          we measured
-
-    apic_write_reg(APIC_TIMER_LVT,  LVT_ENTRY_TIMER(APIC_TIMER_IV, LVT_TIMER_PERIODIC));
-    intr_subscribe(APIC_TIMER_IV, test_timer);
-    
-    apic_write_reg(APIC_TIMER_ICR, apic_timer_base_freq);
-}
-
-volatile rtc_datetime datetime;
-
-void
-test_timer(const isr_param* param) {
-
-    rtc_get_datetime(&datetime);
-
-    kprintf(KWARN "%u/%02u/%02u %02u:%02u:%02u\r",
-           datetime.year,
-           datetime.month,
-           datetime.day,
-           datetime.hour,
-           datetime.minute,
-           datetime.second);
-}
-
-void
-temp_intr_routine_rtc_tick(const isr_param* param) {
-    rtc_counter++;
-
-    // dummy read on register C so RTC can send anther interrupt
-    //  This strange behaviour observed in virtual box & bochs 
-    (void) rtc_read_reg(RTC_REG_C);
-}
-
-void
-temp_intr_routine_apic_timer(const isr_param* param) {
-    apic_timer_base_freq = APIC_CALIBRATION_CONST / rtc_counter * RTC_TIMER_BASE_FREQUENCY;
-    apic_timer_done = 1;
-
-    rtc_disable_timer();
-}
\ No newline at end of file
index 60a1aed86654652bb225e655fdd46669aa439719..0e3673c2184800e39866bf335aaf674c2c8adcc8 100644 (file)
@@ -9,6 +9,7 @@
  * 
  */
 #include <hal/rtc.h>
+#include <lunaix/time.h>
 #include <klibc/string.h>
 
 void
@@ -42,7 +43,7 @@ bcd2dec(uint8_t bcd)
 }
 
 int
-rtc_date_same(rtc_datetime* a, rtc_datetime* b) {
+rtc_date_same(datetime_t* a, datetime_t* b) {
     return a->year == b->year &&
            a->month == b->month &&
            a->day == b->day &&
@@ -52,14 +53,14 @@ rtc_date_same(rtc_datetime* a, rtc_datetime* b) {
 }
 
 void
-rtc_get_datetime(rtc_datetime* datetime)
+time_getdatetime(datetime_t* datetime)
 {
-    rtc_datetime current;
+    datetime_t current;
     
     do
     {
         while (rtc_read_reg(RTC_REG_A) & 0x80);
-        memcpy(&current, datetime, sizeof(rtc_datetime));
+        memcpy(&current, datetime, sizeof(datetime_t));
 
         datetime->year = rtc_read_reg(RTC_REG_YRS);
         datetime->month = rtc_read_reg(RTC_REG_MTH);
index 851e75eab2916051ba486a5e4faf190e90fe646a..f4ab92c1378050073aed9656b02c76eddf3f2f8a 100644 (file)
@@ -67,7 +67,7 @@
 #define apic_write_reg(reg, val)     (*(uint32_t*)(APIC_BASE_VADDR + (reg)) = (val))
 
 void
-init_apic();
+apic_init();
 
 /**
  * @brief Tell the APIC that the handler for current interrupt is finished.
index 0092171230650eb3c4527082ea95bc2a112750eb..0742f0ce8f0abd47b3e3b25a88fa03be4ab12875 100644 (file)
 #define RTC_FREQUENCY_1024HZ    0b110
 #define RTC_DIVIDER_33KHZ       (0b010 << 4)
 
-typedef struct
-{
-    uint32_t year;      // use int32 as we need to store the 4-digit year
-    uint8_t month;
-    uint8_t day;
-    uint8_t weekday;
-    uint8_t hour;
-    uint8_t minute;
-    uint8_t second;
-} rtc_datetime;
-
 void
 rtc_init();
 
@@ -52,9 +41,6 @@ rtc_read_reg(uint8_t reg_selector);
 void
 rtc_write_reg(uint8_t reg_selector, uint8_t val);
 
-void 
-rtc_get_datetime(rtc_datetime* datetime);
-
 void
 rtc_enable_timer();
 
diff --git a/lunaix-os/includes/lunaix/common.h b/lunaix-os/includes/lunaix/common.h
new file mode 100644 (file)
index 0000000..d9ffeae
--- /dev/null
@@ -0,0 +1,31 @@
+#ifndef __LUNAIX_CONSTANTS_H
+#define __LUNAIX_CONSTANTS_H
+
+#include <stddef.h>
+
+#define K_STACK_SIZE            (64 << 10)
+#define K_STACK_START           ((0xFFBFFFFFU - K_STACK_SIZE) + 1)
+#define HIGHER_HLF_BASE         0xC0000000UL
+#define MEM_1MB                 0x100000UL
+
+#define VGA_BUFFER_VADDR        0xB0000000UL
+#define VGA_BUFFER_PADDR        0xB8000UL
+#define VGA_BUFFER_SIZE         4096
+
+#define SYS_TIMER_FREQUENCY_HZ  2048
+
+
+// From Linux kernel v2.6.0 <kernel.h:194>
+/**
+ * container_of - cast a member of a structure out to the containing structure
+ *
+ * @ptr:       the pointer to the member.
+ * @type:      the type of the container struct this is embedded in.
+ * @member:    the name of the member within the struct.
+ *
+ */
+#define container_of(ptr, type, member) ({                                 \
+        const typeof( ((type *)0)->member ) *__mptr = (ptr);   \
+        (type *)( (char *)__mptr - offsetof(type,member) );})
+
+#endif /* __LUNAIX_CONSTANTS_H */
diff --git a/lunaix-os/includes/lunaix/constants.h b/lunaix-os/includes/lunaix/constants.h
deleted file mode 100644 (file)
index 6fc0c48..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-#ifndef __LUNAIX_CONSTANTS_H
-#define __LUNAIX_CONSTANTS_H
-
-#define K_STACK_SIZE            (64 << 10)
-#define K_STACK_START           ((0xFFBFFFFFU - K_STACK_SIZE) + 1)
-#define HIGHER_HLF_BASE         0xC0000000UL
-#define MEM_1MB                 0x100000UL
-
-#define VGA_BUFFER_VADDR        0xB0000000UL
-#define VGA_BUFFER_PADDR        0xB8000UL
-#define VGA_BUFFER_SIZE         4096
-
-#endif /* __LUNAIX_CONSTANTS_H */
diff --git a/lunaix-os/includes/lunaix/ds/llist.h b/lunaix-os/includes/lunaix/ds/llist.h
new file mode 100644 (file)
index 0000000..effee95
--- /dev/null
@@ -0,0 +1,82 @@
+/**
+ * @file llist.h
+ * @author Lunaixsky
+ * @brief This doubly linked cyclic list is adopted from Linux kernel <linux/list.h>
+ * @version 0.1
+ * @date 2022-03-12
+ * 
+ * @copyright Copyright (c) 2022
+ * 
+ */
+#ifndef __LUNAIX_LLIST_H
+#define __LUNAIX_LLIST_H
+
+#include <lunaix/common.h>
+
+struct llist_header
+{
+    struct llist_header* prev;
+    struct llist_header* next;
+};
+
+static inline void
+__llist_add(struct llist_header* elem,
+            struct llist_header* prev,
+            struct llist_header* next)
+{
+    next->prev = elem;
+    elem->next = next;
+    elem->prev = prev;
+    prev->next = elem;
+}
+
+static inline void
+llist_init_head(struct llist_header* head) {
+    head->next = head;
+    head->prev = head;
+}
+
+static inline void
+llist_append(struct llist_header* head, struct llist_header* elem)
+{
+    __llist_add(elem, head, head->next);
+}
+
+static inline void
+llist_prepend(struct llist_header* head, struct llist_header* elem)
+{
+    __llist_add(elem, head->prev, head);
+}
+
+static inline void
+llist_delete(struct llist_header* elem) {
+    elem->prev->next = elem->next;
+    elem->next->prev = elem->next;
+    
+    // make elem orphaned
+    elem->prev = elem;
+    elem->next = elem;
+}
+
+/**
+ * list_entry - get the struct for this entry
+ * @ptr:       the &struct list_head pointer.
+ * @type:      the type of the struct this is embedded in.
+ * @member:    the name of the list_struct within the struct.
+ */
+#define list_entry(ptr, type, member) \
+       container_of(ptr, type, member)
+
+/**
+ * list_for_each_entry -       iterate over list of given type
+ * @pos:       the type * to use as a loop counter.
+ * @head:      the head for your list.
+ * @member:    the name of the list_struct within the struct.
+ */
+#define llist_for_each(pos, n, head, member)                               \
+       for (pos = list_entry((head)->next, typeof(*pos), member),      \
+               n = list_entry(pos->member.next, typeof(*pos), member); \
+            &pos->member != (head);                                                \
+            pos = n, n = list_entry(n->member.next, typeof(*n), member))
+
+#endif /* __LUNAIX_LLIST_H */
index c20218405d88fae25b9f9a2f15578da302b295e9..415ea9e09847c1d4177c0f2d8eadd9b328820880 100644 (file)
@@ -1,7 +1,7 @@
 #ifndef __LUNAIX_PAGE_H
 #define __LUNAIX_PAGE_H
 #include <stdint.h>
-#include <lunaix/constants.h>
+#include <lunaix/common.h>
 
 #define PG_SIZE_BITS                12
 #define PG_SIZE                     (1 << PG_SIZE_BITS)
diff --git a/lunaix-os/includes/lunaix/time.h b/lunaix-os/includes/lunaix/time.h
new file mode 100644 (file)
index 0000000..522cc1f
--- /dev/null
@@ -0,0 +1,20 @@
+#ifndef __LUNAIX_TIME_H
+#define __LUNAIX_TIME_H
+
+#include <stdint.h>
+
+typedef struct
+{
+    uint32_t year;      // use int32 as we need to store the 4-digit year
+    uint8_t month;
+    uint8_t day;
+    uint8_t weekday;
+    uint8_t hour;
+    uint8_t minute;
+    uint8_t second;
+} datetime_t;
+
+void 
+time_getdatetime(datetime_t* datetime);
+
+#endif /* __LUNAIX_TIME_H */
diff --git a/lunaix-os/includes/lunaix/timer.h b/lunaix-os/includes/lunaix/timer.h
new file mode 100644 (file)
index 0000000..5c40696
--- /dev/null
@@ -0,0 +1,42 @@
+#ifndef __LUNAIX_TIMER_H
+#define __LUNAIX_TIMER_H
+
+#include <lunaix/ds/llist.h>
+#include <stdint.h>
+
+#define SYS_TIMER_FREQUENCY_HZ      2048UL
+
+#define TIMER_MODE_PERIODIC   0x1
+
+struct lx_timer_context {
+    struct lx_timer *active_timers;
+    uint32_t base_frequency;
+    uint32_t running_frequency;
+    uint32_t tick_interval;
+};
+
+struct lx_timer {
+    struct llist_header link;
+    uint32_t deadline;
+    uint32_t counter;
+    void* payload;
+    void (*callback)(void*);
+    uint8_t flags;
+};
+
+
+/**
+ * @brief Initialize the system timer that runs at specified frequency
+ * 
+ * @param frequency The frequency that timer should run in Hz.
+ */
+void
+timer_init(uint32_t frequency);
+
+int
+timer_run_second(uint32_t second, void (*callback)(void*), void* payload, uint8_t flags);
+
+int
+timer_run(uint32_t ticks, void (*callback)(void*), void* payload, uint8_t flags);
+
+#endif /* __LUNAIX_TIMER_H */
index 869569f81928e31e3270b51a1166b22e17c426f8..a7ac25a5107c9ca516aff6c1dbaf83fa5fedf804 100644 (file)
@@ -4,7 +4,7 @@
 #include <lunaix/syslog.h>
 #include <lunaix/tty/tty.h>
 
-int_subscriber subscribers[256];
+static int_subscriber subscribers[256];
 
 static int_subscriber fallback = (int_subscriber) 0;
 
index 2dbd64ee7d76f87261716f7e05cea38a77193267..5e6cc99fd252f8b8f05e9efdf92262d8745b4551 100644 (file)
@@ -1,4 +1,4 @@
-#include <lunaix/constants.h>
+#include <lunaix/common.h>
 #include <lunaix/tty/tty.h>
 
 #include <lunaix/mm/page.h>
@@ -7,6 +7,7 @@
 #include <lunaix/mm/kalloc.h>
 #include <lunaix/spike.h>
 #include <lunaix/syslog.h>
+#include <lunaix/timer.h>
 
 #include <hal/rtc.h>
 #include <hal/apic.h>
@@ -27,6 +28,8 @@ extern uint8_t __kernel_start;
 extern uint8_t __kernel_end;
 extern uint8_t __init_hhk_end;
 
+
+// Set remotely by kernel/asm/x86/prologue.S
 multiboot_info_t* _k_init_mb_info;
 
 LOG_MODULE("INIT");
@@ -93,7 +96,8 @@ _kernel_post_init() {
     vmm_set_mapping(IOAPIC_BASE_VADDR, ioapic_addr, PG_PREM_RW);
 
     ioapic_init();
-    init_apic();
+    apic_init();
+    timer_init(SYS_TIMER_FREQUENCY_HZ);
 
     for (size_t i = 256; i < hhk_init_pg_count; i++) {
         vmm_unmap_page((void*)(i << PG_SIZE_BITS));
index 8104408692b8f4111a0aa168c8125c24266e7160..a78562a822550f9f2d107d0f5b5d1c3b6cd0886a 100644 (file)
@@ -1,15 +1,19 @@
 #include <hal/cpu.h>
-#include <hal/rtc.h>
 #include <lunaix/syslog.h>
 #include <lunaix/mm/kalloc.h>
 #include <lunaix/mm/vmm.h>
 #include <lunaix/spike.h>
+#include <lunaix/time.h>
+#include <lunaix/timer.h>
 #include <stdint.h>
 
 extern uint8_t __kernel_start;
 
 LOG_MODULE("LX")
 
+void 
+test_timer(void* payload);
+
 void
 _kernel_main()
 {
@@ -47,5 +51,21 @@ _kernel_main()
     lxfree(arr);
     lxfree(big_);
 
+    timer_run_second(1, test_timer, NULL, TIMER_MODE_PERIODIC);
+
     spin();
+}
+
+static datetime_t datetime;
+
+void test_timer(void* payload) {
+    time_getdatetime(&datetime);
+
+    kprintf(KWARN "%u/%02u/%02u %02u:%02u:%02u\r",
+           datetime.year,
+           datetime.month,
+           datetime.day,
+           datetime.hour,
+           datetime.minute,
+           datetime.second);
 }
\ No newline at end of file
index 82202baf082c50aac0b388441df18ca0b56a757a..3bae8673e8714d52470d90a3763f4e5de4cd65de 100644 (file)
@@ -5,7 +5,7 @@
 #define MAX_KPRINTF_BUF_SIZE 1024
 #define MAX_XFMT_SIZE 1024
 
-char buf[MAX_KPRINTF_BUF_SIZE];
+static char buf[MAX_KPRINTF_BUF_SIZE];
 
 void
 __kprintf(const char* component, const char* fmt, va_list args) {
index 6f04e2edebcabe6a2497e00b5288e4e1509af0bb..e9f05648c6caf00ade4abb92f3fcbe56fac5ad36 100644 (file)
@@ -14,7 +14,7 @@
 #include <lunaix/mm/kalloc.h>
 #include <lunaix/mm/dmm.h>
 
-#include <lunaix/constants.h>
+#include <lunaix/common.h>
 #include <lunaix/spike.h>
 
 #include <klibc/string.h>
@@ -23,7 +23,7 @@
 
 extern uint8_t __kernel_heap_start;
 
-heap_context_t __kalloc_kheap;
+static heap_context_t __kalloc_kheap;
 
 void*
 lx_malloc_internal(heap_context_t* heap, size_t size);
index 304d19dc47192f49606d546a777916bf90207034..054b247743404044a2c184ad7bfbabcfe15e7e2c 100644 (file)
@@ -13,9 +13,9 @@
     uint32_t leading_shifts =                                                  \
       (page_count + offset) < 8 ? page_count : 8 - offset;
 
-uint8_t pm_bitmap[PM_BMP_MAX_SIZE];
+static uint8_t pm_bitmap[PM_BMP_MAX_SIZE];
 
-uintptr_t max_pg;
+static uintptr_t max_pg;
 
 //  ... |xxxx xxxx |
 //  ... |-->|
diff --git a/lunaix-os/kernel/timer.c b/lunaix-os/kernel/timer.c
new file mode 100644 (file)
index 0000000..bf3e699
--- /dev/null
@@ -0,0 +1,196 @@
+/**
+ * @file timer.c
+ * @author Lunaixsky
+ * @brief A simple timer implementation based on APIC with adjustable frequency and subscribable "timerlets"
+ * @version 0.1
+ * @date 2022-03-12
+ * 
+ * @copyright Copyright (c) 2022
+ * 
+ */
+#include <arch/x86/interrupts.h>
+#include <hal/apic.h>
+#include <hal/rtc.h>
+
+#include <lunaix/mm/kalloc.h>
+#include <lunaix/spike.h>
+#include <lunaix/syslog.h>
+#include <lunaix/time.h>
+#include <lunaix/timer.h>
+
+#define LVT_ENTRY_TIMER(vector, mode) (LVT_DELIVERY_FIXED | mode | vector)
+
+
+LOG_MODULE("TIMER");
+
+static void
+temp_intr_routine_rtc_tick(const isr_param* param);
+
+static void
+temp_intr_routine_apic_timer(const isr_param* param);
+
+static void
+timer_update(const isr_param* param);
+
+static volatile struct lx_timer_context* timer_ctx;
+
+// Don't optimize them! Took me an half hour to figure that out...
+
+static volatile uint32_t rtc_counter = 0;
+static volatile uint8_t apic_timer_done = 0;
+
+#define APIC_CALIBRATION_CONST 0x100000
+
+void
+timer_init_context()
+{
+    timer_ctx =
+      (struct lx_timer_context*)lxmalloc(sizeof(struct lx_timer_context));
+
+    assert_msg(timer_ctx, "Fail to initialize timer contex");
+
+    timer_ctx->active_timers =
+      (struct lx_timer*)lxmalloc(sizeof(struct lx_timer));
+    llist_init_head(timer_ctx->active_timers);
+}
+
+void
+timer_init(uint32_t frequency)
+{
+    timer_init_context();
+
+    cpu_disable_interrupt();
+
+    // Setup APIC timer
+
+    // Setup a one-shot timer, we will use this to measure the bus speed. So we
+    // can
+    //   then calibrate apic timer to work at *nearly* accurate hz
+    apic_write_reg(APIC_TIMER_LVT,
+                   LVT_ENTRY_TIMER(APIC_TIMER_IV, LVT_TIMER_ONESHOT));
+
+    // Set divider to 64
+    apic_write_reg(APIC_TIMER_DCR, APIC_TIMER_DIV64);
+
+    /*
+        Timer calibration process - measure the APIC timer base frequency
+
+         step 1: setup a temporary isr for RTC timer which trigger at each tick
+       (1024Hz) step 2: setup a temporary isr for #APIC_TIMER_IV step 3: setup
+       the divider, APIC_TIMER_DCR step 4: Startup RTC timer step 5: Write a
+       large value, v, to APIC_TIMER_ICR to start APIC timer (this must be
+       followed immediately after step 4) step 6: issue a write to EOI and clean
+       up.
+
+        When the APIC ICR counting down to 0 #APIC_TIMER_IV triggered, save the
+       rtc timer's counter, k, and disable RTC timer immediately (although the
+       RTC interrupts should be blocked by local APIC as we are currently busy
+       on handling #APIC_TIMER_IV)
+
+        So the apic timer frequency F_apic in Hz can be calculate as
+            v / F_apic = k / 1024
+            =>  F_apic = v / k * 1024
+
+    */
+
+    timer_ctx->base_frequency = 0;
+    rtc_counter = 0;
+    apic_timer_done = 0;
+
+    intr_subscribe(APIC_TIMER_IV, temp_intr_routine_apic_timer);
+    intr_subscribe(RTC_TIMER_IV, temp_intr_routine_rtc_tick);
+
+    rtc_enable_timer();                                     // start RTC timer
+    apic_write_reg(APIC_TIMER_ICR, APIC_CALIBRATION_CONST); // start APIC timer
+
+    // enable interrupt, just for our RTC start ticking!
+    cpu_enable_interrupt();
+
+    wait_until(apic_timer_done);
+
+    // cpu_disable_interrupt();
+
+    assert_msg(timer_ctx->base_frequency, "Fail to initialize timer (NOFREQ)");
+
+    kprintf(KINFO "Base frequency: %u Hz\n", timer_ctx->base_frequency);
+
+    timer_ctx->running_frequency = frequency;
+    timer_ctx->tick_interval = timer_ctx->base_frequency / frequency;
+
+    // cleanup
+    intr_unsubscribe(APIC_TIMER_IV, temp_intr_routine_apic_timer);
+    intr_unsubscribe(RTC_TIMER_IV, temp_intr_routine_rtc_tick);
+
+    apic_write_reg(APIC_TIMER_LVT,
+                   LVT_ENTRY_TIMER(APIC_TIMER_IV, LVT_TIMER_PERIODIC));
+    intr_subscribe(APIC_TIMER_IV, timer_update);
+
+    apic_write_reg(APIC_TIMER_ICR, timer_ctx->tick_interval);
+}
+
+int
+timer_run_second(uint32_t second, void (*callback)(void*), void* payload, uint8_t flags)
+{
+    return timer_run(second * timer_ctx->running_frequency, callback, payload, flags);
+}
+
+int
+timer_run(uint32_t ticks, void (*callback)(void*), void* payload, uint8_t flags)
+{
+    struct lx_timer* timer = (struct lx_timer*)lxmalloc(sizeof(struct lx_timer));
+
+    if (!timer) return 0;
+
+    timer->callback = callback;
+    timer->counter = ticks;
+    timer->deadline = ticks;
+    timer->payload = payload;
+    timer->flags = flags;
+
+    llist_append(timer_ctx->active_timers, timer);
+
+    return 1;
+}
+
+static void
+timer_update(const isr_param* param)
+{
+    struct lx_timer *pos, *n;
+    struct lx_timer* timer_list_head = timer_ctx->active_timers;
+
+    llist_for_each(pos, n, &timer_list_head->link, link)
+    {
+        if (--pos->counter) {
+            continue;
+        }
+
+        pos->callback ? pos->callback(pos->payload) : 1;
+
+        if (pos->flags & TIMER_MODE_PERIODIC) {
+            pos->counter = pos->deadline;
+        } else {
+            llist_delete(pos);
+            lxfree(pos);
+        }
+    }
+}
+
+static void
+temp_intr_routine_rtc_tick(const isr_param* param)
+{
+    rtc_counter++;
+
+    // dummy read on register C so RTC can send anther interrupt
+    //  This strange behaviour observed in virtual box & bochs
+    (void)rtc_read_reg(RTC_REG_C);
+}
+
+static void
+temp_intr_routine_apic_timer(const isr_param* param)
+{
+    timer_ctx->base_frequency =
+      APIC_CALIBRATION_CONST / rtc_counter * RTC_TIMER_BASE_FREQUENCY;
+    apic_timer_done = 1;
+
+    rtc_disable_timer();
+}
\ No newline at end of file
index e9aa42aba3fe09c713e2508baf2e7e0a07844726..cc70087992a14ed4c9167ec3d29de89a15f667d7 100644 (file)
@@ -1,17 +1,17 @@
 #include <klibc/string.h>
 #include <lunaix/tty/tty.h>
-#include <lunaix/constants.h>
+#include <lunaix/common.h>
 #include <stdint.h>
 
 #define TTY_WIDTH 80
 #define TTY_HEIGHT 25
 
-vga_attribute* tty_vga_buffer = (vga_attribute*)VGA_BUFFER_PADDR;
+static vga_attribute* tty_vga_buffer = (vga_attribute*)VGA_BUFFER_PADDR;
 
-vga_attribute tty_theme_color = VGA_COLOR_BLACK;
+static vga_attribute tty_theme_color = VGA_COLOR_BLACK;
 
-uint32_t tty_x = 0;
-uint16_t tty_y = 0;
+static uint32_t tty_x = 0;
+static uint16_t tty_y = 0;
 
 void tty_init(void* vga_buf) {
     tty_vga_buffer = (vga_attribute*)vga_buf;