feat: a simple console that support scrolling.
[lunaix-os.git] / lunaix-os / kernel / lxconsole.c
1 #include <klibc/string.h>
2 #include <lunaix/lxconsole.h>
3 #include <lunaix/mm/pmm.h>
4 #include <lunaix/mm/vmm.h>
5 #include <lunaix/tty/console.h>
6 #include <lunaix/tty/tty.h>
7
8 static struct console lx_console;
9
10 volatile int can_flush = 0;
11
12 void
13 lxconsole_init()
14 {
15     memset(&lx_console, 0, sizeof(lx_console));
16     lx_console.buffer.data = VGA_BUFFER_VADDR + 0x1000;
17     lx_console.buffer.size = 8192;
18     mutex_init(&lx_console.buffer.lock);
19
20     // 分配控制台缓存
21     for (size_t i = 0; i < PG_ALIGN(lx_console.buffer.size); i += PG_SIZE) {
22         uintptr_t pa = pmm_alloc_page(KERNEL_PID, 0);
23         vmm_set_mapping(PD_REFERENCED,
24                         (uintptr_t)lx_console.buffer.data + i,
25                         pa,
26                         PG_PREM_URW,
27                         0);
28     }
29
30     memset(lx_console.buffer.data, 0, lx_console.buffer.size);
31
32     lx_console.flush_timer = NULL;
33 }
34
35 void
36 console_schedule_flush()
37 {
38     // TODO make the flush on-demand rather than periodic
39 }
40
41 void
42 console_view_up()
43 {
44     struct fifo_buffer* buffer = &lx_console.buffer;
45     mutex_lock(&buffer->lock);
46     size_t p = lx_console.erd_pos - 2;
47     while (p < lx_console.erd_pos && p != buffer->wr_pos &&
48            ((char*)buffer->data)[p] != '\n') {
49         p--;
50     }
51     p++;
52
53     if (p > lx_console.erd_pos) {
54         p = 0;
55     }
56
57     buffer->flags |= FIFO_DIRTY;
58     lx_console.erd_pos = p;
59     mutex_unlock(&buffer->lock);
60 }
61
62 size_t
63 __find_next_line(size_t start)
64 {
65     size_t p = start;
66     while (p != lx_console.buffer.wr_pos &&
67            ((char*)lx_console.buffer.data)[p] != '\n') {
68         p = (p + 1) % lx_console.buffer.size;
69     }
70     return p + 1;
71 }
72
73 void
74 console_view_down()
75 {
76     struct fifo_buffer* buffer = &lx_console.buffer;
77     mutex_lock(&buffer->lock);
78
79     lx_console.erd_pos = __find_next_line(lx_console.erd_pos);
80     buffer->flags |= FIFO_DIRTY;
81     mutex_unlock(&buffer->lock);
82 }
83
84 void
85 __flush_cb(void* arg)
86 {
87     if (mutex_on_hold(&lx_console.buffer.lock)) {
88         return;
89     }
90     if (!(lx_console.buffer.flags & FIFO_DIRTY)) {
91         return;
92     }
93
94     tty_flush_buffer(lx_console.buffer.data,
95                      lx_console.erd_pos,
96                      lx_console.buffer.wr_pos,
97                      lx_console.buffer.size);
98     lx_console.buffer.flags &= ~FIFO_DIRTY;
99 }
100
101 void
102 console_write(struct console* console, uint8_t* data, size_t size)
103 {
104     mutex_lock(&console->buffer.lock);
105     uint8_t* buffer = console->buffer.data;
106     uintptr_t ptr = console->buffer.wr_pos;
107     uintptr_t rd_ptr = console->buffer.rd_pos;
108
109     char c;
110     int lines = 0;
111     for (size_t i = 0; i < size; i++) {
112         c = data[i];
113         buffer[(ptr + i) % console->buffer.size] = c;
114         // chars += (31 < c && c < 127);
115         lines += (c == '\n');
116     }
117
118     uintptr_t new_ptr = (ptr + size) % console->buffer.size;
119     console->buffer.wr_pos = new_ptr;
120
121     if (console->lines > TTY_HEIGHT && lines > 0) {
122         console->buffer.rd_pos =
123           __find_next_line((size + rd_ptr) % console->buffer.size);
124     }
125
126     if (new_ptr < ptr + size && new_ptr > rd_ptr) {
127         console->buffer.rd_pos = new_ptr;
128     }
129
130     console->lines += lines;
131     console->erd_pos = console->buffer.rd_pos;
132     console->buffer.flags |= FIFO_DIRTY;
133     mutex_unlock(&console->buffer.lock);
134 }
135
136 void
137 console_write_str(char* str)
138 {
139     console_write(&lx_console, str, strlen(str));
140 }
141
142 void
143 console_write_char(char str)
144 {
145     console_write(&lx_console, &str, 1);
146 }
147
148 void
149 console_start_flushing()
150 {
151     struct lx_timer* timer =
152       timer_run_ms(20, __flush_cb, NULL, TIMER_MODE_PERIODIC);
153     lx_console.flush_timer = timer;
154 }