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 #include "../bsd_porting.h"
11 #include "../tty_config.h"
12 #include "../terminal.h"
13 #include "../tty_internal.h"
14 
15 #include <rtdef.h>
16 #include <sys/ioctl.h>
17 
18 /*-
19  * SPDX-License-Identifier: BSD-2-Clause
20  *
21  * Copyright (c) 2008 Ed Schouten <ed@FreeBSD.org>
22  * All rights reserved.
23  *
24  * Portions of this software were developed under sponsorship from Snow
25  * B.V., the Netherlands.
26  *
27  * Redistribution and use in source and binary forms, with or without
28  * modification, are permitted provided that the following conditions
29  * are met:
30  * 1. Redistributions of source code must retain the above copyright
31  *    notice, this list of conditions and the following disclaimer.
32  * 2. Redistributions in binary form must reproduce the above copyright
33  *    notice, this list of conditions and the following disclaimer in the
34  *    documentation and/or other materials provided with the distribution.
35  *
36  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
37  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
38  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
39  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
40  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
41  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
42  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
43  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
44  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
45  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
46  * SUCH DAMAGE.
47  */
48 
49 static void tty_rel_free(struct lwp_tty *tp);
50 
51 /* Character device of /dev/console. */
52 static struct rt_device *dev_console;
53 #ifdef USING_BSD_CONSOLE_NAME
54 static const char *dev_console_filename;
55 #endif
56 
57 /*
58  * Flags that are supported and stored by this implementation.
59  */
60 #ifndef ALTWERASE
61 #define ALTWERASE 0
62 #endif
63 #ifndef NOKERNINFO
64 #define NOKERNINFO 0
65 #endif
66 #define TTYSUP_IFLAG                                                      \
67     (IGNBRK | BRKINT | IGNPAR | PARMRK | INPCK | ISTRIP | INLCR | IGNCR | \
68      ICRNL | IXON | IXOFF | IXANY | IMAXBEL | IUTF8)
69 #define TTYSUP_OFLAG (OPOST | ONLCR | TAB3 | ONOEOT | OCRNL | ONOCR | ONLRET)
70 #define TTYSUP_LFLAG                                                     \
71     (ECHOKE | ECHOE | ECHOK | ECHO | ECHONL | ECHOPRT | ECHOCTL | ISIG | \
72      ICANON | ALTWERASE | IEXTEN | TOSTOP | FLUSHO | NOKERNINFO | NOFLSH)
73 #define TTYSUP_CFLAG                                                       \
74     (CIGNORE | CSIZE | CSTOPB | CREAD | PARENB | PARODD | HUPCL | CLOCAL | \
75      CCTS_OFLOW | CRTS_IFLOW | CDTR_IFLOW | CDSR_OFLOW | CCAR_OFLOW |      \
76      CNO_RTSDTR | CBAUD)
77 
78 /*
79  * Set TTY buffer sizes.
80  */
81 
82 #define TTYBUF_MAX 65536
83 
84 #ifdef PRINTF_BUFR_SIZE
85 #define TTY_PRBUF_SIZE PRINTF_BUFR_SIZE
86 #else
87 #define TTY_PRBUF_SIZE 256
88 #endif
89 
90 /* Note: access to struct cdev:si_drv0.
91     Since pull-in, pull-out is not provided, this field
92     is constant value 0 in smart system */
93 #define dev2unit(d)        (0)
94 #define TTY_CALLOUT(tp, d) (dev2unit(d) & TTYUNIT_CALLOUT)
95 
96 /*
97  * Allocate buffer space if necessary, and set low watermarks, based on speed.
98  * Note that the ttyxxxq_setsize() functions may drop and then reacquire the tty
99  * lock during memory allocation.  They will return ENXIO if the tty disappears
100  * while unlocked.
101  */
tty_watermarks(struct lwp_tty * tp)102 static int tty_watermarks(struct lwp_tty *tp)
103 {
104     size_t bs = 0;
105     int error;
106 
107     /* Provide an input buffer for 2 seconds of data. */
108     if (tp->t_termios.c_cflag & CREAD)
109         bs =
110             MIN(bsd_speed_to_integer(tp->t_termios.__c_ispeed) / 5, TTYBUF_MAX);
111     error = ttyinq_setsize(&tp->t_inq, tp, bs);
112     if (error != 0)
113         return error;
114 
115     /* Set low watermark at 10% (when 90% is available). */
116     tp->t_inlow = (ttyinq_getallocatedsize(&tp->t_inq) * 9) / 10;
117 
118     /* Provide an output buffer for 2 seconds of data. */
119     bs = MIN(bsd_speed_to_integer(tp->t_termios.__c_ospeed) / 5, TTYBUF_MAX);
120     error = ttyoutq_setsize(&tp->t_outq, tp, bs);
121     if (error != 0)
122         return error;
123 
124     /* Set low watermark at 10% (when 90% is available). */
125     tp->t_outlow = (ttyoutq_getallocatedsize(&tp->t_outq) * 9) / 10;
126 
127     return 0;
128 }
129 
130 /**
131  * Drain outq
132  */
tty_drain(struct lwp_tty * tp,int leaving)133 static int tty_drain(struct lwp_tty *tp, int leaving)
134 {
135     rt_tick_t timeout_tick;
136     size_t bytes;
137     int error;
138 
139 #ifdef USING_BSD_HOOK
140     if (ttyhook_hashook(tp, getc_inject))
141         /* buffer is inaccessible */
142         return 0;
143 #endif /* USING_BSD_HOOK */
144 
145     /*
146      * For close(), use the recent historic timeout of "1 second without
147      * making progress".  For tcdrain(), use t_drainwait as the timeout,
148      * with zero meaning "no timeout" which gives POSIX behavior.
149      */
150     if (leaving)
151         timeout_tick = rt_tick_get() + RT_TICK_PER_SECOND;
152     else if (tp->t_drainwait != 0)
153         timeout_tick = rt_tick_get() + RT_TICK_PER_SECOND * tp->t_drainwait;
154     else
155         timeout_tick = 0;
156 
157     /*
158      * Poll the output buffer and the hardware for completion, at 10 Hz.
159      * Polling is required for devices which are not able to signal an
160      * interrupt when the transmitter becomes idle (most USB serial devs).
161      * The unusual structure of this loop ensures we check for busy one more
162      * time after tty_timedwait() returns EWOULDBLOCK, so that success has
163      * higher priority than timeout if the IO completed in the last 100mS.
164      */
165     error = 0;
166     bytes = ttyoutq_bytesused(&tp->t_outq);
167     for (;;)
168     {
169         if (ttyoutq_bytesused(&tp->t_outq) == 0 && !ttydevsw_busy(tp))
170             return 0;
171         if (error != 0)
172             return error;
173         ttydevsw_outwakeup(tp);
174         error = tty_timedwait(tp, &tp->t_outwait, RT_TICK_PER_SECOND / 10);
175         if (error != 0 && error != EWOULDBLOCK)
176             return error;
177         else if (timeout_tick == 0 || rt_tick_get() < timeout_tick)
178             error = 0;
179         else if (leaving && ttyoutq_bytesused(&tp->t_outq) < bytes)
180         {
181             /* In close, making progress, grant an extra second. */
182             error = 0;
183             timeout_tick += RT_TICK_PER_SECOND;
184             bytes = ttyoutq_bytesused(&tp->t_outq);
185         }
186     }
187 }
188 
189 /*
190  * Though ttydev_enter() and ttydev_leave() seem to be related, they
191  * don't have to be used together. ttydev_enter() is used by the cdev
192  * operations to prevent an actual operation from being processed when
193  * the TTY has been abandoned. ttydev_leave() is used by ttydev_open()
194  * and ttydev_close() to determine whether per-TTY data should be
195  * deallocated.
196  */
197 
ttydev_enter(struct lwp_tty * tp)198 rt_inline int ttydev_enter(struct lwp_tty *tp)
199 {
200     rt_err_t error = tty_lock(tp);
201     if (error)
202         RT_ASSERT(0);
203 
204     if (tty_gone(tp) || !tty_opened(tp))
205     {
206         /* Device is already gone. */
207         tty_unlock(tp);
208         return -ENXIO;
209     }
210 
211     return 0;
212 }
213 
ttydev_leave(struct lwp_tty * tp)214 static void ttydev_leave(struct lwp_tty *tp)
215 {
216     tty_assert_locked(tp);
217 
218     if (tty_opened(tp) || tp->t_flags & TF_OPENCLOSE)
219     {
220         /* Device is still opened somewhere. */
221         tty_unlock(tp);
222         return;
223     }
224 
225     tp->t_flags |= TF_OPENCLOSE;
226 
227     /* Remove console TTY. */
228     constty_clear(tp);
229 
230     /* Drain any output. */
231     if (!tty_gone(tp))
232         tty_drain(tp, 1);
233 
234     ttydisc_close(tp);
235 
236     /* Free i/o queues now since they might be large. */
237     ttyinq_free(&tp->t_inq);
238     tp->t_inlow = 0;
239     ttyoutq_free(&tp->t_outq);
240     tp->t_outlow = 0;
241 
242 #ifdef USING_BSD_KNOTE
243     knlist_clear(&tp->t_inpoll.si_note, 1);
244     knlist_clear(&tp->t_outpoll.si_note, 1);
245 #endif
246 
247     if (!tty_gone(tp))
248         ttydevsw_close(tp);
249 
250     tp->t_flags &= ~TF_OPENCLOSE;
251     cv_broadcast(&tp->t_dcdwait);
252     tty_rel_free(tp);
253 }
254 
255 /*
256  * Operations that are exposed through the character device in /dev.
257  */
ttydev_open(struct lwp_tty * tp,int oflags,int devtype,struct rt_thread * td)258 static int ttydev_open(struct lwp_tty *tp, int oflags, int devtype,
259                        struct rt_thread *td)
260 {
261     rt_device_t dev = &tp->parent;
262     int error;
263 
264     error = 0;
265     tty_lock(tp);
266     if (tty_gone(tp))
267     {
268         /* Device is already gone. */
269         tty_unlock(tp);
270         return -ENXIO;
271     }
272 
273     /*
274      * Block when other processes are currently opening or closing
275      * the TTY.
276      */
277     while (tp->t_flags & TF_OPENCLOSE)
278     {
279         error = tty_wait(tp, &tp->t_dcdwait);
280         if (error != 0)
281         {
282             tty_unlock(tp);
283             return error;
284         }
285     }
286     tp->t_flags |= TF_OPENCLOSE;
287 
288     /*
289      * Make sure the "tty" and "cua" device cannot be opened at the
290      * same time.  The console is a "tty" device.
291      */
292     if (TTY_CALLOUT(tp, dev))
293     {
294         if (tp->t_flags & (TF_OPENED_CONS | TF_OPENED_IN))
295         {
296             error = EBUSY;
297             goto done;
298         }
299     }
300     else
301     {
302         if (tp->t_flags & TF_OPENED_OUT)
303         {
304             error = EBUSY;
305             goto done;
306         }
307     }
308 
309     if (tp->t_flags & TF_EXCLUDE && priv_check(td, PRIV_TTY_EXCLUSIVE))
310     {
311         error = EBUSY;
312         goto done;
313     }
314 
315     if (!tty_opened(tp))
316     {
317         /* Set proper termios flags. */
318         if (TTY_CALLOUT(tp, dev))
319 #ifdef USING_BSD_INIT_LOCK_DEVICE
320             tp->t_termios = tp->t_termios_init_out;
321 #else
322             ;
323 #endif /* USING_BSD_INIT_LOCK_DEVICE */
324         else
325             tp->t_termios = tp->t_termios_init_in;
326 
327         ttydevsw_param(tp, &tp->t_termios);
328         /* Prevent modem control on callout devices and /dev/console. */
329         if (TTY_CALLOUT(tp, dev) || dev == dev_console)
330             tp->t_termios.c_cflag |= CLOCAL;
331 
332         if ((tp->t_termios.c_cflag & CNO_RTSDTR) == 0)
333             ttydevsw_modem(tp, SER_DTR | SER_RTS, 0);
334 
335         error = ttydevsw_open(tp);
336         if (error != 0)
337             goto done;
338 
339         ttydisc_open(tp);
340         error = tty_watermarks(tp);
341         if (error != 0)
342             goto done;
343     }
344 
345     /* Wait for Carrier Detect. */
346     if ((oflags & O_NONBLOCK) == 0 && (tp->t_termios.c_cflag & CLOCAL) == 0)
347     {
348         while ((ttydevsw_modem(tp, 0, 0) & SER_DCD) == 0)
349         {
350             error = tty_wait(tp, &tp->t_dcdwait);
351             if (error != 0)
352                 goto done;
353         }
354     }
355 
356     if (dev == dev_console)
357         tp->t_flags |= TF_OPENED_CONS;
358     else if (TTY_CALLOUT(tp, dev))
359         tp->t_flags |= TF_OPENED_OUT;
360     else
361         tp->t_flags |= TF_OPENED_IN;
362     MPASS((tp->t_flags & (TF_OPENED_CONS | TF_OPENED_IN)) == 0 ||
363           (tp->t_flags & TF_OPENED_OUT) == 0);
364 
365 done:
366     tp->t_flags &= ~TF_OPENCLOSE;
367     cv_broadcast(&tp->t_dcdwait);
368     ttydev_leave(tp);
369 
370     return error;
371 }
372 
ttydev_close(struct lwp_tty * tp,int fflag,int devtype __unused,struct rt_thread * td __unused)373 static int ttydev_close(struct lwp_tty *tp, int fflag, int devtype __unused,
374                         struct rt_thread *td __unused)
375 {
376     rt_device_t dev = &tp->parent;
377 
378     tty_lock(tp);
379 
380     /*
381      * Don't actually close the device if it is being used as the
382      * console.
383      */
384     MPASS((tp->t_flags & (TF_OPENED_CONS | TF_OPENED_IN)) == 0 ||
385           (tp->t_flags & TF_OPENED_OUT) == 0);
386     if (dev == dev_console)
387         tp->t_flags &= ~TF_OPENED_CONS;
388     else
389         tp->t_flags &= ~(TF_OPENED_IN | TF_OPENED_OUT);
390 
391     if (tp->t_flags & TF_OPENED)
392     {
393         tty_unlock(tp);
394         return 0;
395     }
396 
397     /* If revoking, flush output now to avoid draining it later. */
398     if (fflag & FREVOKE)
399         tty_flush(tp, FWRITE);
400 
401     tp->t_flags &= ~TF_EXCLUDE;
402 
403     /* Properly wake up threads that are stuck - revoke(). */
404     tp->t_revokecnt++;
405     tty_wakeup(tp, FREAD | FWRITE);
406     cv_broadcast(&tp->t_bgwait);
407     cv_broadcast(&tp->t_dcdwait);
408 
409     ttydev_leave(tp);
410 
411     return 0;
412 }
413 
tty_wait_background(struct lwp_tty * tp,struct rt_thread * td,int sig)414 int tty_wait_background(struct lwp_tty *tp, struct rt_thread *td, int sig)
415 {
416     struct rt_lwp *p;
417     struct rt_processgroup *pg;
418     int error;
419 
420     MPASS(sig == SIGTTIN || sig == SIGTTOU);
421     tty_assert_locked(tp);
422 
423     p = td->lwp;
424     for (;;)
425     {
426         pg = p->pgrp;
427         PGRP_LOCK(pg);
428         LWP_LOCK(p);
429 
430         /*
431          * pg may no longer be our process group.
432          * Re-check after locking.
433          */
434         if (p->pgrp != pg)
435         {
436             LWP_UNLOCK(p);
437             PGRP_UNLOCK(pg);
438             continue;
439         }
440 
441         /*
442          * The process should only sleep, when:
443          * - This terminal is the controlling terminal
444          * - Its process group is not the foreground process
445          *   group
446          * - The parent process isn't waiting for the child to
447          *   exit
448          * - the signal to send to the process isn't masked
449          */
450         if (!tty_is_ctty(tp, p) || pg == tp->t_pgrp)
451         {
452             /* Allow the action to happen. */
453             LWP_UNLOCK(p);
454             PGRP_UNLOCK(pg);
455             return 0;
456         }
457 
458         /* Note: process itself don't have a sigmask in smart */
459         if (lwp_sigisign(p, sig) ||
460             lwp_sigismember(&td->signal.sigset_mask, sig))
461         {
462             /* Only allow them in write()/ioctl(). */
463             LWP_UNLOCK(p);
464             PGRP_UNLOCK(pg);
465             return (sig == SIGTTOU ? 0 : -EIO);
466         }
467 
468 #ifdef USING_VFORK_FLAG
469         if ((p->p_flag & P_PPWAIT) != 0 || pg->is_orphaned)
470 #else
471         if (pg->is_orphaned)
472 #endif
473         {
474             /* Don't allow the action to happen. */
475             LWP_UNLOCK(p);
476             PGRP_UNLOCK(pg);
477             return -EIO;
478         }
479         LWP_UNLOCK(p);
480 
481         /*
482          * Send the signal and sleep until we're the new
483          * foreground process group.
484          */
485         if (sig != 0)
486         {
487             lwp_pgrp_signal_kill(pg, sig, SI_KERNEL, 0);
488         }
489 
490         PGRP_UNLOCK(pg);
491 
492         error = tty_wait(tp, &tp->t_bgwait);
493         if (error)
494             return error;
495     }
496 }
497 
ttydev_read(struct lwp_tty * tp,struct uio * uio,int ioflag)498 static int ttydev_read(struct lwp_tty *tp, struct uio *uio, int ioflag)
499 {
500     int error;
501 
502     error = ttydev_enter(tp);
503     if (error)
504         goto done;
505     error = ttydisc_read(tp, uio, ioflag);
506     tty_unlock(tp);
507 
508     /*
509      * The read() call should not throw an error when the device is
510      * being destroyed. Silently convert it to an EOF.
511      */
512 done:
513     if (error == -ENXIO)
514         error = 0;
515     return error;
516 }
517 
ttydev_write(struct lwp_tty * tp,struct uio * uio,int ioflag)518 static int ttydev_write(struct lwp_tty *tp, struct uio *uio, int ioflag)
519 {
520 #ifdef USING_BSD_DEFER_STOP
521     int defer;
522 #endif
523     int error;
524 
525     error = ttydev_enter(tp);
526     if (error)
527         return error;
528 
529     if (tp->t_termios.c_lflag & TOSTOP)
530     {
531         error = tty_wait_background(tp, curthread, SIGTTOU);
532         if (error)
533             goto done;
534     }
535 
536     if (ioflag & IO_NDELAY && tp->t_flags & TF_BUSY_OUT)
537     {
538         /* Allow non-blocking writes to bypass serialization. */
539         error = ttydisc_write(tp, uio, ioflag);
540     }
541     else
542     {
543         /* Serialize write() calls. */
544         while (tp->t_flags & TF_BUSY_OUT)
545         {
546             error = tty_wait(tp, &tp->t_outserwait);
547             if (error)
548                 goto done;
549         }
550 
551         tp->t_flags |= TF_BUSY_OUT;
552 #ifdef USING_BSD_DEFER_STOP
553         defer = sigdeferstop(SIGDEFERSTOP_ERESTART);
554 #endif
555         error = ttydisc_write(tp, uio, ioflag);
556 #ifdef USING_BSD_DEFER_STOP
557         sigallowstop(defer);
558 #endif
559         tp->t_flags &= ~TF_BUSY_OUT;
560         cv_signal(&tp->t_outserwait);
561     }
562 
563 done:
564     tty_unlock(tp);
565     return error;
566 }
567 
ttydev_ioctl(struct lwp_tty * tp,rt_ubase_t cmd,rt_caddr_t data,int fflag,struct rt_thread * td)568 static int ttydev_ioctl(struct lwp_tty *tp, rt_ubase_t cmd, rt_caddr_t data, int fflag,
569                         struct rt_thread *td)
570 {
571     int error;
572 
573     error = ttydev_enter(tp);
574     if (error)
575         return (error);
576 
577     switch (cmd)
578     {
579         case TIOCCBRK:
580         case TIOCCONS:
581         case TIOCDRAIN:
582         case TIOCEXCL:
583         case TIOCFLUSH:
584         case TIOCNXCL:
585         case TIOCSBRK:
586         case TIOCSCTTY:
587         case TIOCSETA:
588         case TIOCSETAF:
589         case TIOCSETAW:
590         case TIOCSPGRP:
591         case TIOCSTART:
592         case TIOCSTAT:
593         case TIOCSTI:
594         case TIOCSTOP:
595         case TIOCSWINSZ:
596 #if USING_BSD_TIOCSDRAINWAIT
597     case TIOCSDRAINWAIT:
598     case TIOCSETD:
599 #endif /* USING_BSD_TIOCSDRAINWAIT */
600 #ifdef COMPAT_43TTY
601         case TIOCLBIC:
602         case TIOCLBIS:
603         case TIOCLSET:
604         case TIOCSETC:
605         case OTIOCSETD:
606         case TIOCSETN:
607         case TIOCSETP:
608         case TIOCSLTC:
609 #endif /* COMPAT_43TTY */
610             /*
611              * If the ioctl() causes the TTY to be modified, let it
612              * wait in the background.
613              */
614             error = tty_wait_background(tp, curthread, SIGTTOU);
615             if (error)
616                 goto done;
617     }
618 
619 #ifdef USING_BSD_INIT_LOCK_DEVICE
620     if (cmd == TIOCSETA || cmd == TIOCSETAW || cmd == TIOCSETAF)
621     {
622         struct termios *old = &tp->t_termios;
623         struct termios *new = (struct termios *)data;
624         struct termios *lock = TTY_CALLOUT(tp, dev) ? &tp->t_termios_lock_out
625                                                     : &tp->t_termios_lock_in;
626         int cc;
627 
628         /*
629          * Lock state devices.  Just overwrite the values of the
630          * commands that are currently in use.
631          */
632         new->c_iflag =
633             (old->c_iflag & lock->c_iflag) | (new->c_iflag & ~lock->c_iflag);
634         new->c_oflag =
635             (old->c_oflag & lock->c_oflag) | (new->c_oflag & ~lock->c_oflag);
636         new->c_cflag =
637             (old->c_cflag & lock->c_cflag) | (new->c_cflag & ~lock->c_cflag);
638         new->c_lflag =
639             (old->c_lflag & lock->c_lflag) | (new->c_lflag & ~lock->c_lflag);
640         for (cc = 0; cc < NCCS; ++cc)
641             if (lock->c_cc[cc])
642                 new->c_cc[cc] = old->c_cc[cc];
643         if (lock->__c_ispeed)
644             new->__c_ispeed = old->__c_ispeed;
645         if (lock->__c_ospeed)
646             new->__c_ospeed = old->__c_ospeed;
647     }
648 #endif /* USING_BSD_INIT_LOCK_DEVICE */
649 
650     error = tty_ioctl(tp, cmd, data, fflag, td);
651 done:
652     tty_unlock(tp);
653 
654     return (error);
655 }
656 
ttydev_poll(struct lwp_tty * tp,rt_pollreq_t * req,struct rt_thread * td)657 static int ttydev_poll(struct lwp_tty *tp, rt_pollreq_t *req, struct rt_thread *td)
658 {
659     int events = req->_key;
660     int error, revents = 0;
661 
662     error = ttydev_enter(tp);
663     if (error)
664         return ((events & (POLLIN | POLLRDNORM)) | POLLHUP);
665 
666     if (events & (POLLIN | POLLRDNORM))
667     {
668         /* See if we can read something. */
669         if (ttydisc_read_poll(tp) > 0)
670             revents |= events & (POLLIN | POLLRDNORM);
671     }
672 
673     if (tp->t_flags & TF_ZOMBIE)
674     {
675         /* Hangup flag on zombie state. */
676         revents |= POLLHUP;
677     }
678     else if (events & (POLLOUT | POLLWRNORM))
679     {
680         /* See if we can write something. */
681         if (ttydisc_write_poll(tp) > 0)
682             revents |= events & (POLLOUT | POLLWRNORM);
683     }
684 
685     if (revents == 0)
686     {
687         if (events & (POLLIN | POLLRDNORM))
688             rt_poll_add(&tp->t_inpoll, req);
689         if (events & (POLLOUT | POLLWRNORM))
690             rt_poll_add(&tp->t_outpoll, req);
691     }
692 
693     tty_unlock(tp);
694 
695     return revents;
696 }
697 
698 static struct cdevsw ttydev_cdevsw = {
699     .d_open = ttydev_open,
700     .d_close = ttydev_close,
701     .d_read = ttydev_read,
702     .d_write = ttydev_write,
703     .d_ioctl = ttydev_ioctl,
704 #if 0
705     .d_kqfilter = ttydev_kqfilter,
706 #endif
707     .d_poll = ttydev_poll,
708 #if 0
709     .d_mmap = ttydev_mmap,
710 #endif
711 
712 #ifdef USING_BSD_RAW_CDEVSW
713     .d_version = D_VERSION.d_name = "ttydev",
714     .d_flags = D_TTY,
715 #endif /* USING_BSD_RAW_CDEVSW */
716 };
717 
718 extern struct cdevsw bsd_ttydev_methods __attribute__((alias("ttydev_cdevsw")));
719 
720 /*
721  * Standard device routine implementations, mostly meant for
722  * pseudo-terminal device drivers. When a driver creates a new terminal
723  * device class, missing routines are patched.
724  */
725 #define panic(msg) RT_ASSERT(0 && msg)
726 
ttydevsw_defopen(struct lwp_tty * tp __unused)727 static int ttydevsw_defopen(struct lwp_tty *tp __unused)
728 {
729     return 0;
730 }
731 
ttydevsw_defclose(struct lwp_tty * tp __unused)732 static void ttydevsw_defclose(struct lwp_tty *tp __unused)
733 {
734 }
735 
ttydevsw_defoutwakeup(struct lwp_tty * tp __unused)736 static void ttydevsw_defoutwakeup(struct lwp_tty *tp __unused)
737 {
738     panic("Terminal device has output, while not implemented");
739 }
740 
ttydevsw_definwakeup(struct lwp_tty * tp __unused)741 static void ttydevsw_definwakeup(struct lwp_tty *tp __unused)
742 {
743 }
744 
ttydevsw_defioctl(struct lwp_tty * tp __unused,rt_ubase_t cmd __unused,rt_caddr_t data __unused,struct rt_thread * td __unused)745 static int ttydevsw_defioctl(struct lwp_tty *tp __unused, rt_ubase_t cmd __unused,
746                              rt_caddr_t data __unused,
747                              struct rt_thread *td __unused)
748 {
749     return -ENOSYS;
750 }
751 
ttydevsw_defcioctl(struct lwp_tty * tp __unused,int unit __unused,rt_ubase_t cmd __unused,rt_caddr_t data __unused,struct rt_thread * td __unused)752 static int ttydevsw_defcioctl(struct lwp_tty *tp __unused, int unit __unused,
753                               rt_ubase_t cmd __unused, rt_caddr_t data __unused,
754                               struct rt_thread *td __unused)
755 {
756     return -ENOSYS;
757 }
758 
ttydevsw_defparam(struct lwp_tty * tp __unused,struct termios * t)759 static int ttydevsw_defparam(struct lwp_tty *tp __unused, struct termios *t)
760 {
761     /*
762      * Allow the baud rate to be adjusted for pseudo-devices, but at
763      * least restrict it to 115200 to prevent excessive buffer
764      * usage.  Also disallow 0, to prevent foot shooting.
765      */
766     if (t->__c_ispeed < B50)
767         t->__c_ispeed = B50;
768     else if (t->__c_ispeed > B115200)
769         t->__c_ispeed = B115200;
770     if (t->__c_ospeed < B50)
771         t->__c_ospeed = B50;
772     else if (t->__c_ospeed > B115200)
773         t->__c_ospeed = B115200;
774     t->c_cflag |= CREAD;
775 
776     return 0;
777 }
778 
ttydevsw_defmodem(struct lwp_tty * tp __unused,int sigon __unused,int sigoff __unused)779 static int ttydevsw_defmodem(struct lwp_tty *tp __unused, int sigon __unused,
780                              int sigoff __unused)
781 {
782     /* Simulate a carrier to make the TTY layer happy. */
783     return (SER_DCD);
784 }
785 
ttydevsw_defmmap(struct lwp_tty * tp __unused,vm_ooffset_t offset __unused,vm_paddr_t * paddr __unused,int nprot __unused,vm_memattr_t * memattr __unused)786 static int ttydevsw_defmmap(struct lwp_tty *tp __unused,
787                             vm_ooffset_t offset __unused,
788                             vm_paddr_t *paddr __unused, int nprot __unused,
789                             vm_memattr_t *memattr __unused)
790 {
791     return (-1);
792 }
793 
ttydevsw_defpktnotify(struct lwp_tty * tp __unused,char event __unused)794 static void ttydevsw_defpktnotify(struct lwp_tty *tp __unused,
795                                   char event __unused)
796 {
797 }
798 
ttydevsw_deffree(void * softc __unused)799 static void ttydevsw_deffree(void *softc __unused)
800 {
801     panic("Terminal device freed without a free-handler");
802 }
803 
ttydevsw_defbusy(struct lwp_tty * tp __unused)804 static rt_bool_t ttydevsw_defbusy(struct lwp_tty *tp __unused)
805 {
806     return (RT_FALSE);
807 }
808 
bsd_devsw_init(struct lwp_ttydevsw * tsw)809 void bsd_devsw_init(struct lwp_ttydevsw *tsw)
810 {
811     /* Make sure the driver defines all routines. */
812 #define PATCH_FUNC(x)                       \
813     do                                      \
814     {                                       \
815         if (tsw->tsw_##x == NULL)           \
816             tsw->tsw_##x = ttydevsw_def##x; \
817     } while (0)
818 
819     PATCH_FUNC(open);
820     PATCH_FUNC(close);
821     PATCH_FUNC(outwakeup);
822     PATCH_FUNC(inwakeup);
823     PATCH_FUNC(ioctl);
824     PATCH_FUNC(cioctl);
825     PATCH_FUNC(param);
826     PATCH_FUNC(modem);
827     PATCH_FUNC(mmap);
828     PATCH_FUNC(pktnotify);
829     PATCH_FUNC(free);
830     PATCH_FUNC(busy);
831 #undef PATCH_FUNC
832 }
833 
834 /* release tty, and free the cdev resource */
tty_rel_free(struct lwp_tty * tp)835 static void tty_rel_free(struct lwp_tty *tp)
836 {
837 #ifdef USING_BSD_CHAR_DEVICE
838     struct cdev *dev;
839 #endif
840 
841     tty_assert_locked(tp);
842 
843 #define TF_ACTIVITY (TF_GONE | TF_OPENED | TF_HOOK | TF_OPENCLOSE)
844     if (tp->t_sessioncnt != 0 || (tp->t_flags & TF_ACTIVITY) != TF_GONE)
845     {
846         /* TTY is still in use. */
847         tty_unlock(tp);
848         return;
849     }
850 
851 #ifdef USING_BSD_AIO
852     /* Stop asynchronous I/O. */
853     funsetown(&tp->t_sigio);
854 #endif /* USING_BSD_AIO */
855 
856 #ifdef USING_BSD_CHAR_DEVICE
857     /* TTY can be deallocated. */
858     dev = tp->t_dev;
859     tp->t_dev = NULL;
860 #endif /* USING_BSD_CHAR_DEVICE */
861 
862     tty_unlock(tp);
863 
864 #ifdef USING_BSD_CHAR_DEVICE
865     if (dev != NULL)
866     {
867         sx_xlock(&tty_list_sx);
868         TAILQ_REMOVE(&tty_list, tp, t_list);
869         tty_list_count--;
870         sx_xunlock(&tty_list_sx);
871         destroy_dev_sched_cb(dev, tty_dealloc, tp);
872     }
873 #else
874     lwp_tty_delete(tp);
875 #endif
876 }
877 
tty_rel_pgrp(struct lwp_tty * tp,struct rt_processgroup * pg)878 void tty_rel_pgrp(struct lwp_tty *tp, struct rt_processgroup *pg)
879 {
880     MPASS(tp->t_sessioncnt > 0);
881     tty_assert_locked(tp);
882 
883     if (tp->t_pgrp == pg)
884         tp->t_pgrp = NULL;
885 
886     tty_unlock(tp);
887 }
888 
tty_rel_sess(struct lwp_tty * tp,struct rt_session * sess)889 void tty_rel_sess(struct lwp_tty *tp, struct rt_session *sess)
890 {
891     MPASS(tp->t_sessioncnt > 0);
892 
893     /* Current session has left. */
894     if (tp->t_session == sess)
895     {
896         tp->t_session = NULL;
897         MPASS(tp->t_pgrp == NULL);
898     }
899     tp->t_sessioncnt--;
900     tty_rel_free(tp);
901 }
902 
903 /* deallocate the tty */
tty_rel_gone(struct lwp_tty * tp)904 void tty_rel_gone(struct lwp_tty *tp)
905 {
906     tty_assert_locked(tp);
907     MPASS(!tty_gone(tp));
908 
909     /* Simulate carrier removal. */
910     ttydisc_modem(tp, 0);
911 
912     /* Wake up all blocked threads. */
913     tty_wakeup(tp, FREAD | FWRITE);
914     cv_broadcast(&tp->t_bgwait);
915     cv_broadcast(&tp->t_dcdwait);
916 
917     tp->t_flags |= TF_GONE;
918     tty_rel_free(tp);
919 }
920 
tty_drop_ctty(struct lwp_tty * tp,struct rt_lwp * p)921 static int tty_drop_ctty(struct lwp_tty *tp, struct rt_lwp *p)
922 {
923     struct rt_session *session;
924 #ifdef USING_BSD_VNODE
925     struct vnode *vp;
926 #endif
927 
928     /*
929      * This looks terrible, but it's generally safe as long as the tty
930      * hasn't gone away while we had the lock dropped.  All of our sanity
931      * checking that this operation is OK happens after we've picked it back
932      * up, so other state changes are generally not fatal and the potential
933      * for this particular operation to happen out-of-order in a
934      * multithreaded scenario is likely a non-issue.
935      */
936     tty_unlock(tp);
937     LWP_LOCK(p);
938     tty_lock(tp);
939     if (tty_gone(tp))
940     {
941         LWP_UNLOCK(p);
942         return -ENODEV;
943     }
944 
945     /*
946      * If the session doesn't have a controlling TTY, or if we weren't
947      * invoked on the controlling TTY, we'll return ENOIOCTL as we've
948      * historically done.
949      */
950     session = p->pgrp->session;
951     if (session->ctty == NULL || session->ctty != tp)
952     {
953         LWP_UNLOCK(p);
954         return -ENOTTY;
955     }
956 
957     if (!is_sess_leader(p))
958     {
959         LWP_UNLOCK(p);
960         return -EPERM;
961     }
962 
963     SESS_LOCK(session);
964 #ifdef USING_BSD_VNODE
965     vp = session->s_ttyvp;
966 #endif
967     session->ctty = NULL;
968 #ifdef USING_BSD_VNODE
969     session->s_ttyvp = NULL;
970     session->s_ttydp = NULL;
971 #endif
972     SESS_UNLOCK(session);
973 
974     tp->t_sessioncnt--;
975     p->term_ctrlterm = RT_FALSE;
976     LWP_UNLOCK(p);
977 
978 #ifdef USING_BSD_VNODE
979     /*
980      * If we did have a vnode, release our reference.  Ordinarily we manage
981      * these at the devfs layer, but we can't necessarily know that we were
982      * invoked on the vnode referenced in the session (i.e. the vnode we
983      * hold a reference to).  We explicitly don't check VBAD/VIRF_DOOMED here
984      * to avoid a vnode leak -- in circumstances elsewhere where we'd hit a
985      * VIRF_DOOMED vnode, release has been deferred until the controlling TTY
986      * is either changed or released.
987      */
988     if (vp != NULL)
989         devfs_ctty_unref(vp);
990 #endif
991 
992     return 0;
993 }
994 
tty_wakeup(struct lwp_tty * tp,int flags)995 void tty_wakeup(struct lwp_tty *tp, int flags)
996 {
997 #ifdef USING_BSD_AIO
998     if (tp->t_flags & TF_ASYNC && tp->t_sigio != NULL)
999         pgsigio(&tp->t_sigio, SIGIO, (tp->t_session != NULL));
1000 #endif
1001 
1002     if (flags & FWRITE)
1003     {
1004         cv_broadcast(&tp->t_outwait);
1005 #ifdef USING_BSD_POLL
1006         selwakeup(&tp->t_outpoll);
1007         KNOTE_LOCKED(&tp->t_outpoll.si_note, 0);
1008 #else
1009         rt_wqueue_wakeup_all(&tp->t_outpoll, (void *)POLLOUT);
1010 #endif
1011     }
1012     if (flags & FREAD)
1013     {
1014         cv_broadcast(&tp->t_inwait);
1015 #ifdef USING_BSD_POLL
1016         selwakeup(&tp->t_inpoll);
1017         KNOTE_LOCKED(&tp->t_inpoll.si_note, 0);
1018 #else
1019         rt_wqueue_wakeup_all(&tp->t_inpoll, (void *)POLLIN);
1020 #endif
1021     }
1022 }
1023 
tty_wait(struct lwp_tty * tp,struct rt_condvar * cv)1024 int tty_wait(struct lwp_tty *tp, struct rt_condvar *cv)
1025 {
1026     int error;
1027     int revokecnt = tp->t_revokecnt;
1028 
1029     tty_lock_assert(tp, MA_OWNED | MA_NOTRECURSED);
1030     MPASS(!tty_gone(tp));
1031 
1032     error = cv_wait_sig(cv, tp->t_mtx);
1033 
1034     /* Bail out when the device slipped away. */
1035     if (tty_gone(tp))
1036         return -ENXIO;
1037 
1038     /* Restart the system call when we may have been revoked. */
1039     if (tp->t_revokecnt != revokecnt)
1040         return -ERESTART;
1041 
1042     return error;
1043 }
1044 
tty_timedwait(struct lwp_tty * tp,struct rt_condvar * cv,rt_tick_t timeout)1045 int tty_timedwait(struct lwp_tty *tp, struct rt_condvar *cv, rt_tick_t timeout)
1046 {
1047     int error;
1048     int revokecnt = tp->t_revokecnt;
1049 
1050     tty_lock_assert(tp, MA_OWNED | MA_NOTRECURSED);
1051     MPASS(!tty_gone(tp));
1052 
1053     error = cv_timedwait_sig(cv, tp->t_mtx, timeout);
1054 
1055     /* Bail out when the device slipped away. */
1056     if (tty_gone(tp))
1057         return -ENXIO;
1058 
1059     /* Restart the system call when we may have been revoked. */
1060     if (tp->t_revokecnt != revokecnt)
1061         return -ERESTART;
1062 
1063     return error;
1064 }
1065 
1066 /* discard data in I/O buffers */
tty_flush(struct lwp_tty * tp,int flags)1067 void tty_flush(struct lwp_tty *tp, int flags)
1068 {
1069     if (flags & FWRITE)
1070     {
1071         tp->t_flags &= ~TF_HIWAT_OUT;
1072         ttyoutq_flush(&tp->t_outq);
1073         tty_wakeup(tp, FWRITE);
1074         if (!tty_gone(tp))
1075         {
1076             ttydevsw_outwakeup(tp);
1077             ttydevsw_pktnotify(tp, TIOCPKT_FLUSHWRITE);
1078         }
1079     }
1080     if (flags & FREAD)
1081     {
1082         tty_hiwat_in_unblock(tp);
1083         ttyinq_flush(&tp->t_inq);
1084         tty_wakeup(tp, FREAD);
1085         if (!tty_gone(tp))
1086         {
1087             ttydevsw_inwakeup(tp);
1088             ttydevsw_pktnotify(tp, TIOCPKT_FLUSHREAD);
1089         }
1090     }
1091 }
1092 
tty_set_winsize(struct lwp_tty * tp,const struct winsize * wsz)1093 void tty_set_winsize(struct lwp_tty *tp, const struct winsize *wsz)
1094 {
1095     if (memcmp(&tp->t_winsize, wsz, sizeof(*wsz)) == 0)
1096         return;
1097     tp->t_winsize = *wsz;
1098     lwp_tty_signal_pgrp(tp, SIGWINCH);
1099 }
1100 
tty_generic_ioctl(struct lwp_tty * tp,rt_ubase_t cmd,void * data,int fflag,struct rt_thread * td)1101 static int tty_generic_ioctl(struct lwp_tty *tp, rt_ubase_t cmd, void *data,
1102                              int fflag, struct rt_thread *td)
1103 {
1104     int error;
1105 
1106     switch (cmd)
1107     {
1108         /*
1109          * Modem commands.
1110          * The SER_* and TIOCM_* flags are the same, but one bit
1111          * shifted. I don't know why.
1112          */
1113         case TIOCSDTR:
1114             ttydevsw_modem(tp, SER_DTR, 0);
1115             return 0;
1116         case TIOCCDTR:
1117             ttydevsw_modem(tp, 0, SER_DTR);
1118             return 0;
1119         case TIOCMSET: {
1120             int bits = *(int *)data;
1121             ttydevsw_modem(tp, (bits & (TIOCM_DTR | TIOCM_RTS)) >> 1,
1122                            ((~bits) & (TIOCM_DTR | TIOCM_RTS)) >> 1);
1123             return 0;
1124         }
1125         case TIOCMBIS: {
1126             int bits = *(int *)data;
1127             ttydevsw_modem(tp, (bits & (TIOCM_DTR | TIOCM_RTS)) >> 1, 0);
1128             return 0;
1129         }
1130         case TIOCMBIC: {
1131             int bits = *(int *)data;
1132             ttydevsw_modem(tp, 0, (bits & (TIOCM_DTR | TIOCM_RTS)) >> 1);
1133             return 0;
1134         }
1135         case TIOCMGET:
1136             *(int *)data = TIOCM_LE + (ttydevsw_modem(tp, 0, 0) << 1);
1137             return 0;
1138 
1139         case FIOASYNC:
1140             if (*(int *)data)
1141                 tp->t_flags |= TF_ASYNC;
1142             else
1143                 tp->t_flags &= ~TF_ASYNC;
1144             return 0;
1145         case FIONBIO:
1146             /* This device supports non-blocking operation. */
1147             return 0;
1148         case FIONREAD:
1149             *(int *)data = ttyinq_bytescanonicalized(&tp->t_inq);
1150             return 0;
1151         case FIONWRITE:
1152         case TIOCOUTQ:
1153             *(int *)data = ttyoutq_bytesused(&tp->t_outq);
1154             return 0;
1155 #if BSD_USING_FIO_OWNERSHIP
1156         case FIOSETOWN:
1157             if (tp->t_session != NULL && !tty_is_ctty(tp, td->lwp))
1158                 /* Not allowed to set ownership. */
1159                 return -ENOTTY;
1160 
1161             /* Temporarily unlock the TTY to set ownership. */
1162             tty_unlock(tp);
1163             error = fsetown(*(int *)data, &tp->t_sigio);
1164             tty_lock(tp);
1165             return (error);
1166         case FIOGETOWN:
1167             if (tp->t_session != NULL && !tty_is_ctty(tp, td->lwp))
1168                 /* Not allowed to set ownership. */
1169                 return -ENOTTY;
1170 
1171             /* Get ownership. */
1172             *(int *)data = fgetown(&tp->t_sigio);
1173             return 0;
1174 #endif
1175         case TIOCGETA:
1176             /* Obtain terminal flags through tcgetattr(). */
1177             *(struct termios *)data = tp->t_termios;
1178             return 0;
1179         case TIOCSETA:
1180         case TIOCSETAW:
1181         case TIOCSETAF: {
1182             struct termios *t = data;
1183 
1184             /*
1185              * Who makes up these funny rules? According to POSIX,
1186              * input baud rate is set equal to the output baud rate
1187              * when zero.
1188              */
1189             if (t->__c_ispeed == 0)
1190                 t->__c_ispeed = t->__c_ospeed;
1191 
1192             /* Discard any unsupported bits. */
1193             t->c_iflag &= TTYSUP_IFLAG;
1194             t->c_oflag &= TTYSUP_OFLAG;
1195             t->c_lflag &= TTYSUP_LFLAG;
1196             t->c_cflag &= TTYSUP_CFLAG;
1197 
1198             /* Set terminal flags through tcsetattr(). */
1199             if (cmd == TIOCSETAW || cmd == TIOCSETAF)
1200             {
1201                 error = tty_drain(tp, 0);
1202                 if (error)
1203                     return (error);
1204                 if (cmd == TIOCSETAF)
1205                     tty_flush(tp, FREAD);
1206             }
1207 
1208             /*
1209              * Only call param() when the flags really change.
1210              */
1211             if ((t->c_cflag & CIGNORE) == 0 &&
1212                 (tp->t_termios.c_cflag != t->c_cflag ||
1213                  ((tp->t_termios.c_iflag ^ t->c_iflag) &
1214                   (IXON | IXOFF | IXANY)) ||
1215                  tp->t_termios.__c_ispeed != t->__c_ispeed ||
1216                  tp->t_termios.__c_ospeed != t->__c_ospeed))
1217             {
1218                 error = ttydevsw_param(tp, t);
1219                 if (error)
1220                     return (error);
1221 
1222                 /* XXX: CLOCAL? */
1223 
1224                 tp->t_termios.c_cflag = t->c_cflag & ~CIGNORE;
1225                 tp->t_termios.__c_ispeed = t->__c_ispeed;
1226                 tp->t_termios.__c_ospeed = t->__c_ospeed;
1227 
1228                 /* Baud rate has changed - update watermarks. */
1229                 error = tty_watermarks(tp);
1230                 if (error)
1231                     return (error);
1232             }
1233 
1234             /* Copy new non-device driver parameters. */
1235             tp->t_termios.c_iflag = t->c_iflag;
1236             tp->t_termios.c_oflag = t->c_oflag;
1237             tp->t_termios.c_lflag = t->c_lflag;
1238             memcpy(&tp->t_termios.c_cc, t->c_cc, sizeof t->c_cc);
1239 
1240             ttydisc_optimize(tp);
1241 
1242             if ((t->c_lflag & ICANON) == 0)
1243             {
1244                 /*
1245                  * When in non-canonical mode, wake up all
1246                  * readers. Canonicalize any partial input. VMIN
1247                  * and VTIME could also be adjusted.
1248                  */
1249                 ttyinq_canonicalize(&tp->t_inq);
1250                 tty_wakeup(tp, FREAD);
1251             }
1252 
1253             /**
1254              * For packet mode: notify the PTY consumer that VSTOP
1255              * and VSTART may have been changed.
1256              *
1257              * TODO: change the _CONTROL('S') to a CSTOP?
1258              */
1259             if (tp->t_termios.c_iflag & IXON &&
1260                 tp->t_termios.c_cc[VSTOP] == _CONTROL('S') &&
1261                 tp->t_termios.c_cc[VSTART] == _CONTROL('Q'))
1262                 ttydevsw_pktnotify(tp, TIOCPKT_DOSTOP);
1263             else
1264                 ttydevsw_pktnotify(tp, TIOCPKT_NOSTOP);
1265             return 0;
1266         }
1267         case TIOCGETD:
1268             /* For compatibility - we only support TTYDISC. */
1269             *(int *)data = TTYDISC;
1270             return 0;
1271         case TIOCGPGRP:
1272             if (!tty_is_ctty(tp, td->lwp))
1273                 return -ENOTTY;
1274 
1275             if (tp->t_pgrp != NULL)
1276                 *(int *)data = tp->t_pgrp->pgid;
1277             else
1278                 *(int *)data = NO_PID;
1279             return 0;
1280         case TIOCGSID:
1281             if (!tty_is_ctty(tp, td->lwp))
1282                 return -ENOTTY;
1283 
1284             MPASS(tp->t_session);
1285             *(int *)data = tp->t_session->sid;
1286             return 0;
1287         case TIOCNOTTY:
1288             return tty_drop_ctty(tp, td->lwp);
1289         case TIOCSCTTY:
1290             return lwp_tty_set_ctrl_proc(tp, td);
1291         case TIOCSPGRP: {
1292             int pgid;
1293             if (lwp_in_user_space((void *)data))
1294             {
1295                 if (lwp_get_from_user(&pgid, data, sizeof(int)) != sizeof(int))
1296                     return -EFAULT;
1297             }
1298             else
1299             {
1300                 pgid = *(int *)data;
1301             }
1302             return lwp_tty_assign_foreground(tp, td, pgid);
1303         }
1304         case TIOCFLUSH: {
1305             int flags = *(int *)data;
1306 
1307             if (flags == 0)
1308                 flags = (FREAD | FWRITE);
1309             else
1310                 flags &= (FREAD | FWRITE);
1311             tty_flush(tp, flags);
1312             return 0;
1313         }
1314         case TIOCDRAIN:
1315             /* Drain TTY output. */
1316             return tty_drain(tp, 0);
1317         case TIOCGDRAINWAIT:
1318             *(int *)data = tp->t_drainwait;
1319             return 0;
1320         case TIOCSDRAINWAIT:
1321             error = priv_check(td, PRIV_TTY_DRAINWAIT);
1322             if (error == 0)
1323                 tp->t_drainwait = *(int *)data;
1324             return (error);
1325         case TIOCCONS:
1326             /* Set terminal as console TTY. */
1327             if (*(int *)data)
1328             {
1329                 error = priv_check(td, PRIV_TTY_CONSOLE);
1330                 if (error)
1331                     return (error);
1332                 error = constty_set(tp);
1333             }
1334             else
1335             {
1336                 error = constty_clear(tp);
1337             }
1338             return (error);
1339         case TIOCGWINSZ:
1340             /* Obtain window size. */
1341             *(struct winsize *)data = tp->t_winsize;
1342             return 0;
1343         case TIOCSWINSZ:
1344             /* Set window size. */
1345             tty_set_winsize(tp, data);
1346             return 0;
1347         case TIOCEXCL:
1348             tp->t_flags |= TF_EXCLUDE;
1349             return 0;
1350         case TIOCNXCL:
1351             tp->t_flags &= ~TF_EXCLUDE;
1352             return 0;
1353         case TIOCSTOP:
1354             tp->t_flags |= TF_STOPPED;
1355             ttydevsw_pktnotify(tp, TIOCPKT_STOP);
1356             return 0;
1357         case TIOCSTART:
1358             tp->t_flags &= ~TF_STOPPED;
1359             tp->t_termios.c_lflag &= ~FLUSHO;
1360             ttydevsw_outwakeup(tp);
1361             ttydevsw_pktnotify(tp, TIOCPKT_START);
1362             return 0;
1363         case TIOCSTAT:
1364             tty_info(tp);
1365             return 0;
1366         case TIOCSTI:
1367             if ((fflag & FREAD) == 0 && priv_check(td, PRIV_TTY_STI))
1368                 return -EPERM;
1369             if (!tty_is_ctty(tp, td->lwp) && priv_check(td, PRIV_TTY_STI))
1370                 return -EACCES;
1371             ttydisc_rint(tp, *(char *)data, 0);
1372             ttydisc_rint_done(tp);
1373             return 0;
1374     }
1375 
1376 #ifdef COMPAT_43TTY
1377     return tty_ioctl_compat(tp, cmd, data, fflag, td);
1378 #else  /* !COMPAT_43TTY */
1379     return -ENOIOCTL;
1380 #endif /* COMPAT_43TTY */
1381 }
1382 
tty_ioctl(struct lwp_tty * tp,rt_ubase_t cmd,void * data,int fflag,struct rt_thread * td)1383 int tty_ioctl(struct lwp_tty *tp, rt_ubase_t cmd, void *data, int fflag,
1384               struct rt_thread *td)
1385 {
1386     int error;
1387 
1388     tty_assert_locked(tp);
1389 
1390     if (tty_gone(tp))
1391         return -ENXIO;
1392 
1393     error = ttydevsw_ioctl(tp, cmd, data, td);
1394     if (error == -ENOIOCTL)
1395         error = tty_generic_ioctl(tp, cmd, data, fflag, td);
1396 
1397     return error;
1398 }
1399 
tty_checkoutq(struct lwp_tty * tp)1400 int tty_checkoutq(struct lwp_tty *tp)
1401 {
1402     /* 256 bytes should be enough to print a log message. */
1403     return (ttyoutq_bytesleft(&tp->t_outq) >= 256);
1404 }
1405 
tty_hiwat_in_block(struct lwp_tty * tp)1406 void tty_hiwat_in_block(struct lwp_tty *tp)
1407 {
1408     if ((tp->t_flags & TF_HIWAT_IN) == 0 && tp->t_termios.c_iflag & IXOFF &&
1409         tp->t_termios.c_cc[VSTOP] != _POSIX_VDISABLE)
1410     {
1411         /*
1412          * Input flow control. Only enter the high watermark when we
1413          * can successfully store the VSTOP character.
1414          */
1415         if (ttyoutq_write_nofrag(&tp->t_outq, &tp->t_termios.c_cc[VSTOP], 1) ==
1416             0)
1417             tp->t_flags |= TF_HIWAT_IN;
1418     }
1419     else
1420     {
1421         /* No input flow control. */
1422         tp->t_flags |= TF_HIWAT_IN;
1423     }
1424 }
1425 
tty_hiwat_in_unblock(struct lwp_tty * tp)1426 void tty_hiwat_in_unblock(struct lwp_tty *tp)
1427 {
1428     if (tp->t_flags & TF_HIWAT_IN && tp->t_termios.c_iflag & IXOFF &&
1429         tp->t_termios.c_cc[VSTART] != _POSIX_VDISABLE)
1430     {
1431         /*
1432          * Input flow control. Only leave the high watermark when we
1433          * can successfully store the VSTART character.
1434          */
1435         if (ttyoutq_write_nofrag(&tp->t_outq, &tp->t_termios.c_cc[VSTART], 1) ==
1436             0)
1437             tp->t_flags &= ~TF_HIWAT_IN;
1438     }
1439     else
1440     {
1441         /* No input flow control. */
1442         tp->t_flags &= ~TF_HIWAT_IN;
1443     }
1444 
1445     if (!tty_gone(tp))
1446         ttydevsw_inwakeup(tp);
1447 }
1448