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