0c404a51208d035111b54a936e5c8db55ff494ae
[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_TCSETCHDEV: {
42             int fd = va_arg(args, int);
43             struct v_fd* vfd;
44
45             if (vfs_getfd(fd, &vfd)) {
46                 err = EINVAL;
47                 goto done;
48             }
49
50             struct device* cdev = device_cast(vfd->file->inode->data);
51             if (!cdev) {
52                 err = ENOTDEV;
53                 goto done;
54             }
55             if (cdev->dev_type != DEV_IFSEQ) {
56                 err = EINVAL;
57                 goto done;
58             }
59
60             term_bind(term, cdev);
61             break;
62         }
63         case TDEV_TCGETCHDEV: {
64             struct dev_info* devinfo = va_arg(args, struct dev_info*);
65
66             if (!term->chdev) {
67                 err = ENODEV;
68                 goto done;
69             }
70
71             if (devinfo) {
72                 device_populate_info(term->chdev, devinfo);
73             }
74             break;
75         }
76         case TDEV_TCGETATTR: {
77             struct _termios* tios = va_arg(args, struct _termios*);
78             *tios = (struct _termios){.c_oflag = term->oflags,
79                                       .c_iflag = term->iflags,
80                                       .c_lflag = term->lflags};
81             memcpy(tios->c_cc, term->cc, _NCCS * sizeof(cc_t));
82             tios->c_baud = term->iospeed;
83         } break;
84         case TDEV_TCSETATTR: {
85             struct _termios* tios = va_arg(args, struct _termios*);
86             term->iflags = tios->c_iflag;
87             term->oflags = tios->c_oflag;
88             term->lflags = tios->c_lflag;
89             memcpy(term->cc, tios->c_cc, _NCCS * sizeof(cc_t));
90
91             if (tios->c_baud != term->iospeed) {
92                 term->iospeed = tios->c_baud;
93                 if (!term->chdev_ops.set_speed) {
94                     goto done;
95                 }
96
97                 term->chdev_ops.set_speed(term->chdev, tios->c_baud);
98             }
99         } break;
100         default:
101             err = EINVAL;
102             goto done;
103     }
104
105 done:
106     device_unlock(dev);
107     return err;
108 }
109
110 static int
111 tdev_do_write(struct device* dev, void* buf, size_t offset, size_t len)
112 {
113     struct term* tdev = termdev(dev);
114     lbuf_ref_t current = ref_current(&tdev->line_out);
115     size_t wrsz = 0;
116     while (wrsz < len) {
117         wrsz += rbuffer_puts(
118             deref(current), &((char*)buf)[offset + wrsz], len - wrsz);
119
120         if (rbuffer_full(deref(current))) {
121             term_flush(tdev);
122         }
123     }
124
125     return 0;
126 }
127
128 static int
129 tdev_do_read(struct device* dev, void* buf, size_t offset, size_t len)
130 {
131     struct term* tdev = termdev(dev);
132     lbuf_ref_t current = ref_current(&tdev->line_in);
133     bool cont = true;
134     size_t rdsz = 0;
135     while (cont && rdsz < len) {
136         if (rbuffer_empty(deref(current))) {
137             tdev->line_in.sflags = 0;
138             cont = term_read(tdev);
139         }
140
141         rdsz += rbuffer_gets(
142             deref(current), &((char*)buf)[offset + rdsz], len - rdsz);
143     }
144
145     return rdsz;
146 }
147
148 static cc_t default_cc[_NCCS] = {4, '\n', 0x7f, 3, 1, 24, 22, 0, 0, 1, 1};
149
150 struct term*
151 term_create(struct device* chardev, char* suffix)
152 {
153     struct term* terminal = vzalloc(sizeof(struct term));
154
155     if (!terminal) {
156         return NULL;
157     }
158
159     terminal->dev = device_allocseq(NULL, terminal);
160     terminal->chdev = chardev;
161
162     terminal->dev->ops.read = tdev_do_read;
163     terminal->dev->ops.write = tdev_do_write;
164
165     // TODO choice of lcntl can be flexible
166     terminal->lcntl = line_controls[ANSI_LCNTL];
167
168     line_alloc(&terminal->line_in, 1024);
169     line_alloc(&terminal->line_out, 1024);
170
171     if (chardev) {
172         int cdev_var = DEV_VAR_FROM(chardev->ident.unique);
173         device_register(terminal->dev, &termdev, "tty%s%d", suffix, cdev_var);
174     } else {
175         device_register(terminal->dev, &termdev, "tty%d", termdev.variant++);
176     }
177
178     terminal->lflags = _ICANON | _IEXTEN | _ISIG | _ECHO | _ECHOE | _ECHONL;
179     terminal->iflags = _ICRNL | _IGNBRK;
180     terminal->oflags = _ONLCR | _OPOST;
181     memcpy(terminal->cc, default_cc, _NCCS * sizeof(cc_t));
182
183     return terminal;
184 }
185
186 int
187 term_bind(struct term* term, struct device* chdev)
188 {
189     device_lock(term->dev);
190
191     term->chdev = chdev;
192
193     device_unlock(term->dev);
194
195     return 0;
196 }
197
198 void
199 line_alloc(struct linebuffer* lbf, size_t sz_hlf)
200 {
201     char* buffer = valloc(sz_hlf * 2);
202     memset(lbf, 0, sizeof(*lbf));
203     lbf->current = rbuffer_create(buffer, sz_hlf);
204     lbf->next = rbuffer_create(&buffer[sz_hlf], sz_hlf);
205     lbf->sz_hlf = sz_hlf;
206 }
207
208 void
209 line_free(struct linebuffer* lbf, size_t sz_hlf)
210 {
211     void* ptr =
212         (void*)MIN((ptr_t)lbf->current->buffer, (ptr_t)lbf->next->buffer);
213     vfree(ptr);
214 }
215
216 void
217 term_sendsig(struct term* tdev, int signal)
218 {
219     if ((tdev->lflags & _ISIG)) {
220         proc_setsignal(get_process(tdev->fggrp), signal);
221     }
222 }