b566c555a1ca4c5a53d2beb9a8a94e4c1642eddd
[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/process.h>
6 #include <lunaix/spike.h>
7 #include <lunaix/status.h>
8
9 #include <usr/lunaix/ioctl_defs.h>
10
11 #define ANSI_LCNTL 0
12 #define termdev(dev) ((struct term*)(dev)->underlay)
13
14 extern struct term_lcntl ansi_line_controller;
15 static struct term_lcntl* line_controls[] = {[ANSI_LCNTL] =
16                                                  &ansi_line_controller};
17 #define LCNTL_TABLE_LEN (sizeof(line_controls) / sizeof(struct term_lcntl*))
18
19 static struct devclass termdev = DEVCLASS(DEVIF_NON, DEVFN_TTY, DEV_VTERM);
20
21 static int
22 term_exec_cmd(struct device* dev, u32_t req, va_list args)
23 {
24     struct term* term = termdev(dev);
25     int err = 0;
26
27     device_lock(dev);
28
29     switch (req) {
30     case TIOCSPGRP: {
31         pid_t pgid = va_arg(args, pid_t);
32         if (pgid < 0) {
33             err = EINVAL;
34             goto done;
35         }
36         term->fggrp = pgid;
37         break;
38     }
39     case TIOCGPGRP:
40         return term->fggrp;
41     case TDEV_TCPUSHLC: {
42         u32_t lcntl_idx = va_arg(args, u32_t);
43         struct term_lcntl* lcntl = term_get_lcntl(lcntl_idx);
44
45         if (!lcntl) {
46             err = EINVAL;
47             goto done;
48         }
49
50         term_push_lcntl(term, lcntl);
51         break;
52     }
53     case TDEV_TCPOPLC:
54         term_pop_lcntl(term);
55         break;
56     case TDEV_TCSETCHDEV: {
57         int fd = va_arg(args, int);
58         struct v_fd* vfd;
59
60         if (vfs_getfd(fd, &vfd)) {
61             err = EINVAL;
62             goto done;
63         }
64
65         struct device* cdev = device_cast(vfd->file->inode->data);
66         if (!cdev) {
67             err = ENOTDEV;
68             goto done;
69         }
70         if (cdev->dev_type != DEV_IFSEQ) {
71             err = EINVAL;
72             goto done;
73         }
74
75         term_bind(term, cdev);
76         break;
77     }
78     case TDEV_TCGETCHDEV: {
79         struct dev_info* devinfo = va_arg(args, struct dev_info*);
80
81         if (!term->chdev) {
82             err = ENODEV;
83             goto done;
84         }
85
86         if (devinfo) {
87             device_populate_info(term->chdev, devinfo);
88         }
89         break;
90     }
91     case TDEV_TCGETATTR: {
92         struct _termios* tios = va_arg(args, struct _termios*);
93         *tios = (struct _termios){.c_oflag = term->oflags,
94                                   .c_iflag = term->iflags,
95                                   .c_lflag = term->lflags};
96         memcpy(tios->c_cc, term->cc, _NCCS * sizeof(cc_t));
97         tios->c_baud = term->iospeed;
98     } break;
99     case TDEV_TCSETATTR: {
100         struct _termios* tios = va_arg(args, struct _termios*);
101         term->iflags = tios->c_iflag;
102         term->oflags = tios->c_oflag;
103         term->lflags = tios->c_lflag;
104         memcpy(term->cc, tios->c_cc, _NCCS * sizeof(cc_t));
105
106         if (tios->c_baud != term->iospeed) {
107             term->iospeed = tios->c_baud;
108             if (!term->chdev_ops.set_speed) {
109                 goto done;
110             }
111
112             term->chdev_ops.set_speed(term->chdev, tios->c_baud);
113         }
114     } break;
115     default:
116         err = EINVAL;
117         goto done;
118     }
119
120 done:
121     device_unlock(dev);
122     return err;
123 }
124
125 static int
126 tdev_do_write(struct device* dev, void* buf, size_t offset, size_t len)
127 {
128     struct term* tdev = termdev(dev);
129     lbuf_ref_t current = ref_current(&tdev->line_out);
130     size_t wrsz = 0;
131     while (wrsz < len) {
132         wrsz += rbuffer_puts(deref(current), &((char*)buf)[offset + wrsz],
133                              len - wrsz);
134         if (rbuffer_full(deref(current))) {
135             term_flush(tdev);
136         }
137     }
138
139     return 0;
140 }
141
142 static int
143 tdev_do_read(struct device* dev, void* buf, size_t offset, size_t len)
144 {
145     struct term* tdev = termdev(dev);
146     lbuf_ref_t current = ref_current(&tdev->line_in);
147     bool cont = true;
148     size_t rdsz = 0;
149     while (cont && rdsz < len) {
150         if (rbuffer_empty(deref(current))) {
151             tdev->line_in.sflags = 0;
152             cont = term_read(tdev);
153         }
154
155         rdsz += rbuffer_gets(deref(current), &((char*)buf)[offset + rdsz],
156                              len - rdsz);
157     }
158
159     return rdsz;
160 }
161
162 static cc_t default_cc[_NCCS] = {4, '\n', 8, 3, 1, 24, 22, 0, 0, 1, 1};
163
164 struct term*
165 term_create(struct device* chardev, char* suffix)
166 {
167     struct term* terminal = vzalloc(sizeof(struct term));
168
169     if (!terminal) {
170         return NULL;
171     }
172
173     terminal->dev = device_allocseq(NULL, terminal);
174     terminal->chdev = chardev;
175
176     terminal->dev->ops.read = tdev_do_read;
177     terminal->dev->ops.write = tdev_do_write;
178
179     llist_init_head(&terminal->lcntl_stack);
180     line_alloc(&terminal->line_in, 1024);
181     line_alloc(&terminal->line_out, 1024);
182
183     if (chardev) {
184         int cdev_var = DEV_VAR_FROM(chardev->ident.unique);
185         device_register(terminal->dev, &termdev, "tty%s%d", suffix, cdev_var);
186     } else {
187         device_register(terminal->dev, &termdev, "tty%d", termdev.variant++);
188     }
189
190     terminal->lflags = _ICANON | _IEXTEN | _ISIG | _ECHO;
191     terminal->iflags = _ICRNL;
192     memcpy(terminal->cc, default_cc, _NCCS * sizeof(cc_t));
193
194     return terminal;
195 }
196
197 int
198 term_bind(struct term* term, struct device* chdev)
199 {
200     device_lock(term->dev);
201
202     term->chdev = chdev;
203
204     device_unlock(term->dev);
205
206     return 0;
207 }
208
209 struct term_lcntl*
210 term_get_lcntl(u32_t lcntl_index)
211 {
212     if (lcntl_index >= LCNTL_TABLE_LEN) {
213         return NULL;
214     }
215
216     struct term_lcntl* lcntl_template = line_controls[lcntl_index];
217     struct term_lcntl* lcntl_instance = valloc(sizeof(struct term_lcntl));
218
219     if (!lcntl_instance) {
220         return NULL;
221     }
222
223     lcntl_instance->process_and_put = lcntl_template->process_and_put;
224
225     return lcntl_instance;
226 }
227
228 int
229 term_push_lcntl(struct term* term, struct term_lcntl* lcntl)
230 {
231     device_lock(term->dev);
232
233     llist_append(&term->lcntl_stack, &lcntl->lcntls);
234
235     device_unlock(term->dev);
236
237     return 0;
238 }
239
240 int
241 term_pop_lcntl(struct term* term)
242 {
243     if (term->lcntl_stack.prev == &term->lcntl_stack) {
244         return 0;
245     }
246
247     device_lock(term->dev);
248
249     struct term_lcntl* lcntl =
250         list_entry(term->lcntl_stack.prev, struct term_lcntl, lcntls);
251     llist_delete(term->lcntl_stack.prev);
252
253     vfree(lcntl);
254
255     device_unlock(term->dev);
256
257     return 1;
258 }
259
260 void
261 line_alloc(struct linebuffer* lbf, size_t sz_hlf)
262 {
263     char* buffer = valloc(sz_hlf * 2);
264     memset(lbf, 0, sizeof(*lbf));
265     lbf->current = rbuffer_create(buffer, sz_hlf);
266     lbf->next = rbuffer_create(&buffer[sz_hlf], sz_hlf);
267     lbf->sz_hlf = sz_hlf;
268 }
269
270 void
271 line_free(struct linebuffer* lbf, size_t sz_hlf)
272 {
273     void* ptr =
274         (void*)MIN((ptr_t)lbf->current->buffer, (ptr_t)lbf->next->buffer);
275     vfree(ptr);
276 }
277
278 void
279 term_sendsig(struct term* tdev, int signal)
280 {
281     if ((tdev->lflags & _ISIG)) {
282         proc_setsignal(get_process(tdev->fggrp), signal);
283     }
284 }