教程介绍和环境搭建 (#19)
[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             memcpy(tios->c_cc, term->cc, _NCCS * sizeof(cc_t));
84             tios->c_baud = term->iospeed;
85         } break;
86         case TDEV_TCSETATTR: {
87             struct _termios* tios = va_arg(args, struct _termios*);
88             term->iflags = tios->c_iflag;
89             term->oflags = tios->c_oflag;
90             term->lflags = tios->c_lflag;
91             memcpy(term->cc, tios->c_cc, _NCCS * sizeof(cc_t));
92
93             if (tios->c_baud != term->iospeed) {
94                 term->iospeed = tios->c_baud;
95                 if (!term->chdev_ops.set_speed) {
96                     goto done;
97                 }
98
99                 term->chdev_ops.set_speed(term->chdev, tios->c_baud);
100             }
101         } break;
102         default:
103             err = EINVAL;
104             goto done;
105     }
106
107 done:
108     device_unlock(dev);
109     return err;
110 }
111
112 static int
113 tdev_do_write(struct device* dev, void* buf, size_t offset, size_t len)
114 {
115     struct term* tdev = termdev(dev);
116     lbuf_ref_t current = ref_current(&tdev->line_out);
117     size_t wrsz = 0;
118     while (wrsz < len) {
119         wrsz += rbuffer_puts(
120             deref(current), &((char*)buf)[offset + wrsz], len - wrsz);
121
122         if (rbuffer_full(deref(current))) {
123             term_flush(tdev);
124         }
125     }
126     
127     if (!rbuffer_empty(deref(current))) {
128         term_flush(tdev);
129     }
130     return 0;
131 }
132
133 static int
134 tdev_do_read(struct device* dev, void* buf, size_t offset, size_t len)
135 {
136     struct term* tdev = termdev(dev);
137     lbuf_ref_t current = ref_current(&tdev->line_in);
138     bool cont = true;
139     size_t rdsz = 0;
140     while (cont && rdsz < len) {
141         if (rbuffer_empty(deref(current))) {
142             tdev->line_in.sflags = 0;
143             cont = term_read(tdev);
144         }
145
146         rdsz += rbuffer_gets(
147             deref(current), &((char*)buf)[offset + rdsz], len - rdsz);
148     }
149
150     return rdsz;
151 }
152
153 static cc_t default_cc[_NCCS] = {4, '\n', 0x7f, 3, 1, 24, 22, 0, 0, 1, 1};
154
155 static void 
156 load_default_setting(struct term* tdev) 
157 {
158     tdev->lflags = _ICANON | _IEXTEN | _ISIG | _ECHO | _ECHOE | _ECHONL;
159     tdev->iflags = _ICRNL | _IGNBRK;
160     tdev->oflags = _ONLCR | _OPOST;
161     memcpy(tdev->cc, default_cc, _NCCS * sizeof(cc_t));
162 }
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     // TODO choice of lcntl can be flexible
180     terminal->lcntl = line_controls[ANSI_LCNTL];
181
182     line_alloc(&terminal->line_in, 1024);
183     line_alloc(&terminal->line_out, 1024);
184
185     if (chardev) {
186         int cdev_var = DEV_VAR_FROM(chardev->ident.unique);
187         register_device(terminal->dev, &termdev, "tty%s%d", suffix, cdev_var);
188     } else {
189         register_device(terminal->dev, &termdev, "tty%d", termdev.variant++);
190     }
191
192     load_default_setting(terminal);
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 void
210 line_alloc(struct linebuffer* lbf, size_t sz_hlf)
211 {
212     char* buffer = valloc(sz_hlf * 2);
213     memset(lbf, 0, sizeof(*lbf));
214     lbf->current = rbuffer_create(buffer, sz_hlf);
215     lbf->next = rbuffer_create(&buffer[sz_hlf], sz_hlf);
216     lbf->sz_hlf = sz_hlf;
217 }
218
219 void
220 line_free(struct linebuffer* lbf, size_t sz_hlf)
221 {
222     void* ptr =
223         (void*)MIN((ptr_t)lbf->current->buffer, (ptr_t)lbf->next->buffer);
224     vfree(ptr);
225 }
226
227 void
228 term_sendsig(struct term* tdev, int signal)
229 {
230     if ((tdev->lflags & _ISIG)) {
231         proc_setsignal(get_process(tdev->fggrp), signal);
232     }
233 }