refactor: add a async read/write variant to device ops, with allow async io to be...
[lunaix-os.git] / lunaix-os / hal / term / lcntls / lcntl.c
1 /**
2  * @file lcntl.c
3  * @author Lunaixsky (lunaxisky@qq.com)
4  * @brief Line controller master that handle all POSIX-control code
5  * @version 0.1
6  * @date 2023-11-25
7  *
8  * @copyright Copyright (c) 2023
9  *
10  */
11 #include <hal/term.h>
12 #include <usr/lunaix/term.h>
13
14 #include <lunaix/process.h>
15 #include <lunaix/spike.h>
16
17 static inline void
18 raise_sig(struct term* at_term, struct linebuffer* lbuf, int sig)
19 {
20     term_sendsig(at_term, SIGINT);
21     lbuf->sflags |= LSTATE_SIGRAISE;
22 }
23
24 static inline int must_inline optimize("-fipa-cp-clone")
25 lcntl_transform_seq(struct term* tdev, struct linebuffer* lbuf, bool out)
26 {
27     struct rbuffer* raw = lbuf->current;
28     struct rbuffer* cooked = lbuf->next;
29     struct rbuffer* output = tdev->line_out.current;
30
31     int i = 0, _if = tdev->iflags & -!out, _of = tdev->oflags & -!!out,
32         _lf = tdev->lflags;
33     int allow_more = 1, latest_eol = cooked->ptr;
34     char c;
35     bool should_flush = false;
36
37     int (*lcntl_slave_put)(struct term*, struct linebuffer*, char) =
38         tdev->lcntl->process_and_put;
39
40 #define EOL tdev->cc[_VEOL]
41 #define EOF tdev->cc[_VEOF]
42 #define ERASE tdev->cc[_VERASE]
43 #define KILL tdev->cc[_VKILL]
44 #define INTR tdev->cc[_VINTR]
45 #define QUIT tdev->cc[_VQUIT]
46 #define SUSP tdev->cc[_VSUSP]
47
48     if (!out) {
49         // Keep all cc's (except VMIN & VTIME) up to L2 cache.
50         for (size_t i = 0; i < _VMIN; i++) {
51             prefetch_rd(&tdev->cc[i], 2);
52         }
53     }
54
55     while (rbuffer_get(raw, &c) && allow_more) {
56
57         if (c == '\r' && ((_if & _ICRNL) || (_of & _OCRNL))) {
58             c = '\n';
59         } else if (c == '\n') {
60             if ((_if & _INLCR) || (_of & (_ONLRET | _ONLCR))) {
61                 c = '\r';
62             }
63         }
64
65         if (c == '\0') {
66             if ((_if & _BRKINT)) {
67                 raise_sig(tdev, lbuf, SIGINT);
68                 break;
69             }
70
71             if ((_if & _IGNBRK)) {
72                 continue;
73             }
74         }
75
76         if ('a' <= c && c <= 'z') {
77             if ((_if & _IUCLC)) {
78                 c = c | 0b100000;
79             } else if ((_of & _OLCUC)) {
80                 c = c & ~0b100000;
81             }
82         }
83
84         if (c == '\n') {
85             latest_eol = cooked->ptr + 1;
86             if (!out && (_lf & _ECHONL)) {
87                 rbuffer_put(output, c);
88             }
89             should_flush = true;
90         }
91
92         if (out) {
93             goto put_char;
94         }
95
96         // For input procesing
97
98         if (c == '\n' || c == EOL) {
99             lbuf->sflags |= LSTATE_EOL;
100             goto keep;
101         } else if (c == EOF) {
102             lbuf->sflags |= LSTATE_EOF;
103             rbuffer_clear(raw);
104             break;
105         } else if (c == INTR) {
106             raise_sig(tdev, lbuf, SIGINT);
107         } else if (c == QUIT) {
108             raise_sig(tdev, lbuf, SIGKILL);
109             break;
110         } else if (c == SUSP) {
111             raise_sig(tdev, lbuf, SIGSTOP);
112         } else if (c == ERASE) {
113             rbuffer_erase(cooked);
114         } else if (c == KILL) {
115             // TODO shrink the rbuffer
116         } else {
117             goto keep;
118         }
119
120         continue;
121
122     keep:
123         if ((_lf & _ECHO)) {
124             rbuffer_put(output, c);
125         }
126         if ((_lf & _ECHOE) && c == ERASE) {
127             rbuffer_erase(output);
128         }
129         if ((_lf & _ECHOK) && c == KILL) {
130             rbuffer_put(output, c);
131             rbuffer_put(output, '\n');
132         }
133
134     put_char:
135         if (!out && (_lf & _IEXTEN) && lcntl_slave_put) {
136             allow_more = lcntl_slave_put(tdev, lbuf, c);
137         } else {
138             allow_more = rbuffer_put(cooked, c);
139         }
140     }
141
142     if (should_flush && !(_lf & _NOFLSH)) {
143         term_flush(tdev);
144     }
145
146     line_flip(lbuf);
147
148     return i;
149 }
150
151 int
152 lcntl_transform_inseq(struct term* tdev)
153 {
154     return lcntl_transform_seq(tdev, &tdev->line_in, false);
155 }
156
157 int
158 lcntl_transform_outseq(struct term* tdev)
159 {
160     return lcntl_transform_seq(tdev, &tdev->line_in, true);
161 }