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 #ifndef __LWP_TERMINAL_H__
12 #define __LWP_TERMINAL_H__
13 
14 #include "bsd_ttyqueue.h"
15 #include "bsd_ttydisc.h"
16 
17 #ifdef USING_BSD_HOOK
18 #include "bsd_ttyhook.h"
19 #endif
20 
21 #include <lwp.h>
22 #include <rtdef.h>
23 
24 /* include kernel header for termios base definitions */
25 #include <termios.h>
26 /* for _POSIX_VDISABLE */
27 #include <unistd.h>
28 
29 /*-
30  * SPDX-License-Identifier: BSD-2-Clause
31  *
32  * Copyright (c) 2008 Ed Schouten <ed@FreeBSD.org>
33  * All rights reserved.
34  *
35  * Portions of this software were developed under sponsorship from Snow
36  * B.V., the Netherlands.
37  *
38  * Redistribution and use in source and binary forms, with or without
39  * modification, are permitted provided that the following conditions
40  * are met:
41  * 1. Redistributions of source code must retain the above copyright
42  *    notice, this list of conditions and the following disclaimer.
43  * 2. Redistributions in binary form must reproduce the above copyright
44  *    notice, this list of conditions and the following disclaimer in the
45  *    documentation and/or other materials provided with the distribution.
46  *
47  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
48  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
49  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
50  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
51  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
52  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
53  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
54  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
55  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
56  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
57  * SUCH DAMAGE.
58  */
59 
60 struct lwp_tty;
61 
62 /*
63  * Driver routines that are called from the line discipline to adjust
64  * hardware parameters and such.
65  */
66 typedef int tsw_open_t(struct lwp_tty *tp);
67 typedef void tsw_close_t(struct lwp_tty *tp);
68 typedef void tsw_outwakeup_t(struct lwp_tty *tp);
69 typedef void tsw_inwakeup_t(struct lwp_tty *tp);
70 typedef int tsw_ioctl_t(struct lwp_tty *tp, rt_ubase_t cmd, rt_caddr_t data,
71                         struct rt_thread *td);
72 typedef int tsw_cioctl_t(struct lwp_tty *tp, int unit, rt_ubase_t cmd, rt_caddr_t data,
73                          struct rt_thread *td);
74 typedef int tsw_param_t(struct lwp_tty *tp, struct termios *t);
75 typedef int tsw_modem_t(struct lwp_tty *tp, int sigon, int sigoff);
76 typedef int tsw_mmap_t(struct lwp_tty *tp, vm_ooffset_t offset,
77                        vm_paddr_t *paddr, int nprot, vm_memattr_t *memattr);
78 
79 typedef void tsw_pktnotify_t(struct lwp_tty *tp, char event);
80 typedef void tsw_free_t(void *softc);
81 typedef rt_bool_t tsw_busy_t(struct lwp_tty *tp);
82 
83 struct lwp_ttydevsw
84 {
85     unsigned int tsw_flags; /* Default TTY flags. */
86 
87     tsw_open_t *tsw_open;   /* Device opening. */
88     tsw_close_t *tsw_close; /* Device closure. */
89 
90     tsw_outwakeup_t *tsw_outwakeup; /* Output available. */
91     tsw_inwakeup_t *tsw_inwakeup;   /* Input can be stored again. */
92 
93     tsw_ioctl_t *tsw_ioctl;   /* ioctl() hooks. */
94     tsw_cioctl_t *tsw_cioctl; /* ioctl() on control devices. */
95     tsw_param_t *tsw_param;   /* TIOCSETA device parameter setting. */
96     tsw_modem_t *tsw_modem;   /* Modem sigon/sigoff. */
97 
98     tsw_mmap_t *tsw_mmap;           /* mmap() hooks. */
99     tsw_pktnotify_t *tsw_pktnotify; /* TIOCPKT events. */
100 
101     tsw_free_t *tsw_free; /* Destructor. */
102 
103     tsw_busy_t *tsw_busy; /* Draining output. */
104 
105     void *tsw_spare[3]; /* For future use. */
106 };
107 typedef struct lwp_ttydevsw *lwp_ttydevsw_t;
108 
109 struct lwp_tty
110 {
111     struct rt_device parent;  /* inherit from Class:RT_Device */
112     struct rt_mutex *t_mtx;   /* TTY lock. */
113     struct rt_mutex t_mtxobj; /* Per-TTY lock (when not borrowing). */
114     rt_list_t t_list;         /* (l) TTY list entry. */
115     int t_drainwait;          /* (t) TIOCDRAIN timeout seconds. */
116     unsigned int t_flags;     /* (t) Terminal option flags. */
117 /* Keep flags in sync with db_show_tty and pstat(8). */
118 #define TF_NOPREFIX    0x00001 /* Don't prepend "tty" to device name. */
119 #define TF_INITLOCK    0x00002 /* Create init/lock state devices. */
120 #define TF_CALLOUT     0x00004 /* Create "cua" devices. */
121 #define TF_OPENED_IN   0x00008 /* "tty" node is in use. */
122 #define TF_OPENED_OUT  0x00010 /* "cua" node is in use. */
123 #define TF_OPENED_CONS 0x00020 /* Device in use as console. */
124 #define TF_OPENED      (TF_OPENED_IN | TF_OPENED_OUT | TF_OPENED_CONS)
125 #define TF_GONE        0x00040 /* Device node is gone. */
126 #define TF_OPENCLOSE   0x00080 /* Device is in open()/close(). */
127 #define TF_ASYNC       0x00100 /* Asynchronous I/O enabled. */
128 #define TF_LITERAL     0x00200 /* Accept the next character literally. */
129 #define TF_HIWAT_IN    0x00400 /* We've reached the input watermark. */
130 #define TF_HIWAT_OUT   0x00800 /* We've reached the output watermark. */
131 #define TF_HIWAT       (TF_HIWAT_IN | TF_HIWAT_OUT)
132 #define TF_STOPPED     0x01000 /* Output flow control - stopped. */
133 #define TF_EXCLUDE     0x02000 /* Exclusive access. */
134 #define TF_BYPASS      0x04000 /* Optimized input path. */
135 #define TF_ZOMBIE      0x08000 /* Modem disconnect received. */
136 #define TF_HOOK        0x10000 /* TTY has hook attached. */
137 #define TF_BUSY_IN     0x20000 /* Process busy in read() -- not supported. */
138 #define TF_BUSY_OUT    0x40000 /* Process busy in write(). */
139 #define TF_BUSY        (TF_BUSY_IN | TF_BUSY_OUT)
140     unsigned int t_revokecnt; /* (t) revoke() count. */
141 
142     /* Buffering mechanisms. */
143     struct ttyinq t_inq;   /* (t) Input queue. */
144     size_t t_inlow;        /* (t) Input low watermark. */
145     struct ttyoutq t_outq; /* (t) Output queue. */
146     size_t t_outlow;       /* (t) Output low watermark. */
147 
148     /* Sleeping mechanisms. */
149     struct rt_condvar t_inwait;     /* (t) Input wait queue. */
150     struct rt_condvar t_outwait;    /* (t) Output wait queue. */
151     struct rt_condvar t_outserwait; /* (t) Serial output wait queue. */
152     struct rt_condvar t_bgwait;     /* (t) Background wait queue. */
153     struct rt_condvar t_dcdwait;    /* (t) Carrier Detect wait queue. */
154 
155     struct rt_wqueue t_inpoll;  /* (t) Input poll queue. */
156     struct rt_wqueue t_outpoll; /* (t) Output poll queue. */
157 
158 #ifdef USING_BSD_AIO
159     struct sigio *t_sigio; /* (t) Asynchronous I/O. */
160 #endif
161 
162     struct termios t_termios; /* (t) I/O processing flags. */
163     struct winsize t_winsize; /* (t) Window size. */
164     unsigned int t_column;    /* (t) Current cursor position. */
165     unsigned int t_writepos;  /* (t) Where input was interrupted. */
166     int t_compatflags;        /* (t) COMPAT_43TTY flags. */
167 
168     /* Init/lock-state devices. */
169     struct termios t_termios_init_in; /* tty%s.init. */
170     struct termios t_termios_lock_in;  /* tty%s.lock. */
171 #ifdef USING_BSD_INIT_LOCK_DEVICE
172     struct termios t_termios_init_out; /* cua%s.init. */
173     struct termios t_termios_lock_out; /* cua%s.lock. */
174 
175 #endif /* USING_BSD_INIT_LOCK_DEVICE */
176 
177     struct lwp_ttydevsw *t_devsw; /* (c) Driver hooks. */
178 #ifdef USING_BSD_HOOK
179     struct lwp_ttyhook *t_hook; /* (t) Capture/inject hook. */
180 #endif
181 
182     /* Process signal delivery. */
183     struct rt_processgroup *t_pgrp; /* (t) Foreground process group. */
184     struct rt_session *t_session;   /* (t) Associated session. */
185     unsigned int t_sessioncnt;      /* (t) Backpointing sessions. */
186 
187     void *t_devswsoftc; /* (c) Soft config, for drivers. */
188 #ifdef USING_BSD_HOOK
189     void *t_hooksoftc; /* (t) Soft config, for hooks. */
190 #endif
191 #ifdef USING_BSD_CHAR_DEVICE
192     struct cdev *t_dev; /* (c) Primary character device. */
193 #endif                  /* USING_BSD_CHAR_DEVICE */
194 #ifdef USING_BSD_SIGINFO
195     size_t t_prbufsz; /* (t) SIGINFO buffer size. */
196     char t_prbuf[];   /* (t) SIGINFO buffer. */
197 #endif /* USING_BSD_SIGINFO */
198 };
199 typedef struct lwp_tty *lwp_tty_t;
200 
201 /* Allocation and deallocation. */
202 void tty_rel_pgrp(struct lwp_tty *tp, struct rt_processgroup *pgrp);
203 void tty_rel_sess(struct lwp_tty *tp, struct rt_session *sess);
204 void tty_rel_gone(struct lwp_tty *tp);
205 
206 /* tty locking mechanism */
207 #define tty_getlock(tp) ((tp)->t_mtx)
208 #define tty_lock(tp)    rt_mutex_take(tty_getlock(tp), RT_WAITING_FOREVER);
209 #define tty_unlock(tp)  rt_mutex_release(tty_getlock(tp))
210 #define tty_lock_owned(tp) \
211     (rt_mutex_get_owner(tty_getlock(tp)) == rt_thread_self())
212 #define tty_lock_notrecused(tp) (rt_mutex_get_hold(tty_getlock(tp)) == 1)
213 #define tty_assert_locked(tp)   RT_ASSERT(tty_lock_owned(tp))
214 #define tty_lock_assert(tp, option)                        \
215     RT_ASSERT(((option) == (MA_OWNED | MA_NOTRECURSED)) && \
216     (tty_lock_owned(tp) && tty_lock_notrecused(tp)))
217 
218 /* System messages. */
219 int tty_checkoutq(struct lwp_tty *tp);
220 int tty_putchar(struct lwp_tty *tp, char c);
221 int tty_putstrn(struct lwp_tty *tp, const char *p, size_t n);
222 
223 int tty_ioctl(struct lwp_tty *tp, rt_ubase_t cmd, void *data, int fflag,
224               struct rt_thread *td);
225 int tty_ioctl_compat(struct lwp_tty *tp, rt_ubase_t cmd, rt_caddr_t data, int fflag,
226                      struct rt_thread *td);
227 void tty_set_winsize(struct lwp_tty *tp, const struct winsize *wsz);
228 void tty_init_console(struct lwp_tty *tp, speed_t speed);
229 void tty_flush(struct lwp_tty *tp, int flags);
230 void tty_hiwat_in_block(struct lwp_tty *tp);
231 void tty_hiwat_in_unblock(struct lwp_tty *tp);
232 dev_t tty_udev(struct lwp_tty *tp);
233 
234 /* tesing on tty */
235 #define tty_opened(tp)  ((tp)->t_flags & TF_OPENED)
236 #define tty_gone(tp)    ((tp)->t_flags & TF_GONE)
237 #define tty_softc(tp)   ((tp)->t_devswsoftc)
238 #define tty_devname(tp) ((tp)->parent.parent.name)
239 
240 /**
241  * @brief TTY registeration on device subsystem
242  *
243  * @warning It's the duty of the caller to ensure that the name is not
244  *          identical to any existed registered devices.
245  *
246  * @param terminal the target tty device
247  * @param name name of the device (must be exclusive)
248  * @return rt_err_t RT_EOK on success
249  */
250 rt_err_t lwp_tty_register(lwp_tty_t terminal, const char *name);
251 
252 /**
253  * @brief TTY allocation and deallocation. TTY devices can be deallocated when
254  *        the driver doesn't use it anymore, when the TTY isn't a session's
255  *        controlling TTY and when the device node isn't opened through devfs.
256  *
257  * @param handle device handle of tty
258  * @param softc device configuration binding on tty
259  * @param prefix device name prefix
260  * @param cutom_mtx the lock provided to protect tty
261  * @return lwp_tty_t NULL on failure
262  */
263 lwp_tty_t lwp_tty_create_ext(lwp_ttydevsw_t handle, void *softc,
264                              rt_mutex_t custom_mtx);
265 
266 /**
267  * @brief Handful version of lwp_tty_create_ext
268  *
269  * @param softc device configuration binding on tty
270  * @param cutom_mtx the lock provided to protect tty
271  * @param prefix device name prefix
272  * @return lwp_tty_t NULL on failure
273  */
274 lwp_tty_t lwp_tty_create(lwp_ttydevsw_t handle, void *softc);
275 
276 void lwp_tty_delete(lwp_tty_t tp);
277 
278 void lwp_tty_signal_sessleader(struct lwp_tty *tp, int sig);
279 
280 void lwp_tty_signal_pgrp(struct lwp_tty *tp, int sig);
281 
282 /**
283  * @brief Create a new pseudo-terminal multiplexer
284  *
285  * @param root_path path of root mount point of ptyfs
286  * @return rt_device_t new device object if succeed, otherwise NULL
287  */
288 rt_err_t lwp_ptmx_init(rt_device_t ptmx_device, const char *root_path);
289 
290 #define LWP_CONSOLE_LOWEST_PRIOR 0
291 #define LWP_CONSOLE_HIGHEST_PRIO INT_MAX
292 /**
293  * @brief Register an alternative backend tty device as console
294  */
295 rt_err_t lwp_console_register_backend(struct rt_device *bakdev, int prio);
296 
ttydevsw_open(struct lwp_tty * tp)297 rt_inline int ttydevsw_open(struct lwp_tty *tp)
298 {
299     tty_assert_locked(tp);
300     MPASS(!tty_gone(tp));
301 
302     return (tp->t_devsw->tsw_open(tp));
303 }
304 
ttydevsw_close(struct lwp_tty * tp)305 rt_inline void ttydevsw_close(struct lwp_tty *tp)
306 {
307     tty_assert_locked(tp);
308     MPASS(!tty_gone(tp));
309 
310     tp->t_devsw->tsw_close(tp);
311 }
312 
ttydevsw_outwakeup(struct lwp_tty * tp)313 rt_inline void ttydevsw_outwakeup(struct lwp_tty *tp)
314 {
315     tty_assert_locked(tp);
316     MPASS(!tty_gone(tp));
317 
318     /* Prevent spurious wakeups. */
319     if (ttydisc_getc_poll(tp) == 0)
320         return;
321 
322     tp->t_devsw->tsw_outwakeup(tp);
323 }
324 
ttydevsw_inwakeup(struct lwp_tty * tp)325 rt_inline void ttydevsw_inwakeup(struct lwp_tty *tp)
326 {
327     tty_assert_locked(tp);
328     MPASS(!tty_gone(tp));
329 
330     /* Prevent spurious wakeups. */
331     if (tp->t_flags & TF_HIWAT_IN)
332         return;
333 
334     tp->t_devsw->tsw_inwakeup(tp);
335 }
336 
ttydevsw_ioctl(struct lwp_tty * tp,rt_ubase_t cmd,rt_caddr_t data,struct rt_thread * td)337 rt_inline int ttydevsw_ioctl(struct lwp_tty *tp, rt_ubase_t cmd, rt_caddr_t data,
338                              struct rt_thread *td)
339 {
340     tty_assert_locked(tp);
341     MPASS(!tty_gone(tp));
342 
343     return (tp->t_devsw->tsw_ioctl(tp, cmd, data, td));
344 }
345 
ttydevsw_cioctl(struct lwp_tty * tp,int unit,rt_ubase_t cmd,rt_caddr_t data,struct rt_thread * td)346 rt_inline int ttydevsw_cioctl(struct lwp_tty *tp, int unit, rt_ubase_t cmd,
347                               rt_caddr_t data, struct rt_thread *td)
348 {
349     tty_assert_locked(tp);
350     MPASS(!tty_gone(tp));
351 
352     return (tp->t_devsw->tsw_cioctl(tp, unit, cmd, data, td));
353 }
354 
ttydevsw_param(struct lwp_tty * tp,struct termios * t)355 rt_inline int ttydevsw_param(struct lwp_tty *tp, struct termios *t)
356 {
357     MPASS(!tty_gone(tp));
358 
359     return (tp->t_devsw->tsw_param(tp, t));
360 }
361 
ttydevsw_modem(struct lwp_tty * tp,int sigon,int sigoff)362 rt_inline int ttydevsw_modem(struct lwp_tty *tp, int sigon, int sigoff)
363 {
364     MPASS(!tty_gone(tp));
365 
366     return (tp->t_devsw->tsw_modem(tp, sigon, sigoff));
367 }
368 
ttydevsw_mmap(struct lwp_tty * tp,vm_ooffset_t offset,vm_paddr_t * paddr,int nprot,vm_memattr_t * memattr)369 rt_inline int ttydevsw_mmap(struct lwp_tty *tp, vm_ooffset_t offset,
370                             vm_paddr_t *paddr, int nprot, vm_memattr_t *memattr)
371 {
372     MPASS(!tty_gone(tp));
373 
374     return (tp->t_devsw->tsw_mmap(tp, offset, paddr, nprot, memattr));
375 }
376 
ttydevsw_pktnotify(struct lwp_tty * tp,char event)377 rt_inline void ttydevsw_pktnotify(struct lwp_tty *tp, char event)
378 {
379     tty_assert_locked(tp);
380     MPASS(!tty_gone(tp));
381 
382     tp->t_devsw->tsw_pktnotify(tp, event);
383 }
384 
ttydevsw_free(struct lwp_tty * tp)385 rt_inline void ttydevsw_free(struct lwp_tty *tp)
386 {
387     MPASS(tty_gone(tp));
388 
389     tp->t_devsw->tsw_free(tty_softc(tp));
390 }
391 
ttydevsw_busy(struct lwp_tty * tp)392 rt_inline rt_bool_t ttydevsw_busy(struct lwp_tty *tp)
393 {
394     tty_assert_locked(tp);
395     MPASS(!tty_gone(tp));
396 
397     return (tp->t_devsw->tsw_busy(tp));
398 }
399 
ttydisc_read_poll(struct lwp_tty * tp)400 rt_inline size_t ttydisc_read_poll(struct lwp_tty *tp)
401 {
402     tty_assert_locked(tp);
403 
404     return ttyinq_bytescanonicalized(&tp->t_inq);
405 }
406 
ttydisc_write_poll(struct lwp_tty * tp)407 rt_inline size_t ttydisc_write_poll(struct lwp_tty *tp)
408 {
409     tty_assert_locked(tp);
410 
411     return ttyoutq_bytesleft(&tp->t_outq);
412 }
413 
414 #endif /* __LWP_TERMINAL_H__ */
415