refactor: clean up the virtual memory mappings
[lunaix-os.git] / lunaix-os / kernel / tty / lxconsole.c
1 #include <klibc/string.h>
2 #include <lunaix/device.h>
3 #include <lunaix/input.h>
4 #include <lunaix/keyboard.h>
5 #include <lunaix/lxconsole.h>
6 #include <lunaix/mm/pmm.h>
7 #include <lunaix/mm/valloc.h>
8 #include <lunaix/mm/vmm.h>
9 #include <lunaix/sched.h>
10 #include <lunaix/tty/console.h>
11 #include <lunaix/tty/tty.h>
12
13 static struct console lx_console;
14
15 int
16 __tty_write(struct device* dev, void* buf, size_t offset, size_t len);
17
18 int
19 __tty_read(struct device* dev, void* buf, size_t offset, size_t len);
20
21 static waitq_t lx_reader;
22 static volatile char key;
23
24 int
25 __lxconsole_listener(struct input_device* dev)
26 {
27     uint32_t keycode = dev->current_pkt.sys_code;
28     uint32_t type = dev->current_pkt.pkt_type;
29     if (type == PKT_PRESS) {
30         if (keycode == KEY_PG_UP) {
31             console_view_up();
32         } else if (keycode == KEY_PG_DOWN) {
33             console_view_down();
34         }
35         goto done;
36     }
37     if ((keycode & 0xff00) > KEYPAD) {
38         goto done;
39     }
40
41     key = (char)(keycode & 0x00ff);
42
43     pwake_all(&lx_reader);
44
45 done:
46     return INPUT_EVT_NEXT;
47 }
48
49 void
50 lxconsole_init()
51 {
52     memset(&lx_console, 0, sizeof(lx_console));
53     fifo_init(&lx_console.output, valloc(8192), 8192, 0);
54     fifo_init(&lx_console.input, valloc(4096), 4096, 0);
55
56     lx_console.flush_timer = NULL;
57
58     struct device* tty_dev = device_addseq(NULL, &lx_console, "tty");
59     tty_dev->write = __tty_write;
60     tty_dev->read = __tty_read;
61
62     waitq_init(&lx_reader);
63     input_add_listener(__lxconsole_listener);
64 }
65
66 int
67 __tty_write(struct device* dev, void* buf, size_t offset, size_t len)
68 {
69     struct console* console = (struct console*)dev->underlay;
70     console_write(console, buf, len);
71 }
72
73 int
74 __tty_read(struct device* dev, void* buf, size_t offset, size_t len)
75 {
76     struct console* console = (struct console*)dev->underlay;
77
78     size_t count = fifo_read(&console->input, buf, len);
79     if (count > 0 && ((char*)buf)[count - 1] == '\n') {
80         return count;
81     }
82
83     while (count < len) {
84         pwait(&lx_reader);
85
86         if (key == 0x08) {
87             if (fifo_backone(&console->input)) {
88                 console_write_char(key);
89             }
90             continue;
91         }
92         console_write_char(key);
93         if (!fifo_putone(&console->input, key) || key == '\n') {
94             break;
95         }
96     }
97     return count + fifo_read(&console->input, buf + count, len - count);
98 }
99
100 void
101 console_schedule_flush()
102 {
103     // TODO make the flush on-demand rather than periodic
104 }
105
106 size_t
107 __find_next_line(size_t start)
108 {
109     size_t p = start - 1;
110     struct fifo_buf* buffer = &lx_console.output;
111     do {
112         p = (p + 1) % buffer->size;
113     } while (p != buffer->wr_pos && ((char*)buffer->data)[p] != '\n');
114     return p + (((char*)buffer->data)[p] == '\n');
115 }
116
117 size_t
118 __find_prev_line(size_t start)
119 {
120     size_t p = start - 1;
121     struct fifo_buf* buffer = &lx_console.output;
122     do {
123         p--;
124     } while (p < lx_console.wnd_start && p != buffer->wr_pos &&
125              ((char*)buffer->data)[p] != '\n');
126
127     if (p > lx_console.wnd_start) {
128         return 0;
129     }
130     return p + 1;
131 }
132
133 void
134 console_view_up()
135 {
136     struct fifo_buf* buffer = &lx_console.output;
137     mutex_lock(&buffer->lock);
138     fifo_set_rdptr(buffer, __find_prev_line(buffer->rd_pos));
139     buffer->flags |= FIFO_DIRTY;
140     mutex_unlock(&buffer->lock);
141
142     console_flush();
143 }
144
145 void
146 console_view_down()
147 {
148     struct fifo_buf* buffer = &lx_console.output;
149     mutex_lock(&buffer->lock);
150
151     size_t wnd = lx_console.wnd_start;
152     size_t p = __find_next_line(buffer->rd_pos);
153     fifo_set_rdptr(buffer, p > wnd ? wnd : p);
154     buffer->flags |= FIFO_DIRTY;
155     mutex_unlock(&buffer->lock);
156
157     console_flush();
158 }
159
160 void
161 console_flush()
162 {
163     if (mutex_on_hold(&lx_console.output.lock)) {
164         return;
165     }
166     if (!(lx_console.output.flags & FIFO_DIRTY)) {
167         return;
168     }
169
170     size_t rdpos_save = lx_console.output.rd_pos;
171     tty_flush_buffer(&lx_console.output);
172     fifo_set_rdptr(&lx_console.output, rdpos_save);
173
174     lx_console.output.flags &= ~FIFO_DIRTY;
175 }
176
177 void
178 console_write(struct console* console, uint8_t* data, size_t size)
179 {
180     struct fifo_buf* fbuf = &console->output;
181     mutex_lock(&console->output.lock);
182     fifo_set_rdptr(fbuf, console->wnd_start);
183
184     uint8_t* buffer = fbuf->data;
185     uintptr_t ptr = fbuf->wr_pos;
186     uintptr_t rd_ptr = fbuf->rd_pos;
187
188     char c;
189     for (size_t i = 0; i < size; i++) {
190         c = data[i];
191         if (!c) {
192             continue;
193         }
194         if (c == '\n') {
195             console->lines++;
196         } else if (c == '\x08') {
197             ptr = ptr ? ptr - 1 : fbuf->size - 1;
198             continue;
199         }
200         buffer[ptr] = c;
201         ptr = (ptr + 1) % fbuf->size;
202     }
203
204     fifo_set_wrptr(fbuf, ptr);
205
206     while (console->lines >= TTY_HEIGHT) {
207         rd_ptr = __find_next_line(rd_ptr);
208         console->lines--;
209     }
210
211     fifo_set_rdptr(&lx_console.output, rd_ptr);
212     console->wnd_start = rd_ptr;
213     fbuf->flags |= FIFO_DIRTY;
214     mutex_unlock(&fbuf->lock);
215 }
216
217 void
218 console_write_str(char* str)
219 {
220     console_write(&lx_console, str, strlen(str));
221 }
222
223 void
224 console_write_char(char str)
225 {
226     console_write(&lx_console, &str, 1);
227 }
228
229 void
230 console_start_flushing()
231 {
232     struct lx_timer* timer =
233       timer_run_ms(20, console_flush, NULL, TIMER_MODE_PERIODIC);
234     lx_console.flush_timer = timer;
235 }