Merge branch 'master' into isa/arm64
[lunaix-os.git] / lunaix-os / hal / char / lxconsole.c
1 /**
2  * @file lxconsole.c
3  * @author Lunaixsky (lunaxisky@qq.com)
4  * @brief Provides simple terminal support
5  * @version 0.1
6  * @date 2023-06-18
7  *
8  * @copyright Copyright (c) 2023
9  *
10  */
11
12 #include <klibc/string.h>
13 #include <lunaix/device.h>
14 #include <lunaix/input.h>
15 #include <lunaix/ioctl.h>
16 #include <lunaix/keyboard.h>
17 #include <lunaix/mm/pmm.h>
18 #include <lunaix/mm/valloc.h>
19 #include <lunaix/mm/vmm.h>
20 #include <lunaix/sched.h>
21 #include <lunaix/signal.h>
22 #include <lunaix/tty/tty.h>
23 #include <lunaix/ds/fifo.h>
24 #include <lunaix/owloysius.h>
25
26 #include <hal/term.h>
27
28 struct console
29 {
30     struct potens_meta* tp_cap;
31     struct lx_timer* flush_timer;
32     struct fifo_buf output;
33     struct fifo_buf input;
34     size_t wnd_start;
35     size_t lines;
36 };
37
38 static struct console lx_console;
39
40 int
41 __tty_write(struct device* dev, void* buf, size_t offset, size_t len);
42
43 int
44 __tty_read(struct device* dev, void* buf, size_t offset, size_t len);
45
46 void
47 console_write(struct console* console, u8_t* data, size_t size);
48
49 void
50 console_flush();
51
52 void
53 console_view_up();
54
55 void
56 console_view_down();
57
58 static waitq_t lx_reader;
59
60 static volatile pid_t fg_pgid = 0;
61
62 int
63 __lxconsole_listener(struct input_device* dev)
64 {
65     u32_t key = dev->current_pkt.sys_code;
66     u32_t type = dev->current_pkt.pkt_type;
67     kbd_kstate_t state = key >> 16;
68     u8_t ttychr = key & 0xff;
69     key = key & 0xffff;
70
71     if (type == PKT_RELEASE) {
72         goto done;
73     }
74
75     if ((state & KBD_KEY_FLCTRL_HELD)) {
76         char cntrl = (char)(ttychr | 0x20);
77         if ('a' > cntrl || cntrl > 'z') {
78             goto done;
79         }
80         ttychr = cntrl - 'a' + 1;
81     } else if (key == KEY_PG_UP) {
82         console_view_up();
83         goto done;
84     } else if (key == KEY_PG_DOWN) {
85         console_view_down();
86         goto done;
87     } else if ((key & 0xff00) <= KEYPAD) {
88         ttychr = key;
89     } else {
90         goto done;
91     }
92
93     fifo_putone(&lx_console.input, ttychr);
94
95     struct termport_potens* tpcap;
96     tpcap = get_potens(lx_console.tp_cap, typeof(*tpcap));
97     term_notify_data_avaliable(tpcap);
98     
99     pwake_all(&lx_reader);
100 done:
101     return INPUT_EVT_NEXT;
102 }
103
104 int
105 __tty_write_pg(struct device* dev, void* buf, size_t offset)
106 {
107     return __tty_write(dev, buf, offset, PAGE_SIZE);
108 }
109
110 int
111 __tty_read_pg(struct device* dev, void* buf, size_t offset)
112 {
113     return __tty_read(dev, buf, offset, PAGE_SIZE);
114 }
115
116 int
117 __tty_write(struct device* dev, void* buf, size_t offset, size_t len)
118 {
119     struct console* console = (struct console*)dev->underlay;
120     console_write(console, buf, len);
121
122     return len;
123 }
124
125 int
126 __tty_read(struct device* dev, void* buf, size_t offset, size_t len)
127 {
128     struct console* console = (struct console*)dev->underlay;
129
130     size_t count = fifo_read(&console->input, buf, len);
131     if (count > 0) {
132         return count;
133     }
134
135     pwait(&lx_reader);
136
137     return count + fifo_read(&console->input, buf + count, len - count);
138 }
139
140 int
141 __tty_read_async(struct device* dev, void* buf, size_t offset, size_t len)
142 {
143     struct console* console = (struct console*)dev->underlay;
144
145     return fifo_read(&console->input, buf, len);
146 }
147
148 size_t
149 __find_next_line(size_t start)
150 {
151     size_t p = start - 1;
152     struct fifo_buf* buffer = &lx_console.output;
153     do {
154         p = (p + 1) % buffer->size;
155     } while (p != buffer->wr_pos && ((char*)buffer->data)[p] != '\n');
156     return p + (((char*)buffer->data)[p] == '\n');
157 }
158
159 size_t
160 __find_prev_line(size_t start)
161 {
162     size_t p = start - 1;
163     struct fifo_buf* buffer = &lx_console.output;
164     do {
165         p--;
166     } while (p < lx_console.wnd_start && p != buffer->wr_pos &&
167              ((char*)buffer->data)[p] != '\n');
168
169     if (p > lx_console.wnd_start) {
170         return 0;
171     }
172     return p + 1;
173 }
174
175 void
176 console_view_up()
177 {
178     struct fifo_buf* buffer = &lx_console.output;
179     mutex_lock(&buffer->lock);
180     fifo_set_rdptr(buffer, __find_prev_line(buffer->rd_pos));
181     buffer->flags |= FIFO_DIRTY;
182     mutex_unlock(&buffer->lock);
183
184     console_flush();
185 }
186
187 void
188 console_view_down()
189 {
190     struct fifo_buf* buffer = &lx_console.output;
191     mutex_lock(&buffer->lock);
192
193     size_t wnd = lx_console.wnd_start;
194     size_t p = __find_next_line(buffer->rd_pos);
195     fifo_set_rdptr(buffer, p > wnd ? wnd : p);
196     buffer->flags |= FIFO_DIRTY;
197     mutex_unlock(&buffer->lock);
198
199     console_flush();
200 }
201
202 void
203 console_flush()
204 {
205     if (mutex_on_hold(&lx_console.output.lock)) {
206         return;
207     }
208     if (!(lx_console.output.flags & FIFO_DIRTY)) {
209         return;
210     }
211
212     size_t rdpos_save = lx_console.output.rd_pos;
213     tty_flush_buffer(&lx_console.output);
214     fifo_set_rdptr(&lx_console.output, rdpos_save);
215
216     lx_console.output.flags &= ~FIFO_DIRTY;
217 }
218
219 void
220 console_write(struct console* console, u8_t* data, size_t size)
221 {
222     struct fifo_buf* fbuf = &console->output;
223     mutex_lock(&console->output.lock);
224     fifo_set_rdptr(fbuf, console->wnd_start);
225
226     u8_t* buffer = fbuf->data;
227     ptr_t ptr = fbuf->wr_pos;
228     ptr_t rd_ptr = fbuf->rd_pos;
229
230     char c;
231     for (size_t i = 0; i < size; i++) {
232         c = data[i];
233         if (!c) {
234             continue;
235         }
236         if (c == '\n') {
237             console->lines++;
238         } else if (c == '\x08') {
239             ptr = ptr ? ptr - 1 : fbuf->size - 1;
240             continue;
241         }
242         buffer[ptr] = c;
243         ptr = (ptr + 1) % fbuf->size;
244     }
245
246     fifo_set_wrptr(fbuf, ptr);
247
248     while (console->lines >= TTY_HEIGHT) {
249         rd_ptr = __find_next_line(rd_ptr);
250         console->lines--;
251     }
252
253     fifo_set_rdptr(&lx_console.output, rd_ptr);
254     console->wnd_start = rd_ptr;
255     fbuf->flags |= FIFO_DIRTY;
256     mutex_unlock(&fbuf->lock);
257
258     if (!lx_console.flush_timer) {
259         console_flush();
260     }
261 }
262
263 void
264 console_write_str(char* str)
265 {
266     console_write(&lx_console, (u8_t*)str, strlen(str));
267 }
268
269 void
270 console_write_char(char str)
271 {
272     console_write(&lx_console, (u8_t*)&str, 1);
273 }
274
275
276 static void
277 lxconsole_init()
278 {
279     memset(&lx_console, 0, sizeof(lx_console));
280     fifo_init(&lx_console.output, valloc(8192), 8192, 0);
281     fifo_init(&lx_console.input, valloc(4096), 4096, 0);
282
283     lx_console.flush_timer = NULL;
284 }
285 owloysius_fetch_init(lxconsole_init, on_earlyboot)
286
287 static int
288 lxconsole_spawn_ttydev(struct device_def* devdef)
289 {
290     struct device* tty_dev = device_allocseq(NULL, &lx_console);
291     tty_dev->ops.write = __tty_write;
292     tty_dev->ops.write_page = __tty_write_pg;
293     tty_dev->ops.read = __tty_read;
294     tty_dev->ops.read_page = __tty_read_pg;
295     tty_dev->ops.write_async = __tty_write;
296     tty_dev->ops.read_async = __tty_read_async;
297
298     waitq_init(&lx_reader);
299     input_add_listener(__lxconsole_listener);
300
301     register_device(tty_dev, &devdef->class, "vcon");
302
303     term_attach_potens(tty_dev, NULL, "VCON");
304
305     return 0;
306 }
307
308 static struct device_def lxconsole_def = {
309     def_device_name("Lunaix Virtual Console"),
310     def_device_class(LUNAIX, TTY, VTERM),
311     def_on_load(lxconsole_spawn_ttydev)
312 };
313 EXPORT_DEVICE(lxconsole, &lxconsole_def, load_onboot);