summaryrefslogtreecommitdiffstats
path: root/sys/kern/tty.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/kern/tty.c')
-rw-r--r--sys/kern/tty.c4182
1 files changed, 1352 insertions, 2830 deletions
diff --git a/sys/kern/tty.c b/sys/kern/tty.c
index e7818b8..af12da3 100644
--- a/sys/kern/tty.c
+++ b/sys/kern/tty.c
@@ -1,19 +1,9 @@
/*-
- * Copyright (c) 1982, 1986, 1990, 1991, 1993
- * The Regents of the University of California. All rights reserved.
- * (c) UNIX System Laboratories, Inc.
- * All or some portions of this file are derived from material licensed
- * to the University of California by American Telephone and Telegraph
- * Co. or Unix System Laboratories, Inc. and are reproduced herein with
- * the permission of UNIX System Laboratories, Inc.
- *
- * Copyright (c) 2002 Networks Associates Technologies, Inc.
+ * Copyright (c) 2008 Ed Schouten <ed@FreeBSD.org>
* All rights reserved.
*
- * Portions of this software were developed for the FreeBSD Project by
- * ThinkSec AS and NAI Labs, the Security Research Division of Network
- * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035
- * ("CBOSS"), as part of the DARPA CHATS research program.
+ * Portions of this software were developed under sponsorship from Snow
+ * B.V., the Netherlands.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -23,14 +13,11 @@
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
- * 4. Neither the name of the University nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
*
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
@@ -38,798 +25,455 @@
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
- *
- * @(#)tty.c 8.8 (Berkeley) 1/21/94
- */
-
-/*-
- * TODO:
- * o Fix races for sending the start char in ttyflush().
- * o Handle inter-byte timeout for "MIN > 0, TIME > 0" in ttyselect().
- * With luck, there will be MIN chars before select() returns().
- * o Handle CLOCAL consistently for ptys. Perhaps disallow setting it.
- * o Don't allow input in TS_ZOMBIE case. It would be visible through
- * FIONREAD.
- * o Do the new sio locking stuff here and use it to avoid special
- * case for EXTPROC?
- * o Lock PENDIN too?
- * o Move EXTPROC and/or PENDIN to t_state?
- * o Wrap most of ttioctl in spltty/splx.
- * o Implement TIOCNOTTY or remove it from <sys/ioctl.h>.
- * o Send STOP if IXOFF is toggled off while TS_TBLOCK is set.
- * o Don't allow certain termios flags to affect disciplines other
- * than TTYDISC. Cancel their effects before switch disciplines
- * and ignore them if they are set while we are in another
- * discipline.
- * o Now that historical speed conversions are handled here, don't
- * do them in drivers.
- * o Check for TS_CARR_ON being set while everything is closed and not
- * waiting for carrier. TS_CARR_ON isn't cleared if nothing is open,
- * so it would live until the next open even if carrier drops.
- * o Restore TS_WOPEN since it is useful in pstat. It must be cleared
- * only when _all_ openers leave open().
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include "opt_compat.h"
-#include "opt_tty.h"
#include <sys/param.h>
-#include <sys/systm.h>
+#include <sys/conf.h>
#include <sys/cons.h>
+#include <sys/fcntl.h>
#include <sys/filio.h>
-#include <sys/lock.h>
-#include <sys/mutex.h>
-#include <sys/namei.h>
-#include <sys/sx.h>
-#if defined(COMPAT_43TTY)
+#ifdef COMPAT_43TTY
#include <sys/ioctl_compat.h>
-#endif
+#endif /* COMPAT_43TTY */
+#include <sys/kernel.h>
+#include <sys/limits.h>
+#include <sys/malloc.h>
+#include <sys/mount.h>
+#include <sys/namei.h>
+#include <sys/poll.h>
#include <sys/priv.h>
#include <sys/proc.h>
-#define TTYDEFCHARS
-#include <sys/tty.h>
-#undef TTYDEFCHARS
-#include <sys/fcntl.h>
-#include <sys/conf.h>
-#include <sys/poll.h>
-#include <sys/kernel.h>
-#include <sys/vnode.h>
#include <sys/serial.h>
-#include <sys/signalvar.h>
-#include <sys/malloc.h>
-#include <sys/filedesc.h>
+#include <sys/signal.h>
+#include <sys/stat.h>
+#include <sys/sx.h>
#include <sys/sysctl.h>
-#include <sys/timepps.h>
+#include <sys/systm.h>
+#include <sys/tty.h>
+#include <sys/ttycom.h>
+#define TTYDEFCHARS
+#include <sys/ttydefaults.h>
+#undef TTYDEFCHARS
+#include <sys/ucred.h>
+#include <sys/vnode.h>
#include <machine/stdarg.h>
-MALLOC_DEFINE(M_TTYS, "ttys", "tty data structures");
-
-long tk_cancc;
-long tk_nin;
-long tk_nout;
-long tk_rawcc;
-
-static d_open_t ttysopen;
-static d_close_t ttysclose;
-static d_read_t ttysrdwr;
-static d_ioctl_t ttysioctl;
-static d_purge_t ttypurge;
-
-/* Default cdevsw for common tty devices */
-static struct cdevsw tty_cdevsw = {
- .d_version = D_VERSION,
- .d_open = ttyopen,
- .d_close = ttyclose,
- .d_ioctl = ttyioctl,
- .d_purge = ttypurge,
- .d_name = "ttydrv",
- .d_flags = D_TTY | D_NEEDGIANT,
-};
+static MALLOC_DEFINE(M_TTY, "tty", "tty device");
-/* Cdevsw for slave tty devices */
-static struct cdevsw ttys_cdevsw = {
- .d_version = D_VERSION,
- .d_open = ttysopen,
- .d_close = ttysclose,
- .d_read = ttysrdwr,
- .d_write = ttysrdwr,
- .d_ioctl = ttysioctl,
- .d_name = "TTYS",
- .d_flags = D_TTY | D_NEEDGIANT,
-};
+static void tty_rel_free(struct tty *tp);
-static int ttnread(struct tty *tp);
-static void ttyecho(int c, struct tty *tp);
-static int ttyoutput(int c, struct tty *tp);
-static void ttypend(struct tty *tp);
-static void ttyretype(struct tty *tp);
-static void ttyrub(int c, struct tty *tp);
-static void ttyrubo(struct tty *tp, int cnt);
-static void ttyunblock(struct tty *tp);
-static int ttywflush(struct tty *tp);
-static int filt_ttyread(struct knote *kn, long hint);
-static void filt_ttyrdetach(struct knote *kn);
-static int filt_ttywrite(struct knote *kn, long hint);
-static void filt_ttywdetach(struct knote *kn);
+static TAILQ_HEAD(, tty) tty_list = TAILQ_HEAD_INITIALIZER(tty_list);
+static struct sx tty_list_sx;
+SX_SYSINIT(tty_list, &tty_list_sx, "tty list");
+static unsigned int tty_list_count = 0;
/*
- * Table with character classes and parity. The 8th bit indicates parity,
- * the 7th bit indicates the character is an alphameric or underscore (for
- * ALTWERASE), and the low 6 bits indicate delay type. If the low 6 bits
- * are 0 then the character needs no special processing on output; classes
- * other than 0 might be translated or (not currently) require delays.
+ * Flags that are supported and stored by this implementation.
*/
-#define E 0x00 /* Even parity. */
-#define O 0x80 /* Odd parity. */
-#define PARITY(c) (char_type[c] & O)
-
-#define ALPHA 0x40 /* Alpha or underscore. */
-#define ISALPHA(c) (char_type[(c) & TTY_CHARMASK] & ALPHA)
-
-#define CCLASSMASK 0x3f
-#define CCLASS(c) (char_type[c] & CCLASSMASK)
-
-#define BS BACKSPACE
-#define CC CONTROL
-#define CR RETURN
-#define NA ORDINARY | ALPHA
-#define NL NEWLINE
-#define NO ORDINARY
-#define TB TAB
-#define VT VTAB
-
-static u_char const char_type[] = {
- E|CC, O|CC, O|CC, E|CC, O|CC, E|CC, E|CC, O|CC, /* nul - bel */
- O|BS, E|TB, E|NL, O|CC, E|VT, O|CR, O|CC, E|CC, /* bs - si */
- O|CC, E|CC, E|CC, O|CC, E|CC, O|CC, O|CC, E|CC, /* dle - etb */
- E|CC, O|CC, O|CC, E|CC, O|CC, E|CC, E|CC, O|CC, /* can - us */
- O|NO, E|NO, E|NO, O|NO, E|NO, O|NO, O|NO, E|NO, /* sp - ' */
- E|NO, O|NO, O|NO, E|NO, O|NO, E|NO, E|NO, O|NO, /* ( - / */
- E|NA, O|NA, O|NA, E|NA, O|NA, E|NA, E|NA, O|NA, /* 0 - 7 */
- O|NA, E|NA, E|NO, O|NO, E|NO, O|NO, O|NO, E|NO, /* 8 - ? */
- O|NO, E|NA, E|NA, O|NA, E|NA, O|NA, O|NA, E|NA, /* @ - G */
- E|NA, O|NA, O|NA, E|NA, O|NA, E|NA, E|NA, O|NA, /* H - O */
- E|NA, O|NA, O|NA, E|NA, O|NA, E|NA, E|NA, O|NA, /* P - W */
- O|NA, E|NA, E|NA, O|NO, E|NO, O|NO, O|NO, O|NA, /* X - _ */
- E|NO, O|NA, O|NA, E|NA, O|NA, E|NA, E|NA, O|NA, /* ` - g */
- O|NA, E|NA, E|NA, O|NA, E|NA, O|NA, O|NA, E|NA, /* h - o */
- O|NA, E|NA, E|NA, O|NA, E|NA, O|NA, O|NA, E|NA, /* p - w */
- E|NA, O|NA, O|NA, E|NO, O|NO, E|NO, E|NO, O|CC, /* x - del */
- /*
- * Meta chars; should be settable per character set;
- * for now, treat them all as normal characters.
- */
- NA, NA, NA, NA, NA, NA, NA, NA,
- NA, NA, NA, NA, NA, NA, NA, NA,
- NA, NA, NA, NA, NA, NA, NA, NA,
- NA, NA, NA, NA, NA, NA, NA, NA,
- NA, NA, NA, NA, NA, NA, NA, NA,
- NA, NA, NA, NA, NA, NA, NA, NA,
- NA, NA, NA, NA, NA, NA, NA, NA,
- NA, NA, NA, NA, NA, NA, NA, NA,
- NA, NA, NA, NA, NA, NA, NA, NA,
- NA, NA, NA, NA, NA, NA, NA, NA,
- NA, NA, NA, NA, NA, NA, NA, NA,
- NA, NA, NA, NA, NA, NA, NA, NA,
- NA, NA, NA, NA, NA, NA, NA, NA,
- NA, NA, NA, NA, NA, NA, NA, NA,
- NA, NA, NA, NA, NA, NA, NA, NA,
- NA, NA, NA, NA, NA, NA, NA, NA,
-};
-#undef BS
-#undef CC
-#undef CR
-#undef NA
-#undef NL
-#undef NO
-#undef TB
-#undef VT
-
-/* Macros to clear/set/test flags. */
-#define SET(t, f) (t) |= (f)
-#define CLR(t, f) (t) &= ~(f)
-#define ISSET(t, f) ((t) & (f))
-
-#undef MAX_INPUT /* XXX wrong in <sys/syslimits.h> */
-#define MAX_INPUT TTYHOG /* XXX limit is usually larger for !ICANON */
+#define TTYSUP_IFLAG (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK|ISTRIP|\
+ INLCR|IGNCR|ICRNL|IXON|IXOFF|IXANY|IMAXBEL)
+#define TTYSUP_OFLAG (OPOST|ONLCR|TAB3|ONOEOT|OCRNL|ONOCR|ONLRET)
+#define TTYSUP_LFLAG (ECHOKE|ECHOE|ECHOK|ECHO|ECHONL|ECHOPRT|\
+ ECHOCTL|ISIG|ICANON|ALTWERASE|IEXTEN|TOSTOP|\
+ FLUSHO|NOKERNINFO|NOFLSH)
+#define TTYSUP_CFLAG (CIGNORE|CSIZE|CSTOPB|CREAD|PARENB|PARODD|\
+ HUPCL|CLOCAL|CCTS_OFLOW|CRTS_IFLOW|CDTR_IFLOW|\
+ CDSR_OFLOW|CCAR_OFLOW)
+
+#define TTY_CALLOUT(tp,d) ((tp)->t_dev != (d))
/*
- * list of struct tty where pstat(8) can pick it up with sysctl
- *
- * The lock order is to grab the list mutex before the tty mutex.
- * Together with additions going on the tail of the list, this allows
- * the sysctl to avoid doing retries.
+ * Set TTY buffer sizes.
*/
-static TAILQ_HEAD(, tty) tty_list = TAILQ_HEAD_INITIALIZER(tty_list);
-static struct mtx tty_list_mutex;
-MTX_SYSINIT(tty_list, &tty_list_mutex, "ttylist", MTX_DEF);
-static struct unrhdr *tty_unit;
+static void
+tty_watermarks(struct tty *tp)
+{
+ speed_t sp;
+
+ /* Provide an input buffer for 0.2 seconds of data. */
+ sp = MAX(tp->t_termios.c_ispeed, 0);
+ ttyinq_setsize(&tp->t_inq, tp, sp / 5);
+
+ /* Set low watermark at 10% (when 90% is available). */
+ tp->t_inlow = (ttyinq_getsize(&tp->t_inq) * 9) / 10;
-static int drainwait = 5*60;
-SYSCTL_INT(_kern, OID_AUTO, drainwait, CTLFLAG_RW, &drainwait,
- 0, "Output drain timeout in seconds");
+ /* Provide an ouput buffer for 0.2 seconds of data. */
+ sp = MAX(tp->t_termios.c_ospeed, 0);
+ ttyoutq_setsize(&tp->t_outq, tp, sp / 5);
-static struct tty *
-tty_gettp(struct cdev *dev)
+ /* Set low watermark at 10% (when 90% is available). */
+ tp->t_outlow = (ttyoutq_getsize(&tp->t_outq) * 9) / 10;
+}
+
+static void
+tty_freebuffers(struct tty *tp)
{
- struct tty *tp;
- struct cdevsw *csw;
-
- csw = dev_refthread(dev);
- if (csw == NULL)
- return (NULL);
- KASSERT(csw->d_flags & D_TTY,
- ("non D_TTY (%s) in tty code", devtoname(dev)));
- tp = dev->si_tty;
- dev_relthread(dev);
- KASSERT(tp != NULL,
- ("no tty pointer on (%s) in tty code", devtoname(dev)));
- return (tp);
+
+ /* Destroy input buffers. */
+ ttyinq_flush(&tp->t_inq);
+ ttyinq_setsize(&tp->t_inq, NULL, 0);
+ MPASS(ttyinq_getsize(&tp->t_inq) == 0);
+ tp->t_inlow = 0;
+
+ /* Destroy output buffers. */
+ ttyoutq_flush(&tp->t_outq);
+ ttyoutq_setsize(&tp->t_outq, NULL, 0);
+ MPASS(ttyoutq_getsize(&tp->t_outq) == 0);
+ tp->t_outlow = 0;
}
-/*
- * Initial open of tty, or (re)entry to standard tty line discipline.
- */
-int
-tty_open(struct cdev *device, struct tty *tp)
+static int
+tty_drain(struct tty *tp)
{
- int s;
-
- s = spltty();
- tp->t_dev = device;
- tp->t_hotchar = 0;
- if (!ISSET(tp->t_state, TS_ISOPEN)) {
- ttyref(tp);
- SET(tp->t_state, TS_ISOPEN);
- if (ISSET(tp->t_cflag, CLOCAL))
- SET(tp->t_state, TS_CONNECTED);
- bzero(&tp->t_winsize, sizeof(tp->t_winsize));
+ int error;
+
+ while (ttyoutq_bytesused(&tp->t_outq) > 0) {
+ ttydevsw_outwakeup(tp);
+ /* Could be handled synchronously. */
+ if (ttyoutq_bytesused(&tp->t_outq) == 0)
+ return (0);
+
+ /* Wait for data to be drained. */
+ error = tty_wait(tp, &tp->t_outwait);
+ if (error)
+ return (error);
}
- /* XXX don't hang forever on output */
- if (tp->t_timeout < 0)
- tp->t_timeout = drainwait*hz;
- ttsetwater(tp);
- splx(s);
+
return (0);
}
/*
- * Handle close() on a tty line: flush and set to initial state,
- * bumping generation number so that pending read/write calls
- * can detect recycling of the tty.
- * XXX our caller should have done `spltty(); l_close(); tty_close();'
- * and l_close() should have flushed, but we repeat the spltty() and
- * the flush in case there are buggy callers.
+ * Because the revoke() call already calls d_close() without making sure
+ * all threads are purged from the TTY, we can only destroy the buffers
+ * and such when the last thread leaves the TTY. ttydev_enter() and
+ * ttydev_leave() are called from within the cdev functions, to make
+ * sure we can garbage collect the TTY.
*/
-int
-tty_close(struct tty *tp)
+
+static __inline int
+ttydev_enter(struct tty *tp)
{
- int ostate, s;
+ tty_lock(tp);
+ if (tty_gone(tp) || !tty_opened(tp)) {
+ /* Device is already gone. */
+ tty_unlock(tp);
+ return (ENXIO);
+ }
+
+ return (0);
+}
+
+static void
+ttydev_leave(struct tty *tp)
+{
+ tty_lock_assert(tp, MA_OWNED);
+
+ if (tty_opened(tp) || tp->t_flags & TF_OPENCLOSE) {
+ /* Device is still opened somewhere. */
+ tty_unlock(tp);
+ return;
+ }
+
+ tp->t_flags |= TF_OPENCLOSE;
+
+ /* Stop asynchronous I/O. */
funsetown(&tp->t_sigio);
- s = spltty();
+
+ /* Remove console TTY. */
if (constty == tp)
constty_clear();
- ttyflush(tp, FREAD | FWRITE);
- clist_free_cblocks(&tp->t_canq);
- clist_free_cblocks(&tp->t_outq);
- clist_free_cblocks(&tp->t_rawq);
-
- tp->t_gen++;
- tp->t_line = TTYDISC;
- tp->t_hotchar = 0;
- tp->t_pgrp = NULL;
- tp->t_session = NULL;
- ostate = tp->t_state;
- tp->t_state = 0;
- knlist_clear(&tp->t_rsel.si_note, 0);
- knlist_clear(&tp->t_wsel.si_note, 0);
- /*
- * Both final close and revocation close might end up calling
- * this method. Only the thread clearing TS_ISOPEN should
- * release the reference to the tty.
- */
- if (ISSET(ostate, TS_ISOPEN))
- ttyrel(tp);
- splx(s);
- return (0);
-}
+ /* Drain any output. */
+ MPASS((tp->t_flags & TF_STOPPED) == 0);
+ if (!tty_gone(tp))
+ tty_drain(tp);
-#define FLUSHQ(q) { \
- if ((q)->c_cc) \
- ndflush(q, (q)->c_cc); \
-}
+ ttydisc_close(tp);
+
+ /* Destroy associated buffers already. */
+ tty_freebuffers(tp);
+
+ knlist_clear(&tp->t_inpoll.si_note, 1);
+ knlist_clear(&tp->t_outpoll.si_note, 1);
-/* Is 'c' a line delimiter ("break" character)? */
-#define TTBREAKC(c, lflag) \
- ((c) == '\n' || (((c) == cc[VEOF] || \
- (c) == cc[VEOL] || ((c) == cc[VEOL2] && lflag & IEXTEN)) && \
- (c) != _POSIX_VDISABLE))
+ if (!tty_gone(tp))
+ ttydevsw_close(tp);
+
+ tp->t_flags &= ~TF_OPENCLOSE;
+ tty_rel_free(tp);
+}
/*
- * Process input of a single character received on a tty.
+ * Operations that are exposed through the character device in /dev.
*/
-int
-ttyinput(int c, struct tty *tp)
+static int
+ttydev_open(struct cdev *dev, int oflags, int devtype, struct thread *td)
{
- tcflag_t iflag, lflag;
- cc_t *cc;
- int i, err;
+ struct tty *tp = dev->si_drv1;
+ int error;
+ /* Disallow access when the TTY belongs to a different prison. */
+ if (dev->si_cred != NULL &&
+ dev->si_cred->cr_prison != td->td_ucred->cr_prison &&
+ priv_check(td, PRIV_TTY_PRISON)) {
+ return (EPERM);
+ }
+
+ tty_lock(tp);
+ if (tty_gone(tp)) {
+ /* Device is already gone. */
+ tty_unlock(tp);
+ return (ENXIO);
+ }
/*
- * If input is pending take it first.
- */
- lflag = tp->t_lflag;
- if (ISSET(lflag, PENDIN))
- ttypend(tp);
- /*
- * Gather stats.
+ * Prevent the TTY from being opened when being torn down or
+ * built up by unrelated processes.
*/
- if (ISSET(lflag, ICANON)) {
- ++tk_cancc;
- ++tp->t_cancc;
- } else {
- ++tk_rawcc;
- ++tp->t_rawcc;
+ if (tp->t_flags & TF_OPENCLOSE) {
+ tty_unlock(tp);
+ return (EBUSY);
}
- ++tk_nin;
+ tp->t_flags |= TF_OPENCLOSE;
/*
- * Block further input iff:
- * current input > threshold AND input is available to user program
- * AND input flow control is enabled and not yet invoked.
- * The 3 is slop for PARMRK.
+ * Make sure the "tty" and "cua" device cannot be opened at the
+ * same time.
*/
- iflag = tp->t_iflag;
- if (tp->t_rawq.c_cc + tp->t_canq.c_cc > tp->t_ihiwat - 3 &&
- (!ISSET(lflag, ICANON) || tp->t_canq.c_cc != 0) &&
- (ISSET(tp->t_cflag, CRTS_IFLOW) || ISSET(iflag, IXOFF)) &&
- !ISSET(tp->t_state, TS_TBLOCK))
- ttyblock(tp);
-
- /* Handle exceptional conditions (break, parity, framing). */
- cc = tp->t_cc;
- err = (ISSET(c, TTY_ERRORMASK));
- if (err) {
- CLR(c, TTY_ERRORMASK);
- if (ISSET(err, TTY_BI)) {
- if (ISSET(iflag, IGNBRK))
- return (0);
- if (ISSET(iflag, BRKINT)) {
- ttyflush(tp, FREAD | FWRITE);
- if (tp->t_pgrp != NULL) {
- PGRP_LOCK(tp->t_pgrp);
- pgsignal(tp->t_pgrp, SIGINT, 1);
- PGRP_UNLOCK(tp->t_pgrp);
- }
- goto endcase;
- }
- if (ISSET(iflag, PARMRK))
- goto parmrk;
- } else if ((ISSET(err, TTY_PE) && ISSET(iflag, INPCK))
- || ISSET(err, TTY_FE)) {
- if (ISSET(iflag, IGNPAR))
- return (0);
- else if (ISSET(iflag, PARMRK)) {
-parmrk:
- if (tp->t_rawq.c_cc + tp->t_canq.c_cc >
- MAX_INPUT - 3)
- goto input_overflow;
- (void)putc(0377 | TTY_QUOTE, &tp->t_rawq);
- (void)putc(0 | TTY_QUOTE, &tp->t_rawq);
- (void)putc(c | TTY_QUOTE, &tp->t_rawq);
- goto endcase;
- } else
- c = 0;
+ if (TTY_CALLOUT(tp, dev)) {
+ if (tp->t_flags & TF_OPENED_IN) {
+ error = EBUSY;
+ goto done;
+ }
+ } else {
+ if (tp->t_flags & TF_OPENED_OUT) {
+ error = EBUSY;
+ goto done;
}
}
- if (!ISSET(tp->t_state, TS_TYPEN) && ISSET(iflag, ISTRIP))
- CLR(c, 0x80);
- if (!ISSET(lflag, EXTPROC)) {
- /*
- * Check for literal nexting very first
- */
- if (ISSET(tp->t_state, TS_LNCH)) {
- SET(c, TTY_QUOTE);
- CLR(tp->t_state, TS_LNCH);
- }
- /*
- * Scan for special characters. This code
- * is really just a big case statement with
- * non-constant cases. The bottom of the
- * case statement is labeled ``endcase'', so goto
- * it after a case match, or similar.
- */
+ if (tp->t_flags & TF_EXCLUDE && priv_check(td, PRIV_TTY_EXCLUSIVE)) {
+ error = EBUSY;
+ goto done;
+ }
- /*
- * Control chars which aren't controlled
- * by ICANON, ISIG, or IXON.
- */
- if (ISSET(lflag, IEXTEN)) {
- if (CCEQ(cc[VLNEXT], c)) {
- if (ISSET(lflag, ECHO)) {
- if (ISSET(lflag, ECHOE)) {
- (void)ttyoutput('^', tp);
- (void)ttyoutput('\b', tp);
- } else
- ttyecho(c, tp);
- }
- SET(tp->t_state, TS_LNCH);
- goto endcase;
- }
- if (CCEQ(cc[VDISCARD], c)) {
- if (ISSET(lflag, FLUSHO))
- CLR(tp->t_lflag, FLUSHO);
- else {
- ttyflush(tp, FWRITE);
- ttyecho(c, tp);
- if (tp->t_rawq.c_cc + tp->t_canq.c_cc)
- ttyretype(tp);
- SET(tp->t_lflag, FLUSHO);
- }
- goto startoutput;
- }
+ if (!tty_opened(tp)) {
+ /* Set proper termios flags. */
+ if (TTY_CALLOUT(tp, dev)) {
+ tp->t_termios = tp->t_termios_init_out;
+ } else {
+ tp->t_termios = tp->t_termios_init_in;
}
- /*
- * Signals.
- */
- if (ISSET(lflag, ISIG)) {
- if (CCEQ(cc[VINTR], c) || CCEQ(cc[VQUIT], c)) {
- if (!ISSET(lflag, NOFLSH))
- ttyflush(tp, FREAD | FWRITE);
- ttyecho(c, tp);
- if (tp->t_pgrp != NULL) {
- PGRP_LOCK(tp->t_pgrp);
- pgsignal(tp->t_pgrp,
- CCEQ(cc[VINTR], c) ? SIGINT : SIGQUIT, 1);
- PGRP_UNLOCK(tp->t_pgrp);
- }
- goto endcase;
- }
- if (CCEQ(cc[VSUSP], c)) {
- if (!ISSET(lflag, NOFLSH))
- ttyflush(tp, FREAD);
- ttyecho(c, tp);
- if (tp->t_pgrp != NULL) {
- PGRP_LOCK(tp->t_pgrp);
- pgsignal(tp->t_pgrp, SIGTSTP, 1);
- PGRP_UNLOCK(tp->t_pgrp);
- }
- goto endcase;
- }
- }
- /*
- * Handle start/stop characters.
- */
- if (ISSET(iflag, IXON)) {
- if (CCEQ(cc[VSTOP], c)) {
- if (!ISSET(tp->t_state, TS_TTSTOP)) {
- SET(tp->t_state, TS_TTSTOP);
- tt_stop(tp, 0);
- return (0);
- }
- if (!CCEQ(cc[VSTART], c))
- return (0);
- /*
- * if VSTART == VSTOP then toggle
- */
- goto endcase;
- }
- if (CCEQ(cc[VSTART], c))
- goto restartoutput;
- }
- /*
- * IGNCR, ICRNL, & INLCR
- */
- if (c == '\r') {
- if (ISSET(iflag, IGNCR))
- return (0);
- else if (ISSET(iflag, ICRNL))
- c = '\n';
- } else if (c == '\n' && ISSET(iflag, INLCR))
- c = '\r';
+ ttydevsw_param(tp, &tp->t_termios);
+
+ ttydevsw_modem(tp, SER_DTR|SER_RTS, 0);
+
+ error = ttydevsw_open(tp);
+ if (error != 0)
+ goto done;
+
+ ttydisc_open(tp);
+ tty_watermarks(tp);
}
- if (!ISSET(tp->t_lflag, EXTPROC) && ISSET(lflag, ICANON)) {
- /*
- * From here on down canonical mode character
- * processing takes place.
- */
- /*
- * erase or erase2 (^H / ^?)
- */
- if (CCEQ(cc[VERASE], c) || CCEQ(cc[VERASE2], c) ) {
- if (tp->t_rawq.c_cc)
- ttyrub(unputc(&tp->t_rawq), tp);
- goto endcase;
- }
- /*
- * kill (^U)
- */
- if (CCEQ(cc[VKILL], c)) {
- if (ISSET(lflag, ECHOKE) &&
- tp->t_rawq.c_cc == tp->t_rocount &&
- !ISSET(lflag, ECHOPRT))
- while (tp->t_rawq.c_cc)
- ttyrub(unputc(&tp->t_rawq), tp);
- else {
- ttyecho(c, tp);
- if (ISSET(lflag, ECHOK) ||
- ISSET(lflag, ECHOKE))
- ttyecho('\n', tp);
- FLUSHQ(&tp->t_rawq);
- tp->t_rocount = 0;
- }
- CLR(tp->t_state, TS_LOCAL);
- goto endcase;
- }
- /*
- * word erase (^W)
- */
- if (CCEQ(cc[VWERASE], c) && ISSET(lflag, IEXTEN)) {
- int ctype;
- /*
- * erase whitespace
- */
- while ((c = unputc(&tp->t_rawq)) == ' ' || c == '\t')
- ttyrub(c, tp);
- if (c == -1)
- goto endcase;
- /*
- * erase last char of word and remember the
- * next chars type (for ALTWERASE)
- */
- ttyrub(c, tp);
- c = unputc(&tp->t_rawq);
- if (c == -1)
- goto endcase;
- if (c == ' ' || c == '\t') {
- (void)putc(c, &tp->t_rawq);
- goto endcase;
- }
- ctype = ISALPHA(c);
- /*
- * erase rest of word
- */
- do {
- ttyrub(c, tp);
- c = unputc(&tp->t_rawq);
- if (c == -1)
- goto endcase;
- } while (c != ' ' && c != '\t' &&
- (!ISSET(lflag, ALTWERASE) || ISALPHA(c) == ctype));
- (void)putc(c, &tp->t_rawq);
- goto endcase;
- }
- /*
- * reprint line (^R)
- */
- if (CCEQ(cc[VREPRINT], c) && ISSET(lflag, IEXTEN)) {
- ttyretype(tp);
- goto endcase;
- }
- /*
- * ^T - kernel info and generate SIGINFO
- */
- if (CCEQ(cc[VSTATUS], c) && ISSET(lflag, IEXTEN)) {
- if (ISSET(lflag, ISIG) && tp->t_pgrp != NULL) {
- PGRP_LOCK(tp->t_pgrp);
- pgsignal(tp->t_pgrp, SIGINFO, 1);
- PGRP_UNLOCK(tp->t_pgrp);
- }
- if (!ISSET(lflag, NOKERNINFO))
- ttyinfo(tp);
- goto endcase;
+ /* Wait for Carrier Detect. */
+ if (!TTY_CALLOUT(tp, dev) && (oflags & O_NONBLOCK) == 0 &&
+ (tp->t_termios.c_cflag & CLOCAL) == 0) {
+ while ((ttydevsw_modem(tp, 0, 0) & SER_DCD) == 0) {
+ error = tty_wait(tp, &tp->t_dcdwait);
+ if (error != 0)
+ goto done;
}
}
- /*
- * Check for input buffer overflow
- */
- if (tp->t_rawq.c_cc + tp->t_canq.c_cc >= MAX_INPUT) {
-input_overflow:
- if (ISSET(iflag, IMAXBEL)) {
- if (tp->t_outq.c_cc < tp->t_ohiwat)
- (void)ttyoutput(CTRL('g'), tp);
- }
- goto endcase;
+
+ if (TTY_CALLOUT(tp, dev)) {
+ tp->t_flags |= TF_OPENED_OUT;
+ } else {
+ tp->t_flags |= TF_OPENED_IN;
}
- if ( c == 0377 && ISSET(iflag, PARMRK) && !ISSET(iflag, ISTRIP)
- && ISSET(iflag, IGNBRK|IGNPAR) != (IGNBRK|IGNPAR))
- (void)putc(0377 | TTY_QUOTE, &tp->t_rawq);
+done: tp->t_flags &= ~TF_OPENCLOSE;
+ ttydev_leave(tp);
+ return (error);
+}
+
+static int
+ttydev_close(struct cdev *dev, int fflag, int devtype, struct thread *td)
+{
+ struct tty *tp = dev->si_drv1;
+
+ tty_lock(tp);
/*
- * Put data char in q for user and
- * wakeup on seeing a line delimiter.
+ * This can only be called once. The callin and the callout
+ * devices cannot be opened at the same time.
*/
- if (putc(c, &tp->t_rawq) >= 0) {
- if (!ISSET(lflag, ICANON)) {
- ttwakeup(tp);
- ttyecho(c, tp);
- goto endcase;
- }
- if (TTBREAKC(c, lflag)) {
- tp->t_rocount = 0;
- catq(&tp->t_rawq, &tp->t_canq);
- ttwakeup(tp);
- } else if (tp->t_rocount++ == 0)
- tp->t_rocol = tp->t_column;
- if (ISSET(tp->t_state, TS_ERASE)) {
- /*
- * end of prterase \.../
- */
- CLR(tp->t_state, TS_ERASE);
- (void)ttyoutput('/', tp);
- }
- i = tp->t_column;
- ttyecho(c, tp);
- if (CCEQ(cc[VEOF], c) && ISSET(lflag, ECHO)) {
- /*
- * Place the cursor over the '^' of the ^D.
- */
- i = imin(2, tp->t_column - i);
- while (i > 0) {
- (void)ttyoutput('\b', tp);
- i--;
- }
+ MPASS((tp->t_flags & TF_OPENED) != TF_OPENED);
+ tp->t_flags &= ~(TF_OPENED|TF_EXCLUDE|TF_STOPPED);
+
+ /* Properly wake up threads that are stuck - revoke(). */
+ tp->t_revokecnt++;
+ tty_wakeup(tp, FREAD|FWRITE);
+ cv_broadcast(&tp->t_bgwait);
+
+ ttydev_leave(tp);
+
+ return (0);
+}
+
+static __inline int
+tty_is_ctty(struct tty *tp, struct proc *p)
+{
+ tty_lock_assert(tp, MA_OWNED);
+
+ return (p->p_session == tp->t_session && p->p_flag & P_CONTROLT);
+}
+
+static int
+tty_wait_background(struct tty *tp, struct thread *td, int sig)
+{
+ struct proc *p = td->td_proc;
+ struct pgrp *pg;
+ int error;
+
+ MPASS(sig == SIGTTIN || sig == SIGTTOU);
+ tty_lock_assert(tp, MA_OWNED);
+
+ for (;;) {
+ PROC_LOCK(p);
+ /*
+ * The process should only sleep, when:
+ * - This terminal is the controling terminal
+ * - Its process group is not the foreground process
+ * group
+ * - The parent process isn't waiting for the child to
+ * exit
+ * - the signal to send to the process isn't masked
+ */
+ if (!tty_is_ctty(tp, p) ||
+ p->p_pgrp == tp->t_pgrp || p->p_flag & P_PPWAIT ||
+ SIGISMEMBER(p->p_sigacts->ps_sigignore, sig) ||
+ SIGISMEMBER(td->td_sigmask, sig)) {
+ /* Allow the action to happen. */
+ PROC_UNLOCK(p);
+ return (0);
}
+
+ /*
+ * Send the signal and sleep until we're the new
+ * foreground process group.
+ */
+ pg = p->p_pgrp;
+ PROC_UNLOCK(p);
+ if (pg->pg_jobc == 0)
+ return (EIO);
+ PGRP_LOCK(pg);
+ pgsignal(pg, sig, 1);
+ PGRP_UNLOCK(pg);
+
+ error = tty_wait(tp, &tp->t_bgwait);
+ if (error)
+ return (error);
}
-endcase:
+}
+
+static int
+ttydev_read(struct cdev *dev, struct uio *uio, int ioflag)
+{
+ struct tty *tp = dev->si_drv1;
+ int error;
+
+ error = ttydev_enter(tp);
+ if (error)
+ return (0);
+
+ error = tty_wait_background(tp, curthread, SIGTTIN);
+ if (error)
+ goto done;
+
+ error = ttydisc_read(tp, uio, ioflag);
+done: ttydev_leave(tp);
+
/*
- * IXANY means allow any character to restart output.
+ * The read() and write() calls should not throw an error when
+ * the device is ripped offline.
*/
- if (ISSET(tp->t_state, TS_TTSTOP) &&
- !ISSET(iflag, IXANY) && cc[VSTART] != cc[VSTOP])
+ if (error == ENXIO)
return (0);
-restartoutput:
- CLR(tp->t_lflag, FLUSHO);
- CLR(tp->t_state, TS_TTSTOP);
-startoutput:
- return (ttstart(tp));
+
+ return (error);
}
-/*
- * Output a single character on a tty, doing output processing
- * as needed (expanding tabs, newline processing, etc.).
- * Returns < 0 if succeeds, otherwise returns char to resend.
- * Must be recursive.
- */
static int
-ttyoutput(int c, struct tty *tp)
+ttydev_write(struct cdev *dev, struct uio *uio, int ioflag)
{
- tcflag_t oflag;
- int col, s;
-
- oflag = tp->t_oflag;
- if (!ISSET(oflag, OPOST)) {
- if (ISSET(tp->t_lflag, FLUSHO))
- return (-1);
- if (putc(c, &tp->t_outq))
- return (c);
- tk_nout++;
- tp->t_outcc++;
- return (-1);
- }
- /*
- * Do tab expansion if OXTABS is set. Special case if we external
- * processing, we don't do the tab expansion because we'll probably
- * get it wrong. If tab expansion needs to be done, let it happen
- * externally.
- */
- CLR(c, ~TTY_CHARMASK);
- if (c == '\t' &&
- ISSET(oflag, OXTABS) && !ISSET(tp->t_lflag, EXTPROC)) {
- c = 8 - (tp->t_column & 7);
- if (!ISSET(tp->t_lflag, FLUSHO)) {
- s = spltty(); /* Don't interrupt tabs. */
- c -= b_to_q(" ", c, &tp->t_outq);
- tk_nout += c;
- tp->t_outcc += c;
- splx(s);
- }
- tp->t_column += c;
- return (c ? -1 : '\t');
+ struct tty *tp = dev->si_drv1;
+ int error;
+
+ error = ttydev_enter(tp);
+ if (error)
+ return (0);
+
+ if (tp->t_termios.c_lflag & TOSTOP) {
+ error = tty_wait_background(tp, curthread, SIGTTOU);
+ if (error)
+ goto done;
}
- if (c == CEOT && ISSET(oflag, ONOEOT))
- return (-1);
+
+ error = ttydisc_write(tp, uio, ioflag);
+done: ttydev_leave(tp);
/*
- * Newline translation: if ONLCR is set,
- * translate newline into "\r\n".
+ * The read() and write() calls should not throw an error when
+ * the device is ripped offline.
*/
- if (c == '\n' && ISSET(tp->t_oflag, ONLCR)) {
- tk_nout++;
- tp->t_outcc++;
- if (!ISSET(tp->t_lflag, FLUSHO) && putc('\r', &tp->t_outq))
- return (c);
- }
- /* If OCRNL is set, translate "\r" into "\n". */
- else if (c == '\r' && ISSET(tp->t_oflag, OCRNL))
- c = '\n';
- /* If ONOCR is set, don't transmit CRs when on column 0. */
- else if (c == '\r' && ISSET(tp->t_oflag, ONOCR) && tp->t_column == 0)
- return (-1);
-
- tk_nout++;
- tp->t_outcc++;
- if (!ISSET(tp->t_lflag, FLUSHO) && putc(c, &tp->t_outq))
- return (c);
+ if (error == ENXIO)
+ return (0);
- col = tp->t_column;
- switch (CCLASS(c)) {
- case BACKSPACE:
- if (col > 0)
- --col;
- break;
- case CONTROL:
- break;
- case NEWLINE:
- if (ISSET(tp->t_oflag, ONLCR | ONLRET))
- col = 0;
- break;
- case RETURN:
- col = 0;
- break;
- case ORDINARY:
- ++col;
- break;
- case TAB:
- col = (col + 8) & ~7;
- break;
- }
- tp->t_column = col;
- return (-1);
+ return (error);
}
-/*
- * Ioctls for all tty devices. Called after line-discipline specific ioctl
- * has been called to do discipline-specific functions and/or reject any
- * of these ioctl commands.
- */
-/* ARGSUSED */
-int
-ttioctl(struct tty *tp, u_long cmd, void *data, int flag)
+static int
+ttydev_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag,
+ struct thread *td)
{
- struct proc *p;
- struct thread *td;
- struct pgrp *pgrp;
- int s, error, bits, sig, sig2;
+ struct tty *tp = dev->si_drv1;
+ int error;
- td = curthread; /* XXX */
- p = td->td_proc;
+ error = ttydev_enter(tp);
+ if (error)
+ return (error);
- /* If the ioctl involves modification, hang if in the background. */
switch (cmd) {
- case TIOCCBRK:
- case TIOCCONS:
- case TIOCDRAIN:
- case TIOCEXCL:
- case TIOCFLUSH:
-#ifdef TIOCHPCL
- case TIOCHPCL:
+ case TIOCCBRK:
+ case TIOCCONS:
+ case TIOCDRAIN:
+ case TIOCEXCL:
+ case TIOCFLUSH:
+ case TIOCNXCL:
+ case TIOCSBRK:
+ case TIOCSCTTY:
+ case TIOCSETA:
+ case TIOCSETAF:
+ case TIOCSETAW:
+ case TIOCSPGRP:
+ case TIOCSTART:
+ case TIOCSTAT:
+ case TIOCSTOP:
+ case TIOCSWINSZ:
+#if 0
+ case TIOCSDRAINWAIT:
+ case TIOCSETD:
+ case TIOCSTI:
#endif
- case TIOCNXCL:
- case TIOCSBRK:
- case TIOCSCTTY:
- case TIOCSDRAINWAIT:
- case TIOCSETA:
- case TIOCSETAF:
- case TIOCSETAW:
- case TIOCSETD:
- case TIOCSPGRP:
- case TIOCSTART:
- case TIOCSTAT:
- case TIOCSTI:
- case TIOCSTOP:
- case TIOCSWINSZ:
-#if defined(COMPAT_43TTY)
+#ifdef COMPAT_43TTY
case TIOCLBIC:
case TIOCLBIS:
case TIOCLSET:
@@ -838,2390 +482,1268 @@ ttioctl(struct tty *tp, u_long cmd, void *data, int flag)
case TIOCSETN:
case TIOCSETP:
case TIOCSLTC:
-#endif
- sx_slock(&proctree_lock);
- PROC_LOCK(p);
- while (isbackground(p, tp) && !(p->p_flag & P_PPWAIT) &&
- !SIGISMEMBER(p->p_sigacts->ps_sigignore, SIGTTOU) &&
- !SIGISMEMBER(td->td_sigmask, SIGTTOU)) {
- pgrp = p->p_pgrp;
- PROC_UNLOCK(p);
- if (pgrp->pg_jobc == 0) {
- sx_sunlock(&proctree_lock);
- return (EIO);
- }
- PGRP_LOCK(pgrp);
- sx_sunlock(&proctree_lock);
- pgsignal(pgrp, SIGTTOU, 1);
- PGRP_UNLOCK(pgrp);
- error = ttysleep(tp, &lbolt, TTOPRI | PCATCH, "ttybg1",
- 0);
- if (error)
- return (error);
- sx_slock(&proctree_lock);
- PROC_LOCK(p);
- }
- PROC_UNLOCK(p);
- sx_sunlock(&proctree_lock);
- break;
- }
-
-
- if (tp->t_modem != NULL) {
- switch (cmd) {
- case TIOCSDTR:
- tt_modem(tp, SER_DTR, 0);
- return (0);
- case TIOCCDTR:
- tt_modem(tp, 0, SER_DTR);
- return (0);
- case TIOCMSET:
- bits = *(int *)data;
- sig = (bits & (TIOCM_DTR | TIOCM_RTS)) >> 1;
- sig2 = ((~bits) & (TIOCM_DTR | TIOCM_RTS)) >> 1;
- tt_modem(tp, sig, sig2);
- return (0);
- case TIOCMBIS:
- bits = *(int *)data;
- sig = (bits & (TIOCM_DTR | TIOCM_RTS)) >> 1;
- tt_modem(tp, sig, 0);
- return (0);
- case TIOCMBIC:
- bits = *(int *)data;
- sig = (bits & (TIOCM_DTR | TIOCM_RTS)) >> 1;
- tt_modem(tp, 0, sig);
- return (0);
- case TIOCMGET:
- sig = tt_modem(tp, 0, 0);
- /* See <sys/serial.h. for the "<< 1" stuff */
- bits = TIOCM_LE + (sig << 1);
- *(int *)data = bits;
- return (0);
- default:
- break;
- }
- }
-
- if (tp->t_pps != NULL) {
- error = pps_ioctl(cmd, data, tp->t_pps);
- if (error != ENOIOCTL)
- return (error);
- }
-
- switch (cmd) { /* Process the ioctl. */
- case FIOASYNC: /* set/clear async i/o */
- s = spltty();
- if (*(int *)data)
- SET(tp->t_state, TS_ASYNC);
- else
- CLR(tp->t_state, TS_ASYNC);
- splx(s);
- break;
- case FIONBIO: /* set/clear non-blocking i/o */
- break; /* XXX: delete. */
- case FIONREAD: /* get # bytes to read */
- s = spltty();
- *(int *)data = ttnread(tp);
- splx(s);
- break;
-
- case FIOSETOWN:
+#endif /* COMPAT_43TTY */
/*
- * Policy -- Don't allow FIOSETOWN on someone else's
- * controlling tty
+ * If the ioctl() causes the TTY to be modified, let it
+ * wait in the background.
*/
- if (tp->t_session != NULL && !isctty(p, tp))
- return (ENOTTY);
-
- error = fsetown(*(int *)data, &tp->t_sigio);
+ error = tty_wait_background(tp, curthread, SIGTTOU);
if (error)
- return (error);
- break;
- case FIOGETOWN:
- if (tp->t_session != NULL && !isctty(p, tp))
- return (ENOTTY);
- *(int *)data = fgetown(&tp->t_sigio);
- break;
-
- case TIOCEXCL: /* set exclusive use of tty */
- s = spltty();
- SET(tp->t_state, TS_XCLUDE);
- splx(s);
- break;
- case TIOCFLUSH: { /* flush buffers */
- int flags = *(int *)data;
-
- if (flags == 0)
- flags = FREAD | FWRITE;
- else
- flags &= FREAD | FWRITE;
- ttyflush(tp, flags);
- break;
+ goto done;
}
- case TIOCCONS: /* become virtual console */
- if (*(int *)data) {
- struct nameidata nid;
- if (constty && constty != tp &&
- ISSET(constty->t_state, TS_CONNECTED))
- return (EBUSY);
-
- /* Ensure user can open the real console. */
- NDINIT(&nid, LOOKUP, LOCKLEAF | FOLLOW, UIO_SYSSPACE,
- "/dev/console", td);
- if ((error = namei(&nid)) != 0)
- return (error);
- NDFREE(&nid, NDF_ONLY_PNBUF);
- error = VOP_ACCESS(nid.ni_vp, VREAD, td->td_ucred, td);
- vput(nid.ni_vp);
- if (error)
- return (error);
-
- constty_set(tp);
- } else if (tp == constty)
- constty_clear();
- break;
- case TIOCDRAIN: /* wait till output drained */
- error = ttywait(tp);
- if (error)
- return (error);
- break;
- case TIOCGETA: { /* get termios struct */
- struct termios *t = (struct termios *)data;
+ error = tty_ioctl(tp, cmd, data, td);
+done: ttydev_leave(tp);
- bcopy(&tp->t_termios, t, sizeof(struct termios));
- break;
- }
- case TIOCGETD: /* get line discipline */
- *(int *)data = tp->t_line;
- break;
- case TIOCGWINSZ: /* get window size */
- *(struct winsize *)data = tp->t_winsize;
- break;
- case TIOCGPGRP: /* get pgrp of tty */
- if (!isctty(p, tp))
- return (ENOTTY);
- *(int *)data = tp->t_pgrp ? tp->t_pgrp->pg_id : NO_PID;
- break;
- case TIOCGSID: /* get sid of tty */
- if (!isctty(p, tp))
- return (ENOTTY);
- *(int *)data = tp->t_session->s_sid;
- break;
-#ifdef TIOCHPCL
- case TIOCHPCL: /* hang up on last close */
- s = spltty();
- SET(tp->t_cflag, HUPCL);
- splx(s);
- break;
-#endif
- case TIOCMGDTRWAIT:
- *(int *)data = tp->t_dtr_wait * 100 / hz;
- break;
- case TIOCMSDTRWAIT:
- /* must be root since the wait applies to following logins */
- error = priv_check(td, PRIV_TTY_DTRWAIT);
- if (error)
- return (error);
- tp->t_dtr_wait = *(int *)data * hz / 100;
- break;
- case TIOCNXCL: /* reset exclusive use of tty */
- s = spltty();
- CLR(tp->t_state, TS_XCLUDE);
- splx(s);
- break;
- case TIOCOUTQ: /* output queue size */
- *(int *)data = tp->t_outq.c_cc;
- break;
- case TIOCSETA: /* set termios struct */
- case TIOCSETAW: /* drain output, set */
- case TIOCSETAF: { /* drn out, fls in, set */
- struct termios *t = (struct termios *)data;
+ return (error);
+}
- if (t->c_ispeed == 0)
- t->c_ispeed = t->c_ospeed;
- if (t->c_ispeed == 0)
- t->c_ispeed = tp->t_ospeed;
- if (t->c_ispeed == 0)
- return (EINVAL);
- s = spltty();
- if (cmd == TIOCSETAW || cmd == TIOCSETAF) {
- error = ttywait(tp);
- if (error) {
- splx(s);
- return (error);
- }
- if (cmd == TIOCSETAF)
- ttyflush(tp, FREAD);
- }
- if (!ISSET(t->c_cflag, CIGNORE)) {
- /*
- * Set device hardware.
- */
- error = tt_param(tp, t);
- if (error) {
- splx(s);
- return (error);
- }
- if (ISSET(t->c_cflag, CLOCAL) &&
- !ISSET(tp->t_cflag, CLOCAL)) {
- /*
- * XXX disconnections would be too hard to
- * get rid of without this kludge. The only
- * way to get rid of controlling terminals
- * is to exit from the session leader.
- */
- CLR(tp->t_state, TS_ZOMBIE);
-
- wakeup(TSA_CARR_ON(tp));
- ttwakeup(tp);
- ttwwakeup(tp);
- }
- if ((ISSET(tp->t_state, TS_CARR_ON) ||
- ISSET(t->c_cflag, CLOCAL)) &&
- !ISSET(tp->t_state, TS_ZOMBIE))
- SET(tp->t_state, TS_CONNECTED);
- else
- CLR(tp->t_state, TS_CONNECTED);
- tp->t_cflag = t->c_cflag;
- tp->t_ispeed = t->c_ispeed;
- if (t->c_ospeed != 0)
- tp->t_ospeed = t->c_ospeed;
- ttsetwater(tp);
- }
- if (ISSET(t->c_lflag, ICANON) != ISSET(tp->t_lflag, ICANON) &&
- cmd != TIOCSETAF) {
- if (ISSET(t->c_lflag, ICANON))
- SET(tp->t_lflag, PENDIN);
- else {
- /*
- * XXX we really shouldn't allow toggling
- * ICANON while we're in a non-termios line
- * discipline. Now we have to worry about
- * panicing for a null queue.
- */
- if (tp->t_canq.c_cbreserved > 0 &&
- tp->t_rawq.c_cbreserved > 0) {
- catq(&tp->t_rawq, &tp->t_canq);
- /*
- * XXX the queue limits may be
- * different, so the old queue
- * swapping method no longer works.
- */
- catq(&tp->t_canq, &tp->t_rawq);
- }
- CLR(tp->t_lflag, PENDIN);
- }
- ttwakeup(tp);
- }
- tp->t_iflag = t->c_iflag;
- tp->t_oflag = t->c_oflag;
- /*
- * Make the EXTPROC bit read only.
- */
- if (ISSET(tp->t_lflag, EXTPROC))
- SET(t->c_lflag, EXTPROC);
- else
- CLR(t->c_lflag, EXTPROC);
- tp->t_lflag = t->c_lflag | ISSET(tp->t_lflag, PENDIN);
- if (t->c_cc[VMIN] != tp->t_cc[VMIN] ||
- t->c_cc[VTIME] != tp->t_cc[VTIME])
- ttwakeup(tp);
- bcopy(t->c_cc, tp->t_cc, sizeof(t->c_cc));
- splx(s);
- break;
+static int
+ttydev_poll(struct cdev *dev, int events, struct thread *td)
+{
+ struct tty *tp = dev->si_drv1;
+ int error, revents = 0;
+
+ error = ttydev_enter(tp);
+ if (error) {
+ /* Don't return the error here, but the event mask. */
+ return (events &
+ (POLLHUP|POLLIN|POLLRDNORM|POLLOUT|POLLWRNORM));
}
- case TIOCSETD: { /* set line discipline */
- int t = *(int *)data;
- if ((u_int)t >= nlinesw)
- return (ENXIO);
- if (t == tp->t_line)
- return (0);
- s = spltty();
- ttyld_close(tp, flag);
- tp->t_line = t;
- /* XXX: we should use the correct cdev here */
- error = ttyld_open(tp, tp->t_dev);
- if (error) {
- /*
- * If we fail to switch line discipline we cannot
- * fall back to the previous, because we can not
- * trust that ldisc to open successfully either.
- * Fall back to the default ldisc which we know
- * will allways succeed.
- */
- tp->t_line = TTYDISC;
- (void)ttyld_open(tp, tp->t_dev);
- }
- splx(s);
- return (error);
- break;
+ if (events & (POLLIN|POLLRDNORM)) {
+ /* See if we can read something. */
+ if (ttydisc_read_poll(tp) > 0)
+ revents |= events & (POLLIN|POLLRDNORM);
}
- case TIOCSTART: /* start output, like ^Q */
- s = spltty();
- if (ISSET(tp->t_state, TS_TTSTOP) ||
- ISSET(tp->t_lflag, FLUSHO)) {
- CLR(tp->t_lflag, FLUSHO);
- CLR(tp->t_state, TS_TTSTOP);
- ttstart(tp);
- }
- splx(s);
- break;
- case TIOCSTI: /* simulate terminal input */
- if ((flag & FREAD) == 0 && priv_check(td, PRIV_TTY_STI))
- return (EPERM);
- if (!isctty(p, tp) && priv_check(td, PRIV_TTY_STI))
- return (EACCES);
- s = spltty();
- ttyld_rint(tp, *(u_char *)data);
- splx(s);
- break;
- case TIOCSTOP: /* stop output, like ^S */
- s = spltty();
- if (!ISSET(tp->t_state, TS_TTSTOP)) {
- SET(tp->t_state, TS_TTSTOP);
- tt_stop(tp, 0);
- }
- splx(s);
- break;
- case TIOCSCTTY: /* become controlling tty */
- /* Session ctty vnode pointer set in vnode layer. */
- sx_slock(&proctree_lock);
- if (!SESS_LEADER(p) ||
- ((p->p_session->s_ttyvp || tp->t_session) &&
- (tp->t_session != p->p_session))) {
- sx_sunlock(&proctree_lock);
- return (EPERM);
- }
- tp->t_session = p->p_session;
- tp->t_pgrp = p->p_pgrp;
- SESS_LOCK(p->p_session);
- ttyref(tp); /* ttyrel(): kern_proc.c:pgdelete() */
- p->p_session->s_ttyp = tp;
- SESS_UNLOCK(p->p_session);
- PROC_LOCK(p);
- p->p_flag |= P_CONTROLT;
- PROC_UNLOCK(p);
- sx_sunlock(&proctree_lock);
- break;
- case TIOCSPGRP: { /* set pgrp of tty */
- sx_slock(&proctree_lock);
- pgrp = pgfind(*(int *)data);
- if (!isctty(p, tp)) {
- if (pgrp != NULL)
- PGRP_UNLOCK(pgrp);
- sx_sunlock(&proctree_lock);
- return (ENOTTY);
- }
- if (pgrp == NULL) {
- sx_sunlock(&proctree_lock);
- return (EPERM);
- }
- PGRP_UNLOCK(pgrp);
- if (pgrp->pg_session != p->p_session) {
- sx_sunlock(&proctree_lock);
- return (EPERM);
- }
- sx_sunlock(&proctree_lock);
- tp->t_pgrp = pgrp;
- break;
+ if (events & (POLLOUT|POLLWRNORM)) {
+ /* See if we can write something. */
+ if (ttydisc_write_poll(tp) > 0)
+ revents |= events & (POLLOUT|POLLWRNORM);
}
- case TIOCSTAT: /* simulate control-T */
- s = spltty();
- ttyinfo(tp);
- splx(s);
- break;
- case TIOCSWINSZ: /* set window size */
- if (bcmp((caddr_t)&tp->t_winsize, data,
- sizeof (struct winsize))) {
- tp->t_winsize = *(struct winsize *)data;
- if (tp->t_pgrp != NULL) {
- PGRP_LOCK(tp->t_pgrp);
- pgsignal(tp->t_pgrp, SIGWINCH, 1);
- PGRP_UNLOCK(tp->t_pgrp);
- }
- }
- break;
- case TIOCSDRAINWAIT:
- error = priv_check(td, PRIV_TTY_DRAINWAIT);
- if (error)
- return (error);
- tp->t_timeout = *(int *)data * hz;
- wakeup(TSA_OCOMPLETE(tp));
- wakeup(TSA_OLOWAT(tp));
- break;
- case TIOCGDRAINWAIT:
- *(int *)data = tp->t_timeout / hz;
- break;
- case TIOCSBRK:
- return (tt_break(tp, 1));
- case TIOCCBRK:
- return (tt_break(tp, 0));
- default:
-#if defined(COMPAT_43TTY)
- return (ttcompat(tp, cmd, data, flag));
-#else
- return (ENOIOCTL);
-#endif
+ if (tp->t_flags & TF_ZOMBIE)
+ /* Hangup flag on zombie state. */
+ revents |= events & POLLHUP;
+
+ if (revents == 0) {
+ if (events & (POLLIN|POLLRDNORM))
+ selrecord(td, &tp->t_inpoll);
+ if (events & (POLLOUT|POLLWRNORM))
+ selrecord(td, &tp->t_outpoll);
}
- return (0);
-}
-int
-ttypoll(struct cdev *dev, int events, struct thread *td)
-{
- int s;
- int revents = 0;
- struct tty *tp;
-
- tp = tty_gettp(dev);
-
- if (tp == NULL) /* XXX used to return ENXIO, but that means true! */
- return ((events & (POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM))
- | POLLHUP);
+ ttydev_leave(tp);
- s = spltty();
- if (events & (POLLIN | POLLRDNORM)) {
- if (ISSET(tp->t_state, TS_ZOMBIE))
- revents |= (events & (POLLIN | POLLRDNORM)) |
- POLLHUP;
- else if (ttnread(tp) > 0)
- revents |= events & (POLLIN | POLLRDNORM);
- else
- selrecord(td, &tp->t_rsel);
- }
- if (events & POLLOUT) {
- if (ISSET(tp->t_state, TS_ZOMBIE))
- revents |= POLLHUP;
- else if (tp->t_outq.c_cc <= tp->t_olowat &&
- ISSET(tp->t_state, TS_CONNECTED))
- revents |= events & POLLOUT;
- else
- selrecord(td, &tp->t_wsel);
- }
- splx(s);
return (revents);
}
-static struct filterops ttyread_filtops =
- { 1, NULL, filt_ttyrdetach, filt_ttyread };
-static struct filterops ttywrite_filtops =
- { 1, NULL, filt_ttywdetach, filt_ttywrite };
-
-int
-ttykqfilter(struct cdev *dev, struct knote *kn)
+static int
+ttydev_mmap(struct cdev *dev, vm_offset_t offset, vm_paddr_t *paddr, int nprot)
{
- struct tty *tp;
- struct knlist *klist;
- int s;
-
- tp = tty_gettp(dev);
- if (tp == NULL || (tp->t_state & TS_GONE))
- return (ENODEV);
-
- switch (kn->kn_filter) {
- case EVFILT_READ:
- klist = &tp->t_rsel.si_note;
- kn->kn_fop = &ttyread_filtops;
- break;
- case EVFILT_WRITE:
- klist = &tp->t_wsel.si_note;
- kn->kn_fop = &ttywrite_filtops;
- break;
- default:
- return (EINVAL);
- }
+ struct tty *tp = dev->si_drv1;
+ int error;
- kn->kn_hook = (caddr_t)tp;
+ /* Handle mmap() through the driver. */
- s = spltty();
- knlist_add(klist, kn, 0);
- splx(s);
+ error = ttydev_enter(tp);
+ if (error)
+ return (-1);
+ error = ttydevsw_mmap(tp, offset, paddr, nprot);
+ ttydev_leave(tp);
- return (0);
+ return (error);
}
+/*
+ * kqueue support.
+ */
+
static void
-filt_ttyrdetach(struct knote *kn)
+tty_kqops_read_detach(struct knote *kn)
{
- struct tty *tp = (struct tty *)kn->kn_hook;
- int s = spltty();
+ struct tty *tp = kn->kn_hook;
- knlist_remove(&tp->t_rsel.si_note, kn, 0);
- splx(s);
+ knlist_remove(&tp->t_inpoll.si_note, kn, 0);
}
static int
-filt_ttyread(struct knote *kn, long hint)
+tty_kqops_read_event(struct knote *kn, long hint)
{
- struct tty *tp = (struct tty *)kn->kn_hook;
+ struct tty *tp = kn->kn_hook;
+
+ tty_lock_assert(tp, MA_OWNED);
- kn->kn_data = ttnread(tp);
- if ((tp->t_state & TS_GONE) || ISSET(tp->t_state, TS_ZOMBIE)) {
+ if (tty_gone(tp) || tp->t_flags & TF_ZOMBIE) {
kn->kn_flags |= EV_EOF;
return (1);
+ } else {
+ kn->kn_data = ttydisc_read_poll(tp);
+ return (kn->kn_data > 0);
}
- return (kn->kn_data > 0);
}
static void
-filt_ttywdetach(struct knote *kn)
+tty_kqops_write_detach(struct knote *kn)
{
- struct tty *tp = (struct tty *)kn->kn_hook;
- int s = spltty();
+ struct tty *tp = kn->kn_hook;
- knlist_remove(&tp->t_wsel.si_note, kn, 0);
- splx(s);
+ knlist_remove(&tp->t_outpoll.si_note, kn, 0);
}
static int
-filt_ttywrite(struct knote *kn, long hint)
+tty_kqops_write_event(struct knote *kn, long hint)
{
- struct tty *tp = (struct tty *)kn->kn_hook;
+ struct tty *tp = kn->kn_hook;
+
+ tty_lock_assert(tp, MA_OWNED);
- kn->kn_data = tp->t_outq.c_cc;
- if ((tp->t_state & TS_GONE) || ISSET(tp->t_state, TS_ZOMBIE))
+ if (tty_gone(tp)) {
+ kn->kn_flags |= EV_EOF;
return (1);
- return (kn->kn_data <= tp->t_olowat &&
- ISSET(tp->t_state, TS_CONNECTED));
+ } else {
+ kn->kn_data = ttydisc_write_poll(tp);
+ return (kn->kn_data > 0);
+ }
}
-/*
- * Must be called at spltty().
- */
+static struct filterops tty_kqops_read =
+ { 1, NULL, tty_kqops_read_detach, tty_kqops_read_event };
+static struct filterops tty_kqops_write =
+ { 1, NULL, tty_kqops_write_detach, tty_kqops_write_event };
+
static int
-ttnread(struct tty *tp)
+ttydev_kqfilter(struct cdev *dev, struct knote *kn)
{
- int nread;
-
- if (ISSET(tp->t_lflag, PENDIN))
- ttypend(tp);
- nread = tp->t_canq.c_cc;
- if (!ISSET(tp->t_lflag, ICANON)) {
- nread += tp->t_rawq.c_cc;
- if (nread < tp->t_cc[VMIN] && tp->t_cc[VTIME] == 0)
- nread = 0;
+ struct tty *tp = dev->si_drv1;
+ int error;
+
+ error = ttydev_enter(tp);
+ if (error)
+ return (error);
+
+ switch (kn->kn_filter) {
+ case EVFILT_READ:
+ kn->kn_hook = tp;
+ kn->kn_fop = &tty_kqops_read;
+ knlist_add(&tp->t_inpoll.si_note, kn, 1);
+ break;
+ case EVFILT_WRITE:
+ kn->kn_hook = tp;
+ kn->kn_fop = &tty_kqops_write;
+ knlist_add(&tp->t_outpoll.si_note, kn, 1);
+ break;
+ default:
+ error = EINVAL;
+ break;
}
- return (nread);
+
+ ttydev_leave(tp);
+ return (error);
}
+static struct cdevsw ttydev_cdevsw = {
+ .d_version = D_VERSION,
+ .d_open = ttydev_open,
+ .d_close = ttydev_close,
+ .d_read = ttydev_read,
+ .d_write = ttydev_write,
+ .d_ioctl = ttydev_ioctl,
+ .d_kqfilter = ttydev_kqfilter,
+ .d_poll = ttydev_poll,
+ .d_mmap = ttydev_mmap,
+ .d_name = "ttydev",
+ .d_flags = D_TTY,
+};
+
/*
- * Wait for output to drain.
+ * Init/lock-state devices
*/
-int
-ttywait(struct tty *tp)
+
+static int
+ttyil_open(struct cdev *dev, int oflags, int devtype, struct thread *td)
{
- int error, s;
-
- error = 0;
- s = spltty();
- while ((tp->t_outq.c_cc || ISSET(tp->t_state, TS_BUSY)) &&
- ISSET(tp->t_state, TS_CONNECTED) && tp->t_oproc) {
- tt_oproc(tp);
- if ((tp->t_outq.c_cc || ISSET(tp->t_state, TS_BUSY)) &&
- ISSET(tp->t_state, TS_CONNECTED)) {
- SET(tp->t_state, TS_SO_OCOMPLETE);
- error = ttysleep(tp, TSA_OCOMPLETE(tp),
- TTOPRI | PCATCH, "ttywai",
- tp->t_timeout);
- if (error) {
- if (error == EWOULDBLOCK)
- error = EIO;
- break;
- }
- } else
- break;
- }
- if (!error && (tp->t_outq.c_cc || ISSET(tp->t_state, TS_BUSY)))
- error = EIO;
- splx(s);
+ struct tty *tp = dev->si_drv1;
+ int error = 0;
+
+ tty_lock(tp);
+ if (tty_gone(tp))
+ error = ENODEV;
+ tty_unlock(tp);
+
return (error);
}
-/*
- * Flush if successfully wait.
- */
static int
-ttywflush(struct tty *tp)
+ttyil_close(struct cdev *dev, int flag, int mode, struct thread *td)
{
- int error;
+ return (0);
+}
- if ((error = ttywait(tp)) == 0)
- ttyflush(tp, FREAD);
- return (error);
+static int
+ttyil_rdwr(struct cdev *dev, struct uio *uio, int ioflag)
+{
+ return (ENODEV);
}
-/*
- * Flush tty read and/or write queues, notifying anyone waiting.
- */
-void
-ttyflush(struct tty *tp, int rw)
+static int
+ttyil_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag,
+ struct thread *td)
{
- int s;
+ struct tty *tp = dev->si_drv1;
+ int error = 0;
- s = spltty();
-#if 0
-again:
-#endif
- if (rw & FWRITE) {
- FLUSHQ(&tp->t_outq);
- CLR(tp->t_state, TS_TTSTOP);
+ tty_lock(tp);
+ if (tty_gone(tp)) {
+ error = ENODEV;
+ goto done;
}
- tt_stop(tp, rw);
- if (rw & FREAD) {
- FLUSHQ(&tp->t_canq);
- FLUSHQ(&tp->t_rawq);
- CLR(tp->t_lflag, PENDIN);
- tp->t_rocount = 0;
- tp->t_rocol = 0;
- CLR(tp->t_state, TS_LOCAL);
- ttwakeup(tp);
- if (ISSET(tp->t_state, TS_TBLOCK)) {
- if (rw & FWRITE)
- FLUSHQ(&tp->t_outq);
- ttyunblock(tp);
- /*
- * Don't let leave any state that might clobber the
- * next line discipline (although we should do more
- * to send the START char). Not clearing the state
- * may have caused the "putc to a clist with no
- * reserved cblocks" panic/printf.
- */
- CLR(tp->t_state, TS_TBLOCK);
-
-#if 0 /* forget it, sleeping isn't always safe and we don't know when it is */
- if (ISSET(tp->t_iflag, IXOFF)) {
- /*
- * XXX wait a bit in the hope that the stop
- * character (if any) will go out. Waiting
- * isn't good since it allows races. This
- * will be fixed when the stop character is
- * put in a special queue. Don't bother with
- * the checks in ttywait() since the timeout
- * will save us.
- */
- SET(tp->t_state, TS_SO_OCOMPLETE);
- ttysleep(tp, TSA_OCOMPLETE(tp), TTOPRI,
- "ttyfls", hz / 10);
- /*
- * Don't try sending the stop character again.
- */
- CLR(tp->t_state, TS_TBLOCK);
- goto again;
- }
-#endif
- }
- }
- if (rw & FWRITE) {
- FLUSHQ(&tp->t_outq);
- ttwwakeup(tp);
+ switch (cmd) {
+ case TIOCGETA:
+ /* Obtain terminal flags through tcgetattr(). */
+ bcopy(dev->si_drv2, data, sizeof(struct termios));
+ break;
+ case TIOCSETA:
+ /* Set terminal flags through tcsetattr(). */
+ error = priv_check(td, PRIV_TTY_SETA);
+ if (error)
+ break;
+ bcopy(data, dev->si_drv2, sizeof(struct termios));
+ return (0);
+ break;
+ case TIOCGETD:
+ *(int *)data = TTYDISC;
+ break;
+ case TIOCGWINSZ:
+ bzero(data, sizeof(struct winsize));
+ break;
+ default:
+ error = ENOTTY;
}
- splx(s);
+
+done: tty_unlock(tp);
+ return (error);
}
-/*
- * Copy in the default termios characters.
- */
-void
-termioschars(struct termios *t)
+static struct cdevsw ttyil_cdevsw = {
+ .d_version = D_VERSION,
+ .d_open = ttyil_open,
+ .d_close = ttyil_close,
+ .d_read = ttyil_rdwr,
+ .d_write = ttyil_rdwr,
+ .d_ioctl = ttyil_ioctl,
+ .d_name = "ttyil",
+ .d_flags = D_TTY,
+};
+
+static void
+tty_init_termios(struct tty *tp)
{
+ struct termios *t = &tp->t_termios_init_in;
- bcopy(ttydefchars, t->c_cc, sizeof t->c_cc);
+ t->c_cflag = TTYDEF_CFLAG;
+ t->c_iflag = TTYDEF_IFLAG;
+ t->c_lflag = TTYDEF_LFLAG;
+ t->c_oflag = TTYDEF_OFLAG;
+ t->c_ispeed = TTYDEF_SPEED;
+ t->c_ospeed = TTYDEF_SPEED;
+ bcopy(ttydefchars, &t->c_cc, sizeof ttydefchars);
+
+ tp->t_termios_init_out = *t;
}
-/*
- * Old interface.
- */
void
-ttychars(struct tty *tp)
+tty_init_console(struct tty *tp, speed_t s)
{
+ struct termios *ti = &tp->t_termios_init_in;
+ struct termios *to = &tp->t_termios_init_out;
+
+ if (s != 0) {
+ ti->c_ispeed = ti->c_ospeed = s;
+ to->c_ispeed = to->c_ospeed = s;
+ }
- termioschars(&tp->t_termios);
+ ti->c_cflag |= CLOCAL;
+ to->c_cflag |= CLOCAL;
}
/*
- * Handle input high water. Send stop character for the IXOFF case. Turn
- * on our input flow control bit and propagate the changes to the driver.
- * XXX the stop character should be put in a special high priority queue.
+ * Standard device routine implementations, mostly meant for
+ * pseudo-terminal device drivers. When a driver creates a new terminal
+ * device class, missing routines are patched.
*/
-void
-ttyblock(struct tty *tp)
+
+static int
+ttydevsw_defopen(struct tty *tp)
{
- SET(tp->t_state, TS_TBLOCK);
- if (ISSET(tp->t_iflag, IXOFF) && tp->t_cc[VSTOP] != _POSIX_VDISABLE &&
- putc(tp->t_cc[VSTOP], &tp->t_outq) != 0)
- CLR(tp->t_state, TS_TBLOCK); /* try again later */
- ttstart(tp);
+ return (0);
}
-/*
- * Handle input low water. Send start character for the IXOFF case. Turn
- * off our input flow control bit and propagate the changes to the driver.
- * XXX the start character should be put in a special high priority queue.
- */
static void
-ttyunblock(struct tty *tp)
+ttydevsw_defclose(struct tty *tp)
{
-
- CLR(tp->t_state, TS_TBLOCK);
- if (ISSET(tp->t_iflag, IXOFF) && tp->t_cc[VSTART] != _POSIX_VDISABLE &&
- putc(tp->t_cc[VSTART], &tp->t_outq) != 0)
- SET(tp->t_state, TS_TBLOCK); /* try again later */
- ttstart(tp);
}
-#ifdef notyet
-/* Not used by any current (i386) drivers. */
-/*
- * Restart after an inter-char delay.
- */
-void
-ttrstrt(void *tp_arg)
+static void
+ttydevsw_defoutwakeup(struct tty *tp)
{
- struct tty *tp;
- int s;
- KASSERT(tp_arg != NULL, ("ttrstrt"));
+ panic("Terminal device has output, while not implemented");
+}
- tp = tp_arg;
- s = spltty();
+static void
+ttydevsw_definwakeup(struct tty *tp)
+{
+}
- CLR(tp->t_state, TS_TIMEOUT);
- ttstart(tp);
+static int
+ttydevsw_defioctl(struct tty *tp, u_long cmd, caddr_t data, struct thread *td)
+{
- splx(s);
+ return (ENOIOCTL);
}
-#endif
-int
-ttstart(struct tty *tp)
+static int
+ttydevsw_defparam(struct tty *tp, struct termios *t)
{
- tt_oproc(tp);
+ /* Use a fake baud rate, we're not a real device. */
+ t->c_ispeed = t->c_ospeed = TTYDEF_SPEED_PSEUDO;
+
return (0);
}
-/*
- * "close" a line discipline
- */
-int
-ttylclose(struct tty *tp, int flag)
+static int
+ttydevsw_defmodem(struct tty *tp, int sigon, int sigoff)
{
- if (flag & FNONBLOCK || ttywflush(tp))
- ttyflush(tp, FREAD | FWRITE);
- return (0);
+ /* Simulate a carrier to make the TTY layer happy. */
+ return (SER_DCD);
}
-/*
- * Handle modem control transition on a tty.
- * Flag indicates new state of carrier.
- * Returns 0 if the line should be turned off, otherwise 1.
- */
-int
-ttymodem(struct tty *tp, int flag)
+static int
+ttydevsw_defmmap(struct tty *tp, vm_offset_t offset, vm_paddr_t *paddr,
+ int nprot)
{
- if (ISSET(tp->t_state, TS_CARR_ON) && ISSET(tp->t_cflag, MDMBUF)) {
- /*
- * MDMBUF: do flow control according to carrier flag
- * XXX TS_CAR_OFLOW doesn't do anything yet. TS_TTSTOP
- * works if IXON and IXANY are clear.
- */
- if (flag) {
- CLR(tp->t_state, TS_CAR_OFLOW);
- CLR(tp->t_state, TS_TTSTOP);
- ttstart(tp);
- } else if (!ISSET(tp->t_state, TS_CAR_OFLOW)) {
- SET(tp->t_state, TS_CAR_OFLOW);
- SET(tp->t_state, TS_TTSTOP);
- tt_stop(tp, 0);
- }
- } else if (flag == 0) {
- /*
- * Lost carrier.
- */
- CLR(tp->t_state, TS_CARR_ON);
- if (ISSET(tp->t_state, TS_ISOPEN) &&
- !ISSET(tp->t_cflag, CLOCAL)) {
- SET(tp->t_state, TS_ZOMBIE);
- CLR(tp->t_state, TS_CONNECTED);
- if (tp->t_session) {
- sx_slock(&proctree_lock);
- if (tp->t_session && tp->t_session->s_leader) {
- struct proc *p;
-
- p = tp->t_session->s_leader;
- PROC_LOCK(p);
- psignal(p, SIGHUP);
- PROC_UNLOCK(p);
- }
- sx_sunlock(&proctree_lock);
- }
- ttyflush(tp, FREAD | FWRITE);
- return (0);
- }
- } else {
- /*
- * Carrier now on.
- */
- SET(tp->t_state, TS_CARR_ON);
- if (!ISSET(tp->t_state, TS_ZOMBIE))
- SET(tp->t_state, TS_CONNECTED);
- wakeup(TSA_CARR_ON(tp));
- ttwakeup(tp);
- ttwwakeup(tp);
- }
- return (1);
+ return (-1);
}
-/*
- * Reinput pending characters after state switch
- * call at spltty().
- */
static void
-ttypend(struct tty *tp)
+ttydevsw_deffree(void *softc)
{
- struct clist tq;
- int c;
- CLR(tp->t_lflag, PENDIN);
- SET(tp->t_state, TS_TYPEN);
- /*
- * XXX this assumes too much about clist internals. It may even
- * fail if the cblock slush pool is empty. We can't allocate more
- * cblocks here because we are called from an interrupt handler
- * and clist_alloc_cblocks() can wait.
- */
- tq = tp->t_rawq;
- bzero(&tp->t_rawq, sizeof tp->t_rawq);
- tp->t_rawq.c_cbmax = tq.c_cbmax;
- tp->t_rawq.c_cbreserved = tq.c_cbreserved;
- while ((c = getc(&tq)) >= 0)
- ttyinput(c, tp);
- CLR(tp->t_state, TS_TYPEN);
+ panic("Terminal device freed without a free-handler");
}
/*
- * Process a read call on a tty device.
+ * TTY allocation and deallocation. TTY devices can be deallocated when
+ * the driver doesn't use it anymore, when the TTY isn't a session's
+ * controlling TTY and when the device node isn't opened through devfs.
*/
-int
-ttread(struct tty *tp, struct uio *uio, int flag)
+
+struct tty *
+tty_alloc(struct ttydevsw *tsw, void *sc, struct mtx *mutex)
{
- struct clist *qp;
- int c;
- tcflag_t lflag;
- cc_t *cc = tp->t_cc;
- struct thread *td;
- struct proc *p;
- int s, first, error = 0;
- int has_stime = 0, last_cc = 0;
- long slp = 0; /* XXX this should be renamed `timo'. */
- struct timeval stime = { 0, 0 };
- struct pgrp *pg;
+ struct tty *tp;
- td = curthread;
- p = td->td_proc;
-loop:
- s = spltty();
- lflag = tp->t_lflag;
- /*
- * take pending input first
- */
- if (ISSET(lflag, PENDIN)) {
- ttypend(tp);
- splx(s); /* reduce latency */
- s = spltty();
- lflag = tp->t_lflag; /* XXX ttypend() clobbers it */
+ /* Make sure the driver defines all routines. */
+#define PATCH_FUNC(x) do { \
+ if (tsw->tsw_ ## x == NULL) \
+ tsw->tsw_ ## x = ttydevsw_def ## x; \
+} while (0)
+ PATCH_FUNC(open);
+ PATCH_FUNC(close);
+ PATCH_FUNC(outwakeup);
+ PATCH_FUNC(inwakeup);
+ PATCH_FUNC(ioctl);
+ PATCH_FUNC(param);
+ PATCH_FUNC(modem);
+ PATCH_FUNC(mmap);
+ PATCH_FUNC(free);
+#undef PATCH_FUNC
+
+ tp = malloc(sizeof(struct tty), M_TTY, M_WAITOK|M_ZERO);
+ tp->t_devsw = tsw;
+ tp->t_softc = sc;
+ tp->t_flags = tsw->tsw_flags;
+
+ tty_init_termios(tp);
+
+ cv_init(&tp->t_inwait, "tty input");
+ cv_init(&tp->t_outwait, "tty output");
+ cv_init(&tp->t_bgwait, "tty background");
+ cv_init(&tp->t_dcdwait, "tty dcd");
+
+ TAILQ_INIT(&tp->t_inq.ti_list);
+ STAILQ_INIT(&tp->t_outq.to_list);
+
+ /* Allow drivers to use a custom mutex to lock the TTY. */
+ if (mutex != NULL) {
+ tp->t_mtx = mutex;
+ } else {
+ tp->t_mtx = &tp->t_mtxobj;
+ mtx_init(&tp->t_mtxobj, "tty lock", NULL, MTX_DEF);
}
- /*
- * Hang process if it's in the background.
- */
- if (isbackground(p, tp)) {
- splx(s);
- sx_slock(&proctree_lock);
- PROC_LOCK(p);
- if (SIGISMEMBER(p->p_sigacts->ps_sigignore, SIGTTIN) ||
- SIGISMEMBER(td->td_sigmask, SIGTTIN) ||
- (p->p_flag & P_PPWAIT) || p->p_pgrp->pg_jobc == 0) {
- PROC_UNLOCK(p);
- sx_sunlock(&proctree_lock);
- return (EIO);
- }
- pg = p->p_pgrp;
- PROC_UNLOCK(p);
- PGRP_LOCK(pg);
- sx_sunlock(&proctree_lock);
- pgsignal(pg, SIGTTIN, 1);
- PGRP_UNLOCK(pg);
- error = ttysleep(tp, &lbolt, TTIPRI | PCATCH, "ttybg2", 0);
- if (error)
- return (error);
- goto loop;
- }
+ knlist_init(&tp->t_inpoll.si_note, tp->t_mtx, NULL, NULL, NULL);
+ knlist_init(&tp->t_outpoll.si_note, tp->t_mtx, NULL, NULL, NULL);
- if (ISSET(tp->t_state, TS_ZOMBIE)) {
- splx(s);
- return (0); /* EOF */
- }
+ sx_xlock(&tty_list_sx);
+ TAILQ_INSERT_TAIL(&tty_list, tp, t_list);
+ tty_list_count++;
+ sx_xunlock(&tty_list_sx);
- /*
- * If canonical, use the canonical queue,
- * else use the raw queue.
- *
- * (should get rid of clists...)
- */
- qp = ISSET(lflag, ICANON) ? &tp->t_canq : &tp->t_rawq;
+ return (tp);
+}
- if (flag & IO_NDELAY) {
- if (qp->c_cc > 0)
- goto read;
- if (!ISSET(lflag, ICANON) && cc[VMIN] == 0) {
- splx(s);
- return (0);
- }
- splx(s);
- return (EWOULDBLOCK);
- }
- if (!ISSET(lflag, ICANON)) {
- int m = cc[VMIN];
- long t = cc[VTIME];
- struct timeval timecopy;
+static void
+tty_dealloc(void *arg)
+{
+ struct tty *tp = arg;
- /*
- * Check each of the four combinations.
- * (m > 0 && t == 0) is the normal read case.
- * It should be fairly efficient, so we check that and its
- * companion case (m == 0 && t == 0) first.
- * For the other two cases, we compute the target sleep time
- * into slp.
- */
- if (t == 0) {
- if (qp->c_cc < m)
- goto sleep;
- if (qp->c_cc > 0)
- goto read;
-
- /* m, t and qp->c_cc are all 0. 0 is enough input. */
- splx(s);
- return (0);
- }
- t *= 100000; /* time in us */
-#define diff(t1, t2) (((t1).tv_sec - (t2).tv_sec) * 1000000 + \
- ((t1).tv_usec - (t2).tv_usec))
- if (m > 0) {
- if (qp->c_cc <= 0)
- goto sleep;
- if (qp->c_cc >= m)
- goto read;
- getmicrotime(&timecopy);
- if (!has_stime) {
- /* first character, start timer */
- has_stime = 1;
- stime = timecopy;
- slp = t;
- } else if (qp->c_cc > last_cc) {
- /* got a character, restart timer */
- stime = timecopy;
- slp = t;
- } else {
- /* nothing, check expiration */
- slp = t - diff(timecopy, stime);
- if (slp <= 0)
- goto read;
- }
- last_cc = qp->c_cc;
- } else { /* m == 0 */
- if (qp->c_cc > 0)
- goto read;
- getmicrotime(&timecopy);
- if (!has_stime) {
- has_stime = 1;
- stime = timecopy;
- slp = t;
- } else {
- slp = t - diff(timecopy, stime);
- if (slp <= 0) {
- /* Timed out, but 0 is enough input. */
- splx(s);
- return (0);
- }
- }
- }
-#undef diff
- if (slp != 0) {
- struct timeval tv; /* XXX style bug. */
+ sx_xlock(&tty_list_sx);
+ TAILQ_REMOVE(&tty_list, tp, t_list);
+ tty_list_count--;
+ sx_xunlock(&tty_list_sx);
- tv.tv_sec = slp / 1000000;
- tv.tv_usec = slp % 1000000;
- slp = tvtohz(&tv);
- /*
- * XXX bad variable names. slp was the timeout in
- * usec. Now it is the timeout in ticks.
- */
- }
- goto sleep;
- }
- if (qp->c_cc <= 0) {
-sleep:
- /*
- * There is no input, or not enough input and we can block.
- */
- error = ttysleep(tp, TSA_HUP_OR_INPUT(tp), TTIPRI | PCATCH,
- ISSET(tp->t_state, TS_CONNECTED) ?
- "ttyin" : "ttyhup", (int)slp);
- splx(s);
- if (error == EWOULDBLOCK)
- error = 0;
- else if (error)
- return (error);
- /*
- * XXX what happens if another process eats some input
- * while we are asleep (not just here)? It would be
- * safest to detect changes and reset our state variables
- * (has_stime and last_cc).
- */
- slp = 0;
- goto loop;
- }
-read:
- splx(s);
- /*
- * Input present, check for input mapping and processing.
- */
- first = 1;
- if (ISSET(lflag, ICANON | ISIG))
- goto slowcase;
- for (;;) {
- char ibuf[IBUFSIZ];
- int icc;
-
- icc = imin(uio->uio_resid, IBUFSIZ);
- icc = q_to_b(qp, ibuf, icc);
- if (icc <= 0) {
- if (first)
- goto loop;
- break;
- }
- error = uiomove(ibuf, icc, uio);
- /*
- * XXX if there was an error then we should ungetc() the
- * unmoved chars and reduce icc here.
- */
- if (error)
- break;
- if (uio->uio_resid == 0)
- break;
- first = 0;
- }
- goto out;
-slowcase:
- for (;;) {
- c = getc(qp);
- if (c < 0) {
- if (first)
- goto loop;
- break;
- }
- /*
- * delayed suspend (^Y)
- */
- if (CCEQ(cc[VDSUSP], c) &&
- ISSET(lflag, IEXTEN | ISIG) == (IEXTEN | ISIG)) {
- if (tp->t_pgrp != NULL) {
- PGRP_LOCK(tp->t_pgrp);
- pgsignal(tp->t_pgrp, SIGTSTP, 1);
- PGRP_UNLOCK(tp->t_pgrp);
- }
- if (first) {
- error = ttysleep(tp, &lbolt, TTIPRI | PCATCH,
- "ttybg3", 0);
- if (error)
- break;
- goto loop;
- }
- break;
- }
- /*
- * Interpret EOF only in canonical mode.
- */
- if (CCEQ(cc[VEOF], c) && ISSET(lflag, ICANON))
- break;
- /*
- * Give user character.
- */
- error = ureadc(c, uio);
- if (error)
- /* XXX should ungetc(c, qp). */
- break;
- if (uio->uio_resid == 0)
- break;
- /*
- * In canonical mode check for a "break character"
- * marking the end of a "line of input".
- */
- if (ISSET(lflag, ICANON) && TTBREAKC(c, lflag))
- break;
- first = 0;
- }
+ knlist_destroy(&tp->t_inpoll.si_note);
+ knlist_destroy(&tp->t_outpoll.si_note);
-out:
- /*
- * Look to unblock input now that (presumably)
- * the input queue has gone down.
- */
- s = spltty();
- if (ISSET(tp->t_state, TS_TBLOCK) &&
- tp->t_rawq.c_cc + tp->t_canq.c_cc <= tp->t_ilowat)
- ttyunblock(tp);
- splx(s);
+ cv_destroy(&tp->t_inwait);
+ cv_destroy(&tp->t_outwait);
+ cv_destroy(&tp->t_bgwait);
+ cv_destroy(&tp->t_dcdwait);
- return (error);
+ if (tp->t_mtx == &tp->t_mtxobj)
+ mtx_destroy(&tp->t_mtxobj);
+ ttydevsw_free(tp);
+ free(tp, M_TTY);
}
-/*
- * Check the output queue on tp for space for a kernel message (from uprintf
- * or tprintf). Allow some space over the normal hiwater mark so we don't
- * lose messages due to normal flow control, but don't let the tty run amok.
- * Sleeps here are not interruptible, but we return prematurely if new signals
- * arrive.
- */
-int
-ttycheckoutq(struct tty *tp, int wait)
+static void
+tty_rel_free(struct tty *tp)
{
- int hiwat, s;
- sigset_t oldmask;
- struct thread *td;
- struct proc *p;
+ struct cdev *dev;
- td = curthread;
- p = td->td_proc;
- hiwat = tp->t_ohiwat;
- SIGEMPTYSET(oldmask);
- s = spltty();
- if (wait) {
- PROC_LOCK(p);
- oldmask = td->td_siglist;
- PROC_UNLOCK(p);
- }
- if (tp->t_outq.c_cc > hiwat + OBUFSIZ + 100)
- while (tp->t_outq.c_cc > hiwat) {
- ttstart(tp);
- if (tp->t_outq.c_cc <= hiwat)
- break;
- if (!wait) {
- splx(s);
- return (0);
- }
- PROC_LOCK(p);
- if (!SIGSETEQ(td->td_siglist, oldmask)) {
- PROC_UNLOCK(p);
- splx(s);
- return (0);
- }
- PROC_UNLOCK(p);
- SET(tp->t_state, TS_SO_OLOWAT);
- tsleep(TSA_OLOWAT(tp), PZERO - 1, "ttoutq", hz);
- }
- splx(s);
- return (1);
-}
+ tty_lock_assert(tp, MA_OWNED);
-/*
- * Process a write call on a tty device.
- */
-int
-ttwrite(struct tty *tp, struct uio *uio, int flag)
-{
- char *cp = NULL;
- int cc, ce;
- struct thread *td;
- struct proc *p;
- int i, hiwat, cnt, error, s;
- char obuf[OBUFSIZ];
-
- hiwat = tp->t_ohiwat;
- cnt = uio->uio_resid;
- error = 0;
- cc = 0;
- td = curthread;
- p = td->td_proc;
-loop:
- s = spltty();
- if (ISSET(tp->t_state, TS_ZOMBIE)) {
- splx(s);
- if (uio->uio_resid == cnt)
- error = EIO;
- goto out;
- }
- if (!ISSET(tp->t_state, TS_CONNECTED)) {
- if (flag & IO_NDELAY) {
- splx(s);
- error = EWOULDBLOCK;
- goto out;
- }
- error = ttysleep(tp, TSA_CARR_ON(tp), TTIPRI | PCATCH,
- "ttywdcd", 0);
- splx(s);
- if (error)
- goto out;
- goto loop;
- }
- splx(s);
- /*
- * Hang the process if it's in the background.
- */
- sx_slock(&proctree_lock);
- PROC_LOCK(p);
- if (isbackground(p, tp) &&
- ISSET(tp->t_lflag, TOSTOP) && !(p->p_flag & P_PPWAIT) &&
- !SIGISMEMBER(p->p_sigacts->ps_sigignore, SIGTTOU) &&
- !SIGISMEMBER(td->td_sigmask, SIGTTOU)) {
- if (p->p_pgrp->pg_jobc == 0) {
- PROC_UNLOCK(p);
- sx_sunlock(&proctree_lock);
- error = EIO;
- goto out;
- }
- PROC_UNLOCK(p);
- PGRP_LOCK(p->p_pgrp);
- sx_sunlock(&proctree_lock);
- pgsignal(p->p_pgrp, SIGTTOU, 1);
- PGRP_UNLOCK(p->p_pgrp);
- error = ttysleep(tp, &lbolt, TTIPRI | PCATCH, "ttybg4", 0);
- if (error)
- goto out;
- goto loop;
- } else {
- PROC_UNLOCK(p);
- sx_sunlock(&proctree_lock);
- }
- /*
- * Process the user's data in at most OBUFSIZ chunks. Perform any
- * output translation. Keep track of high water mark, sleep on
- * overflow awaiting device aid in acquiring new space.
- */
- while (uio->uio_resid > 0 || cc > 0) {
- if (ISSET(tp->t_lflag, FLUSHO)) {
- uio->uio_resid = 0;
- return (0);
- }
- if (tp->t_outq.c_cc > hiwat)
- goto ovhiwat;
- /*
- * Grab a hunk of data from the user, unless we have some
- * leftover from last time.
- */
- if (cc == 0) {
- cc = imin(uio->uio_resid, OBUFSIZ);
- cp = obuf;
- error = uiomove(cp, cc, uio);
- if (error) {
- cc = 0;
- break;
- }
- }
- /*
- * If nothing fancy need be done, grab those characters we
- * can handle without any of ttyoutput's processing and
- * just transfer them to the output q. For those chars
- * which require special processing (as indicated by the
- * bits in char_type), call ttyoutput. After processing
- * a hunk of data, look for FLUSHO so ^O's will take effect
- * immediately.
- */
- while (cc > 0) {
- if (!ISSET(tp->t_oflag, OPOST))
- ce = cc;
- else {
- ce = cc - scanc((u_int)cc, (u_char *)cp,
- char_type, CCLASSMASK);
- /*
- * If ce is zero, then we're processing
- * a special character through ttyoutput.
- */
- if (ce == 0) {
- tp->t_rocount = 0;
- if (ttyoutput(*cp, tp) >= 0) {
- /* No Clists, wait a bit. */
- ttstart(tp);
- if (flag & IO_NDELAY) {
- error = EWOULDBLOCK;
- goto out;
- }
- error = ttysleep(tp, &lbolt,
- TTOPRI|PCATCH,
- "ttybf1", 0);
- if (error)
- goto out;
- goto loop;
- }
- cp++;
- cc--;
- if (ISSET(tp->t_lflag, FLUSHO) ||
- tp->t_outq.c_cc > hiwat)
- goto ovhiwat;
- continue;
- }
- }
- /*
- * A bunch of normal characters have been found.
- * Transfer them en masse to the output queue and
- * continue processing at the top of the loop.
- * If there are any further characters in this
- * <= OBUFSIZ chunk, the first should be a character
- * requiring special handling by ttyoutput.
- */
- tp->t_rocount = 0;
- i = b_to_q(cp, ce, &tp->t_outq);
- ce -= i;
- tp->t_column += ce;
- cp += ce, cc -= ce, tk_nout += ce;
- tp->t_outcc += ce;
- if (i > 0) {
- /* No Clists, wait a bit. */
- ttstart(tp);
- if (flag & IO_NDELAY) {
- error = EWOULDBLOCK;
- goto out;
- }
- error = ttysleep(tp, &lbolt, TTOPRI | PCATCH,
- "ttybf2", 0);
- if (error)
- goto out;
- goto loop;
- }
- if (ISSET(tp->t_lflag, FLUSHO) ||
- tp->t_outq.c_cc > hiwat)
- break;
- }
- ttstart(tp);
+ if (tp->t_sessioncnt != 0 ||
+ (tp->t_flags & (TF_GONE|TF_OPENED)) != TF_GONE) {
+ /* TTY is still in use. */
+ tty_unlock(tp);
+ return;
}
-out:
- /*
- * If cc is nonzero, we leave the uio structure inconsistent, as the
- * offset and iov pointers have moved forward, but it doesn't matter
- * (the call will either return short or restart with a new uio).
- */
- uio->uio_resid += cc;
- return (error);
-ovhiwat:
- ttstart(tp);
- s = spltty();
- /*
- * This can only occur if FLUSHO is set in t_lflag,
- * or if ttstart/oproc is synchronous (or very fast).
- */
- if (tp->t_outq.c_cc <= hiwat) {
- splx(s);
- goto loop;
- }
- if (flag & IO_NDELAY) {
- splx(s);
- uio->uio_resid += cc;
- return (uio->uio_resid == cnt ? EWOULDBLOCK : 0);
- }
- SET(tp->t_state, TS_SO_OLOWAT);
- error = ttysleep(tp, TSA_OLOWAT(tp), TTOPRI | PCATCH, "ttywri",
- tp->t_timeout);
- splx(s);
- if (error == EWOULDBLOCK)
- error = EIO;
- if (error)
- goto out;
- goto loop;
+ tty_freebuffers(tp);
+
+ /* TTY can be deallocated. */
+ dev = tp->t_dev;
+ tp->t_dev = NULL;
+ tty_unlock(tp);
+
+ destroy_dev_sched_cb(dev, tty_dealloc, tp);
}
-/*
- * Rubout one character from the rawq of tp
- * as cleanly as possible.
- */
-static void
-ttyrub(int c, struct tty *tp)
+void
+tty_rel_pgrp(struct tty *tp, struct pgrp *pg)
{
- char *cp;
- int savecol;
- int tabc, s;
+ tty_lock_assert(tp, MA_OWNED);
- if (!ISSET(tp->t_lflag, ECHO) || ISSET(tp->t_lflag, EXTPROC))
- return;
- CLR(tp->t_lflag, FLUSHO);
- if (ISSET(tp->t_lflag, ECHOE)) {
- if (tp->t_rocount == 0) {
- /*
- * Screwed by ttwrite; retype
- */
- ttyretype(tp);
- return;
- }
- if (c == ('\t' | TTY_QUOTE) || c == ('\n' | TTY_QUOTE))
- ttyrubo(tp, 2);
- else {
- CLR(c, ~TTY_CHARMASK);
- switch (CCLASS(c)) {
- case ORDINARY:
- ttyrubo(tp, 1);
- break;
- case BACKSPACE:
- case CONTROL:
- case NEWLINE:
- case RETURN:
- case VTAB:
- if (ISSET(tp->t_lflag, ECHOCTL))
- ttyrubo(tp, 2);
- break;
- case TAB:
- if (tp->t_rocount < tp->t_rawq.c_cc) {
- ttyretype(tp);
- return;
- }
- s = spltty();
- savecol = tp->t_column;
- SET(tp->t_state, TS_CNTTB);
- SET(tp->t_lflag, FLUSHO);
- tp->t_column = tp->t_rocol;
- cp = tp->t_rawq.c_cf;
- if (cp)
- tabc = *cp; /* XXX FIX NEXTC */
- for (; cp; cp = nextc(&tp->t_rawq, cp, &tabc))
- ttyecho(tabc, tp);
- CLR(tp->t_lflag, FLUSHO);
- CLR(tp->t_state, TS_CNTTB);
- splx(s);
-
- /* savecol will now be length of the tab. */
- savecol -= tp->t_column;
- tp->t_column += savecol;
- if (savecol > 8)
- savecol = 8; /* overflow screw */
- while (--savecol >= 0)
- (void)ttyoutput('\b', tp);
- break;
- default: /* XXX */
-#define PANICSTR "ttyrub: would panic c = %d, val = %d\n"
- (void)printf(PANICSTR, c, CCLASS(c));
-#ifdef notdef
- panic(PANICSTR, c, CCLASS(c));
-#endif
- }
- }
- } else if (ISSET(tp->t_lflag, ECHOPRT)) {
- if (!ISSET(tp->t_state, TS_ERASE)) {
- SET(tp->t_state, TS_ERASE);
- (void)ttyoutput('\\', tp);
- }
- ttyecho(c, tp);
- } else {
- ttyecho(tp->t_cc[VERASE], tp);
- /*
- * This code may be executed not only when an ERASE key
- * is pressed, but also when ^U (KILL) or ^W (WERASE) are.
- * So, I didn't think it was worthwhile to pass the extra
- * information (which would need an extra parameter,
- * changing every call) needed to distinguish the ERASE2
- * case from the ERASE.
- */
- }
- --tp->t_rocount;
+ if (tp->t_pgrp == pg)
+ tp->t_pgrp = NULL;
}
-/*
- * Back over cnt characters, erasing them.
- */
-static void
-ttyrubo(struct tty *tp, int cnt)
+void
+tty_rel_sess(struct tty *tp, struct session *sess)
{
+ MPASS(tp->t_sessioncnt > 0);
- while (cnt-- > 0) {
- (void)ttyoutput('\b', tp);
- (void)ttyoutput(' ', tp);
- (void)ttyoutput('\b', tp);
+ /* Current session has left. */
+ if (tp->t_session == sess) {
+ tp->t_session = NULL;
+ MPASS(tp->t_pgrp == NULL);
}
+ tp->t_sessioncnt--;
+ tty_rel_free(tp);
}
-/*
- * ttyretype --
- * Reprint the rawq line. Note, it is assumed that c_cc has already
- * been checked.
- */
-static void
-ttyretype(struct tty *tp)
+void
+tty_rel_gone(struct tty *tp)
{
- char *cp;
- int s, c;
+ MPASS(!tty_gone(tp));
- /* Echo the reprint character. */
- if (tp->t_cc[VREPRINT] != _POSIX_VDISABLE)
- ttyecho(tp->t_cc[VREPRINT], tp);
+ /* Simulate carrier removal. */
+ ttydisc_modem(tp, 0);
- (void)ttyoutput('\n', tp);
+ /* Wake up misc. blocked threads. */
+ cv_broadcast(&tp->t_bgwait);
+ cv_broadcast(&tp->t_dcdwait);
- /*
- * XXX
- * FIX: NEXTC IS BROKEN - DOESN'T CHECK QUOTE
- * BIT OF FIRST CHAR.
- */
- s = spltty();
- for (cp = tp->t_canq.c_cf, c = (cp != NULL ? *cp : 0);
- cp != NULL; cp = nextc(&tp->t_canq, cp, &c))
- ttyecho(c, tp);
- for (cp = tp->t_rawq.c_cf, c = (cp != NULL ? *cp : 0);
- cp != NULL; cp = nextc(&tp->t_rawq, cp, &c))
- ttyecho(c, tp);
- CLR(tp->t_state, TS_ERASE);
- splx(s);
-
- tp->t_rocount = tp->t_rawq.c_cc;
- tp->t_rocol = 0;
+ tp->t_flags |= TF_GONE;
+ tty_rel_free(tp);
}
/*
- * Echo a typed character to the terminal.
+ * Exposing information about current TTY's through sysctl
*/
+
static void
-ttyecho(int c, struct tty *tp)
+tty_to_xtty(struct tty *tp, struct xtty *xt)
{
-
- if (!ISSET(tp->t_state, TS_CNTTB))
- CLR(tp->t_lflag, FLUSHO);
- if ((!ISSET(tp->t_lflag, ECHO) &&
- (c != '\n' || !ISSET(tp->t_lflag, ECHONL))) ||
- ISSET(tp->t_lflag, EXTPROC))
- return;
- if (ISSET(tp->t_lflag, ECHOCTL) &&
- ((ISSET(c, TTY_CHARMASK) <= 037 && c != '\t' && c != '\n') ||
- ISSET(c, TTY_CHARMASK) == 0177)) {
- (void)ttyoutput('^', tp);
- CLR(c, ~TTY_CHARMASK);
- if (c == 0177)
- c = '?';
- else
- c += 'A' - 1;
- }
- (void)ttyoutput(c, tp);
+ tty_lock_assert(tp, MA_OWNED);
+
+ xt->xt_size = sizeof(struct xtty);
+ xt->xt_insize = ttyinq_getsize(&tp->t_inq);
+ xt->xt_incc = ttyinq_bytescanonicalized(&tp->t_inq);
+ xt->xt_inlc = ttyinq_bytesline(&tp->t_inq);
+ xt->xt_inlow = tp->t_inlow;
+ xt->xt_outsize = ttyoutq_getsize(&tp->t_outq);
+ xt->xt_outcc = ttyoutq_bytesused(&tp->t_outq);
+ xt->xt_outlow = tp->t_outlow;
+ xt->xt_column = tp->t_column;
+ xt->xt_pgid = tp->t_pgrp ? tp->t_pgrp->pg_id : 0;
+ xt->xt_sid = tp->t_session ? tp->t_session->s_sid : 0;
+ xt->xt_flags = tp->t_flags;
+ xt->xt_dev = tp->t_dev ? dev2udev(tp->t_dev) : NODEV;
}
-/*
- * Wake up any readers on a tty.
- */
-void
-ttwakeup(struct tty *tp)
+static int
+sysctl_kern_ttys(SYSCTL_HANDLER_ARGS)
{
+ unsigned long lsize;
+ struct xtty *xtlist, *xt;
+ struct tty *tp;
+ int error;
- if (SEL_WAITING(&tp->t_rsel))
- selwakeuppri(&tp->t_rsel, TTIPRI);
- if (ISSET(tp->t_state, TS_ASYNC) && tp->t_sigio != NULL)
- pgsigio(&tp->t_sigio, SIGIO, (tp->t_session != NULL));
- wakeup(TSA_HUP_OR_INPUT(tp));
- KNOTE_UNLOCKED(&tp->t_rsel.si_note, 0);
+ sx_slock(&tty_list_sx);
+ lsize = tty_list_count * sizeof(struct xtty);
+ if (lsize == 0) {
+ sx_sunlock(&tty_list_sx);
+ return (0);
+ }
+
+ xtlist = xt = malloc(lsize, M_TEMP, M_WAITOK);
+
+ TAILQ_FOREACH(tp, &tty_list, t_list) {
+ tty_lock(tp);
+ tty_to_xtty(tp, xt);
+ tty_unlock(tp);
+ xt++;
+ }
+ sx_sunlock(&tty_list_sx);
+
+ error = SYSCTL_OUT(req, xtlist, lsize);
+ free(xtlist, M_TEMP);
+ return (error);
}
+SYSCTL_PROC(_kern, OID_AUTO, ttys, CTLTYPE_OPAQUE|CTLFLAG_RD,
+ 0, 0, sysctl_kern_ttys, "S,xtty", "List of TTYs");
+
/*
- * Wake up any writers on a tty.
+ * Device node creation. Device has been set up, now we can expose it to
+ * the user.
*/
+
void
-ttwwakeup(struct tty *tp)
+tty_makedev(struct tty *tp, struct ucred *cred, const char *fmt, ...)
{
+ va_list ap;
+ struct cdev *dev;
+ const char *prefix = "tty";
+ char name[SPECNAMELEN - 3]; /* for "tty" and "cua". */
+ uid_t uid;
+ gid_t gid;
+ mode_t mode;
- if (SEL_WAITING(&tp->t_wsel) && tp->t_outq.c_cc <= tp->t_olowat)
- selwakeuppri(&tp->t_wsel, TTOPRI);
- if (ISSET(tp->t_state, TS_ASYNC) && tp->t_sigio != NULL)
- pgsigio(&tp->t_sigio, SIGIO, (tp->t_session != NULL));
- if (ISSET(tp->t_state, TS_BUSY | TS_SO_OCOMPLETE) ==
- TS_SO_OCOMPLETE && tp->t_outq.c_cc == 0) {
- CLR(tp->t_state, TS_SO_OCOMPLETE);
- wakeup(TSA_OCOMPLETE(tp));
+ /* Remove "tty" prefix from devices like PTY's. */
+ if (tp->t_flags & TF_NOPREFIX)
+ prefix = "";
+
+ va_start(ap, fmt);
+ vsnrprintf(name, sizeof name, 32, fmt, ap);
+ va_end(ap);
+
+ if (cred == NULL) {
+ /* System device. */
+ uid = UID_ROOT;
+ gid = GID_WHEEL;
+ mode = S_IRUSR|S_IWUSR;
+ } else {
+ /* User device. */
+ uid = cred->cr_ruid;
+ gid = GID_TTY;
+ mode = S_IRUSR|S_IWUSR|S_IWGRP;
}
- if (ISSET(tp->t_state, TS_SO_OLOWAT) &&
- tp->t_outq.c_cc <= tp->t_olowat) {
- CLR(tp->t_state, TS_SO_OLOWAT);
- wakeup(TSA_OLOWAT(tp));
+
+ /* Master call-in device. */
+ dev = make_dev_cred(&ttydev_cdevsw, 0, cred,
+ uid, gid, mode, "%s%s", prefix, name);
+ dev->si_drv1 = tp;
+ tp->t_dev = dev;
+
+ /* Slave call-in devices. */
+ if (tp->t_flags & TF_INITLOCK) {
+ dev = make_dev_cred(&ttyil_cdevsw, 0, cred,
+ uid, gid, mode, "%s%s.init", prefix, name);
+ dev_depends(tp->t_dev, dev);
+ dev->si_drv1 = tp;
+ dev->si_drv2 = &tp->t_termios_init_in;
+
+ dev = make_dev_cred(&ttyil_cdevsw, 0, cred,
+ uid, gid, mode, "%s%s.lock", prefix, name);
+ dev_depends(tp->t_dev, dev);
+ dev->si_drv1 = tp;
+ dev->si_drv2 = &tp->t_termios_lock_in;
+ }
+
+ /* Call-out devices. */
+ if (tp->t_flags & TF_CALLOUT) {
+ dev = make_dev_cred(&ttydev_cdevsw, 0, cred,
+ UID_UUCP, GID_DIALER, 0660, "cua%s", name);
+ dev_depends(tp->t_dev, dev);
+ dev->si_drv1 = tp;
+
+ /* Slave call-out devices. */
+ if (tp->t_flags & TF_INITLOCK) {
+ dev = make_dev_cred(&ttyil_cdevsw, 0, cred,
+ UID_UUCP, GID_DIALER, 0660, "cua%s.init", name);
+ dev_depends(tp->t_dev, dev);
+ dev->si_drv1 = tp;
+ dev->si_drv2 = &tp->t_termios_init_out;
+
+ dev = make_dev_cred(&ttyil_cdevsw, 0, cred,
+ UID_UUCP, GID_DIALER, 0660, "cua%s.lock", name);
+ dev_depends(tp->t_dev, dev);
+ dev->si_drv1 = tp;
+ dev->si_drv2 = &tp->t_termios_lock_out;
+ }
}
- KNOTE_UNLOCKED(&tp->t_wsel.si_note, 0);
}
/*
- * Look up a code for a specified speed in a conversion table;
- * used by drivers to map software speed values to hardware parameters.
+ * Signalling processes.
*/
-int
-ttspeedtab(int speed, struct speedtab *table)
+
+void
+tty_signal_sessleader(struct tty *tp, int sig)
{
+ struct proc *p;
- for ( ; table->sp_speed != -1; table++)
- if (table->sp_speed == speed)
- return (table->sp_code);
- return (-1);
+ tty_lock_assert(tp, MA_OWNED);
+ MPASS(sig >= 1 && sig < NSIG);
+
+ /* Make signals start output again. */
+ tp->t_flags &= ~TF_STOPPED;
+
+ if (tp->t_session != NULL && tp->t_session->s_leader != NULL) {
+ p = tp->t_session->s_leader;
+ PROC_LOCK(p);
+ psignal(p, sig);
+ PROC_UNLOCK(p);
+ }
}
-/*
- * Set input and output watermarks and buffer sizes. For input, the
- * high watermark is about one second's worth of input above empty, the
- * low watermark is slightly below high water, and the buffer size is a
- * driver-dependent amount above high water. For output, the watermarks
- * are near the ends of the buffer, with about 1 second's worth of input
- * between them. All this only applies to the standard line discipline.
- */
void
-ttsetwater(struct tty *tp)
+tty_signal_pgrp(struct tty *tp, int sig)
{
- int cps, ttmaxhiwat, x;
-
- /* Input. */
- clist_alloc_cblocks(&tp->t_canq, TTYHOG, 512);
- switch (tp->t_ispeedwat) {
- case (speed_t)-1:
- cps = tp->t_ispeed / 10;
- break;
- case 0:
- /*
- * This case is for old drivers that don't know about
- * t_ispeedwat. Arrange for them to get the old buffer
- * sizes and watermarks.
- */
- cps = TTYHOG - 2 * 256;
- tp->t_ififosize = 2 * 256;
- break;
- default:
- cps = tp->t_ispeedwat / 10;
- break;
+ tty_lock_assert(tp, MA_OWNED);
+ MPASS(sig >= 1 && sig < NSIG);
+
+ /* Make signals start output again. */
+ tp->t_flags &= ~TF_STOPPED;
+
+ if (sig == SIGINFO && !(tp->t_termios.c_lflag & NOKERNINFO))
+ tty_info(tp);
+ if (tp->t_pgrp != NULL) {
+ PGRP_LOCK(tp->t_pgrp);
+ pgsignal(tp->t_pgrp, sig, 1);
+ PGRP_UNLOCK(tp->t_pgrp);
}
- tp->t_ihiwat = cps;
- tp->t_ilowat = 7 * cps / 8;
- x = cps + tp->t_ififosize;
- clist_alloc_cblocks(&tp->t_rawq, x, x);
-
- /* Output. */
- switch (tp->t_ospeedwat) {
- case (speed_t)-1:
- cps = tp->t_ospeed / 10;
- ttmaxhiwat = 2 * TTMAXHIWAT;
- break;
- case 0:
- cps = tp->t_ospeed / 10;
- ttmaxhiwat = TTMAXHIWAT;
- break;
- default:
- cps = tp->t_ospeedwat / 10;
- ttmaxhiwat = 8 * TTMAXHIWAT;
- break;
- }
-#define CLAMP(x, h, l) ((x) > h ? h : ((x) < l) ? l : (x))
- tp->t_olowat = x = CLAMP(cps / 2, TTMAXLOWAT, TTMINLOWAT);
- x += cps;
- x = CLAMP(x, ttmaxhiwat, TTMINHIWAT); /* XXX clamps are too magic */
- tp->t_ohiwat = roundup(x, CBSIZE); /* XXX for compat */
- x = imax(tp->t_ohiwat, TTMAXHIWAT); /* XXX for compat/safety */
- x += OBUFSIZ + 100;
- clist_alloc_cblocks(&tp->t_outq, x, x);
-#undef CLAMP
}
-/*
- * Output char to tty; console putchar style.
- */
-int
-tputchar(int c, struct tty *tp)
+void
+tty_wakeup(struct tty *tp, int flags)
{
- int s;
+ if (tp->t_flags & TF_ASYNC && tp->t_sigio != NULL)
+ pgsigio(&tp->t_sigio, SIGIO, (tp->t_session != NULL));
- s = spltty();
- if (!ISSET(tp->t_state, TS_CONNECTED)) {
- splx(s);
- return (-1);
+ if (flags & FWRITE) {
+ cv_broadcast(&tp->t_outwait);
+ selwakeup(&tp->t_outpoll);
+ KNOTE_LOCKED(&tp->t_outpoll.si_note, 0);
+ }
+ if (flags & FREAD) {
+ cv_broadcast(&tp->t_inwait);
+ selwakeup(&tp->t_inpoll);
+ KNOTE_LOCKED(&tp->t_inpoll.si_note, 0);
}
- if (c == '\n')
- (void)ttyoutput('\r', tp);
- (void)ttyoutput(c, tp);
- ttstart(tp);
- splx(s);
- return (0);
}
-/*
- * Sleep on chan, returning ERESTART if tty changed while we napped and
- * returning any errors (e.g. EINTR/EWOULDBLOCK) reported by tsleep. If
- * the tty is revoked, restarting a pending call will redo validation done
- * at the start of the call.
- */
int
-ttysleep(struct tty *tp, void *chan, int pri, char *wmesg, int timo)
+tty_wait(struct tty *tp, struct cv *cv)
{
int error;
- int gen;
+ int revokecnt = tp->t_revokecnt;
+
+#if 0
+ /* XXX: /dev/console also picks up Giant. */
+ tty_lock_assert(tp, MA_OWNED|MA_NOTRECURSED);
+#endif
+ tty_lock_assert(tp, MA_OWNED);
+
+ error = cv_wait_sig(cv, tp->t_mtx);
- gen = tp->t_gen;
- error = tsleep(chan, pri, wmesg, timo);
- if (tp->t_state & TS_GONE)
+ /* Restart the system call when we may have been revoked. */
+ if (tp->t_revokecnt != revokecnt)
+ return (ERESTART);
+
+ /* Bail out when the device slipped away. */
+ if (tty_gone(tp))
return (ENXIO);
- if (error)
- return (error);
- return (tp->t_gen == gen ? 0 : ERESTART);
+
+ return (error);
}
-/*
- * Gain a reference to a TTY
- */
int
-ttyref(struct tty *tp)
+tty_timedwait(struct tty *tp, struct cv *cv, int hz)
{
- int i;
+ int error;
+ int revokecnt = tp->t_revokecnt;
+
+#if 0
+ /* XXX: /dev/console also picks up Giant. */
+ tty_lock_assert(tp, MA_OWNED|MA_NOTRECURSED);
+#endif
+ tty_lock_assert(tp, MA_OWNED);
+
+ error = cv_timedwait_sig(cv, tp->t_mtx, hz);
+
+ /* Restart the system call when we may have been revoked. */
+ if (tp->t_revokecnt != revokecnt)
+ return (ERESTART);
- mtx_lock(&tp->t_mtx);
- KASSERT(tp->t_refcnt > 0,
- ("ttyref(): tty refcnt is %d (%s)",
- tp->t_refcnt, tp->t_dev != NULL ? devtoname(tp->t_dev) : "??"));
- i = ++tp->t_refcnt;
- mtx_unlock(&tp->t_mtx);
- return (i);
+ /* Bail out when the device slipped away. */
+ if (tty_gone(tp))
+ return (ENXIO);
+
+ return (error);
}
-/*
- * Drop a reference to a TTY.
- * When reference count drops to zero, we free it.
- */
-int
-ttyrel(struct tty *tp)
+void
+tty_flush(struct tty *tp, int flags)
{
- int i;
-
- mtx_lock(&tty_list_mutex);
- mtx_lock(&tp->t_mtx);
- KASSERT(tp->t_refcnt > 0,
- ("ttyrel(): tty refcnt is %d (%s)",
- tp->t_refcnt, tp->t_dev != NULL ? devtoname(tp->t_dev) : "??"));
- i = --tp->t_refcnt;
- if (i != 0) {
- mtx_unlock(&tp->t_mtx);
- mtx_unlock(&tty_list_mutex);
- return (i);
+ if (flags & FWRITE) {
+ tp->t_flags &= ~TF_HIWAT_OUT;
+ ttyoutq_flush(&tp->t_outq);
+ tty_wakeup(tp, FWRITE);
+ }
+ if (flags & FREAD) {
+ tty_hiwat_in_unblock(tp);
+ ttyinq_flush(&tp->t_inq);
+ ttydevsw_inwakeup(tp);
}
- TAILQ_REMOVE(&tty_list, tp, t_list);
- mtx_unlock(&tp->t_mtx);
- mtx_unlock(&tty_list_mutex);
- knlist_destroy(&tp->t_rsel.si_note);
- knlist_destroy(&tp->t_wsel.si_note);
- mtx_destroy(&tp->t_mtx);
- free(tp, M_TTYS);
- return (i);
}
-/*
- * Allocate a tty struct. Clists in the struct will be allocated by
- * tty_open().
- */
-struct tty *
-ttyalloc()
+static int
+tty_generic_ioctl(struct tty *tp, u_long cmd, void *data, struct thread *td)
{
- struct tty *tp;
-
- tp = malloc(sizeof *tp, M_TTYS, M_WAITOK | M_ZERO);
- mtx_init(&tp->t_mtx, "tty", NULL, MTX_DEF);
+ int error;
+ switch (cmd) {
/*
- * Set up the initial state
+ * Modem commands.
+ * The SER_* and TIOCM_* flags are the same, but one bit
+ * shifted. I don't know why.
*/
- tp->t_refcnt = 1;
- tp->t_timeout = -1;
- tp->t_dtr_wait = 3 * hz;
-
- ttyinitmode(tp, 0, 0);
- bcopy(ttydefchars, tp->t_init_in.c_cc, sizeof tp->t_init_in.c_cc);
+ case TIOCSDTR:
+ ttydevsw_modem(tp, SER_DTR, 0);
+ return (0);
+ case TIOCCDTR:
+ ttydevsw_modem(tp, 0, SER_DTR);
+ return (0);
+ case TIOCMSET: {
+ int bits = *(int *)data;
+ ttydevsw_modem(tp,
+ (bits & (TIOCM_DTR | TIOCM_RTS)) >> 1,
+ ((~bits) & (TIOCM_DTR | TIOCM_RTS)) >> 1);
+ return (0);
+ }
+ case TIOCMBIS: {
+ int bits = *(int *)data;
+ ttydevsw_modem(tp, (bits & (TIOCM_DTR | TIOCM_RTS)) >> 1, 0);
+ return (0);
+ }
+ case TIOCMBIC: {
+ int bits = *(int *)data;
+ ttydevsw_modem(tp, 0, (bits & (TIOCM_DTR | TIOCM_RTS)) >> 1);
+ return (0);
+ }
+ case TIOCMGET:
+ *(int *)data = TIOCM_LE + (ttydevsw_modem(tp, 0, 0) << 1);
+ return (0);
- /* Make callout the same as callin */
- tp->t_init_out = tp->t_init_in;
+ case FIOASYNC:
+ if (*(int *)data)
+ tp->t_flags |= TF_ASYNC;
+ else
+ tp->t_flags &= ~TF_ASYNC;
+ return (0);
+ case FIONBIO:
+ /* This device supports non-blocking operation. */
+ return (0);
+ case FIONREAD:
+ *(int *)data = ttyinq_bytescanonicalized(&tp->t_inq);
+ return (0);
+ case FIOSETOWN:
+ if (tp->t_session != NULL && !tty_is_ctty(tp, td->td_proc))
+ /* Not allowed to set ownership. */
+ return (ENOTTY);
- mtx_lock(&tty_list_mutex);
- TAILQ_INSERT_TAIL(&tty_list, tp, t_list);
- mtx_unlock(&tty_list_mutex);
- knlist_init(&tp->t_rsel.si_note, &tp->t_mtx, NULL, NULL, NULL);
- knlist_init(&tp->t_wsel.si_note, &tp->t_mtx, NULL, NULL, NULL);
- return (tp);
-}
+ /* Temporarily unlock the TTY to set ownership. */
+ tty_unlock(tp);
+ error = fsetown(*(int *)data, &tp->t_sigio);
+ tty_lock(tp);
+ return (error);
+ case FIOGETOWN:
+ if (tp->t_session != NULL && !tty_is_ctty(tp, td->td_proc))
+ /* Not allowed to set ownership. */
+ return (ENOTTY);
-static void
-ttypurge(struct cdev *dev)
-{
+ /* Get ownership. */
+ *(int *)data = fgetown(&tp->t_sigio);
+ return (0);
+ case TIOCGETA:
+ /* Obtain terminal flags through tcgetattr(). */
+ bcopy(&tp->t_termios, data, sizeof(struct termios));
+ return (0);
+ case TIOCSETA:
+ case TIOCSETAW:
+ case TIOCSETAF: {
+ struct termios *t = data;
- if (dev->si_tty == NULL)
- return;
- ttygone(dev->si_tty);
-}
+ /*
+ * Who makes up these funny rules? According to POSIX,
+ * input baud rate is set equal to the output baud rate
+ * when zero.
+ */
+ if (t->c_ispeed == 0)
+ t->c_ispeed = t->c_ospeed;
-/*
- * ttycreate()
- *
- * Create the device entries for this tty thereby opening it for business.
- *
- * The flags argument controls if "cua" units are created.
- *
- * The t_sc filed is copied to si_drv1 in the created cdevs. This
- * is particularly important for ->t_cioctl() users.
- *
- * XXX: implement the init and lock devices by cloning.
- */
+ /* Don't allow invalid flags to be set. */
+ if ((t->c_iflag & ~TTYSUP_IFLAG) != 0 ||
+ (t->c_oflag & ~TTYSUP_OFLAG) != 0 ||
+ (t->c_lflag & ~TTYSUP_LFLAG) != 0 ||
+ (t->c_cflag & ~TTYSUP_CFLAG) != 0)
+ return (EINVAL);
-int
-ttycreate(struct tty *tp, int flags, const char *fmt, ...)
-{
- char namebuf[SPECNAMELEN - 3]; /* XXX space for "tty" */
- struct cdevsw *csw = NULL;
- int unit = 0;
- va_list ap;
- struct cdev *cp;
- int i, minor, sminor, sunit;
+ /* Set terminal flags through tcsetattr(). */
+ if (cmd == TIOCSETAW || cmd == TIOCSETAF) {
+ error = tty_drain(tp);
+ if (error)
+ return (error);
+ if (cmd == TIOCSETAF)
+ tty_flush(tp, FREAD);
+ }
- mtx_assert(&Giant, MA_OWNED);
+ /*
+ * Only call param() when the flags really change.
+ */
+ if ((t->c_cflag & CIGNORE) == 0 &&
+ (tp->t_termios.c_cflag != t->c_cflag ||
+ tp->t_termios.c_ispeed != t->c_ispeed ||
+ tp->t_termios.c_ospeed != t->c_ospeed)) {
+ error = ttydevsw_param(tp, t);
+ if (error)
+ return (error);
- if (tty_unit == NULL)
- tty_unit = new_unrhdr(0, 0xffff, NULL);
+ /* XXX: CLOCAL? */
+
+ tp->t_termios.c_cflag = t->c_cflag;
+ tp->t_termios.c_ispeed = t->c_ispeed;
+ tp->t_termios.c_ospeed = t->c_ospeed;
- sunit = alloc_unr(tty_unit);
- tp->t_devunit = sunit;
+ /* Baud rate has changed - update watermarks. */
+ tty_watermarks(tp);
+ }
- if (csw == NULL) {
- csw = &tty_cdevsw;
- unit = sunit;
- }
- KASSERT(csw->d_purge == NULL || csw->d_purge == ttypurge,
- ("tty should not have d_purge"));
+ /* Copy new non-device driver parameters. */
+ tp->t_termios.c_iflag = t->c_iflag;
+ tp->t_termios.c_oflag = t->c_oflag;
+ tp->t_termios.c_lflag = t->c_lflag;
+ bcopy(t->c_cc, &tp->t_termios.c_cc, sizeof(t->c_cc));
- csw->d_purge = ttypurge;
+ ttydisc_optimize(tp);
- minor = unit2minor(unit);
- sminor = unit2minor(sunit);
- va_start(ap, fmt);
- i = vsnrprintf(namebuf, sizeof namebuf, 32, fmt, ap);
- va_end(ap);
- KASSERT(i < sizeof namebuf, ("Too long tty name (%s)", namebuf));
-
- cp = make_dev(csw, minor,
- UID_ROOT, GID_WHEEL, 0600, "tty%s", namebuf);
- tp->t_dev = cp;
- tp->t_mdev = cp;
- cp->si_tty = tp;
- cp->si_drv1 = tp->t_sc;
-
- cp = make_dev(&ttys_cdevsw, sminor | MINOR_INIT,
- UID_ROOT, GID_WHEEL, 0600, "tty%s.init", namebuf);
- dev_depends(tp->t_dev, cp);
- cp->si_drv1 = tp->t_sc;
- cp->si_drv2 = &tp->t_init_in;
- cp->si_tty = tp;
-
- cp = make_dev(&ttys_cdevsw, sminor | MINOR_LOCK,
- UID_ROOT, GID_WHEEL, 0600, "tty%s.lock", namebuf);
- dev_depends(tp->t_dev, cp);
- cp->si_drv1 = tp->t_sc;
- cp->si_drv2 = &tp->t_lock_in;
- cp->si_tty = tp;
-
- if (flags & TS_CALLOUT) {
- cp = make_dev(csw, minor | MINOR_CALLOUT,
- UID_UUCP, GID_DIALER, 0660, "cua%s", namebuf);
- dev_depends(tp->t_dev, cp);
- cp->si_drv1 = tp->t_sc;
- cp->si_tty = tp;
-
- cp = make_dev(&ttys_cdevsw, sminor | MINOR_CALLOUT | MINOR_INIT,
- UID_UUCP, GID_DIALER, 0660, "cua%s.init", namebuf);
- dev_depends(tp->t_dev, cp);
- cp->si_drv1 = tp->t_sc;
- cp->si_drv2 = &tp->t_init_out;
- cp->si_tty = tp;
-
- cp = make_dev(&ttys_cdevsw, sminor | MINOR_CALLOUT | MINOR_LOCK,
- UID_UUCP, GID_DIALER, 0660, "cua%s.lock", namebuf);
- dev_depends(tp->t_dev, cp);
- cp->si_drv1 = tp->t_sc;
- cp->si_drv2 = &tp->t_lock_out;
- cp->si_tty = tp;
+ if ((t->c_lflag & ICANON) == 0) {
+ /*
+ * When in non-canonical mode, wake up all
+ * readers. Canonicalize any partial input. VMIN
+ * and VTIME could also be adjusted.
+ */
+ ttyinq_canonicalize(&tp->t_inq);
+ tty_wakeup(tp, FREAD);
+ }
+ return (0);
}
+ case TIOCGETD:
+ /* For compatibility - we only support TTYDISC. */
+ *(int *)data = TTYDISC;
+ return (0);
+ case TIOCGPGRP:
+ if (!tty_is_ctty(tp, td->td_proc))
+ return (ENOTTY);
- return (0);
-}
-
-/*
- * This function is called when the hardware disappears. We set a flag
- * and wake up stuff so all sleeping threads will notice.
- */
-void
-ttygone(struct tty *tp)
-{
-
- tp->t_state |= TS_GONE;
- if (SEL_WAITING(&tp->t_rsel))
- selwakeuppri(&tp->t_rsel, TTIPRI);
- if (SEL_WAITING(&tp->t_wsel))
- selwakeuppri(&tp->t_wsel, TTOPRI);
- if (ISSET(tp->t_state, TS_ASYNC) && tp->t_sigio != NULL)
- pgsigio(&tp->t_sigio, SIGIO, (tp->t_session != NULL));
- wakeup(&tp->t_dtr_wait);
- wakeup(TSA_CARR_ON(tp));
- wakeup(TSA_HUP_OR_INPUT(tp));
- wakeup(TSA_OCOMPLETE(tp));
- wakeup(TSA_OLOWAT(tp));
- KNOTE_UNLOCKED(&tp->t_rsel.si_note, 0);
- KNOTE_UNLOCKED(&tp->t_wsel.si_note, 0);
- tt_purge(tp);
-}
-
-/*
- * ttyfree()
- *
- * Called when the driver is ready to free the tty structure.
- *
- * XXX: This shall sleep until all threads have left the driver.
- */
-void
-ttyfree(struct tty *tp)
-{
- struct cdev *dev;
- u_int unit;
-
- mtx_assert(&Giant, MA_OWNED);
- ttygone(tp);
- unit = tp->t_devunit;
- dev = tp->t_mdev;
- dev->si_tty = NULL;
- tp->t_dev = NULL;
- destroy_dev(dev);
- ttyrel(tp);
- free_unr(tty_unit, unit);
-}
+ if (tp->t_pgrp != NULL)
+ *(int *)data = tp->t_pgrp->pg_id;
+ else
+ *(int *)data = NO_PID;
+ return (0);
+ case TIOCGSID:
+ if (!tty_is_ctty(tp, td->td_proc))
+ return (ENOTTY);
-static int
-sysctl_kern_ttys(SYSCTL_HANDLER_ARGS)
-{
- struct tty *tp, *tp2;
- struct xtty xt;
- int error;
+ MPASS(tp->t_session);
+ *(int *)data = tp->t_session->s_sid;
+ return (0);
+ case TIOCSCTTY: {
+ struct proc *p = td->td_proc;
- error = 0;
- mtx_lock(&tty_list_mutex);
- tp = TAILQ_FIRST(&tty_list);
- if (tp != NULL)
- ttyref(tp);
- while (tp != NULL) {
- if (tp->t_state & TS_GONE)
- goto nexttp;
- bzero(&xt, sizeof xt);
- xt.xt_size = sizeof xt;
-#define XT_COPY(field) xt.xt_##field = tp->t_##field
- xt.xt_rawcc = tp->t_rawq.c_cc;
- xt.xt_cancc = tp->t_canq.c_cc;
- xt.xt_outcc = tp->t_outq.c_cc;
- XT_COPY(line);
+ /* XXX: This looks awful. */
+ tty_unlock(tp);
+ sx_xlock(&proctree_lock);
+ tty_lock(tp);
- /*
- * XXX: We hold the tty list lock while doing this to
- * work around a race with pty/pts tty destruction.
- * They set t_dev to NULL and then call ttyrel() to
- * free the structure which will block on the list
- * lock before they call destroy_dev() on the cdev
- * backing t_dev.
- *
- * XXX: ttyfree() now does the same since it has been
- * fixed to not leak ttys.
- */
- if (tp->t_dev != NULL)
- xt.xt_dev = dev2udev(tp->t_dev);
- XT_COPY(state);
- XT_COPY(flags);
- XT_COPY(timeout);
- if (tp->t_pgrp != NULL)
- xt.xt_pgid = tp->t_pgrp->pg_id;
- if (tp->t_session != NULL)
- xt.xt_sid = tp->t_session->s_sid;
- XT_COPY(termios);
- XT_COPY(winsize);
- XT_COPY(column);
- XT_COPY(rocount);
- XT_COPY(rocol);
- XT_COPY(ififosize);
- XT_COPY(ihiwat);
- XT_COPY(ilowat);
- XT_COPY(ispeedwat);
- XT_COPY(ohiwat);
- XT_COPY(olowat);
- XT_COPY(ospeedwat);
-#undef XT_COPY
- mtx_unlock(&tty_list_mutex);
- error = SYSCTL_OUT(req, &xt, sizeof xt);
- if (error != 0) {
- ttyrel(tp);
- return (error);
+ if (!SESS_LEADER(p)) {
+ /* Only the session leader may do this. */
+ sx_xunlock(&proctree_lock);
+ return (EPERM);
}
- mtx_lock(&tty_list_mutex);
-nexttp: tp2 = TAILQ_NEXT(tp, t_list);
- if (tp2 != NULL)
- ttyref(tp2);
- mtx_unlock(&tty_list_mutex);
- ttyrel(tp);
- tp = tp2;
- mtx_lock(&tty_list_mutex);
- }
- mtx_unlock(&tty_list_mutex);
- return (0);
-}
-SYSCTL_PROC(_kern, OID_AUTO, ttys, CTLTYPE_OPAQUE|CTLFLAG_RD,
- 0, 0, sysctl_kern_ttys, "S,xtty", "All ttys");
-SYSCTL_LONG(_kern, OID_AUTO, tty_nin, CTLFLAG_RD,
- &tk_nin, 0, "Total TTY in characters");
-SYSCTL_LONG(_kern, OID_AUTO, tty_nout, CTLFLAG_RD,
- &tk_nout, 0, "Total TTY out characters");
+ if (tp->t_session != NULL && tp->t_session == p->p_session) {
+ /* This is already our controlling TTY. */
+ sx_xunlock(&proctree_lock);
+ return (0);
+ }
-void
-nottystop(struct tty *tp, int rw)
-{
+ if (!SESS_LEADER(p) || p->p_session->s_ttyvp != NULL ||
+ (tp->t_session != NULL && tp->t_session->s_ttyvp != NULL)) {
+ /*
+ * There is already a relation between a TTY and
+ * a session, or the caller is not the session
+ * leader.
+ *
+ * Allow the TTY to be stolen when the vnode is
+ * NULL, but the reference to the TTY is still
+ * active.
+ */
+ sx_xunlock(&proctree_lock);
+ return (EPERM);
+ }
- return;
-}
+ /* Connect the session to the TTY. */
+ tp->t_session = p->p_session;
+ tp->t_session->s_ttyp = tp;
+ tp->t_sessioncnt++;
+ sx_xunlock(&proctree_lock);
-int
-ttyopen(struct cdev *dev, int flag, int mode, struct thread *td)
-{
- int error;
- int s;
- struct tty *tp;
+ /* Assign foreground process group. */
+ tp->t_pgrp = p->p_pgrp;
+ PROC_LOCK(p);
+ p->p_flag |= P_CONTROLT;
+ PROC_UNLOCK(p);
- tp = dev->si_tty;
+ return (0);
+ }
+ case TIOCSPGRP: {
+ struct pgrp *pg;
- s = spltty();
- /*
- * We jump to this label after all non-interrupted sleeps to pick
- * up any changes of the device state.
- */
-open_top:
- if (tp->t_state & TS_GONE)
- return (ENXIO);
- error = ttydtrwaitsleep(tp);
- if (error)
- goto out;
- if (tp->t_state & TS_ISOPEN) {
/*
- * The device is open, so everything has been initialized.
- * Handle conflicts.
+ * XXX: Temporarily unlock the TTY to locate the process
+ * group. This code would be lot nicer if we would ever
+ * decompose proctree_lock.
*/
- if (ISCALLOUT(dev) && !tp->t_actout)
- return (EBUSY);
- if (tp->t_actout && !ISCALLOUT(dev)) {
- if (flag & O_NONBLOCK)
- return (EBUSY);
- error = tsleep(&tp->t_actout,
- TTIPRI | PCATCH, "ttybi", 0);
- if (error != 0 || (tp->t_state & TS_GONE))
- goto out;
- goto open_top;
+ tty_unlock(tp);
+ sx_slock(&proctree_lock);
+ pg = pgfind(*(int *)data);
+ if (pg != NULL)
+ PGRP_UNLOCK(pg);
+ if (pg == NULL || pg->pg_session != td->td_proc->p_session) {
+ sx_sunlock(&proctree_lock);
+ tty_lock(tp);
+ return (EPERM);
}
- if (tp->t_state & TS_XCLUDE && priv_check(td,
- PRIV_TTY_EXCLUSIVE))
- return (EBUSY);
- } else {
+ tty_lock(tp);
+
/*
- * The device isn't open, so there are no conflicts.
- * Initialize it. Initialization is done twice in many
- * cases: to preempt sleeping callin opens if we are
- * callout, and to complete a callin open after DCD rises.
+ * Determine if this TTY is the controlling TTY after
+ * relocking the TTY.
*/
- tp->t_termios = ISCALLOUT(dev) ? tp->t_init_out : tp->t_init_in;
- tp->t_cflag = tp->t_termios.c_cflag;
- if (tp->t_modem != NULL)
- tt_modem(tp, SER_DTR | SER_RTS, 0);
- ++tp->t_wopeners;
- error = tt_param(tp, &tp->t_termios);
- --tp->t_wopeners;
- if (error == 0)
- error = tt_open(tp, dev);
- if (error != 0)
- goto out;
- if (ISCALLOUT(dev) || (tt_modem(tp, 0, 0) & SER_DCD))
- ttyld_modem(tp, 1);
- }
- /*
- * Wait for DCD if necessary.
- */
- if (!(tp->t_state & TS_CARR_ON) && !ISCALLOUT(dev)
- && !(tp->t_cflag & CLOCAL) && !(flag & O_NONBLOCK)) {
- ++tp->t_wopeners;
- error = tsleep(TSA_CARR_ON(tp), TTIPRI | PCATCH, "ttydcd", 0);
- --tp->t_wopeners;
- if (error != 0 || (tp->t_state & TS_GONE))
- goto out;
- goto open_top;
- }
- error = ttyld_open(tp, dev);
- ttyldoptim(tp);
- if (tp->t_state & TS_ISOPEN && ISCALLOUT(dev))
- tp->t_actout = TRUE;
-out:
- splx(s);
- if (!(tp->t_state & TS_ISOPEN) && tp->t_wopeners == 0)
- tt_close(tp);
- return (error);
-}
+ if (!tty_is_ctty(tp, td->td_proc)) {
+ sx_sunlock(&proctree_lock);
+ return (ENOTTY);
+ }
+ tp->t_pgrp = pg;
+ sx_sunlock(&proctree_lock);
-int
-ttyclose(struct cdev *dev, int flag, int mode, struct thread *td)
-{
- struct tty *tp;
+ /* Wake up the background process groups. */
+ cv_broadcast(&tp->t_bgwait);
+ return (0);
+ }
+ case TIOCFLUSH: {
+ int flags = *(int *)data;
- tp = dev->si_tty;
- ttyld_close(tp, flag);
- ttyldoptim(tp);
- tt_close(tp);
- tp->t_do_timestamp = 0;
- if (tp->t_pps != NULL)
- tp->t_pps->ppsparam.mode = 0;
- tty_close(tp);
- return (0);
-}
+ if (flags == 0)
+ flags = (FREAD|FWRITE);
+ else
+ flags &= (FREAD|FWRITE);
+ tty_flush(tp, flags);
+ return (0);
+ }
+ case TIOCDRAIN:
+ /* Drain TTY output. */
+ return tty_drain(tp);
+ case TIOCCONS:
+ /* Set terminal as console TTY. */
+ if (*(int *)data) {
+ struct nameidata nd;
+ int vfslocked;
-int
-ttyread(struct cdev *dev, struct uio *uio, int flag)
-{
- struct tty *tp;
+ /*
+ * XXX: TTY won't slip away, but constty would
+ * really need to be locked!
+ */
+ tty_unlock(tp);
- tp = tty_gettp(dev);
+ if (constty == tp) {
+ tty_lock(tp);
+ return (0);
+ }
+ if (constty != NULL) {
+ tty_lock(tp);
+ return (EBUSY);
+ }
+ /* XXX: allow disconnected constty's to be stolen! */
- if (tp == NULL || (tp->t_state & TS_GONE))
- return (ENODEV);
- return (ttyld_read(tp, uio, flag));
-}
+ /*
+ * Only allow this to work when the user can
+ * open /dev/console.
+ */
+ NDINIT(&nd, LOOKUP, FOLLOW|LOCKLEAF|MPSAFE,
+ UIO_SYSSPACE, "/dev/console", td);
+ if ((error = namei(&nd)) != 0) {
+ tty_lock(tp);
+ return (error);
+ }
+ vfslocked = NDHASGIANT(&nd);
+ NDFREE(&nd, NDF_ONLY_PNBUF);
-int
-ttywrite(struct cdev *dev, struct uio *uio, int flag)
-{
- struct tty *tp;
+ error = VOP_ACCESS(nd.ni_vp, VREAD, td->td_ucred, td);
+ vput(nd.ni_vp);
+ VFS_UNLOCK_GIANT(vfslocked);
+ if (error) {
+ tty_lock(tp);
+ return (error);
+ }
- tp = tty_gettp(dev);
+ constty_set(tp);
+ tty_lock(tp);
+ } else if (constty == tp) {
+ constty_clear();
+ }
+ return (0);
+ case TIOCGWINSZ:
+ /* Obtain window size. */
+ bcopy(&tp->t_winsize, data, sizeof(struct winsize));
+ return (0);
+ case TIOCSWINSZ:
+ /* Set window size. */
+ if (bcmp(&tp->t_winsize, data, sizeof(struct winsize)) == 0)
+ return (0);
+ bcopy(data, &tp->t_winsize, sizeof(struct winsize));
+ tty_signal_pgrp(tp, SIGWINCH);
+ return (0);
+ case TIOCEXCL:
+ tp->t_flags |= TF_EXCLUDE;
+ return (0);
+ case TIOCNXCL:
+ tp->t_flags &= ~TF_EXCLUDE;
+ return (0);
+ case TIOCOUTQ:
+ *(unsigned int *)data = ttyoutq_bytesused(&tp->t_outq);
+ return (0);
+ case TIOCSTOP:
+ tp->t_flags |= TF_STOPPED;
+ return (0);
+ case TIOCSTART:
+ tp->t_flags &= ~TF_STOPPED;
+ ttydevsw_outwakeup(tp);
+ return (0);
+ case TIOCSTAT:
+ tty_info(tp);
+ return (0);
+ }
- if (tp == NULL || (tp->t_state & TS_GONE))
- return (ENODEV);
- return (ttyld_write(tp, uio, flag));
+#ifdef COMPAT_43TTY
+ return tty_ioctl_compat(tp, cmd, data, td);
+#else /* !COMPAT_43TTY */
+ return (ENOIOCTL);
+#endif /* COMPAT_43TTY */
}
int
-ttyioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td)
+tty_ioctl(struct tty *tp, u_long cmd, void *data, struct thread *td)
{
- struct tty *tp;
- int error;
-
- tp = dev->si_tty;
-
- if (cmd == TIOCSETA || cmd == TIOCSETAW || cmd == TIOCSETAF) {
- int cc;
- struct termios *dt = (struct termios *)data;
- struct termios *lt =
- ISCALLOUT(dev) ? &tp->t_lock_out : &tp->t_lock_in;
-
- dt->c_iflag = (tp->t_iflag & lt->c_iflag)
- | (dt->c_iflag & ~lt->c_iflag);
- dt->c_oflag = (tp->t_oflag & lt->c_oflag)
- | (dt->c_oflag & ~lt->c_oflag);
- dt->c_cflag = (tp->t_cflag & lt->c_cflag)
- | (dt->c_cflag & ~lt->c_cflag);
- dt->c_lflag = (tp->t_lflag & lt->c_lflag)
- | (dt->c_lflag & ~lt->c_lflag);
- for (cc = 0; cc < NCCS; ++cc)
- if (lt->c_cc[cc] != 0)
- dt->c_cc[cc] = tp->t_cc[cc];
- if (lt->c_ispeed != 0)
- dt->c_ispeed = tp->t_ispeed;
- if (lt->c_ospeed != 0)
- dt->c_ospeed = tp->t_ospeed;
- }
+ int error;
- error = ttyld_ioctl(tp, cmd, data, flag, td);
+ tty_lock_assert(tp, MA_OWNED);
+
+ if (tty_gone(tp))
+ return (ENXIO);
+
+ error = ttydevsw_ioctl(tp, cmd, data, td);
if (error == ENOIOCTL)
- error = ttioctl(tp, cmd, data, flag);
- ttyldoptim(tp);
- if (error != ENOIOCTL)
- return (error);
- return (ENOTTY);
-}
+ error = tty_generic_ioctl(tp, cmd, data, td);
-void
-ttyldoptim(struct tty *tp)
-{
- struct termios *t;
-
- t = &tp->t_termios;
- if (!(t->c_iflag & (ICRNL | IGNCR | IMAXBEL | INLCR | ISTRIP | IXON))
- && (!(t->c_iflag & BRKINT) || (t->c_iflag & IGNBRK))
- && (!(t->c_iflag & PARMRK)
- || (t->c_iflag & (IGNPAR | IGNBRK)) == (IGNPAR | IGNBRK))
- && !(t->c_lflag & (ECHO | ICANON | IEXTEN | ISIG | PENDIN))
- && linesw[tp->t_line]->l_rint == ttyinput)
- tp->t_state |= TS_CAN_BYPASS_L_RINT;
- else
- tp->t_state &= ~TS_CAN_BYPASS_L_RINT;
+ return (error);
}
-static void
-ttydtrwaitwakeup(void *arg)
+dev_t
+tty_udev(struct tty *tp)
{
- struct tty *tp;
-
- tp = arg;
- tp->t_state &= ~TS_DTR_WAIT;
- wakeup(&tp->t_dtr_wait);
+ if (tp->t_dev)
+ return dev2udev(tp->t_dev);
+ else
+ return NODEV;
}
-
-void
-ttydtrwaitstart(struct tty *tp)
+int
+tty_checkoutq(struct tty *tp)
{
- if (tp->t_dtr_wait == 0)
- return;
- if (tp->t_state & TS_DTR_WAIT)
- return;
- timeout(ttydtrwaitwakeup, tp, tp->t_dtr_wait);
- tp->t_state |= TS_DTR_WAIT;
+ /* 256 bytes should be enough to print a log message. */
+ return (ttyoutq_bytesleft(&tp->t_outq) >= 256);
}
-int
-ttydtrwaitsleep(struct tty *tp)
+void
+tty_hiwat_in_block(struct tty *tp)
{
- int error;
- error = 0;
- while (error == 0) {
- if (tp->t_state & TS_GONE)
- error = ENXIO;
- else if (!(tp->t_state & TS_DTR_WAIT))
- break;
- else
- error = tsleep(&tp->t_dtr_wait, TTIPRI | PCATCH,
- "dtrwait", 0);
+ if ((tp->t_flags & TF_HIWAT_IN) == 0 &&
+ tp->t_termios.c_iflag & IXOFF &&
+ tp->t_termios.c_cc[VSTOP] != _POSIX_VDISABLE) {
+ /*
+ * Input flow control. Only enter the high watermark when we
+ * can successfully store the VSTOP character.
+ */
+ if (ttyoutq_write_nofrag(&tp->t_outq,
+ &tp->t_termios.c_cc[VSTOP], 1) == 0)
+ tp->t_flags |= TF_HIWAT_IN;
+ } else {
+ /* No input flow control. */
+ tp->t_flags |= TF_HIWAT_IN;
}
- return (error);
}
-static int
-ttysopen(struct cdev *dev, int flag, int mode, struct thread *td)
+void
+tty_hiwat_in_unblock(struct tty *tp)
{
- struct tty *tp;
-
- tp = dev->si_tty;
- KASSERT(tp != NULL,
- ("ttysopen(): no tty pointer on device (%s)", devtoname(dev)));
- if (tp->t_state & TS_GONE)
- return (ENODEV);
- return (0);
-}
-static int
-ttysclose(struct cdev *dev, int flag, int mode, struct thread *td)
-{
+ if ((tp->t_flags & TF_HIWAT_IN) == 0 &&
+ tp->t_termios.c_iflag & IXOFF &&
+ tp->t_termios.c_cc[VSTART] != _POSIX_VDISABLE) {
+ /*
+ * Input flow control. Only leave the high watermark when we
+ * can successfully store the VSTART character.
+ */
+ if (ttyoutq_write_nofrag(&tp->t_outq,
+ &tp->t_termios.c_cc[VSTART], 1) == 0)
+ tp->t_flags &= ~TF_HIWAT_IN;
+ } else {
+ /* No input flow control. */
+ tp->t_flags &= ~TF_HIWAT_IN;
+ }
- return (0);
+ if (!tty_gone(tp))
+ ttydevsw_inwakeup(tp);
}
-static int
-ttysrdwr(struct cdev *dev, struct uio *uio, int flag)
-{
+#include "opt_ddb.h"
+#ifdef DDB
+#include <ddb/ddb.h>
- return (ENODEV);
-}
+static struct {
+ int flag;
+ char val;
+} ttystates[] = {
+#if 0
+ { TF_NOPREFIX, 'N' },
+#endif
+ { TF_INITLOCK, 'I' },
+ { TF_CALLOUT, 'C' },
+
+ /* Keep these together -> 'Oi' and 'Oo'. */
+ { TF_OPENED, 'O' },
+ { TF_OPENED_IN, 'i' },
+ { TF_OPENED_OUT,'o' },
+
+ { TF_GONE, 'G' },
+ { TF_OPENCLOSE, 'B' },
+ { TF_ASYNC, 'Y' },
+ { TF_LITERAL, 'L' },
+
+ /* Keep these together -> 'Hi' and 'Ho'. */
+ { TF_HIWAT, 'H' },
+ { TF_HIWAT_IN, 'i' },
+ { TF_HIWAT_OUT, 'o' },
+
+ { TF_STOPPED, 'S' },
+ { TF_EXCLUDE, 'X' },
+ { TF_BYPASS, 'l' },
+ { TF_ZOMBIE, 'Z' },
+
+ { 0, '\0' },
+};
-static int
-ttysioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td)
+/* DDB command to show TTY statistics. */
+DB_SHOW_COMMAND(ttys, db_show_ttys)
{
- struct tty *tp;
- int error;
- struct termios *ct;
-
- tp = dev->si_tty;
- KASSERT(tp != NULL,
- ("ttysopen(): no tty pointer on device (%s)", devtoname(dev)));
- if (tp->t_state & TS_GONE)
- return (ENODEV);
- ct = dev->si_drv2;
- switch (cmd) {
- case TIOCSETA:
- error = priv_check(td, PRIV_TTY_SETA);
- if (error != 0)
- return (error);
- *ct = *(struct termios *)data;
- return (0);
- case TIOCGETA:
- *(struct termios *)data = *ct;
- return (0);
- case TIOCGETD:
- *(int *)data = TTYDISC;
- return (0);
- case TIOCGWINSZ:
- bzero(data, sizeof(struct winsize));
- return (0);
- default:
- if (tp->t_cioctl != NULL)
- return(tp->t_cioctl(dev, cmd, data, flag, td));
- return (ENOTTY);
+ struct tty *tp;
+ size_t isiz, osiz;
+ int i, j;
+
+ /* Make the output look like `pstat -t'. */
+ db_printf(" LINE INQ CAN LIN LOW OUTQ USE LOW "
+ "COL SESS PGID STATE\n");
+
+ TAILQ_FOREACH(tp, &tty_list, t_list) {
+ isiz = tp->t_inq.ti_nblocks * TTYINQ_DATASIZE;
+ osiz = tp->t_outq.to_nblocks * TTYOUTQ_DATASIZE;
+
+ db_printf("%10s %5zu %4u %4u %4zu %5zu %4u %4zu %5u %5d %5d ",
+ tty_devname(tp),
+ isiz,
+ tp->t_inq.ti_linestart - tp->t_inq.ti_begin,
+ tp->t_inq.ti_end - tp->t_inq.ti_linestart,
+ isiz - tp->t_inlow,
+ osiz,
+ tp->t_outq.to_end - tp->t_outq.to_begin,
+ osiz - tp->t_outlow,
+ tp->t_column,
+ tp->t_session ? tp->t_session->s_sid : 0,
+ tp->t_pgrp ? tp->t_pgrp->pg_id : 0);
+
+ /* Flag bits. */
+ for (i = j = 0; ttystates[i].flag; i++)
+ if (tp->t_flags & ttystates[i].flag) {
+ db_printf("%c", ttystates[i].val);
+ j++;
+ }
+ if (j == 0)
+ db_printf("-");
+ db_printf("\n");
}
}
-
-/*
- * Initialize a tty to sane modes.
- */
-void
-ttyinitmode(struct tty *tp, int echo, int speed)
-{
-
- if (speed == 0)
- speed = TTYDEF_SPEED;
- tp->t_init_in.c_iflag = TTYDEF_IFLAG;
- tp->t_init_in.c_oflag = TTYDEF_OFLAG;
- tp->t_init_in.c_cflag = TTYDEF_CFLAG;
- if (echo)
- tp->t_init_in.c_lflag = TTYDEF_LFLAG_ECHO;
- else
- tp->t_init_in.c_lflag = TTYDEF_LFLAG_NOECHO;
-
- tp->t_init_in.c_ispeed = tp->t_init_in.c_ospeed = speed;
- termioschars(&tp->t_init_in);
- tp->t_init_out = tp->t_init_in;
- tp->t_termios = tp->t_init_in;
-}
-
-/*
- * Use more "normal" termios paramters for consoles.
- */
-void
-ttyconsolemode(struct tty *tp, int speed)
-{
-
- if (speed == 0)
- speed = TTYDEF_SPEED;
- ttyinitmode(tp, 1, speed);
- tp->t_init_in.c_cflag |= CLOCAL;
- tp->t_lock_out.c_cflag = tp->t_lock_in.c_cflag = CLOCAL;
- tp->t_lock_out.c_ispeed = tp->t_lock_out.c_ospeed =
- tp->t_lock_in.c_ispeed = tp->t_lock_in.c_ospeed = speed;
- tp->t_init_out = tp->t_init_in;
- tp->t_termios = tp->t_init_in;
- ttsetwater(tp);
-}
-
-/*
- * Record the relationship between the serial ports notion of modem control
- * signals and the one used in certain ioctls in a way the compiler can enforce
- * XXX: We should define TIOCM_* in terms of SER_ if we can limit the
- * XXX: consequences of the #include work that would take.
- */
-CTASSERT(SER_DTR == TIOCM_DTR / 2);
-CTASSERT(SER_RTS == TIOCM_RTS / 2);
-CTASSERT(SER_STX == TIOCM_ST / 2);
-CTASSERT(SER_SRX == TIOCM_SR / 2);
-CTASSERT(SER_CTS == TIOCM_CTS / 2);
-CTASSERT(SER_DCD == TIOCM_DCD / 2);
-CTASSERT(SER_RI == TIOCM_RI / 2);
-CTASSERT(SER_DSR == TIOCM_DSR / 2);
-
+#endif /* DDB */
OpenPOWER on IntegriCloud