1 /*
2 * Copyright (c) 2006-2023, RT-Thread Development Team
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 *
6 * Change Logs:
7 * Date Author Notes
8 * 2023-11-13 Shell init ver.
9 */
10
11 #define DBG_TAG "lwp.tty"
12 #define DBG_LVL DBG_INFO
13 #include <rtdbg.h>
14
15 #define TTY_CONF_INCLUDE_CCHARS
16 #include "tty_config.h"
17 #include "tty_internal.h"
18 #include "terminal.h"
19
20 /* configure option: timeout of tty drain wait */
21 static int tty_drainwait = 5 * 60;
22
23 #define TTY_NAME_PREFIX "tty"
alloc_device_name(const char * name)24 static char *alloc_device_name(const char *name)
25 {
26 char *tty_dev_name;
27 long name_buf_len = (sizeof(TTY_NAME_PREFIX) - 1) /* raw prefix */
28 + rt_strlen(name) /* custom name */
29 + 1; /* tailing \0 */
30
31 tty_dev_name = rt_malloc(name_buf_len);
32 if (tty_dev_name)
33 sprintf(tty_dev_name, "%s%s", TTY_NAME_PREFIX, name);
34 return tty_dev_name;
35 }
36
37 /* character device for tty */
38 #ifdef RT_USING_DEVICE_OPS
39 const static struct rt_device_ops tty_dev_ops = {
40 /* IO directly through device is not allowed */
41 };
42 #else
43 #error Must enable RT_USING_DEVICE_OPS in Kconfig
44 #endif
45
tty_fops_open(struct dfs_file * file)46 static int tty_fops_open(struct dfs_file *file)
47 {
48 int rc;
49 lwp_tty_t tp;
50 rt_device_t device;
51 int devtype = 0; /* unused */
52
53 if (file->vnode && file->vnode->data)
54 {
55 if (file->vnode->ref_count != 1)
56 {
57 rc = 0;
58 }
59 else
60 {
61 device = (rt_device_t)file->vnode->data;
62 tp = rt_container_of(device, struct lwp_tty, parent);
63 rc = bsd_ttydev_methods.d_open(tp, file->flags, devtype,
64 rt_thread_self());
65 }
66 }
67 else
68 {
69 rc = -EINVAL;
70 }
71
72 return rc;
73 }
74
tty_fops_close(struct dfs_file * file)75 static int tty_fops_close(struct dfs_file *file)
76 {
77 int rc;
78 lwp_tty_t tp;
79 rt_device_t device;
80 int fflags = FFLAGS(file->flags);
81 int devtype = 0; /* unused */
82
83 if (file->vnode && file->vnode->data)
84 {
85 if (file->vnode->ref_count != 1)
86 {
87 rc = 0;
88 }
89 else
90 {
91 device = (rt_device_t)file->vnode->data;
92 tp = rt_container_of(device, struct lwp_tty, parent);
93 rc = bsd_ttydev_methods.d_close(tp, fflags, devtype, rt_thread_self());
94 }
95 }
96 else
97 {
98 rc = -EINVAL;
99 }
100
101 return rc;
102 }
103
tty_fops_ioctl(struct dfs_file * file,int cmd,void * arg)104 static int tty_fops_ioctl(struct dfs_file *file, int cmd, void *arg)
105 {
106 int rc;
107 lwp_tty_t tp;
108 rt_device_t device;
109
110 if (file->vnode && file->vnode->data)
111 {
112 device = (rt_device_t)file->vnode->data;
113 tp = rt_container_of(device, struct lwp_tty, parent);
114 rc = lwp_tty_ioctl_adapter(tp, cmd, file->flags, arg, rt_thread_self());
115 }
116 else
117 {
118 rc = -EINVAL;
119 }
120
121 return rc;
122 }
123
tty_fops_read(struct dfs_file * file,void * buf,size_t count,off_t * pos)124 static ssize_t tty_fops_read(struct dfs_file *file, void *buf, size_t count,
125 off_t *pos)
126 {
127 ssize_t rc = 0;
128 int error;
129 struct uio uio;
130 struct iovec iov;
131 rt_device_t device;
132 struct lwp_tty *tp;
133 int ioflags;
134 int oflags = file->flags;
135
136 if (file->vnode && file->vnode->data)
137 {
138 device = (rt_device_t)file->vnode->data;
139 tp = rt_container_of(device, struct lwp_tty, parent);
140
141 /* setup ioflags */
142 ioflags = 0;
143 if (oflags & O_NONBLOCK)
144 ioflags |= IO_NDELAY;
145
146 /* setup uio parameters */
147 iov.iov_base = (void *)buf;
148 iov.iov_len = count;
149 uio.uio_offset = file->fpos;
150 uio.uio_resid = count;
151 uio.uio_iov = &iov;
152 uio.uio_iovcnt = 1;
153 uio.uio_rw = UIO_READ;
154
155 rc = count;
156 error = bsd_ttydev_methods.d_read(tp, &uio, ioflags);
157 rc -= uio.uio_resid;
158 if (error)
159 {
160 LOG_D("%s: failed to write %d bytes of data. error code %d",
161 __func__, uio.uio_resid, error);
162 rc = error;
163 }
164
165 /* reset file context */
166 file->fpos = uio.uio_offset;
167 }
168
169 if (rc)
170 LOG_D("%s(len=%d, buf=%c \"%d\")", __func__, rc, *((char *)buf),
171 *((char *)buf));
172 return rc;
173 }
174
tty_fops_write(struct dfs_file * file,const void * buf,size_t count,off_t * pos)175 static ssize_t tty_fops_write(struct dfs_file *file, const void *buf,
176 size_t count, off_t *pos)
177 {
178 ssize_t rc = 0;
179 int error;
180 struct uio uio;
181 struct iovec iov;
182 rt_device_t device;
183 struct lwp_tty *tp;
184 int ioflags;
185 int oflags = file->flags;
186
187 if (file->vnode && file->vnode->data)
188 {
189 device = (rt_device_t)file->vnode->data;
190 tp = rt_container_of(device, struct lwp_tty, parent);
191
192 /* setup ioflags */
193 ioflags = 0;
194 if (oflags & O_NONBLOCK)
195 ioflags |= IO_NDELAY;
196
197 /* setup uio parameters */
198 iov.iov_base = (void *)buf;
199 iov.iov_len = count;
200 uio.uio_offset = file->fpos;
201 uio.uio_resid = count;
202 uio.uio_iov = &iov;
203 uio.uio_iovcnt = 1;
204 uio.uio_rw = UIO_WRITE;
205
206 rc = count;
207 error = bsd_ttydev_methods.d_write(tp, &uio, ioflags);
208 if (error)
209 {
210 rc = error;
211 LOG_D("%s: failed to write %d bytes of data. error code %d",
212 __func__, uio.uio_resid, error);
213 }
214 else
215 {
216 rc -= uio.uio_resid;
217 }
218
219 /* reset file context */
220 file->fpos = uio.uio_offset;
221 }
222 return rc;
223 }
224
tty_fops_flush(struct dfs_file * file)225 static int tty_fops_flush(struct dfs_file *file)
226 {
227 return -EINVAL;
228 }
229
tty_fops_lseek(struct dfs_file * file,off_t offset,int wherece)230 static off_t tty_fops_lseek(struct dfs_file *file, off_t offset, int wherece)
231 {
232 return -EINVAL;
233 }
234
tty_fops_truncate(struct dfs_file * file,off_t offset)235 static int tty_fops_truncate(struct dfs_file *file, off_t offset)
236 {
237 /**
238 * regarding to POSIX.1, TRUNC is not supported for tty device.
239 * return 0 always to make filesystem happy
240 */
241 return 0;
242 }
243
tty_fops_poll(struct dfs_file * file,struct rt_pollreq * req)244 static int tty_fops_poll(struct dfs_file *file, struct rt_pollreq *req)
245 {
246 int rc;
247 rt_device_t device;
248 struct lwp_tty *tp;
249
250 if (file->vnode && file->vnode->data)
251 {
252 device = (rt_device_t)file->vnode->data;
253 tp = rt_container_of(device, struct lwp_tty, parent);
254
255 rc = bsd_ttydev_methods.d_poll(tp, req, rt_thread_self());
256 }
257 else
258 {
259 rc = -1;
260 }
261
262 return rc;
263 }
264
tty_fops_mmap(struct dfs_file * file,struct lwp_avl_struct * mmap)265 static int tty_fops_mmap(struct dfs_file *file, struct lwp_avl_struct *mmap)
266 {
267 return -EINVAL;
268 }
269
tty_fops_lock(struct dfs_file * file,struct file_lock * flock)270 static int tty_fops_lock(struct dfs_file *file, struct file_lock *flock)
271 {
272 return -EINVAL;
273 }
274
tty_fops_flock(struct dfs_file * file,int operation,struct file_lock * flock)275 static int tty_fops_flock(struct dfs_file *file, int operation, struct file_lock *flock)
276 {
277 return -EINVAL;
278 }
279
280 static struct dfs_file_ops tty_file_ops = {
281 .open = tty_fops_open,
282 .close = tty_fops_close,
283 .ioctl = tty_fops_ioctl,
284 .read = tty_fops_read,
285 .write = tty_fops_write,
286 .flush = tty_fops_flush,
287 .lseek = tty_fops_lseek,
288 .truncate = tty_fops_truncate,
289 .poll = tty_fops_poll,
290 .mmap = tty_fops_mmap,
291 .lock = tty_fops_lock,
292 .flock = tty_fops_flock,
293 };
294
device_setup(lwp_tty_t terminal)295 rt_inline void device_setup(lwp_tty_t terminal)
296 {
297 terminal->parent.type = RT_Device_Class_Char;
298 #ifdef RT_USING_DEVICE_OPS
299 terminal->parent.ops = &tty_dev_ops;
300 #else
301 #error Must enable RT_USING_DEVICE_OPS in Kconfig
302 #endif
303 }
304
305 /* register TTY device */
lwp_tty_register(lwp_tty_t terminal,const char * name)306 rt_err_t lwp_tty_register(lwp_tty_t terminal, const char *name)
307 {
308 rt_err_t rc = -RT_ENOMEM;
309 const char *tty_name;
310 char *alloc_name;
311
312 if (terminal->t_devsw->tsw_flags & TF_NOPREFIX)
313 {
314 alloc_name = RT_NULL;
315 tty_name = name;
316 }
317 else
318 {
319 alloc_name = alloc_device_name(name);
320 tty_name = alloc_name;
321 }
322
323 if (tty_name)
324 {
325 device_setup(terminal);
326 rc = rt_device_register(&terminal->parent, tty_name, 0);
327 if (rc == RT_EOK)
328 {
329 terminal->parent.fops = &tty_file_ops;
330
331 LOG_D("%s() /dev/%s device registered", __func__, tty_name);
332 }
333
334 rt_free(alloc_name);
335 }
336 return rc;
337 }
338
tty_init_termios(lwp_tty_t tp)339 static void tty_init_termios(lwp_tty_t tp)
340 {
341 struct termios *t = &tp->t_termios_init_in;
342
343 t->c_cflag = TTYDEF_CFLAG;
344 t->c_iflag = TTYDEF_IFLAG;
345 t->c_lflag = TTYDEF_LFLAG;
346 t->c_oflag = TTYDEF_OFLAG;
347 t->__c_ispeed = TTYDEF_SPEED;
348 t->__c_ospeed = TTYDEF_SPEED;
349
350 memcpy(&t->c_cc, tty_ctrl_charset,
351 sizeof(tty_ctrl_charset) / sizeof(tty_ctrl_charset[0]));
352
353 #ifdef USING_BSD_INIT_LOCK_DEVICE
354 tp->t_termios_init_out = *t;
355 #endif /* USING_BSD_INIT_LOCK_DEVICE */
356 }
357
lwp_tty_create_ext(lwp_ttydevsw_t handle,void * softc,rt_mutex_t custom_mtx)358 lwp_tty_t lwp_tty_create_ext(lwp_ttydevsw_t handle, void *softc,
359 rt_mutex_t custom_mtx)
360 {
361 lwp_tty_t tp;
362
363 tp = rt_calloc(1, sizeof(struct lwp_tty)
364 #ifdef USING_BSD_SIGINFO
365 + LWP_TTY_PRBUF_SIZE
366 #endif
367 );
368
369 if (!tp)
370 return tp;
371
372 bsd_devsw_init(handle);
373
374 #ifdef USING_BSD_SIGINFO
375 tp->t_prbufsz = LWP_TTY_PRBUF_SIZE;
376 #endif
377 tp->t_devsw = handle;
378 tp->t_devswsoftc = softc;
379 tp->t_flags = handle->tsw_flags;
380 tp->t_drainwait = tty_drainwait;
381
382 tty_init_termios(tp);
383
384 cv_init(&tp->t_inwait, "ttyin");
385 cv_init(&tp->t_outwait, "ttyout");
386 cv_init(&tp->t_outserwait, "ttyosr");
387 cv_init(&tp->t_bgwait, "ttybg");
388 cv_init(&tp->t_dcdwait, "ttydcd");
389
390 rt_wqueue_init(&tp->t_inpoll);
391 rt_wqueue_init(&tp->t_outpoll);
392
393 /* Allow drivers to use a custom mutex to lock the TTY. */
394 if (custom_mtx != NULL)
395 {
396 tp->t_mtx = custom_mtx;
397 }
398 else
399 {
400 tp->t_mtx = &tp->t_mtxobj;
401 rt_mutex_init(&tp->t_mtxobj, "ttydev", RT_IPC_FLAG_PRIO);
402 }
403
404 #ifdef USING_BSD_POLL
405 knlist_init_mtx(&tp->t_inpoll.si_note, tp->t_mtx);
406 knlist_init_mtx(&tp->t_outpoll.si_note, tp->t_mtx);
407 #endif
408
409 return tp;
410 }
411
lwp_tty_create(lwp_ttydevsw_t handle,void * softc)412 lwp_tty_t lwp_tty_create(lwp_ttydevsw_t handle, void *softc)
413 {
414 return lwp_tty_create_ext(handle, softc, NULL);
415 }
416
lwp_tty_delete(lwp_tty_t tp)417 void lwp_tty_delete(lwp_tty_t tp)
418 {
419 /*
420 * ttyydev_leave() usually frees the i/o queues earlier, but it is
421 * not always called between queue allocation and here. The queues
422 * may be allocated by ioctls on a pty control device without the
423 * corresponding pty slave device ever being open, or after it is
424 * closed.
425 */
426 ttyinq_free(&tp->t_inq);
427 ttyoutq_free(&tp->t_outq);
428 rt_wqueue_wakeup_all(&tp->t_inpoll, (void *)POLLHUP);
429 rt_wqueue_wakeup_all(&tp->t_outpoll, (void *)POLLHUP);
430
431 #ifdef USING_BSD_POLL
432 knlist_destroy(&tp->t_inpoll.si_note);
433 knlist_destroy(&tp->t_outpoll.si_note);
434 #endif
435
436 cv_destroy(&tp->t_inwait);
437 cv_destroy(&tp->t_outwait);
438 cv_destroy(&tp->t_bgwait);
439 cv_destroy(&tp->t_dcdwait);
440 cv_destroy(&tp->t_outserwait);
441
442 if (tp->t_mtx == &tp->t_mtxobj)
443 rt_mutex_detach(&tp->t_mtxobj);
444 ttydevsw_free(tp);
445 rt_device_unregister(&tp->parent);
446 rt_free(tp);
447 }
448
449 /*
450 * Report on state of foreground process group.
451 */
tty_info(struct lwp_tty * tp)452 void tty_info(struct lwp_tty *tp)
453 {
454 /* TODO */
455 return;
456 }
457