feat: IO polling for file descriptor
[lunaix-os.git] / lunaix-os / hal / term / term.c
1 #include <hal/term.h>
2 #include <klibc/string.h>
3 #include <lunaix/fs.h>
4 #include <lunaix/mm/valloc.h>
5 #include <lunaix/spike.h>
6 #include <lunaix/status.h>
7
8 #include <usr/lunaix/ioctl_defs.h>
9
10 #define ANSI_LCNTL 0
11
12 extern struct term_lcntl ansi_line_controller;
13 static struct term_lcntl* line_controls[] = { [ANSI_LCNTL] =
14                                                 &ansi_line_controller };
15 #define LCNTL_TABLE_LEN (sizeof(line_controls) / sizeof(struct term_lcntl*))
16
17 static int
18 term_exec_cmd(struct device* dev, u32_t req, va_list args)
19 {
20     struct term* term = (struct term*)dev->underlay;
21     int err = 0;
22
23     device_lock(dev);
24
25     switch (req) {
26         case TIOCSPGRP: {
27             pid_t pgid = va_arg(args, pid_t);
28             if (pgid < 0) {
29                 err = EINVAL;
30                 goto done;
31             }
32             term->fggrp = pgid;
33             break;
34         }
35         case TIOCGPGRP:
36             return term->fggrp;
37         case TIOCPUSH: {
38             u32_t lcntl_idx = va_arg(args, u32_t);
39             struct term_lcntl* lcntl = term_get_lcntl(lcntl_idx);
40
41             if (!lcntl) {
42                 err = EINVAL;
43                 goto done;
44             }
45
46             term_push_lcntl(term, lcntl);
47             break;
48         }
49         case TIOCPOP:
50             term_pop_lcntl(term);
51             break;
52         case TIOCSCDEV: {
53             int fd = va_arg(args, int);
54             struct v_fd* vfd;
55
56             if (vfs_getfd(fd, &vfd)) {
57                 err = EINVAL;
58                 goto done;
59             }
60
61             struct device* cdev = device_cast(vfd->file->inode->data);
62             if (!cdev) {
63                 err = ENOTDEV;
64                 goto done;
65             }
66             if (cdev->dev_type != DEV_IFSEQ) {
67                 err = EINVAL;
68                 goto done;
69             }
70
71             term_bind(term, cdev);
72             break;
73         }
74         case TIOCGCDEV: {
75             struct dev_info* devinfo = va_arg(args, struct dev_info*);
76
77             if (!term->chdev) {
78                 err = ENODEV;
79                 goto done;
80             }
81
82             if (devinfo) {
83                 device_populate_info(term->chdev, devinfo);
84             }
85             break;
86         }
87         default:
88             err = EINVAL;
89             goto done;
90     }
91
92 done:
93     device_unlock(dev);
94     return err;
95 }
96
97 static int
98 term_write(struct device* dev, void* buf, size_t offset, size_t len)
99 {
100     struct term* term = (struct term*)dev->underlay;
101     size_t sz = MIN(len, term->line.sz_hlf);
102
103     if (!term->chdev) {
104         return ENODEV;
105     }
106
107     device_lock(term->dev);
108
109     memcpy(term->line.current, &((char*)buf)[offset], sz);
110
111     struct term_lcntl *lcntl, *n;
112     llist_for_each(lcntl, n, &term->lcntl_stack, lcntls)
113     {
114         sz = lcntl->apply(term, term->line.current, sz);
115         line_flip(&term->line);
116     }
117
118     int errcode = term_sendline(term, sz);
119
120     device_unlock(term->dev);
121
122     return errcode;
123 }
124
125 static int
126 term_read(struct device* dev, void* buf, size_t offset, size_t len)
127 {
128     struct term* term = (struct term*)dev->underlay;
129     size_t sz = MIN(len, term->line.sz_hlf);
130
131     if (!term->chdev) {
132         return ENODEV;
133     }
134
135     device_lock(term->dev);
136
137     sz = term_readline(term, sz);
138     if (!sz) {
139         device_unlock(term->dev);
140         return 0;
141     }
142
143     struct term_lcntl *pos, *n;
144     llist_for_each(pos, n, &term->lcntl_stack, lcntls)
145     {
146         sz = pos->apply(term, term->line.current, sz);
147         line_flip(&term->line);
148     }
149
150     memcpy(&((char*)buf)[offset], term->line.current, sz);
151
152     device_unlock(term->dev);
153
154     return sz;
155 }
156
157 struct term*
158 term_create()
159 {
160     struct term* terminal = valloc(sizeof(struct term));
161
162     if (!terminal) {
163         return NULL;
164     }
165
166     terminal->dev = device_allocseq(NULL, terminal);
167
168     terminal->dev->ops.read = term_read;
169     terminal->dev->ops.write = term_write;
170
171     llist_init_head(&terminal->lcntl_stack);
172     line_alloc(&terminal->line, 1024);
173
174     return terminal;
175 }
176
177 int
178 term_bind(struct term* term, struct device* chdev)
179 {
180     device_lock(term->dev);
181
182     term->chdev = chdev;
183
184     device_unlock(term->dev);
185
186     return 0;
187 }
188
189 struct term_lcntl*
190 term_get_lcntl(u32_t lcntl_index)
191 {
192     if (lcntl_index >= LCNTL_TABLE_LEN) {
193         return NULL;
194     }
195
196     struct term_lcntl* lcntl_template = line_controls[lcntl_index];
197     struct term_lcntl* lcntl_instance = valloc(sizeof(struct term_lcntl));
198
199     if (!lcntl_instance) {
200         return NULL;
201     }
202
203     lcntl_instance->apply = lcntl_template->apply;
204
205     return lcntl_instance;
206 }
207
208 int
209 term_push_lcntl(struct term* term, struct term_lcntl* lcntl)
210 {
211     device_lock(term->dev);
212
213     llist_append(&term->lcntl_stack, &lcntl->lcntls);
214
215     device_unlock(term->dev);
216
217     return 0;
218 }
219
220 int
221 term_pop_lcntl(struct term* term)
222 {
223     if (term->lcntl_stack.prev == &term->lcntl_stack) {
224         return 0;
225     }
226
227     device_lock(term->dev);
228
229     struct term_lcntl* lcntl =
230       list_entry(term->lcntl_stack.prev, struct term_lcntl, lcntls);
231     llist_delete(term->lcntl_stack.prev);
232
233     vfree(lcntl);
234
235     device_unlock(term->dev);
236
237     return 1;
238 }
239
240 void
241 line_flip(struct linebuffer* lbf)
242 {
243     char* tmp = lbf->current;
244     lbf->current = lbf->next;
245     lbf->next = tmp;
246 }
247
248 void
249 line_alloc(struct linebuffer* lbf, size_t sz_hlf)
250 {
251     char* buffer = valloc(sz_hlf * 2);
252     lbf->current = buffer;
253     lbf->next = &buffer[sz_hlf];
254     lbf->sz_hlf = sz_hlf;
255 }
256
257 void
258 line_free(struct linebuffer* lbf, size_t sz_hlf)
259 {
260     void* ptr = (void*)MIN((ptr_t)lbf->current, (ptr_t)lbf->next);
261     vfree(ptr);
262 }
263
264 int
265 term_sendline(struct term* tdev, size_t len)
266 {
267     struct device* chdev = tdev->chdev;
268
269     device_lock(chdev);
270
271     int code = chdev->ops.write(chdev, tdev->line.current, 0, len);
272
273     device_unlock(chdev);
274
275     return code;
276 }
277
278 int
279 term_readline(struct term* tdev, size_t len)
280 {
281     struct device* chdev = tdev->chdev;
282
283     device_lock(chdev);
284
285     int code = chdev->ops.read(chdev, tdev->line.current, 0, len);
286
287     device_unlock(chdev);
288
289     return code;
290 }
291
292 int
293 term_init(struct device_def* devdef)
294 {
295     struct term* terminal = term_create();
296     struct device* tdev = device_allocseq(NULL, terminal);
297     tdev->ops.read = term_read;
298     tdev->ops.write = term_write;
299     tdev->ops.exec_cmd = term_exec_cmd;
300
301     devdef->class.variant++;
302     device_register(tdev, &devdef->class, "tty%d", devdef->class.variant);
303
304     return 0;
305 }
306
307 static struct device_def vterm_def = {
308     .class = DEVCLASS(DEVIF_NON, DEVFN_TTY, DEV_VTERM),
309     .name = "virtual terminal",
310     .init = term_init
311 };
312 EXPORT_DEVICE(vterm, &vterm_def, load_on_demand);