3cc9741cce9ca47e00bb40801d7283c6580185a4
[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 struct device* sysconsole = NULL;
22
23 static int
24 term_exec_cmd(struct device* dev, u32_t req, va_list args)
25 {
26     struct term* term = termdev(dev);
27     int err = 0;
28
29     device_lock(dev);
30
31     switch (req) {
32         case TIOCSPGRP: {
33             pid_t pgid = va_arg(args, pid_t);
34             if (pgid < 0) {
35                 err = EINVAL;
36                 goto done;
37             }
38             term->fggrp = pgid;
39             break;
40         }
41         case TIOCGPGRP:
42             return term->fggrp;
43         case TDEV_TCSETCHDEV: {
44             int fd = va_arg(args, int);
45             struct v_fd* vfd;
46
47             if (vfs_getfd(fd, &vfd)) {
48                 err = EINVAL;
49                 goto done;
50             }
51
52             struct device* cdev = resolve_device(vfd->file->inode->data);
53             if (!cdev) {
54                 err = ENOTDEV;
55                 goto done;
56             }
57             if (cdev->dev_type != DEV_IFSEQ) {
58                 err = EINVAL;
59                 goto done;
60             }
61
62             term_bind(term, cdev);
63             break;
64         }
65         case TDEV_TCGETCHDEV: {
66             struct dev_info* devinfo = va_arg(args, struct dev_info*);
67
68             if (!term->chdev) {
69                 err = ENODEV;
70                 goto done;
71             }
72
73             if (devinfo) {
74                 device_populate_info(term->chdev, devinfo);
75             }
76             break;
77         }
78         case TDEV_TCGETATTR: {
79             struct termios* tios = va_arg(args, struct termios*);
80             *tios = (struct termios){.c_oflag = term->oflags,
81                                       .c_iflag = term->iflags,
82                                       .c_lflag = term->lflags,
83                                       .c_cflag = term->cflags};
84             memcpy(tios->c_cc, term->cc, _NCCS * sizeof(cc_t));
85             tios->c_baud = term->iospeed;
86         } break;
87         case TDEV_TCSETATTR: {
88             struct termios* tios = va_arg(args, struct termios*);
89             term->iflags = tios->c_iflag;
90             term->oflags = tios->c_oflag;
91             term->lflags = tios->c_lflag;
92             memcpy(term->cc, tios->c_cc, _NCCS * sizeof(cc_t));
93
94             tcflag_t old_cf = term->cflags;
95             term->cflags = tios->c_cflag;
96
97             if (!term->tp_cap) {
98                 goto done;
99             }
100
101             if (tios->c_baud != term->iospeed) {
102                 term->iospeed = tios->c_baud;
103
104                 term->tp_cap->set_speed(term->chdev, tios->c_baud);
105             }
106
107             if (old_cf != tios->c_cflag) {
108                 term->tp_cap->set_cntrl_mode(term->chdev, tios->c_cflag);
109             }
110         } break;
111         default:
112             err = EINVAL;
113             goto done;
114     }
115
116 done:
117     device_unlock(dev);
118     return err;
119 }
120
121 static int
122 tdev_do_write(struct device* dev, void* buf, off_t fpos, size_t len)
123 {
124     struct term* tdev = termdev(dev);
125     lbuf_ref_t current = ref_current(&tdev->line_out);
126     size_t wrsz = 0;
127     while (wrsz < len) {
128         wrsz += rbuffer_puts(
129             deref(current), &((char*)buf)[wrsz], len - wrsz);
130
131         if (rbuffer_full(deref(current))) {
132             term_flush(tdev);
133         }
134     }
135     
136     if (!rbuffer_empty(deref(current))) {
137         term_flush(tdev);
138     }
139     return 0;
140 }
141
142 static int
143 tdev_do_read(struct device* dev, void* buf, off_t fpos, 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(
156             deref(current), &((char*)buf)[rdsz], len - rdsz);
157     }
158
159     return rdsz;
160 }
161
162 static cc_t default_cc[_NCCS] = {4, '\n', 0x7f, 3, 1, 24, 22, 0, 0, 1, 1};
163
164 static void 
165 load_default_setting(struct term* tdev) 
166 {
167     tdev->lflags = _ICANON | _IEXTEN | _ISIG | _ECHO | _ECHOE | _ECHONL;
168     tdev->iflags = _ICRNL | _IGNBRK;
169     tdev->oflags = _ONLCR | _OPOST;
170     memcpy(tdev->cc, default_cc, _NCCS * sizeof(cc_t));
171 }
172
173 struct term*
174 term_create(struct device* chardev, char* suffix)
175 {
176     struct term* terminal = vzalloc(sizeof(struct term));
177
178     if (!terminal) {
179         return NULL;
180     }
181
182     terminal->dev = device_allocseq(NULL, terminal);
183     terminal->chdev = chardev;
184
185     terminal->dev->ops.read = tdev_do_read;
186     terminal->dev->ops.write = tdev_do_write;
187     terminal->dev->ops.exec_cmd = term_exec_cmd;
188
189     // TODO choice of lcntl can be flexible
190     terminal->lcntl = line_controls[ANSI_LCNTL];
191
192     line_alloc(&terminal->line_in, 1024);
193     line_alloc(&terminal->line_out, 1024);
194
195     if (chardev) {
196         int cdev_var = DEV_VAR_FROM(chardev->ident.unique);
197         register_device(terminal->dev, &termdev, "tty%s%d", suffix, cdev_var);
198     } else {
199         register_device(terminal->dev, &termdev, "tty%d", termdev.variant++);
200     }
201
202     struct capability_meta* termport_cap = device_get_capability(chardev, TERMPORT_CAP);
203     if (termport_cap) {
204         terminal->tp_cap = get_capability(termport_cap, struct termport_capability);
205     }
206
207     struct capability_meta* term_cap = new_capability_marker(TERMIOS_CAP);
208     device_grant_capability(terminal->dev, term_cap);
209
210     load_default_setting(terminal);
211
212     return terminal;
213 }
214
215 int
216 term_bind(struct term* term, struct device* chdev)
217 {
218     device_lock(term->dev);
219
220     term->chdev = chdev;
221
222     device_unlock(term->dev);
223
224     return 0;
225 }
226
227 void
228 line_alloc(struct linebuffer* lbf, size_t sz_hlf)
229 {
230     char* buffer = valloc(sz_hlf * 2);
231     memset(lbf, 0, sizeof(*lbf));
232     lbf->current = rbuffer_create(buffer, sz_hlf);
233     lbf->next = rbuffer_create(&buffer[sz_hlf], sz_hlf);
234     lbf->sz_hlf = sz_hlf;
235 }
236
237 void
238 line_free(struct linebuffer* lbf, size_t sz_hlf)
239 {
240     void* ptr =
241         (void*)MIN((ptr_t)lbf->current->buffer, (ptr_t)lbf->next->buffer);
242     vfree(ptr);
243 }
244
245 void
246 term_sendsig(struct term* tdev, int signal)
247 {
248     if ((tdev->lflags & _ISIG)) {
249         proc_setsignal(get_process(tdev->fggrp), signal);
250     }
251 }