Voxia OS v0.0.1
Hobby Project Operating System Targeting x86-64
Loading...
Searching...
No Matches
tty.c
Go to the documentation of this file.
1#include "tty.h"
2#include "hal/cpu/core.h"
4#include "init/init.h"
5#include "input.h"
6#include "libk/serial.h"
7#include "notify.h"
8#include "procc/scheduler.h"
9#include "procc/workqueue.h"
10#include "str.h"
11#include "string.h"
12#include "sys/err_no.h"
13#include "type.h"
14#include "vfs/dentry.h"
15#include "vfs/dev.h"
16#include "vfs/vfs.h"
17#include "vfs/vnode.h"
18#include <autoconf.h>
19#include <console/console.h>
20
21#define TIOCGWINSZ 0x5413
22#define FONT_SIZE 14
23
24// forward declarations
25static void configure_tty(int tty);
26static int char_ioctl(vnode_t* vnode, uint32_t req, void* arg);
27static long char_write(vnode_t* vnode, void* buf, size_t len, size_t offset);
28static void do_scroll(struct tty_internal* priv);
29static int char_read(vnode_t* vnode, void* buf, size_t len, size_t offset);
30
31static dentry_ptr __tty_dentry[VOXIA_TTY_MAX_COUNT] = {0};
32static int __current_tty_active = 0;
34
35// TODO: will be moved
42
43static void do_scroll(struct tty_internal* priv) {
45 priv->cursory = priv->rows - 1;
46 priv->cursorx = 0;
47}
48
49static void tty_input_handler(uint32_t event, void* data, void* ctx) {
50 (void)event;
51 (void)ctx;
52
53 if (!data)
54 return;
55
56 auto input = (struct input_event_data*)data;
57
58 if (data) {
59 // copy into input_buffer
61 if (!dentry)
62 return;
63
64 auto vnode = dentry->vnode;
65 if (!vnode)
66 return;
67
68 auto priv = (struct tty_internal*)vnode->vnode_private;
69 if (!priv)
70 return;
71
72 memcopy(priv->input_buffer + priv->line_buff_tail, &input->code,
73 sizeof(uint16_t));
74
75 priv->line_buff_tail =
76 (priv->line_buff_tail + sizeof(uint16_t)) & (1024 - 1);
77 serial2_printf("%x\n", input->code);
78 }
79}
80
81INIT(TTY) {
83 __tty_ops->ioctl = char_ioctl;
84 __tty_ops->write = char_write;
85 __tty_ops->read = char_read;
86
87 for (int i = 0; i < VOXIA_TTY_MAX_COUNT; i++) {
89 }
90
91 // notify
92 {
93 auto n = (struct notifier*)kalloc(sizeof(struct notifier));
94 memset(n, 0, sizeof(struct notifier));
95 n->callback = tty_input_handler;
96 n->context = 0;
97 n->priority = NOTIFY_HIGHT;
98 n->flags = 0;
99
100 notify_register("/input/trigered", n);
101 }
102}
103
104static int char_ioctl(vnode_t* vnode, uint32_t req, void* arg) {
105 switch (req) {
106 case TIOCGWINSZ: {
107 serial2_printf("ioctl: win size request\n");
108 struct win_size* ws = (struct win_size*)arg;
109 if (!ws)
110 return -EBADF;
111
112 auto priv = (struct tty_internal*)vnode->vnode_private;
113 ws->ws_row = priv ? (uint16_t)priv->rows : 24;
114 ws->ws_col = priv ? (uint16_t)priv->cols : 80;
117 return 0;
118 }
119 }
120
121 return -ENOTTY;
122}
123
124static int char_read(vnode_t *vnode, void *buf, size_t len, size_t offset) {
125 (void)offset;
126
127 /* FIX #1: hapus (void)vnode dan (void)buf — keduanya dipakai */
128 if (!vnode || !buf)
129 return -EINVAL;
130
131 struct tty_internal *priv = (struct tty_internal *)vnode->vnode_private;
132 if (!priv)
133 return -ENOSYS;
134
135 uint8_t *out = (uint8_t *)buf;
136 size_t bytes_read = 0;
137
138 while (bytes_read < len) {
139 /* FIX #2: busy-wait dengan cpu_relax() agar tidak membakar CPU */
140 spin_acquire(&priv->input_lock);
141 int empty = (priv->head == priv->tail);
142 spin_release(&priv->input_lock);
143
144 if (empty) {
145 if (bytes_read > 0)
146 break; /* sudah dapat sebagian data, return */
147
148 {
149 serial2_printf("sleep...\n");
150 auto thr = get_current_core_data()->active_thread;
151 thr->in_kernel_sleep = true;
152 // schedule_yield();
153
154 }
155 continue;
156 }
157 serial2_printf("has data\n");
158
159 /* FIX #3 & #4: baca dari ring buffer dengan lock */
160 spin_acquire(&priv->input_lock);
161
162 while (bytes_read < len && priv->head != priv->tail) {
163 out[bytes_read++] = (uint8_t)priv->input_buffer[priv->head];
164 priv->head = (priv->head + 1) & (1024 - 1);
165 }
166
167 spin_release(&priv->input_lock);
168 }
169
170 return (int)bytes_read;
171}
172
173static long char_write(vnode_t* vnode, void* buf, size_t len, size_t offset) {
174 UNUSED(offset);
175
176 auto priv = (struct tty_internal*)vnode->vnode_private;
177 if (!priv)
178 return -ENOENT;
179
180 if (!priv->enable)
181 return -ENOENT;
182
183 if (!len)
184 return 0;
185
186 auto used =
187 (priv->tail - priv->head) & (VOXIA_TTY_INPUT_BUFFER_SIZE - 1);
188 size_t available = VOXIA_TTY_INPUT_BUFFER_SIZE - 1 - used;
189 if (len > available)
190 return -ENOSPC;
191
192 auto tail = priv->tail;
193 size_t first_chunk = VOXIA_TTY_INPUT_BUFFER_SIZE - tail;
194
195 if (len <= first_chunk) {
196 memcopy((uint8_t*)priv->input_buffer + tail, buf, len);
197 } else {
198 memcopy((uint8_t*)priv->input_buffer + tail, buf, first_chunk);
199 memcopy((uint8_t*)priv->input_buffer,
200 (uint8_t*)buf + first_chunk, len - first_chunk);
201 }
202
203 priv->tail = (tail + len) & (VOXIA_TTY_INPUT_BUFFER_SIZE - 1);
204
205 priv->dirty = true;
206 // if (memchr(buf, '\n', len)) {
207 // vxAddWorkqueueTask((void (*)(void*))tty_check_and_flush, NULL, NULL);
208 // }
210
211 return (long)len;
212}
213
214static void configure_tty(int tty) {
215 auto curr_dentry = &__tty_dentry[tty];
216 auto path_name = str_concat(str("/dev/tty"), itoa(tty, 10));
217 vxnamei(path_name->c_str, curr_dentry);
218 str_release(path_name);
219
222
223 (*curr_dentry)->vnode = vnode;
224 (*curr_dentry)->vnode->ops = __tty_ops;
225 (*curr_dentry)->vnode->permission = 660;
226
228 (*curr_dentry)->vnode->device.major = dev->major;
229 (*curr_dentry)->vnode->device.minor = dev->minor;
230 (*curr_dentry)->vnode->mountedhere = dev;
231
232 auto priv = (struct tty_internal*)kalloc(sizeof(struct tty_internal));
233 memset(priv, 0, sizeof(struct tty_internal));
234 (*curr_dentry)->vnode->vnode_private = priv;
235
236 priv->enable = true;
237 priv->dirty = false;
238 priv->cols = screen_cols();
239 priv->rows = screen_rows();
240 priv->cursorx = 0;
241 priv->cursory = 0;
242 priv->line_buff_tail = 0;
243 priv->line_buff_head = 0;
244}
245
246void start_tty() {
247 console_set_pos(0, 0);
248 clear_screen(0x000000);
249}
250
252
254
258
261 if (!dentry)
262 return;
263
264 auto priv = (struct tty_internal*)dentry->vnode->vnode_private;
265 if (!priv || !priv->dirty)
266 return;
267
268 LOG2_INFO("TTY", "flushed into screen on tty %d", __current_tty_active);
269 priv->dirty = false;
270
271 while (priv->head != priv->tail) {
272 uint8_t c = (uint8_t)priv->input_buffer[priv->head];
273 int clen = utf8_char_len(c);
274
275 if (clen > 1) {
276 char seq[5] = {0};
277 uint32_t temp_head = priv->head;
278 boolean_t complete = true;
279 for (int i = 0; i < clen; i++) {
280 seq[i] = priv->input_buffer[temp_head];
281 temp_head = (temp_head + 1) &
282 (VOXIA_TTY_INPUT_BUFFER_SIZE - 1);
283 if (temp_head == priv->tail && i < clen - 1) {
284 complete = false;
285 break;
286 }
287 }
288
289 if (complete) {
290 putc_utf8(seq, (int)priv->cursorx,
291 (int)priv->cursory, 0xFFFFFF,
292 0x000000);
293 uint32_t width = (clen >= 3) ? 2 : 1;
294 priv->cursorx += width;
295 if (priv->cursorx >= priv->cols) {
296 priv->cursorx = 0;
297 priv->cursory++;
298 if (priv->cursory >= priv->rows)
299 do_scroll(priv);
300 }
301 priv->head = temp_head;
302 continue;
303 }
304 }
305
306 priv->head =
307 (priv->head + 1) & (VOXIA_TTY_INPUT_BUFFER_SIZE - 1);
308
309 if (c == '\n') {
310 priv->cursorx = 0;
311 priv->cursory++;
312 if (priv->cursory >= priv->rows)
313 do_scroll(priv);
314
315 } else if (c == '\r') {
316 priv->cursorx = 0;
317
318 } else if (c == '\b') {
319 if (priv->cursorx > 0) {
320 priv->cursorx--;
321 putc(' ', (int)priv->cursorx,
322 (int)priv->cursory, 0xFFFFFF, 0x000000);
323 }
324 } else {
325 putc((char)c, (int)priv->cursorx, (int)priv->cursory,
326 0xFFFFFF, 0x000000);
327 priv->cursorx++;
328
329 if (priv->cursorx >= priv->cols) {
330 priv->cursorx = 0;
331 priv->cursory++;
332 if (priv->cursory >= priv->rows)
333 do_scroll(priv);
334 }
335 }
336 }
337}
338
339dentry_ptr get_tty_dentry(int tty) { return __tty_dentry[tty]; }
static void do_scroll(void)
Definition console.c:37
void console_set_pos(int x, int y)
Definition console.c:371
each_core_data * get_current_core_data(void)
Definition core.c:54
struct dentry * dentry_ptr
Definition dentry.h:20
struct vnode * vnode_ptr_t
Definition dentry.h:115
int vxnamei(const char *path, dentry_ptr *out)
Resolves a file path to a directory entry (dentry).
cdev_ptr_t create_dev(void *ops, uint32_t major)
#define DEV_MAJOR_TTY
Definition dev.h:32
boolean_t used
Definition ehci.hpp:6
#define ENOENT
Definition err_no.h:5
#define ENOSPC
Definition err_no.h:27
#define ENOSYS
Definition err_no.h:34
#define ENOTTY
Definition err_no.h:26
#define EBADF
Definition err_no.h:12
struct vnode vnode_t
Definition filesystem.h:10
struct fs_data data
Definition filesystem.h:1
uint32_t screen_cols(void)
Definition graphic.c:327
int utf8_char_len(uint8_t c)
Definition graphic.c:180
uint32_t screen_rows(void)
Definition graphic.c:333
void vxScroll(int px)
Definition graphic.c:318
uint32_t vxGetWidth(void)
Definition graphic.c:315
uint32_t vxGetHeight(void)
Definition graphic.c:316
void putc(char c, int col, int row, uint32_t fg, uint32_t bg)
Definition graphic.c:151
void clear_screen(uint32_t color)
Definition graphic.c:290
void putc_utf8(const char *s, int col, int row, uint32_t fg, uint32_t bg)
Definition graphic.c:166
#define FONT_SIZE
Definition graphic.h:26
#define INIT(fn)
Definition init.h:26
void serial2_printf(const char *fmt,...)
#define EINVAL
void * kalloc(size_t size)
#define NOTIFY_HIGHT
Definition notify.h:22
int notify_register(char *name, struct notifier *n)
Definition notify.c:78
size_t len
Definition oct2bin.h:7
#define LOG2_INFO(mod, fmt,...)
Definition serial.h:33
void spin_acquire(spinlock_t *lock)
Definition spinlock.c:8
void spin_release(spinlock_t *lock)
Definition spinlock.c:19
char * itoa(int64_t value, int base)
Definition str.c:302
void memset(void *ptr, int value, size_t num)
void memcopy(void *dest, void *src, size_t size)
void str_release(kstring str)
kstring str_concat(kstring s, const char *suffix)
kstring str(const char *str)
struct vnode * vnode
Definition dentry.h:33
char input_buffer[VOXIA_TTY_INPUT_BUFFER_SIZE]
Definition tty.h:19
uint32_t rows
Definition tty.h:14
uint32_t cursorx
Definition tty.h:16
uint32_t tail
Definition tty.h:21
uint32_t head
Definition tty.h:20
uint32_t cursory
Definition tty.h:17
spinlock_t input_lock
Definition tty.h:27
Definition vnode.h:59
void * ops
Definition vnode.h:64
void * vnode_private
Definition vnode.h:72
uint8_t type
Definition vnode.h:62
Definition tty.c:36
uint16_t ws_ypixel
Definition tty.c:40
uint16_t ws_xpixel
Definition tty.c:39
uint16_t ws_row
Definition tty.c:37
uint16_t ws_col
Definition tty.c:38
void start_tty()
Definition tty.c:246
void tty_check_and_flush()
Definition tty.c:259
#define TIOCGWINSZ
Definition tty.c:21
dentry_ptr get_active_tty_dentry()
Definition tty.c:255
static int __current_tty_active
Definition tty.c:32
static void configure_tty(int tty)
Definition tty.c:214
dentry_ptr get_tty_dentry(int tty)
Definition tty.c:339
static vops_file_t * __tty_ops
Definition tty.c:33
static long char_write(vnode_t *vnode, void *buf, size_t len, size_t offset)
Definition tty.c:173
static int char_ioctl(vnode_t *vnode, uint32_t req, void *arg)
Definition tty.c:104
void change_active_tty(int tty)
Definition tty.c:251
static dentry_ptr __tty_dentry[VOXIA_TTY_MAX_COUNT]
Definition tty.c:31
int get_active_tty()
Definition tty.c:253
static void tty_input_handler(uint32_t event, void *data, void *ctx)
Definition tty.c:49
static int char_read(vnode_t *vnode, void *buf, size_t len, size_t offset)
Definition tty.c:124
uint32_t tail
Definition tty.h:11
uint32_t head
Definition tty.h:10
unsigned short uint16_t
Definition type.h:13
unsigned int uint32_t
Definition type.h:19
uint8_t boolean_t
Definition type.h:89
#define UNUSED(x)
Definition type.h:100
unsigned char uint8_t
Definition type.h:7
vnode_t * create_and_attach_vnode()
uint32_t width
Definition virtio-gpu.hpp:2
uint32_t offset
Definition virtio.h:6
@ VNODE_TYPE_CHR
Definition vnode.h:15