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