9f65d386beb1a1b3e1cd4f3b4b7b066111f5a9d0
[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 termdev(dev) ((struct term*)(dev)->underlay)
12
13 #define LCNTL_TABLE_LEN (sizeof(line_controls) / sizeof(struct term_lcntl*))
14
15 static struct devclass termdev_class = DEVCLASS(DEVIF_NON, DEVFN_TTY, DEV_VTERM);
16
17 struct device* sysconsole = NULL;
18
19 static int
20 term_exec_cmd(struct device* dev, u32_t req, va_list args)
21 {
22     struct term* term = termdev(dev);
23     int err = 0;
24
25     device_lock(dev);
26
27     switch (req) {
28         case TIOCSPGRP: {
29             pid_t pgid = va_arg(args, pid_t);
30             if (pgid < 0) {
31                 err = EINVAL;
32                 goto done;
33             }
34             term->fggrp = pgid;
35             break;
36         }
37         case TIOCGPGRP:
38             return term->fggrp;
39         case TDEV_TCSETCHDEV: {
40             int fd = va_arg(args, int);
41             struct v_fd* vfd;
42
43             if (vfs_getfd(fd, &vfd)) {
44                 err = EINVAL;
45                 goto done;
46             }
47
48             struct device* cdev = resolve_device(vfd->file->inode->data);
49             if (!cdev) {
50                 err = ENOTDEV;
51                 goto done;
52             }
53             if (cdev->dev_type != DEV_IFSEQ) {
54                 err = EINVAL;
55                 goto done;
56             }
57
58             term_bind(term, cdev);
59             break;
60         }
61         case TDEV_TCGETCHDEV: {
62             struct dev_info* devinfo = va_arg(args, struct dev_info*);
63
64             if (!term->chdev) {
65                 err = ENODEV;
66                 goto done;
67             }
68
69             if (devinfo) {
70                 device_populate_info(term->chdev, devinfo);
71             }
72             break;
73         }
74         case TDEV_TCGETATTR: {
75             struct termios* tios = va_arg(args, struct termios*);
76             *tios = (struct termios){.c_oflag = term->oflags,
77                                       .c_iflag = term->iflags,
78                                       .c_lflag = term->lflags,
79                                       .c_cflag = term->cflags};
80             memcpy(tios->c_cc, term->cc, _NCCS * sizeof(cc_t));
81             tios->c_baud = term->iospeed;
82         } break;
83         case TDEV_TCSETATTR: {
84             struct termport_cap_ops* cap_ops;
85             struct termios* tios = va_arg(args, struct termios*);
86
87             term->iflags = tios->c_iflag;
88             term->oflags = tios->c_oflag;
89             term->lflags = tios->c_lflag;
90             memcpy(term->cc, tios->c_cc, _NCCS * sizeof(cc_t));
91
92             tcflag_t old_cf = term->cflags;
93             term->cflags = tios->c_cflag;
94
95             if (!term->tp_cap) {
96                 goto done;
97             }
98
99             cap_ops = term->tp_cap->cap_ops;
100
101             if (tios->c_baud != term->iospeed) {
102                 term->iospeed = tios->c_baud;
103
104                 cap_ops->set_speed(term->chdev, tios->c_baud);
105             }
106
107             if (old_cf != tios->c_cflag) {
108                 cap_ops->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 wrsz;
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
150     while (cont && rdsz < len) {
151         cont = term_read(tdev);
152         rdsz += rbuffer_gets(
153             deref(current), &((char*)buf)[rdsz], len - rdsz);
154     }
155     
156     tdev->line_in.sflags = 0;
157     return rdsz;
158 }
159
160 static cc_t default_cc[_NCCS] = {4, '\n', 0x7f, 3, 1, 24, 22, 0, 0, 1, 1};
161
162 static void 
163 load_default_setting(struct term* tdev) 
164 {
165     tdev->lflags = _ICANON | _IEXTEN | _ISIG | _ECHO | _ECHOE | _ECHONL;
166     tdev->iflags = _ICRNL | _IGNBRK;
167     tdev->oflags = _ONLCR | _OPOST;
168     tdev->iospeed = _B9600;
169     memcpy(tdev->cc, default_cc, _NCCS * sizeof(cc_t));
170 }
171
172 static void
173 alloc_term_buffer(struct term* terminal, size_t sz_hlf) 
174 {
175     line_alloc(&terminal->line_in, sz_hlf);
176     line_alloc(&terminal->line_out, sz_hlf);
177     terminal->scratch_pad = valloc(sz_hlf);
178 }
179
180 struct term*
181 term_create(struct device* chardev, char* suffix)
182 {
183     struct term* terminal;
184     struct device* tdev;
185     struct capability_meta* termport_cap;
186     struct capability_meta* tios_cap;
187
188     terminal = vzalloc(sizeof(struct term));
189     if (!terminal) {
190         return NULL;
191     }
192
193     tdev = device_allocseq(NULL, terminal);
194     terminal->dev = tdev;
195     terminal->chdev = chardev;
196
197     tdev->ops.read = tdev_do_read;
198     tdev->ops.write = tdev_do_write;
199     tdev->ops.exec_cmd = term_exec_cmd;
200
201     waitq_init(&terminal->line_in_event);
202
203     alloc_term_buffer(terminal, 1024);
204
205     if (chardev) {
206         int cdev_var = DEV_VAR_FROM(chardev->ident.unique);
207         register_device(tdev, &termdev_class, "tty%s%d", suffix, cdev_var);
208     } else {
209         register_device(tdev, &termdev_class, "tty%d", termdev_class.variant++);
210     }
211
212     termport_cap = device_get_capability(chardev, TERMPORT_CAP);
213     if (termport_cap) {
214         terminal->tp_cap = 
215             get_capability(termport_cap, struct termport_capability);
216         
217         assert(terminal->tp_cap->cap_ops);
218         terminal->tp_cap->term = terminal;
219     }
220
221     tios_cap = new_capability_marker(TERMIOS_CAP);
222     device_grant_capability(tdev, tios_cap);
223
224     load_default_setting(terminal);
225
226     return terminal;
227 }
228
229 int
230 term_bind(struct term* term, struct device* chdev)
231 {
232     device_lock(term->dev);
233
234     term->chdev = chdev;
235
236     device_unlock(term->dev);
237
238     return 0;
239 }
240
241 void
242 line_alloc(struct linebuffer* lbf, size_t sz_hlf)
243 {
244     char* buffer = valloc(sz_hlf * 2);
245     memset(lbf, 0, sizeof(*lbf));
246     lbf->current = rbuffer_create(buffer, sz_hlf);
247     lbf->next = rbuffer_create(&buffer[sz_hlf], sz_hlf);
248     lbf->sz_hlf = sz_hlf;
249 }
250
251 void
252 line_free(struct linebuffer* lbf, size_t sz_hlf)
253 {
254     void* ptr =
255         (void*)MIN((ptr_t)lbf->current->buffer, (ptr_t)lbf->next->buffer);
256     vfree(ptr);
257 }
258
259 void
260 term_sendsig(struct term* tdev, int signal)
261 {
262     if ((tdev->lflags & _ISIG)) {
263         signal_send(-tdev->fggrp, signal);
264     }
265 }