diff options
Diffstat (limited to 'sys')
85 files changed, 5420 insertions, 6472 deletions
diff --git a/sys/amd64/conf/GENERIC b/sys/amd64/conf/GENERIC index 3d5e9f0..31e1e9c 100644 --- a/sys/amd64/conf/GENERIC +++ b/sys/amd64/conf/GENERIC @@ -257,7 +257,7 @@ device loop # Network loopback device random # Entropy device device ether # Ethernet support device tun # Packet tunnel. -device pty # Pseudo-ttys (telnet etc) +device pty # BSD-style compatibility pseudo ttys device md # Memory "disks" device gif # IPv6 and IPv4 tunneling device faith # IPv6-to-IPv4 relaying (translation) diff --git a/sys/bsm/audit_kevents.h b/sys/bsm/audit_kevents.h index 400b79d..c538431 100644 --- a/sys/bsm/audit_kevents.h +++ b/sys/bsm/audit_kevents.h @@ -548,6 +548,7 @@ #define AUE_MKNODAT 43150 /* FreeBSD. */ #define AUE_READLINKAT 43151 /* FreeBSD. */ #define AUE_SYMLINKAT 43152 /* FreeBSD. */ +#define AUE_POSIXOPENPT 43153 /* FreeBSD. */ /* * Darwin BSM uses a number of AUE_O_* definitions, which are aliased to the diff --git a/sys/compat/freebsd32/freebsd32_syscall.h b/sys/compat/freebsd32/freebsd32_syscall.h index a2b3102..6eebf04 100644 --- a/sys/compat/freebsd32/freebsd32_syscall.h +++ b/sys/compat/freebsd32/freebsd32_syscall.h @@ -355,4 +355,5 @@ #define FREEBSD32_SYS_renameat 501 #define FREEBSD32_SYS_symlinkat 502 #define FREEBSD32_SYS_unlinkat 503 -#define FREEBSD32_SYS_MAXSYSCALL 504 +#define FREEBSD32_SYS_posix_openpt 504 +#define FREEBSD32_SYS_MAXSYSCALL 505 diff --git a/sys/compat/freebsd32/freebsd32_syscalls.c b/sys/compat/freebsd32/freebsd32_syscalls.c index fa21c32..c9638cb 100644 --- a/sys/compat/freebsd32/freebsd32_syscalls.c +++ b/sys/compat/freebsd32/freebsd32_syscalls.c @@ -511,4 +511,5 @@ const char *freebsd32_syscallnames[] = { "renameat", /* 501 = renameat */ "symlinkat", /* 502 = symlinkat */ "unlinkat", /* 503 = unlinkat */ + "posix_openpt", /* 504 = posix_openpt */ }; diff --git a/sys/compat/freebsd32/freebsd32_sysent.c b/sys/compat/freebsd32/freebsd32_sysent.c index 0e81268..29c5841 100644 --- a/sys/compat/freebsd32/freebsd32_sysent.c +++ b/sys/compat/freebsd32/freebsd32_sysent.c @@ -542,4 +542,5 @@ struct sysent freebsd32_sysent[] = { { AS(renameat_args), (sy_call_t *)renameat, AUE_RENAMEAT, NULL, 0, 0 }, /* 501 = renameat */ { AS(symlinkat_args), (sy_call_t *)symlinkat, AUE_SYMLINKAT, NULL, 0, 0 }, /* 502 = symlinkat */ { AS(unlinkat_args), (sy_call_t *)unlinkat, AUE_UNLINKAT, NULL, 0, 0 }, /* 503 = unlinkat */ + { AS(posix_openpt_args), (sy_call_t *)posix_openpt, AUE_POSIXOPENPT, NULL, 0, 0 }, /* 504 = posix_openpt */ }; diff --git a/sys/compat/freebsd32/syscalls.master b/sys/compat/freebsd32/syscalls.master index 5ab7e42..193c92d 100644 --- a/sys/compat/freebsd32/syscalls.master +++ b/sys/compat/freebsd32/syscalls.master @@ -846,3 +846,4 @@ char *path2); } 503 AUE_UNLINKAT NOPROTO { int unlinkat(int fd, char *path, \ int flag); } +504 AUE_POSIXOPENPT NOPROTO { int posix_openpt(int flags); } diff --git a/sys/compat/linux/linux_ioctl.c b/sys/compat/linux/linux_ioctl.c index b166535..083548e 100644 --- a/sys/compat/linux/linux_ioctl.c +++ b/sys/compat/linux/linux_ioctl.c @@ -296,6 +296,11 @@ struct linux_winsize { unsigned short ws_xpixel, ws_ypixel; }; +struct speedtab { + int sp_speed; /* Speed. */ + int sp_code; /* Code. */ +}; + static struct speedtab sptab[] = { { B0, LINUX_B0 }, { B50, LINUX_B50 }, { B75, LINUX_B75 }, { B110, LINUX_B110 }, @@ -395,7 +400,7 @@ bsd_to_linux_termios(struct termios *bios, struct linux_termios *lios) lios->c_oflag |= LINUX_OPOST; if (bios->c_oflag & ONLCR) lios->c_oflag |= LINUX_ONLCR; - if (bios->c_oflag & OXTABS) + if (bios->c_oflag & TAB3) lios->c_oflag |= LINUX_XTABS; lios->c_cflag = bsd_to_linux_speed(bios->c_ispeed, sptab); @@ -537,7 +542,7 @@ linux_to_bsd_termios(struct linux_termios *lios, struct termios *bios) if (lios->c_oflag & LINUX_ONLCR) bios->c_oflag |= ONLCR; if (lios->c_oflag & LINUX_XTABS) - bios->c_oflag |= OXTABS; + bios->c_oflag |= TAB3; bios->c_cflag = (lios->c_cflag & LINUX_CSIZE) << 4; if (lios->c_cflag & LINUX_CSTOPB) diff --git a/sys/compat/linux/linux_stats.c b/sys/compat/linux/linux_stats.c index 7c85fb4..c5f10af 100644 --- a/sys/compat/linux/linux_stats.c +++ b/sys/compat/linux/linux_stats.c @@ -44,6 +44,7 @@ __FBSDID("$FreeBSD$"); #include <sys/stat.h> #include <sys/syscallsubr.h> #include <sys/systm.h> +#include <sys/tty.h> #include <sys/vnode.h> #include <sys/conf.h> #include <sys/fcntl.h> @@ -109,8 +110,17 @@ translate_fd_major_minor(struct thread *td, int fd, struct stat *buf) if (fp->f_vnode != NULL && fp->f_vnode->v_un.vu_cdev != NULL && linux_driver_get_major_minor(fp->f_vnode->v_un.vu_cdev->si_name, - &major, &minor) == 0) + &major, &minor) == 0) { buf->st_rdev = (major << 8 | minor); + } else if (fp->f_type == DTYPE_PTS) { + struct tty *tp = fp->f_data; + + /* Convert the numbers for the slave device. */ + if (linux_driver_get_major_minor(tp->t_dev->si_name, + &major, &minor) == 0) { + buf->st_rdev = (major << 8 | minor); + } + } fdrop(fp, td); } diff --git a/sys/conf/NOTES b/sys/conf/NOTES index aa052ec..a042885 100644 --- a/sys/conf/NOTES +++ b/sys/conf/NOTES @@ -1275,11 +1275,7 @@ options SES_ENABLE_PASSTHROUGH ##################################################################### # MISCELLANEOUS DEVICES AND OPTIONS -# The `pty' device usually turns out to be ``effectively mandatory'', -# as it is required for `telnetd', `rlogind', `screen', `emacs', and -# `xterm', among others. - -device pty #Pseudo ttys +device pty #BSD-style compatibility pseudo ttys device nmdm #back-to-back tty devices device md #Memory/malloc disk device ccd #Concatenated disk driver @@ -1291,9 +1287,6 @@ options LIBICONV # Size of the kernel message buffer. Should be N * pagesize. options MSGBUF_SIZE=40960 -# Maximum size of a tty or pty input buffer. -options TTYHOG=8193 - ##################################################################### # HARDWARE DEVICE CONFIGURATION diff --git a/sys/conf/files b/sys/conf/files index 4e9cfed..7da9135 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -1661,12 +1661,14 @@ kern/sysv_sem.c optional sysvsem kern/sysv_shm.c optional sysvshm kern/tty.c standard kern/tty_compat.c optional compat_43tty -kern/tty_conf.c standard kern/tty_cons.c standard kern/tty_info.c standard -kern/tty_pts.c optional pty +kern/tty_inq.c standard +kern/tty_outq.c standard +kern/tty_pts.c standard kern/tty_pty.c optional pty kern/tty_tty.c standard +kern/tty_ttydisc.c standard kern/uipc_accf.c optional inet kern/uipc_cow.c optional zero_copy_sockets kern/uipc_debug.c optional ddb @@ -2354,4 +2356,4 @@ xen/xenbus/xenbus_xs.c optional xen dev/xen/console/console.c optional xen dev/xen/console/xencons_ring.c optional xen dev/xen/blkfront/blkfront.c optional xen -dev/xen/netfront/netfront.c optional xen
\ No newline at end of file +dev/xen/netfront/netfront.c optional xen diff --git a/sys/conf/options b/sys/conf/options index 63f827b..a8b3723 100644 --- a/sys/conf/options +++ b/sys/conf/options @@ -175,7 +175,6 @@ SYSVSEM opt_sysvipc.h SYSVSHM opt_sysvipc.h SW_WATCHDOG opt_watchdog.h TURNSTILE_PROFILING -TTYHOG opt_tty.h VFS_AIO VERBOSE_SYSINIT opt_global.h WLCACHE opt_wavelan.h @@ -658,6 +657,7 @@ ISAPNP opt_isa.h DEV_BPF opt_bpf.h DEV_MCA opt_mca.h DEV_CARP opt_carp.h +DEV_PTY opt_tty.h DEV_SPLASH opt_splash.h # EISA support diff --git a/sys/dev/dcons/dcons.h b/sys/dev/dcons/dcons.h index 49ef869..e613f5b 100644 --- a/sys/dev/dcons/dcons.h +++ b/sys/dev/dcons/dcons.h @@ -98,7 +98,7 @@ struct dcons_softc { int brk_state; #define DC_GDB 1 int flags; - void *dev; + void *tty; }; int dcons_checkc(struct dcons_softc *); diff --git a/sys/dev/dcons/dcons_os.c b/sys/dev/dcons/dcons_os.c index a43cd28..aad9637 100644 --- a/sys/dev/dcons/dcons_os.c +++ b/sys/dev/dcons/dcons_os.c @@ -1,7 +1,7 @@ /*- * Copyright (C) 2003,2004 * Hidetoshi Shimokawa. All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: @@ -18,7 +18,7 @@ * 4. Neither the name of the author 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 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE @@ -30,15 +30,13 @@ * 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. - * + * * $FreeBSD$ */ #include <sys/param.h> -#if __FreeBSD_version >= 502122 #include <sys/kdb.h> #include <gdb/gdb.h> -#endif #include <sys/kernel.h> #include <sys/module.h> #include <sys/systm.h> @@ -54,13 +52,8 @@ #include <machine/bus.h> -#ifdef __DragonFly__ -#include "dcons.h" -#include "dcons_os.h" -#else #include <dev/dcons/dcons.h> #include <dev/dcons/dcons_os.h> -#endif #include <ddb/ddb.h> #include <sys/reboot.h> @@ -90,53 +83,6 @@ #define DCONS_FORCE_CONSOLE 0 /* Mostly for FreeBSD-4/DragonFly */ #endif -#ifndef DCONS_FORCE_GDB -#define DCONS_FORCE_GDB 1 -#endif - -#if __FreeBSD_version >= 500101 -#define CONS_NODEV 1 -#if __FreeBSD_version < 502122 -static struct consdev gdbconsdev; -#endif -#endif - -static d_open_t dcons_open; -static d_close_t dcons_close; -#if defined(__DragonFly__) || __FreeBSD_version < 500104 -static d_ioctl_t dcons_ioctl; -#endif - -static struct cdevsw dcons_cdevsw = { -#ifdef __DragonFly__ -#define CDEV_MAJOR 184 - "dcons", CDEV_MAJOR, D_TTY, NULL, 0, - dcons_open, dcons_close, ttyread, ttywrite, dcons_ioctl, - ttypoll, nommap, nostrategy, nodump, nopsize, -#elif __FreeBSD_version >= 500104 - .d_version = D_VERSION, - .d_open = dcons_open, - .d_close = dcons_close, - .d_name = "dcons", - .d_flags = D_TTY | D_NEEDGIANT, -#else -#define CDEV_MAJOR 184 - /* open */ dcons_open, - /* close */ dcons_close, - /* read */ ttyread, - /* write */ ttywrite, - /* ioctl */ dcons_ioctl, - /* poll */ ttypoll, - /* mmap */ nommap, - /* strategy */ nostrategy, - /* name */ "dcons", - /* major */ CDEV_MAJOR, - /* dump */ nodump, - /* psize */ nopsize, - /* flags */ D_TTY, -#endif -}; - #ifndef KLD_MODULE static char bssbuf[DCONS_BUF_SIZE]; /* buf in bss */ #endif @@ -156,20 +102,6 @@ static int drv_init = 0; static struct callout dcons_callout; struct dcons_buf *dcons_buf; /* for local dconschat */ -#ifdef __DragonFly__ -#define DEV dev_t -#define THREAD d_thread_t -#elif __FreeBSD_version < 500000 -#define DEV dev_t -#define THREAD struct proc -#else -#define DEV struct cdev * -#define THREAD struct thread -#endif - - -static void dcons_tty_start(struct tty *); -static int dcons_tty_param(struct tty *, struct termios *); static void dcons_timeout(void *); static int dcons_drv_init(int); @@ -181,12 +113,12 @@ static cn_putc_t dcons_cnputc; CONSOLE_DRIVER(dcons); -#if defined(GDB) && (__FreeBSD_version >= 502122) -static gdb_probe_f dcons_dbg_probe; -static gdb_init_f dcons_dbg_init; -static gdb_term_f dcons_dbg_term; -static gdb_getc_f dcons_dbg_getc; -static gdb_putc_f dcons_dbg_putc; +#if defined(GDB) +static gdb_probe_f dcons_dbg_probe; +static gdb_init_f dcons_dbg_init; +static gdb_term_f dcons_dbg_term; +static gdb_getc_f dcons_dbg_getc; +static gdb_putc_f dcons_dbg_putc; GDB_DBGPORT(dcons, dcons_dbg_probe, dcons_dbg_init, dcons_dbg_term, dcons_dbg_getc, dcons_dbg_putc); @@ -194,21 +126,25 @@ GDB_DBGPORT(dcons, dcons_dbg_probe, dcons_dbg_init, dcons_dbg_term, extern struct gdb_dbgport *gdb_cur; #endif +static tsw_outwakeup_t dcons_outwakeup; + +static struct ttydevsw dcons_ttydevsw = { + .tsw_flags = TF_NOPREFIX, + .tsw_outwakeup = dcons_outwakeup, +}; + #if (defined(GDB) || defined(DDB)) && defined(ALT_BREAK_TO_DEBUGGER) static int dcons_check_break(struct dcons_softc *dc, int c) { -#if __FreeBSD_version >= 502122 int kdb_brk; -#endif + if (c < 0) return (c); -#if __FreeBSD_version >= 502122 if ((kdb_brk = kdb_alt_break(c, &dc->brk_state)) != 0) { switch (kdb_brk) { case KDB_REQ_DEBUGGER: - if ((dc->flags & DC_GDB) != 0) { #ifdef GDB if (gdb_cur == &dcons_gdb_dbgport) { @@ -229,27 +165,6 @@ dcons_check_break(struct dcons_softc *dc, int c) break; } } -#else - switch (dc->brk_state) { - case STATE1: - if (c == KEY_TILDE) - dc->brk_state = STATE2; - else - dc->brk_state = STATE0; - break; - case STATE2: - dc->brk_state = STATE0; - if (c == KEY_CTRLB) { -#if DCONS_FORCE_GDB - if (dc->flags & DC_GDB) - boothowto |= RB_GDB; -#endif - breakpoint(); - } - } - if (c == KEY_CR) - dc->brk_state = STATE1; -#endif return (c); } #else @@ -263,7 +178,7 @@ dcons_os_checkc_nopoll(struct dcons_softc *dc) if (dg.dma_tag != NULL) bus_dmamap_sync(dg.dma_tag, dg.dma_map, BUS_DMASYNC_POSTREAD); - + c = dcons_check_break(dc, dcons_checkc(dc)); if (dg.dma_tag != NULL) @@ -279,18 +194,6 @@ dcons_os_checkc(struct dcons_softc *dc) return (dcons_os_checkc_nopoll(dc)); } -#if defined(GDB) || !defined(CONS_NODEV) -static int -dcons_os_getc(struct dcons_softc *dc) -{ - int c; - - while ((c = dcons_os_checkc(dc)) == -1); - - return (c & 0xff); -} -#endif - static void dcons_os_putc(struct dcons_softc *dc, int c) { @@ -302,122 +205,17 @@ dcons_os_putc(struct dcons_softc *dc, int c) if (dg.dma_tag != NULL) bus_dmamap_sync(dg.dma_tag, dg.dma_map, BUS_DMASYNC_PREWRITE); } -static int -dcons_open(DEV dev, int flag, int mode, THREAD *td) -{ - struct tty *tp; - int unit, error, s; - - unit = minor(dev); - if (unit != 0) - return (ENXIO); - - tp = dev->si_tty; - tp->t_oproc = dcons_tty_start; - tp->t_param = dcons_tty_param; - tp->t_stop = nottystop; - tp->t_dev = dev; - - error = 0; - - s = spltty(); - if ((tp->t_state & TS_ISOPEN) == 0) { - tp->t_state |= TS_CARR_ON; - ttyconsolemode(tp, 0); - } else if ((tp->t_state & TS_XCLUDE) && - priv_check(td, PRIV_TTY_EXCLUSIVE)) { - splx(s); - return (EBUSY); - } - splx(s); - -#if __FreeBSD_version < 502113 - error = (*linesw[tp->t_line].l_open)(dev, tp); -#else - error = ttyld_open(tp, dev); -#endif - - return (error); -} - -static int -dcons_close(DEV dev, int flag, int mode, THREAD *td) -{ - int unit; - struct tty *tp; - - unit = minor(dev); - if (unit != 0) - return (ENXIO); - - tp = dev->si_tty; - if (tp->t_state & TS_ISOPEN) { -#if __FreeBSD_version < 502113 - (*linesw[tp->t_line].l_close)(tp, flag); - ttyclose(tp); -#else - ttyld_close(tp, flag); - tty_close(tp); -#endif - } - - return (0); -} - -#if defined(__DragonFly__) || __FreeBSD_version < 500104 -static int -dcons_ioctl(DEV dev, u_long cmd, caddr_t data, int flag, THREAD *td) -{ - int unit; - struct tty *tp; - int error; - - unit = minor(dev); - if (unit != 0) - return (ENXIO); - - tp = dev->si_tty; - error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, td); - if (error != ENOIOCTL) - return (error); - - error = ttioctl(tp, cmd, data, flag); - if (error != ENOIOCTL) - return (error); - - return (ENOTTY); -} -#endif - -static int -dcons_tty_param(struct tty *tp, struct termios *t) -{ - tp->t_ispeed = t->c_ispeed; - tp->t_ospeed = t->c_ospeed; - tp->t_cflag = t->c_cflag; - return 0; -} static void -dcons_tty_start(struct tty *tp) +dcons_outwakeup(struct tty *tp) { struct dcons_softc *dc; - int s; - - dc = (struct dcons_softc *)tp->t_dev->si_drv1; - s = spltty(); - if (tp->t_state & (TS_TIMEOUT | TS_TTSTOP)) { - ttwwakeup(tp); - return; - } + char ch; - tp->t_state |= TS_BUSY; - while (tp->t_outq.c_cc != 0) - dcons_os_putc(dc, getc(&tp->t_outq)); - tp->t_state &= ~TS_BUSY; + dc = tty_softc(tp); - ttwwakeup(tp); - splx(s); + while (ttydisc_getc(tp, &ch, sizeof ch) != 0) + dcons_os_putc(dc, ch); } static void @@ -429,14 +227,13 @@ dcons_timeout(void *v) for (i = 0; i < DCONS_NPORT; i ++) { dc = &sc[i]; - tp = ((DEV)dc->dev)->si_tty; + tp = dc->tty; + + tty_lock(tp); while ((c = dcons_os_checkc_nopoll(dc)) != -1) - if (tp->t_state & TS_ISOPEN) -#if __FreeBSD_version < 502113 - (*linesw[tp->t_line].l_rint)(c, tp); -#else - ttyld_rint(tp, c); -#endif + ttydisc_rint(tp, c, 0); + ttydisc_rint_done(tp); + tty_unlock(tp); } polltime = hz / poll_hz; if (polltime < 1) @@ -447,14 +244,7 @@ dcons_timeout(void *v) static void dcons_cnprobe(struct consdev *cp) { -#ifdef __DragonFly__ - cp->cn_dev = make_dev(&dcons_cdevsw, DCONS_CON, - UID_ROOT, GID_WHEEL, 0600, "dcons"); -#elif __FreeBSD_version >= 501109 sprintf(cp->cn_name, "dcons"); -#else - cp->cn_dev = makedev(CDEV_MAJOR, DCONS_CON); -#endif #if DCONS_FORCE_CONSOLE cp->cn_pri = CN_REMOTE; #else @@ -466,12 +256,7 @@ static void dcons_cninit(struct consdev *cp) { dcons_drv_init(0); -#if CONS_NODEV - cp->cn_arg -#else - cp->cn_dev->si_drv1 -#endif - = (void *)&sc[DCONS_CON]; /* share port0 with unit0 */ + cp->cn_arg = (void *)&sc[DCONS_CON]; /* share port0 with unit0 */ } static void @@ -479,39 +264,19 @@ dcons_cnterm(struct consdev *cp) { } -#if CONS_NODEV static int dcons_cngetc(struct consdev *cp) { struct dcons_softc *dc = (struct dcons_softc *)cp->cn_arg; return (dcons_os_checkc(dc)); } + static void dcons_cnputc(struct consdev *cp, int c) { struct dcons_softc *dc = (struct dcons_softc *)cp->cn_arg; dcons_os_putc(dc, c); } -#else -static int -dcons_cngetc(DEV dev) -{ - struct dcons_softc *dc = (struct dcons_softc *)dev->si_drv1; - return (dcons_os_getc(dc)); -} -static int -dcons_cncheckc(DEV dev) -{ - struct dcons_softc *dc = (struct dcons_softc *)dev->si_drv1; - return (dcons_os_checkc(dc)); -} -static void -dcons_cnputc(DEV dev, int c) -{ - struct dcons_softc *dc = (struct dcons_softc *)dev->si_drv1; - dcons_os_putc(dc, c); -} -#endif static int dcons_drv_init(int stage) @@ -577,24 +342,6 @@ dcons_drv_init(int stage) ok: dcons_buf = dg.buf; -#if __FreeBSD_version < 502122 -#if defined(DDB) && DCONS_FORCE_GDB -#if CONS_NODEV - gdbconsdev.cn_arg = (void *)&sc[DCONS_GDB]; -#if __FreeBSD_version >= 501109 - sprintf(gdbconsdev.cn_name, "dgdb"); -#endif - gdb_arg = &gdbconsdev; -#elif defined(__DragonFly__) - gdbdev = make_dev(&dcons_cdevsw, DCONS_GDB, - UID_ROOT, GID_WHEEL, 0600, "dgdb"); -#else - gdbdev = makedev(CDEV_MAJOR, DCONS_GDB); -#endif - gdb_getc = dcons_cngetc; - gdb_putc = dcons_cnputc; -#endif -#endif drv_init = 1; return 0; @@ -606,23 +353,12 @@ dcons_attach_port(int port, char *name, int flags) { struct dcons_softc *dc; struct tty *tp; - DEV dev; dc = &sc[port]; + tp = tty_alloc(&dcons_ttydevsw, dc, NULL); dc->flags = flags; - dev = make_dev(&dcons_cdevsw, port, - UID_ROOT, GID_WHEEL, 0600, name); - dc->dev = (void *)dev; - tp = ttyalloc(); - - dev->si_drv1 = (void *)dc; - dev->si_tty = tp; - - tp->t_oproc = dcons_tty_start; - tp->t_param = dcons_tty_param; - tp->t_stop = nottystop; - tp->t_dev = dc->dev; - + dc->tty = tp; + tty_makedev(tp, NULL, "%s", name); return(0); } @@ -631,16 +367,9 @@ dcons_attach(void) { int polltime; -#ifdef __DragonFly__ - cdevsw_add(&dcons_cdevsw, -1, 0); -#endif dcons_attach_port(DCONS_CON, "dcons", 0); dcons_attach_port(DCONS_GDB, "dgdb", DC_GDB); -#if __FreeBSD_version < 500000 - callout_init(&dcons_callout); -#else callout_init(&dcons_callout, 0); -#endif polltime = hz / poll_hz; if (polltime < 1) polltime = 1; @@ -655,37 +384,14 @@ dcons_detach(int port) struct dcons_softc *dc; dc = &sc[port]; + tp = dc->tty; - tp = ((DEV)dc->dev)->si_tty; - - if (tp->t_state & TS_ISOPEN) { - printf("dcons: still opened\n"); -#if __FreeBSD_version < 502113 - (*linesw[tp->t_line].l_close)(tp, 0); - tp->t_gen++; - ttyclose(tp); - ttwakeup(tp); - ttwwakeup(tp); -#else - ttyld_close(tp, 0); - tty_close(tp); -#endif - } - /* XXX - * must wait until all device are closed. - */ -#ifdef __DragonFly__ - tsleep((void *)dc, 0, "dcodtc", hz/4); -#else - tsleep((void *)dc, PWAIT, "dcodtc", hz/4); -#endif - destroy_dev(dc->dev); + tty_lock(tp); + tty_rel_gone(tp); return(0); } - -/* cnXXX works only for FreeBSD-5 */ static int dcons_modevent(module_t mode, int type, void *data) { @@ -695,29 +401,16 @@ dcons_modevent(module_t mode, int type, void *data) case MOD_LOAD: ret = dcons_drv_init(1); dcons_attach(); -#if __FreeBSD_version >= 500000 if (ret == 0) { dcons_cnprobe(&dcons_consdev); dcons_cninit(&dcons_consdev); cnadd(&dcons_consdev); } -#endif break; case MOD_UNLOAD: printf("dcons: unload\n"); callout_stop(&dcons_callout); -#if __FreeBSD_version < 502122 -#if defined(DDB) && DCONS_FORCE_GDB -#if CONS_NODEV - gdb_arg = NULL; -#else - gdbdev = NULL; -#endif -#endif -#endif -#if __FreeBSD_version >= 500000 cnremove(&dcons_consdev); -#endif dcons_detach(DCONS_CON); dcons_detach(DCONS_GDB); dg.buf->magic = 0; @@ -737,10 +430,20 @@ dcons_modevent(module_t mode, int type, void *data) return(err); } -#if defined(GDB) && (__FreeBSD_version >= 502122) +#if defined(GDB) /* Debugger interface */ static int +dcons_os_getc(struct dcons_softc *dc) +{ + int c; + + while ((c = dcons_os_checkc(dc)) == -1); + + return (c & 0xff); +} + +static int dcons_dbg_probe(void) { int dcons_gdb; diff --git a/sys/dev/nmdm/nmdm.c b/sys/dev/nmdm/nmdm.c index 8536c18..706d416 100644 --- a/sys/dev/nmdm/nmdm.c +++ b/sys/dev/nmdm/nmdm.c @@ -36,9 +36,6 @@ __FBSDID("$FreeBSD$"); * Mighty handy for use with serial console in Vmware */ -#include "opt_compat.h" -#include "opt_tty.h" - #include <sys/param.h> #include <sys/systm.h> #include <sys/priv.h> @@ -48,252 +45,176 @@ __FBSDID("$FreeBSD$"); #include <sys/fcntl.h> #include <sys/poll.h> #include <sys/kernel.h> +#include <sys/limits.h> #include <sys/module.h> #include <sys/serial.h> #include <sys/signalvar.h> #include <sys/malloc.h> #include <sys/taskqueue.h> -MALLOC_DEFINE(M_NLMDM, "nullmodem", "nullmodem data structures"); - -static d_close_t nmdmclose; -static t_modem_t nmdmmodem; -static d_open_t nmdmopen; -static t_oproc_t nmdmoproc; -static t_param_t nmdmparam; -static t_stop_t nmdmstop; - -static struct cdevsw nmdm_cdevsw = { - .d_version = D_VERSION, - .d_open = nmdmopen, - .d_close = nmdmclose, - .d_name = "nmdn", - .d_flags = D_TTY | D_PSEUDO | D_NEEDGIANT | D_NEEDMINOR, +MALLOC_DEFINE(M_NMDM, "nullmodem", "nullmodem data structures"); + +static tsw_inwakeup_t nmdm_outwakeup; +static tsw_outwakeup_t nmdm_inwakeup; +static tsw_param_t nmdm_param; +static tsw_modem_t nmdm_modem; + +static struct ttydevsw nmdm_class = { + .tsw_flags = TF_NOPREFIX, + .tsw_inwakeup = nmdm_inwakeup, + .tsw_outwakeup = nmdm_outwakeup, + .tsw_param = nmdm_param, + .tsw_modem = nmdm_modem, }; -#define BUFSIZ 100 /* Chunk size iomoved to/from user */ -#define NMDM_MAX_NUM 128 /* Artificially limit # devices. */ -#define PF_STOPPED 0x10 /* user told stopped */ -#define BFLAG CLONE_FLAG0 - -struct softpart { - struct tty *nm_tty; - struct cdev *dev; - int nm_dcd; - struct task pt_task; - struct softpart *other; - struct callout co; - u_long quota; - u_long accumulator; - int rate; - int credits; +static void nmdm_task_tty(void *, int); + +struct nmdmsoftc; + +struct nmdmpart { + struct tty *np_tty; + int np_dcd; + struct task np_task; + struct nmdmpart *np_other; + struct nmdmsoftc *np_pair; + struct callout np_callout; + u_long np_quota; + u_long np_accumulator; + int np_rate; + int np_credits; #define QS 8 /* Quota shift */ }; -struct nm_softc { - TAILQ_ENTRY(nm_softc) pt_list; - int pt_flags; - struct softpart part1, part2; - struct prison *pt_prison; +struct nmdmsoftc { + struct nmdmpart ns_part1; + struct nmdmpart ns_part2; + struct mtx ns_mtx; }; -static struct clonedevs *nmdmclones; -static TAILQ_HEAD(,nm_softc) nmdmhead = TAILQ_HEAD_INITIALIZER(nmdmhead); +static int nmdm_count = 0; + +static struct nmdmsoftc * +nmdm_alloc(unsigned long unit) +{ + struct nmdmsoftc *ns; + struct tty *tp; + + atomic_add_acq_int(&nmdm_count, 1); + + ns = malloc(sizeof(*ns), M_NMDM, M_WAITOK|M_ZERO); + mtx_init(&ns->ns_mtx, "nmdm", NULL, MTX_DEF); + + /* Hook the pairs together. */ + ns->ns_part1.np_pair = ns; + ns->ns_part1.np_other = &ns->ns_part2; + TASK_INIT(&ns->ns_part1.np_task, 0, nmdm_task_tty, &ns->ns_part1); + callout_init(&ns->ns_part1.np_callout, 0); + + ns->ns_part2.np_pair = ns; + ns->ns_part2.np_other = &ns->ns_part1; + TASK_INIT(&ns->ns_part2.np_task, 0, nmdm_task_tty, &ns->ns_part2); + callout_init(&ns->ns_part2.np_callout, 0); + + /* Create device nodes. */ + tp = ns->ns_part1.np_tty = tty_alloc(&nmdm_class, &ns->ns_part1, + &ns->ns_mtx); + tty_makedev(tp, NULL, "nmdm%luA", unit); + + tp = ns->ns_part2.np_tty = tty_alloc(&nmdm_class, &ns->ns_part2, + &ns->ns_mtx); + tty_makedev(tp, NULL, "nmdm%luB", unit); + + return (ns); +} static void nmdm_clone(void *arg, struct ucred *cred, char *name, int nameen, struct cdev **dev) { - int i, unit; - char *p; - struct cdev *d1, *d2; + unsigned long unit; + char *end; + struct nmdmsoftc *ns; if (*dev != NULL) return; - if (strcmp(name, "nmdm") == 0) { - p = NULL; - unit = -1; - } else { - i = dev_stdclone(name, &p, "nmdm", &unit); - if (i == 0) - return; - if (p[0] != '\0' && p[0] != 'A' && p[0] != 'B') - return; - else if (p[0] != '\0' && p[1] != '\0') - return; - } - i = clone_create(&nmdmclones, &nmdm_cdevsw, &unit, &d1, 0); - if (i) { - d1 = make_dev(&nmdm_cdevsw, unit2minor(unit), - 0, 0, 0666, "nmdm%dA", unit); - if (d1 == NULL) - return; - d2 = make_dev(&nmdm_cdevsw, unit2minor(unit) | BFLAG, - 0, 0, 0666, "nmdm%dB", unit); - if (d2 == NULL) { - destroy_dev(d1); - return; - } - d2->si_drv2 = d1; - d1->si_drv2 = d2; - dev_depends(d1, d2); - dev_depends(d2, d1); - d1->si_flags |= SI_CHEAPCLONE; - d2->si_flags |= SI_CHEAPCLONE; - } - if (p != NULL && p[0] == 'B') - *dev = d1->si_drv2; + if (strncmp(name, "nmdm", 4) != 0) + return; + + /* Device name must be "nmdm%lu%c", where %c is 'A' or 'B'. */ + name += 4; + unit = strtoul(name, &end, 10); + if (unit == ULONG_MAX || name == end) + return; + if ((end[0] != 'A' && end[0] != 'B') || end[1] != '\0') + return; + + /* XXX: pass privileges? */ + ns = nmdm_alloc(unit); + + if (end[0] == 'A') + *dev = ns->ns_part1.np_tty->t_dev; else - *dev = d1; - dev_ref(*dev); + *dev = ns->ns_part2.np_tty->t_dev; } static void nmdm_timeout(void *arg) { - struct softpart *sp; - - sp = arg; + struct nmdmpart *np = arg; - if (sp->rate == 0) + if (np->np_rate == 0) return; /* * Do a simple Floyd-Steinberg dither here to avoid FP math. * Wipe out unused quota from last tick. */ - sp->accumulator += sp->credits; - sp->quota = sp->accumulator >> QS; - sp->accumulator &= ((1 << QS) - 1); + np->np_accumulator += np->np_credits; + np->np_quota = np->np_accumulator >> QS; + np->np_accumulator &= ((1 << QS) - 1); - taskqueue_enqueue(taskqueue_swi_giant, &sp->pt_task); - callout_reset(&sp->co, sp->rate, nmdm_timeout, arg); + taskqueue_enqueue(taskqueue_swi, &np->np_task); + callout_reset(&np->np_callout, np->np_rate, nmdm_timeout, np); } static void nmdm_task_tty(void *arg, int pending __unused) { struct tty *tp, *otp; - struct softpart *sp; - int c; + struct nmdmpart *np = arg; + char c; - tp = arg; - sp = tp->t_sc; - otp = sp->other->nm_tty; + tp = np->np_tty; + tty_lock(tp); + + otp = np->np_other->np_tty; KASSERT(otp != NULL, ("NULL otp in nmdmstart")); KASSERT(otp != tp, ("NULL otp == tp nmdmstart")); - if (sp->other->nm_dcd) { - if (!(tp->t_state & TS_ISOPEN)) { - sp->other->nm_dcd = 0; - (void)ttyld_modem(otp, 0); + if (np->np_other->np_dcd) { + if (!tty_opened(tp)) { + np->np_other->np_dcd = 0; + ttydisc_modem(otp, 0); } } else { - if (tp->t_state & TS_ISOPEN) { - sp->other->nm_dcd = 1; - (void)ttyld_modem(otp, 1); + if (tty_opened(tp)) { + np->np_other->np_dcd = 1; + ttydisc_modem(otp, 1); } } - if (tp->t_state & TS_TTSTOP) - return; - while (tp->t_outq.c_cc != 0) { - if (sp->rate && !sp->quota) - return; - if (otp->t_state & TS_TBLOCK) - return; - sp->quota--; - c = getc(&tp->t_outq); - if (otp->t_state & TS_ISOPEN) - ttyld_rint(otp, c); - } - if (tp->t_outq.c_cc == 0) - ttwwakeup(tp); -} - -/* - * This function creates and initializes a pair of ttys. - */ -static void -nmdminit(struct cdev *dev1) -{ - struct cdev *dev2; - struct nm_softc *pt; - - dev2 = dev1->si_drv2; - - dev1->si_flags &= ~SI_CHEAPCLONE; - dev2->si_flags &= ~SI_CHEAPCLONE; - - pt = malloc(sizeof(*pt), M_NLMDM, M_WAITOK | M_ZERO); - TAILQ_INSERT_TAIL(&nmdmhead, pt, pt_list); - - dev1->si_drv1 = dev2->si_drv1 = pt; - - pt->part1.dev = dev1; - pt->part2.dev = dev2; - - pt->part1.nm_tty = ttyalloc(); - pt->part1.nm_tty->t_oproc = nmdmoproc; - pt->part1.nm_tty->t_stop = nmdmstop; - pt->part1.nm_tty->t_modem = nmdmmodem; - pt->part1.nm_tty->t_param = nmdmparam; - pt->part1.nm_tty->t_dev = dev1; - pt->part1.nm_tty->t_sc = &pt->part1; - TASK_INIT(&pt->part1.pt_task, 0, nmdm_task_tty, pt->part1.nm_tty); - callout_init(&pt->part1.co, 0); - - pt->part2.nm_tty = ttyalloc(); - pt->part2.nm_tty->t_oproc = nmdmoproc; - pt->part2.nm_tty->t_stop = nmdmstop; - pt->part2.nm_tty->t_modem = nmdmmodem; - pt->part2.nm_tty->t_param = nmdmparam; - pt->part2.nm_tty->t_dev = dev2; - pt->part2.nm_tty->t_sc = &pt->part2; - TASK_INIT(&pt->part2.pt_task, 0, nmdm_task_tty, pt->part2.nm_tty); - callout_init(&pt->part2.co, 0); - - pt->part1.other = &pt->part2; - pt->part2.other = &pt->part1; - - dev1->si_tty = pt->part1.nm_tty; - dev1->si_drv1 = pt; - - dev2->si_tty = pt->part2.nm_tty; - dev2->si_drv1 = pt; -} - -/* - * Device opened from userland - */ -static int -nmdmopen(struct cdev *dev, int flag, int devtype, struct thread *td) -{ - struct tty *tp, *tp2; - int error; - struct nm_softc *pti; - struct softpart *sp; - - if (dev->si_drv1 == NULL) - nmdminit(dev); - pti = dev->si_drv1; - if (pti->pt_prison != td->td_ucred->cr_prison) - return (EBUSY); - - tp = dev->si_tty; - sp = tp->t_sc; - tp2 = sp->other->nm_tty; - - if ((tp->t_state & TS_ISOPEN) == 0) { - ttyinitmode(tp, 0, 0); - ttsetwater(tp); /* XXX ? */ - } else if (tp->t_state & TS_XCLUDE && - priv_check(td, PRIV_TTY_EXCLUSIVE)) { - return (EBUSY); + while (ttydisc_rint_poll(otp) > 0) { + if (np->np_rate && !np->np_quota) + break; + if (ttydisc_getc(tp, &c, 1) != 1) + break; + np->np_quota--; + ttydisc_rint(otp, c, 0); } - error = ttyld_open(tp, dev); - return (error); + ttydisc_rint_done(otp); + + tty_unlock(tp); } static int @@ -317,18 +238,17 @@ bits_per_char(struct termios *t) } static int -nmdmparam(struct tty *tp, struct termios *t) +nmdm_param(struct tty *tp, struct termios *t) { - struct softpart *sp; + struct nmdmpart *np = tty_softc(tp); struct tty *tp2; int bpc, rate, speed, i; - sp = tp->t_sc; - tp2 = sp->other->nm_tty; + tp2 = np->np_other->np_tty; - if (!((t->c_cflag | tp2->t_cflag) & CDSR_OFLOW)) { - sp->rate = 0; - sp->other->rate = 0; + if (!((t->c_cflag | tp2->t_termios.c_cflag) & CDSR_OFLOW)) { + np->np_rate = 0; + np->np_other->np_rate = 0; return (0); } @@ -343,10 +263,10 @@ nmdmparam(struct tty *tp, struct termios *t) for (i = 0; i < 2; i++) { /* Use the slower of our receive and their transmit rate */ - speed = imin(tp2->t_ospeed, t->c_ispeed); + speed = imin(tp2->t_termios.c_ospeed, t->c_ispeed); if (speed == 0) { - sp->rate = 0; - sp->other->rate = 0; + np->np_rate = 0; + np->np_other->np_rate = 0; return (0); } @@ -359,73 +279,63 @@ nmdmparam(struct tty *tp, struct termios *t) speed *= rate; speed /= hz; /* [(char/sec)/tick, scaled */ - sp->credits = speed; - sp->rate = rate; - callout_reset(&sp->co, rate, nmdm_timeout, sp); + np->np_credits = speed; + np->np_rate = rate; + callout_reset(&np->np_callout, rate, nmdm_timeout, np); /* * swap pointers for second pass so the other end gets * updated as well. */ - sp = sp->other; + np = np->np_other; t = &tp2->t_termios; tp2 = tp; } + return (0); } static int -nmdmmodem(struct tty *tp, int sigon, int sigoff) +nmdm_modem(struct tty *tp, int sigon, int sigoff) { - struct softpart *sp; - int i; + struct nmdmpart *np = tty_softc(tp); + int i = 0; - sp = tp->t_sc; if (sigon || sigoff) { if (sigon & SER_DTR) - sp->other->nm_dcd = 1; + np->np_other->np_dcd = 1; if (sigoff & SER_DTR) - sp->other->nm_dcd = 0; - ttyld_modem(sp->other->nm_tty, sp->other->nm_dcd); + np->np_other->np_dcd = 0; + + ttydisc_modem(np->np_other->np_tty, np->np_other->np_dcd); + return (0); } else { - i = 0; - if (sp->nm_dcd) + if (np->np_dcd) i |= SER_DCD; - if (sp->other->nm_dcd) + if (np->np_other->np_dcd) i |= SER_DTR; + return (i); } } -static int -nmdmclose(struct cdev *dev, int flag, int mode, struct thread *td) -{ - struct tty *tp = dev->si_tty; - int error; - - error = ttyld_close(tp, flag); - (void) tty_close(dev->si_tty); - - return (error); -} - static void -nmdmoproc(struct tty *tp) +nmdm_inwakeup(struct tty *tp) { - struct softpart *pt; + struct nmdmpart *np = tty_softc(tp); - pt = tp->t_sc; - taskqueue_enqueue(taskqueue_swi_giant, &pt->pt_task); + /* We can receive again, so wake up the other side. */ + taskqueue_enqueue(taskqueue_swi, &np->np_other->np_task); } static void -nmdmstop(struct tty *tp, int flush) +nmdm_outwakeup(struct tty *tp) { - struct softpart *pt; + struct nmdmpart *np = tty_softc(tp); - pt = tp->t_sc; - taskqueue_enqueue(taskqueue_swi_giant, &pt->pt_task); + /* We can transmit again, so wake up our side. */ + taskqueue_enqueue(taskqueue_swi, &np->np_task); } /* @@ -435,32 +345,28 @@ static int nmdm_modevent(module_t mod, int type, void *data) { static eventhandler_tag tag; - struct nm_softc *pt, *tpt; - int error = 0; switch(type) { case MOD_LOAD: - clone_setup(&nmdmclones); tag = EVENTHANDLER_REGISTER(dev_clone, nmdm_clone, 0, 1000); if (tag == NULL) return (ENOMEM); break; case MOD_SHUTDOWN: - /* FALLTHROUGH */ + break; + case MOD_UNLOAD: + if (nmdm_count != 0) + return (EBUSY); EVENTHANDLER_DEREGISTER(dev_clone, tag); - TAILQ_FOREACH_SAFE(pt, &nmdmhead, pt_list, tpt) { - destroy_dev(pt->part1.dev); - TAILQ_REMOVE(&nmdmhead, pt, pt_list); - free(pt, M_NLMDM); - } - clone_cleanup(&nmdmclones); break; + default: - error = EOPNOTSUPP; + return (EOPNOTSUPP); } - return (error); + + return (0); } DEV_MODULE(nmdm, nmdm_modevent, NULL); diff --git a/sys/dev/ofw/ofw_console.c b/sys/dev/ofw/ofw_console.c index 156f121..bec52d6 100644 --- a/sys/dev/ofw/ofw_console.c +++ b/sys/dev/ofw/ofw_console.c @@ -49,15 +49,15 @@ __FBSDID("$FreeBSD$"); #endif #define OFBURSTLEN 128 /* max number of bytes to write in one chunk */ -static d_open_t ofw_dev_open; -static d_close_t ofw_dev_close; - -static struct cdevsw ofw_cdevsw = { - .d_version = D_VERSION, - .d_open = ofw_dev_open, - .d_close = ofw_dev_close, - .d_name = "ofw", - .d_flags = D_TTY | D_NEEDGIANT, +static tsw_open_t ofwtty_open; +static tsw_close_t ofwtty_close; +static tsw_outwakeup_t ofwtty_outwakeup; + +static struct ttydevsw ofw_ttydevsw = { + .tsw_flags = TF_NOPREFIX, + .tsw_open = ofwtty_open, + .tsw_close = ofwtty_close, + .tsw_outwakeup = ofwtty_outwakeup, }; static struct tty *ofw_tp = NULL; @@ -69,9 +69,6 @@ static struct callout_handle ofw_timeouthandle static int alt_break_state; #endif -static void ofw_tty_start(struct tty *); -static int ofw_tty_param(struct tty *, struct termios *); -static void ofw_tty_stop(struct tty *, int); static void ofw_timeout(void *); static cn_probe_t ofw_cnprobe; @@ -87,7 +84,7 @@ cn_drvinit(void *unused) { phandle_t options; char output[32]; - struct cdev *dev; + struct tty *tp; if (ofw_consdev.cn_pri != CN_DEAD && ofw_consdev.cn_name[0] != '\0') { @@ -99,9 +96,9 @@ cn_drvinit(void *unused) * XXX: This is a hack and it may result in two /dev/ttya * XXX: devices on platforms where the sab driver works. */ - dev = make_dev(&ofw_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600, "%s", - output); - make_dev_alias(dev, "ofwcons"); + tp = tty_alloc(&ofw_ttydevsw, NULL, NULL); + tty_makedev(tp, NULL, "%s", output); + tty_makealias(tp, "ofwcons"); } } @@ -111,112 +108,36 @@ static int stdin; static int stdout; static int -ofw_dev_open(struct cdev *dev, int flag, int mode, struct thread *td) +ofwtty_open(struct tty *tp) { - struct tty *tp; - int unit; - int error, setuptimeout; - - error = 0; - setuptimeout = 0; - unit = minor(dev); - - /* - * XXX: BAD, should happen at attach time - */ - if (dev->si_tty == NULL) { - ofw_tp = ttyalloc(); - dev->si_tty = ofw_tp; - ofw_tp->t_dev = dev; - } - tp = dev->si_tty; - - tp->t_oproc = ofw_tty_start; - tp->t_param = ofw_tty_param; - tp->t_stop = ofw_tty_stop; - tp->t_dev = dev; - - if ((tp->t_state & TS_ISOPEN) == 0) { - tp->t_state |= TS_CARR_ON; - ttyconsolemode(tp, 0); - - setuptimeout = 1; - } else if ((tp->t_state & TS_XCLUDE) && - priv_check(td, PRIV_TTY_EXCLUSIVE)) { - return (EBUSY); - } - - error = ttyld_open(tp, dev); + polltime = hz / OFWCONS_POLL_HZ; + if (polltime < 1) + polltime = 1; - if (error == 0 && setuptimeout) { - polltime = hz / OFWCONS_POLL_HZ; - if (polltime < 1) { - polltime = 1; - } - - ofw_timeouthandle = timeout(ofw_timeout, tp, polltime); - } + ofw_timeouthandle = timeout(ofw_timeout, tp, polltime); - return (error); + return (0); } -static int -ofw_dev_close(struct cdev *dev, int flag, int mode, struct thread *td) +static void +ofwtty_close(struct tty *tp) { - int unit; - struct tty *tp; - - unit = minor(dev); - tp = dev->si_tty; - - if (unit != 0) { - return (ENXIO); - } /* XXX Should be replaced with callout_stop(9) */ untimeout(ofw_timeout, tp, ofw_timeouthandle); - ttyld_close(tp, flag); - tty_close(tp); - - return (0); -} - - -static int -ofw_tty_param(struct tty *tp, struct termios *t) -{ - - return (0); } static void -ofw_tty_start(struct tty *tp) +ofwtty_outwakeup(struct tty *tp) { - struct clist *cl; int len; u_char buf[OFBURSTLEN]; - - if (tp->t_state & (TS_TIMEOUT | TS_BUSY | TS_TTSTOP)) - return; - - tp->t_state |= TS_BUSY; - cl = &tp->t_outq; - len = q_to_b(cl, buf, OFBURSTLEN); - OF_write(stdout, buf, len); - tp->t_state &= ~TS_BUSY; - - ttwwakeup(tp); -} - -static void -ofw_tty_stop(struct tty *tp, int flag) -{ - - if (tp->t_state & TS_BUSY) { - if ((tp->t_state & TS_TTSTOP) == 0) { - tp->t_state |= TS_FLUSH; - } + for (;;) { + len = ttydisc_getc(tp, buf, sizeof buf); + if (len == 0) + break; + OF_write(stdout, buf, len); } } @@ -228,11 +149,11 @@ ofw_timeout(void *v) tp = (struct tty *)v; - while ((c = ofw_cngetc(NULL)) != -1) { - if (tp->t_state & TS_ISOPEN) { - ttyld_rint(tp, c); - } - } + tty_lock(tp); + while ((c = ofw_cngetc(NULL)) != -1) + ttydisc_rint(tp, c, 0); + ttydisc_rint_done(tp); + tty_unlock(tp); ofw_timeouthandle = timeout(ofw_timeout, tp, polltime); } diff --git a/sys/dev/syscons/schistory.c b/sys/dev/syscons/schistory.c index 860e8f9..860367b 100644 --- a/sys/dev/syscons/schistory.c +++ b/sys/dev/syscons/schistory.c @@ -291,8 +291,7 @@ sc_hist_down_line(scr_stat *scp) } int -sc_hist_ioctl(struct tty *tp, u_long cmd, caddr_t data, int flag, - struct thread *td) +sc_hist_ioctl(struct tty *tp, u_long cmd, caddr_t data, struct thread *td) { scr_stat *scp; int error; @@ -300,7 +299,7 @@ sc_hist_ioctl(struct tty *tp, u_long cmd, caddr_t data, int flag, switch (cmd) { case CONS_HISTORY: /* set history size */ - scp = SC_STAT(tp->t_dev); + scp = SC_STAT(tp); if (*(int *)data <= 0) return EINVAL; if (scp->status & BUFFER_SAVED) @@ -315,7 +314,7 @@ sc_hist_ioctl(struct tty *tp, u_long cmd, caddr_t data, int flag, return error; case CONS_CLRHIST: - scp = SC_STAT(tp->t_dev); + scp = SC_STAT(tp); sc_vtb_clear(scp->history, scp->sc->scr_map[0x20], SC_NORM_ATTR << 8); return 0; diff --git a/sys/dev/syscons/scmouse.c b/sys/dev/syscons/scmouse.c index 25a2da0..2d998d4 100644 --- a/sys/dev/syscons/scmouse.c +++ b/sys/dev/syscons/scmouse.c @@ -605,8 +605,7 @@ sc_mouse_paste(scr_stat *scp) #endif /* SC_NO_CUTPASTE */ int -sc_mouse_ioctl(struct tty *tp, u_long cmd, caddr_t data, int flag, - struct thread *td) +sc_mouse_ioctl(struct tty *tp, u_long cmd, caddr_t data, struct thread *td) { mouse_info_t *mouse; mouse_info_t buf; @@ -616,7 +615,7 @@ sc_mouse_ioctl(struct tty *tp, u_long cmd, caddr_t data, int flag, int s; int f; - scp = SC_STAT(tp->t_dev); + scp = SC_STAT(tp); switch (cmd) { diff --git a/sys/dev/syscons/scterm-sc.c b/sys/dev/syscons/scterm-sc.c index e89debb..b52bea8 100644 --- a/sys/dev/syscons/scterm-sc.c +++ b/sys/dev/syscons/scterm-sc.c @@ -705,7 +705,7 @@ outloop: static int scterm_ioctl(scr_stat *scp, struct tty *tp, u_long cmd, caddr_t data, - int flag, struct thread *td) + struct thread *td) { term_stat *tcp = scp->ts; vid_info_t *vi; diff --git a/sys/dev/syscons/scvesactl.c b/sys/dev/syscons/scvesactl.c index c68d46a..9a2c253 100644 --- a/sys/dev/syscons/scvesactl.c +++ b/sys/dev/syscons/scvesactl.c @@ -48,19 +48,15 @@ __FBSDID("$FreeBSD$"); #include <dev/fb/fbreg.h> #include <dev/syscons/syscons.h> -static d_ioctl_t *prev_user_ioctl; +static tsw_ioctl_t *prev_user_ioctl; static int -vesa_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td) +vesa_ioctl(struct tty *tp, u_long cmd, caddr_t data, struct thread *td) { scr_stat *scp; - struct tty *tp; int mode; - tp = dev->si_tty; - if (!tp) - return ENXIO; - scp = SC_STAT(tp->t_dev); + scp = SC_STAT(tp); switch (cmd) { @@ -123,7 +119,7 @@ vesa_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread * } if (prev_user_ioctl) - return (*prev_user_ioctl)(dev, cmd, data, flag, td); + return (*prev_user_ioctl)(tp, cmd, data, td); else return ENOIOCTL; } diff --git a/sys/dev/syscons/scvidctl.c b/sys/dev/syscons/scvidctl.c index 5e1d910..045f79f 100644 --- a/sys/dev/syscons/scvidctl.c +++ b/sys/dev/syscons/scvidctl.c @@ -241,11 +241,8 @@ sc_set_text_mode(scr_stat *scp, struct tty *tp, int mode, int xsize, int ysize, || tp->t_winsize.ws_row != scp->ysize) { tp->t_winsize.ws_col = scp->xsize; tp->t_winsize.ws_row = scp->ysize; - if (tp->t_pgrp != NULL) { - PGRP_LOCK(tp->t_pgrp); - pgsignal(tp->t_pgrp, SIGWINCH, 1); - PGRP_UNLOCK(tp->t_pgrp); - } + + tty_signal_pgrp(tp, SIGWINCH); } return 0; @@ -308,11 +305,8 @@ sc_set_graphics_mode(scr_stat *scp, struct tty *tp, int mode) || tp->t_winsize.ws_ypixel != scp->ypixel) { tp->t_winsize.ws_xpixel = scp->xpixel; tp->t_winsize.ws_ypixel = scp->ypixel; - if (tp->t_pgrp != NULL) { - PGRP_LOCK(tp->t_pgrp); - pgsignal(tp->t_pgrp, SIGWINCH, 1); - PGRP_UNLOCK(tp->t_pgrp); - } + + tty_signal_pgrp(tp, SIGWINCH); } return 0; @@ -475,7 +469,7 @@ sc_set_pixel_mode(scr_stat *scp, struct tty *tp, int xsize, int ysize, vidd_ioctl((a), (c), (caddr_t)(d))) int -sc_vid_ioctl(struct tty *tp, u_long cmd, caddr_t data, int flag, struct thread *td) +sc_vid_ioctl(struct tty *tp, u_long cmd, caddr_t data, struct thread *td) { scr_stat *scp; video_adapter_t *adp; @@ -488,7 +482,7 @@ sc_vid_ioctl(struct tty *tp, u_long cmd, caddr_t data, int flag, struct thread * int ival; #endif - scp = SC_STAT(tp->t_dev); + scp = SC_STAT(tp); if (scp == NULL) /* tp == SC_MOUSE */ return ENOIOCTL; adp = scp->sc->adp; diff --git a/sys/dev/syscons/syscons.c b/sys/dev/syscons/syscons.c index 96ca0b7..ed5e459 100644 --- a/sys/dev/syscons/syscons.c +++ b/sys/dev/syscons/syscons.c @@ -54,6 +54,7 @@ __FBSDID("$FreeBSD$"); #include <sys/proc.h> #include <sys/random.h> #include <sys/reboot.h> +#include <sys/serial.h> #include <sys/signalvar.h> #include <sys/sysctl.h> #include <sys/tty.h> @@ -69,6 +70,7 @@ __FBSDID("$FreeBSD$"); #include <machine/psl.h> #include <machine/frame.h> #endif +#include <machine/stdarg.h> #include <dev/kbd/kbdreg.h> #include <dev/fb/fbreg.h> @@ -105,7 +107,7 @@ static struct tty *sc_console_tty; static struct consdev *sc_consptr; static void *kernel_console_ts; static scr_stat main_console; -static struct cdev *main_devs[MAXCONS]; +static struct tty *main_devs[MAXCONS]; static char init_done = COLD; static char shutdown_in_progress = FALSE; @@ -150,7 +152,7 @@ SYSCTL_INT(_hw_syscons, OID_AUTO, kbd_debug, CTLFLAG_RW|CTLFLAG_SECURE, &enable_ #include "font.h" #endif - d_ioctl_t *sc_user_ioctl; + tsw_ioctl_t *sc_user_ioctl; static bios_values_t bios_value; @@ -161,24 +163,18 @@ SYSCTL_INT(_machdep, OID_AUTO, enable_panic_key, CTLFLAG_RW, &enable_panic_key, #define SC_CONSOLECTL 255 #define VTY_WCHAN(sc, vty) (&SC_DEV(sc, vty)) -#define VIRTUAL_TTY(sc, x) (SC_DEV((sc), (x)) != NULL ? \ - SC_DEV((sc), (x))->si_tty : NULL) -#define ISTTYOPEN(tp) ((tp) && ((tp)->t_state & TS_ISOPEN)) static int debugger; /* prototypes */ static int sc_allocate_keyboard(sc_softc_t *sc, int unit); -static struct tty *sc_alloc_tty(struct cdev *dev); static int scvidprobe(int unit, int flags, int cons); static int sckbdprobe(int unit, int flags, int cons); static void scmeminit(void *arg); -static int scdevtounit(struct cdev *dev); +static int scdevtounit(struct tty *tp); static kbd_callback_func_t sckbdevent; -static int scparam(struct tty *tp, struct termios *t); -static void scstart(struct tty *tp); static void scinit(int unit, int flags); -static scr_stat *sc_get_stat(struct cdev *devptr); +static scr_stat *sc_get_stat(struct tty *tp); static void scterm(int unit, int flags); static void scshutdown(void *arg, int howto); static u_int scgetc(sc_softc_t *sc, u_int flags); @@ -219,6 +215,7 @@ static int save_kbd_state(scr_stat *scp); static int update_kbd_state(scr_stat *scp, int state, int mask); static int update_kbd_leds(scr_stat *scp, int which); static timeout_t blink_screen; +static struct tty *sc_alloc_tty(int, const char *, ...) __printflike(2, 3); static cn_probe_t sc_cnprobe; static cn_init_t sc_cninit; @@ -228,21 +225,23 @@ static cn_putc_t sc_cnputc; CONSOLE_DRIVER(sc); -static d_open_t scopen; -static d_close_t scclose; -static d_read_t scread; -static d_ioctl_t scioctl; -static d_mmap_t scmmap; - -static struct cdevsw sc_cdevsw = { - .d_version = D_VERSION, - .d_open = scopen, - .d_close = scclose, - .d_read = scread, - .d_ioctl = scioctl, - .d_mmap = scmmap, - .d_name = "sc", - .d_flags = D_TTY | D_NEEDGIANT, +static tsw_open_t sctty_open; +static tsw_close_t sctty_close; +static tsw_outwakeup_t sctty_outwakeup; +static tsw_ioctl_t sctty_ioctl; +static tsw_mmap_t sctty_mmap; + +static struct ttydevsw sc_ttydevsw = { + /* + * XXX: we should use the prefix, but this doesn't work for + * consolectl. + */ + .tsw_flags = TF_NOPREFIX, + .tsw_open = sctty_open, + .tsw_close = sctty_close, + .tsw_outwakeup = sctty_outwakeup, + .tsw_ioctl = sctty_ioctl, + .tsw_mmap = sctty_mmap, }; int @@ -310,17 +309,47 @@ static char return names[i].name[(adp->va_flags & V_ADP_COLOR) ? 0 : 1]; } +static void +sctty_outwakeup(struct tty *tp) +{ + size_t len; + u_char buf[PCBURST]; + scr_stat *scp = sc_get_stat(tp); + + if (scp->status & SLKED || + (scp == scp->sc->cur_scp && scp->sc->blink_in_progress)) + return; + + for (;;) { + len = ttydisc_getc(tp, buf, sizeof buf); + if (len == 0) + break; + sc_puts(scp, buf, len); + } +} + static struct tty * -sc_alloc_tty(struct cdev *dev) +sc_alloc_tty(int index, const char *fmt, ...) { + va_list ap; + struct sc_ttysoftc *stc; struct tty *tp; + char name[11]; /* "consolectl" */ + + va_start(ap, fmt); + + /* Allocate TTY object and softc to store unit number. */ + stc = malloc(sizeof(struct sc_ttysoftc), M_DEVBUF, M_WAITOK); + stc->st_index = index; + stc->st_stat = NULL; + tp = tty_alloc(&sc_ttydevsw, stc, &Giant); + + /* Create device node. */ + va_start(ap, fmt); + vsnrprintf(name, sizeof name, 32, fmt, ap); + va_end(ap); + tty_makedev(tp, NULL, "%s", name); - tp = dev->si_tty = ttyalloc(); - ttyinitmode(tp, 1, 0); - tp->t_oproc = scstart; - tp->t_param = scparam; - tp->t_stop = nottystop; - tp->t_dev = dev; return (tp); } @@ -333,7 +362,6 @@ sc_attach_unit(int unit, int flags) video_info_t info; #endif int vc; - struct cdev *dev; flags &= ~SC_KERNEL_CONSOLE; @@ -418,9 +446,7 @@ sc_attach_unit(int unit, int flags) for (vc = 0; vc < sc->vtys; vc++) { if (sc->dev[vc] == NULL) { - sc->dev[vc] = make_dev(&sc_cdevsw, vc + unit * MAXCONS, - UID_ROOT, GID_WHEEL, 0600, "ttyv%r", vc + unit * MAXCONS); - sc_alloc_tty(sc->dev[vc]); + sc->dev[vc] = sc_alloc_tty(vc, "ttyv%r", vc + unit * MAXCONS); if (vc == 0 && sc->dev == main_devs) SC_STAT(sc->dev[0]) = &main_console; } @@ -431,11 +457,8 @@ sc_attach_unit(int unit, int flags) */ } - dev = make_dev(&sc_cdevsw, SC_CONSOLECTL, - UID_ROOT, GID_WHEEL, 0600, "consolectl"); - sc_console_tty = sc_alloc_tty(dev); - ttyconsolemode(sc_console_tty, 0); - SC_STAT(dev) = sc_console; + sc_console_tty = sc_alloc_tty(0, "consolectl"); + SC_STAT(sc_console_tty) = sc_console; return 0; } @@ -472,9 +495,9 @@ scmeminit(void *arg) SYSINIT(sc_mem, SI_SUB_KMEM, SI_ORDER_ANY, scmeminit, NULL); static int -scdevtounit(struct cdev *dev) +scdevtounit(struct tty *tp) { - int vty = SC_VTY(dev); + int vty = SC_VTY(tp); if (vty == SC_CONSOLECTL) return ((sc_console != NULL) ? sc_console->sc->unit : -1); @@ -485,48 +508,37 @@ scdevtounit(struct cdev *dev) } static int -scopen(struct cdev *dev, int flag, int mode, struct thread *td) +sctty_open(struct tty *tp) { - int unit = scdevtounit(dev); + int unit = scdevtounit(tp); sc_softc_t *sc; - struct tty *tp; scr_stat *scp; #ifndef __sparc64__ keyarg_t key; #endif - int error; DPRINTF(5, ("scopen: dev:%s, unit:%d, vty:%d\n", - devtoname(dev), unit, SC_VTY(dev))); + devtoname(tp->t_dev), unit, SC_VTY(tp))); - tp = dev->si_tty; sc = sc_get_softc(unit, (sc_console_unit == unit) ? SC_KERNEL_CONSOLE : 0); if (sc == NULL) return ENXIO; - if (!ISTTYOPEN(tp)) { - tp->t_termios = tp->t_init_in; + if (!tty_opened(tp)) { /* Use the current setting of the <-- key as default VERASE. */ /* If the Delete key is preferable, an stty is necessary */ #ifndef __sparc64__ if (sc->kbd != NULL) { key.keynum = KEYCODE_BS; kbdd_ioctl(sc->kbd, GIO_KEYMAPENT, (caddr_t)&key); - tp->t_cc[VERASE] = key.key.map[0]; + tp->t_termios.c_cc[VERASE] = key.key.map[0]; } #endif - scparam(tp, &tp->t_termios); - ttyld_modem(tp, 1); } - else - if (tp->t_state & TS_XCLUDE && priv_check(td, PRIV_TTY_EXCLUSIVE)) - return(EBUSY); - error = ttyld_open(tp, dev); - - scp = sc_get_stat(dev); + scp = sc_get_stat(tp); if (scp == NULL) { - scp = SC_STAT(dev) = alloc_scp(sc, SC_VTY(dev)); + scp = SC_STAT(tp) = alloc_scp(sc, SC_VTY(tp)); if (ISGRAPHSC(scp)) sc_set_pixel_mode(scp, NULL, COL, ROW, 16, 8); } @@ -535,18 +547,17 @@ scopen(struct cdev *dev, int flag, int mode, struct thread *td) tp->t_winsize.ws_row = scp->ysize; } - return error; + return (0); } -static int -scclose(struct cdev *dev, int flag, int mode, struct thread *td) +static void +sctty_close(struct tty *tp) { - struct tty *tp = dev->si_tty; scr_stat *scp; int s; - if (SC_VTY(dev) != SC_CONSOLECTL) { - scp = sc_get_stat(tp->t_dev); + if (SC_VTY(tp) != SC_CONSOLECTL) { + scp = sc_get_stat(tp); /* were we in the middle of the VT switching process? */ DPRINTF(5, ("sc%d: scclose(), ", scp->sc->unit)); s = spltty(); @@ -568,7 +579,7 @@ scclose(struct cdev *dev, int flag, int mode, struct thread *td) sc_vtb_destroy(&scp->scr); #endif sc_free_history_buffer(scp, scp->ysize); - SC_STAT(dev) = NULL; + SC_STAT(tp) = NULL; free(scp, M_DEVBUF); } #else @@ -581,13 +592,9 @@ scclose(struct cdev *dev, int flag, int mode, struct thread *td) kbdd_ioctl(scp->sc->kbd, KDSKBMODE, (caddr_t)&scp->kbd_mode); DPRINTF(5, ("done.\n")); } - spltty(); - ttyld_close(tp, flag); - tty_close(tp); - spl0(); - return(0); } +#if 0 /* XXX mpsafetty: fix screensaver. What about outwakeup? */ static int scread(struct cdev *dev, struct uio *uio, int flag) { @@ -595,19 +602,22 @@ scread(struct cdev *dev, struct uio *uio, int flag) sc_touch_scrn_saver(); return ttyread(dev, uio, flag); } +#endif static int sckbdevent(keyboard_t *thiskbd, int event, void *arg) { sc_softc_t *sc; struct tty *cur_tty; - int c; + int c, error = 0; size_t len; u_char *cp; sc = (sc_softc_t *)arg; /* assert(thiskbd == sc->kbd) */ + mtx_lock(&Giant); + switch (event) { case KBDIO_KEYINPUT: break; @@ -615,9 +625,10 @@ sckbdevent(keyboard_t *thiskbd, int event, void *arg) sc->kbd = NULL; sc->keyboard = -1; kbd_release(thiskbd, (void *)&sc->keyboard); - return 0; + goto done; default: - return EINVAL; + error = EINVAL; + goto done; } /* @@ -627,10 +638,12 @@ sckbdevent(keyboard_t *thiskbd, int event, void *arg) */ while ((c = scgetc(sc, SCGETC_NONBLOCK)) != NOKEY) { - cur_tty = VIRTUAL_TTY(sc, sc->cur_scp->index); - if (!ISTTYOPEN(cur_tty)) { + cur_tty = SC_DEV(sc, sc->cur_scp->index); + if (!tty_opened(cur_tty)) { cur_tty = sc_console_tty; - if (!ISTTYOPEN(cur_tty)) + if (cur_tty == NULL) + continue; + if (!tty_opened(cur_tty)) continue; } @@ -639,47 +652,45 @@ sckbdevent(keyboard_t *thiskbd, int event, void *arg) switch (KEYFLAGS(c)) { case 0x0000: /* normal key */ - ttyld_rint(cur_tty, KEYCHAR(c)); + ttydisc_rint(cur_tty, KEYCHAR(c), 0); break; case FKEY: /* function key, return string */ cp = kbdd_get_fkeystr(thiskbd, KEYCHAR(c), &len); if (cp != NULL) { - while (len-- > 0) - ttyld_rint(cur_tty, *cp++); + if (ttydisc_can_bypass(cur_tty)) { + ttydisc_rint_bypass(cur_tty, cp, len); + } else { + while (len-- > 0) + ttydisc_rint(cur_tty, *cp++, 0); + } } break; case MKEY: /* meta is active, prepend ESC */ - ttyld_rint(cur_tty, 0x1b); - ttyld_rint(cur_tty, KEYCHAR(c)); + ttydisc_rint(cur_tty, 0x1b, 0); + ttydisc_rint(cur_tty, KEYCHAR(c), 0); break; case BKEY: /* backtab fixed sequence (esc [ Z) */ - ttyld_rint(cur_tty, 0x1b); - ttyld_rint(cur_tty, '['); - ttyld_rint(cur_tty, 'Z'); + ttydisc_rint(cur_tty, 0x1b, 0); + ttydisc_rint(cur_tty, '[', 0); + ttydisc_rint(cur_tty, 'Z', 0); break; } + + ttydisc_rint_done(cur_tty); } sc->cur_scp->status |= MOUSE_HIDDEN; - return 0; +done: + mtx_unlock(&Giant); + return (error); } static int -scparam(struct tty *tp, struct termios *t) -{ - tp->t_ispeed = t->c_ispeed; - tp->t_ospeed = t->c_ospeed; - tp->t_cflag = t->c_cflag; - return 0; -} - -static int -scioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td) +sctty_ioctl(struct tty *tp, u_long cmd, caddr_t data, struct thread *td) { int error; int i; - struct tty *tp; sc_softc_t *sc; scr_stat *scp; int s; @@ -688,38 +699,36 @@ scioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td) int ival; #endif - tp = dev->si_tty; - /* If there is a user_ioctl function call that first */ if (sc_user_ioctl) { - error = (*sc_user_ioctl)(dev, cmd, data, flag, td); + error = (*sc_user_ioctl)(tp, cmd, data, td); if (error != ENOIOCTL) return error; } - error = sc_vid_ioctl(tp, cmd, data, flag, td); + error = sc_vid_ioctl(tp, cmd, data, td); if (error != ENOIOCTL) return error; #ifndef SC_NO_HISTORY - error = sc_hist_ioctl(tp, cmd, data, flag, td); + error = sc_hist_ioctl(tp, cmd, data, td); if (error != ENOIOCTL) return error; #endif #ifndef SC_NO_SYSMOUSE - error = sc_mouse_ioctl(tp, cmd, data, flag, td); + error = sc_mouse_ioctl(tp, cmd, data, td); if (error != ENOIOCTL) return error; #endif - scp = sc_get_stat(tp->t_dev); + scp = sc_get_stat(tp); /* assert(scp != NULL) */ /* scp is sc_console, if SC_VTY(dev) == SC_CONSOLECTL. */ sc = scp->sc; if (scp->tsw) { - error = (*scp->tsw->te_ioctl)(scp, tp, cmd, data, flag, td); + error = (*scp->tsw->te_ioctl)(scp, tp, cmd, data, td); if (error != ENOIOCTL) return error; } @@ -1031,8 +1040,8 @@ scioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td) case VT_OPENQRY: /* return free virtual console */ for (i = sc->first_vty; i < sc->first_vty + sc->vtys; i++) { - tp = VIRTUAL_TTY(sc, i); - if (!ISTTYOPEN(tp)) { + tp = SC_DEV(sc, i); + if (!tty_opened(tp)) { *(int *)data = i + 1; return 0; } @@ -1053,7 +1062,8 @@ scioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td) splx(s); if (error) return error; - return sc_switch_scr(sc, i); + error = sc_switch_scr(sc, i); + return (error); #if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \ defined(COMPAT_FREEBSD4) || defined(COMPAT_43) @@ -1441,34 +1451,7 @@ scioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td) break; } - return (ttyioctl(dev, cmd, data, flag, td)); -} - -static void -scstart(struct tty *tp) -{ - struct clist *rbp; - int s, len; - u_char buf[PCBURST]; - scr_stat *scp = sc_get_stat(tp->t_dev); - - if (scp->status & SLKED || - (scp == scp->sc->cur_scp && scp->sc->blink_in_progress)) - return; - s = spltty(); - if (!(tp->t_state & (TS_TIMEOUT | TS_BUSY | TS_TTSTOP))) { - tp->t_state |= TS_BUSY; - rbp = &tp->t_outq; - while (rbp->c_cc) { - len = q_to_b(rbp, buf, PCBURST); - splx(s); - sc_puts(scp, buf, len); - s = spltty(); - } - tp->t_state &= ~TS_BUSY; - ttwwakeup(tp); - } - splx(s); + return (ENOIOCTL); } static void @@ -1548,9 +1531,11 @@ sc_cnputc(struct consdev *cd, int c) scp->status |= CURSOR_ENABLED; sc_draw_cursor_image(scp); } - tp = VIRTUAL_TTY(scp->sc, scp->index); - if (ISTTYOPEN(tp)) - scstart(tp); + tp = SC_DEV(scp->sc, scp->index); + tty_lock(tp); + if (tty_opened(tp)) + sctty_outwakeup(tp); + tty_unlock(tp); } #endif /* !SC_NO_HISTORY */ @@ -2281,9 +2266,9 @@ sc_switch_scr(sc_softc_t *sc, u_int next_scr) * if the switch mode is VT_AUTO, unless the next vty is the same * as the current or the current vty has been closed (but showing). */ - tp = VIRTUAL_TTY(sc, cur_scp->index); + tp = SC_DEV(sc, cur_scp->index); if ((cur_scp->index != next_scr) - && ISTTYOPEN(tp) + && tty_opened(tp) && (cur_scp->smode.mode == VT_AUTO) && ISGRAPHSC(cur_scp)) { splx(s); @@ -2299,14 +2284,14 @@ sc_switch_scr(sc_softc_t *sc, u_int next_scr) * console even if it is closed. */ if ((sc_console == NULL) || (next_scr != sc_console->index)) { - tp = VIRTUAL_TTY(sc, next_scr); - if (!ISTTYOPEN(tp)) { + tp = SC_DEV(sc, next_scr); + if (!tty_opened(tp)) { splx(s); sc_bell(cur_scp, bios_value.bell_pitch, BELL_DURATION); DPRINTF(5, ("error 2, requested vty isn't open!\n")); return EINVAL; } - if ((debugger > 0) && (SC_STAT(tp->t_dev)->smode.mode == VT_PROCESS)) { + if ((debugger > 0) && (SC_STAT(tp)->smode.mode == VT_PROCESS)) { splx(s); DPRINTF(5, ("error 3, requested vty is in the VT_PROCESS mode\n")); return EINVAL; @@ -2609,7 +2594,7 @@ void sc_change_cursor_shape(scr_stat *scp, int flags, int base, int height) { sc_softc_t *sc; - struct cdev *dev; + struct tty *tp; int s; int i; @@ -2635,9 +2620,9 @@ sc_change_cursor_shape(scr_stat *scp, int flags, int base, int height) } for (i = sc->first_vty; i < sc->first_vty + sc->vtys; ++i) { - if ((dev = SC_DEV(sc, i)) == NULL) + if ((tp = SC_DEV(sc, i)) == NULL) continue; - if ((scp = sc_get_stat(dev)) == NULL) + if ((scp = sc_get_stat(tp)) == NULL) continue; scp->dflt_curs_attr = sc->curs_attr; change_cursor_shape(scp, CONS_RESET_CURSOR, -1, -1); @@ -2759,10 +2744,9 @@ scinit(int unit, int flags) kernel_default.rev_color); } else { /* assert(sc_malloc) */ - sc->dev = malloc(sizeof(struct cdev *)*sc->vtys, M_DEVBUF, M_WAITOK|M_ZERO); - sc->dev[0] = make_dev(&sc_cdevsw, unit * MAXCONS, - UID_ROOT, GID_WHEEL, 0600, "ttyv%r", unit * MAXCONS); - sc_alloc_tty(sc->dev[0]); + sc->dev = malloc(sizeof(struct tty *)*sc->vtys, M_DEVBUF, + M_WAITOK|M_ZERO); + sc->dev[0] = sc_alloc_tty(0, "ttyv%r", unit * MAXCONS); scp = alloc_scp(sc, sc->first_vty); SC_STAT(sc->dev[0]) = scp; } @@ -3287,9 +3271,9 @@ next_code: scp->status |= CURSOR_ENABLED; sc_draw_cursor_image(scp); } - tp = VIRTUAL_TTY(sc, scp->index); - if (ISTTYOPEN(tp)) - scstart(tp); + tp = SC_DEV(sc, scp->index); + if (tty_opened(tp)) + sctty_outwakeup(tp); #endif } } @@ -3382,8 +3366,8 @@ next_code: for (i = (this_scr - sc->first_vty + 1)%sc->vtys; sc->first_vty + i != this_scr; i = (i + 1)%sc->vtys) { - struct tty *tp = VIRTUAL_TTY(sc, sc->first_vty + i); - if (ISTTYOPEN(tp)) { + struct tty *tp = SC_DEV(sc, sc->first_vty + i); + if (tty_opened(tp)) { sc_switch_scr(scp->sc, sc->first_vty + i); break; } @@ -3395,8 +3379,8 @@ next_code: for (i = (this_scr - sc->first_vty + sc->vtys - 1)%sc->vtys; sc->first_vty + i != this_scr; i = (i + sc->vtys - 1)%sc->vtys) { - struct tty *tp = VIRTUAL_TTY(sc, sc->first_vty + i); - if (ISTTYOPEN(tp)) { + struct tty *tp = SC_DEV(sc, sc->first_vty + i); + if (tty_opened(tp)) { sc_switch_scr(scp->sc, sc->first_vty + i); break; } @@ -3425,11 +3409,11 @@ next_code: } static int -scmmap(struct cdev *dev, vm_offset_t offset, vm_paddr_t *paddr, int nprot) +sctty_mmap(struct tty *tp, vm_offset_t offset, vm_paddr_t *paddr, int nprot) { scr_stat *scp; - scp = sc_get_stat(dev); + scp = sc_get_stat(tp); if (scp != scp->sc->cur_scp) return -1; return vidd_mmap(scp->sc->adp, offset, paddr, nprot); @@ -3586,12 +3570,13 @@ sc_paste(scr_stat *scp, u_char *p, int count) struct tty *tp; u_char *rmap; - tp = VIRTUAL_TTY(scp->sc, scp->sc->cur_scp->index); - if (!ISTTYOPEN(tp)) + tp = SC_DEV(scp->sc, scp->sc->cur_scp->index); + if (!tty_opened(tp)) return; rmap = scp->sc->scr_rmap; for (; count > 0; --count) - ttyld_rint(tp, rmap[*p++]); + ttydisc_rint(tp, rmap[*p++], 0); + ttydisc_rint_done(tp); } void @@ -3626,9 +3611,9 @@ blink_screen(void *arg) if (ISGRAPHSC(scp) || (scp->sc->blink_in_progress <= 1)) { scp->sc->blink_in_progress = 0; mark_all(scp); - tp = VIRTUAL_TTY(scp->sc, scp->index); - if (ISTTYOPEN(tp)) - scstart(tp); + tp = SC_DEV(scp->sc, scp->index); + if (tty_opened(tp)) + sctty_outwakeup(tp); if (scp->sc->delayed_next_scr) sc_switch_scr(scp->sc, scp->sc->delayed_next_scr - 1); } @@ -3650,11 +3635,11 @@ blink_screen(void *arg) */ static scr_stat * -sc_get_stat(struct cdev *devptr) +sc_get_stat(struct tty *tp) { - if (devptr == NULL) + if (tp == NULL) return (&main_console); - return (SC_STAT(devptr)); + return (SC_STAT(tp)); } /* diff --git a/sys/dev/syscons/syscons.h b/sys/dev/syscons/syscons.h index f17b294..548cec6 100644 --- a/sys/dev/syscons/syscons.h +++ b/sys/dev/syscons/syscons.h @@ -102,9 +102,9 @@ */ #define SC_DRIVER_NAME "syscons" #endif -#define SC_VTY(dev) minor(dev) +#define SC_VTY(dev) (((sc_ttysoftc *)tty_softc(tp))->st_index) #define SC_DEV(sc, vty) ((sc)->dev[(vty) - (sc)->first_vty]) -#define SC_STAT(dev) (*((scr_stat **)&(dev)->si_drv1)) +#define SC_STAT(tp) (*((scr_stat **)&((sc_ttysoftc *)tty_softc(tp))->st_stat)) /* printable chars */ #ifndef PRINTABLE @@ -220,7 +220,7 @@ typedef struct sc_softc { int first_vty; int vtys; - struct cdev **dev; + struct tty **dev; struct scr_stat *cur_scp; struct scr_stat *new_scp; struct scr_stat *old_scp; @@ -339,6 +339,12 @@ typedef struct scr_stat { #endif } scr_stat; +/* TTY softc. */ +typedef struct sc_ttysoftc { + int st_index; + scr_stat *st_stat; +} sc_ttysoftc; + #ifndef SC_NORM_ATTR #define SC_NORM_ATTR (FG_LIGHTGREY | BG_BLACK) #endif @@ -364,7 +370,7 @@ typedef int sc_term_init_t(scr_stat *scp, void **tcp, int code); typedef int sc_term_term_t(scr_stat *scp, void **tcp); typedef void sc_term_puts_t(scr_stat *scp, u_char *buf, int len); typedef int sc_term_ioctl_t(scr_stat *scp, struct tty *tp, u_long cmd, - caddr_t data, int flag, struct thread *td); + caddr_t data, struct thread *td); typedef int sc_term_reset_t(scr_stat *scp, int code); #define SC_TE_HARD_RESET 0 #define SC_TE_SOFT_RESET 1 @@ -531,8 +537,8 @@ typedef struct { } while(0) /* syscons.c */ -extern int (*sc_user_ioctl)(struct cdev *dev, u_long cmd, caddr_t data, - int flag, struct thread *td); +extern int (*sc_user_ioctl)(struct tty *tp, u_long cmd, caddr_t data, + struct thread *td); int sc_probe_unit(int unit, int flags); int sc_attach_unit(int unit, int flags); @@ -574,7 +580,7 @@ void sc_hist_end(scr_stat *scp); int sc_hist_up_line(scr_stat *scp); int sc_hist_down_line(scr_stat *scp); int sc_hist_ioctl(struct tty *tp, u_long cmd, caddr_t data, - int flag, struct thread *td); + struct thread *td); #endif /* SC_NO_HISTORY */ /* scmouse.c */ @@ -599,7 +605,7 @@ void sc_mouse_paste(scr_stat *scp); #ifndef SC_NO_SYSMOUSE void sc_mouse_move(scr_stat *scp, int x, int y); int sc_mouse_ioctl(struct tty *tp, u_long cmd, caddr_t data, - int flag, struct thread *td); + struct thread *td); #endif /* SC_NO_SYSMOUSE */ /* scvidctl.c */ @@ -609,7 +615,7 @@ int sc_set_text_mode(scr_stat *scp, struct tty *tp, int mode, int sc_set_graphics_mode(scr_stat *scp, struct tty *tp, int mode); int sc_set_pixel_mode(scr_stat *scp, struct tty *tp, int xsize, int ysize, int fontsize, int font_width); -int sc_vid_ioctl(struct tty *tp, u_long cmd, caddr_t data, int flag, +int sc_vid_ioctl(struct tty *tp, u_long cmd, caddr_t data, struct thread *td); int sc_render_add(sc_renderer_t *rndr); diff --git a/sys/dev/syscons/sysmouse.c b/sys/dev/syscons/sysmouse.c index 35d761b..9d926b8 100644 --- a/sys/dev/syscons/sysmouse.c +++ b/sys/dev/syscons/sysmouse.c @@ -32,8 +32,8 @@ __FBSDID("$FreeBSD$"); #include <sys/param.h> #include <sys/systm.h> -#include <sys/conf.h> #include <sys/priv.h> +#include <sys/serial.h> #include <sys/tty.h> #include <sys/kernel.h> #include <sys/consio.h> @@ -45,106 +45,24 @@ __FBSDID("$FreeBSD$"); #define SC_MOUSE 128 /* minor number */ -static d_open_t smopen; -static d_close_t smclose; -static d_ioctl_t smioctl; - -static struct cdevsw sm_cdevsw = { - .d_version = D_VERSION, - .d_open = smopen, - .d_close = smclose, - .d_ioctl = smioctl, - .d_name = "sysmouse", - .d_flags = D_TTY | D_NEEDGIANT, -}; - /* local variables */ static struct tty *sysmouse_tty; static int mouse_level; /* sysmouse protocol level */ static mousestatus_t mouse_status; -static void smstart(struct tty *tp); -static int smparam(struct tty *tp, struct termios *t); - -static int -smopen(struct cdev *dev, int flag, int mode, struct thread *td) -{ - struct tty *tp; - - DPRINTF(5, ("smopen: dev:%s, vty:%d\n", - devtoname(dev), SC_VTY(dev))); - -#if 0 - if (SC_VTY(dev) != SC_MOUSE) - return ENXIO; -#endif - - tp = dev->si_tty; - if (!(tp->t_state & TS_ISOPEN)) { - ttyinitmode(tp, 0, 0); - smparam(tp, &tp->t_termios); - ttyld_modem(tp, 1); - } else if (tp->t_state & TS_XCLUDE && - priv_check(td, PRIV_TTY_EXCLUSIVE)) { - return EBUSY; - } - - return ttyld_open(tp, dev); -} - -static int -smclose(struct cdev *dev, int flag, int mode, struct thread *td) -{ - struct tty *tp; - int s; - - tp = dev->si_tty; - s = spltty(); - mouse_level = 0; - ttyld_close(tp, flag); - tty_close(tp); - splx(s); - - return 0; -} - static void -smstart(struct tty *tp) -{ - struct clist *rbp; - u_char buf[PCBURST]; - int s; - - s = spltty(); - if (!(tp->t_state & (TS_TIMEOUT | TS_BUSY | TS_TTSTOP))) { - tp->t_state |= TS_BUSY; - rbp = &tp->t_outq; - while (rbp->c_cc) - q_to_b(rbp, buf, PCBURST); - tp->t_state &= ~TS_BUSY; - ttwwakeup(tp); - } - splx(s); -} - -static int -smparam(struct tty *tp, struct termios *t) +smdev_close(struct tty *tp) { - tp->t_ispeed = t->c_ispeed; - tp->t_ospeed = t->c_ospeed; - tp->t_cflag = t->c_cflag; - return 0; + mouse_level = 0; } static int -smioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td) +smdev_ioctl(struct tty *tp, u_long cmd, caddr_t data, struct thread *td) { - struct tty *tp; mousehw_t *hw; mousemode_t *mode; int s; - tp = dev->si_tty; switch (cmd) { case MOUSE_GETHWINFO: /* get device information */ @@ -224,25 +142,35 @@ smioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td) return ENODEV; } - return(ttyioctl(dev, cmd, data, flag, td)); + return (ENOIOCTL); } +static int +smdev_param(struct tty *tp, struct termios *t) +{ + + /* + * Set the output baud rate to zero. The mouse device supports + * no output, so we don't want to waste buffers. + */ + t->c_ispeed = TTYDEF_SPEED_PSEUDO; + t->c_ospeed = B0; + + return (0); +} + +static struct ttydevsw smdev_ttydevsw = { + .tsw_flags = TF_NOPREFIX, + .tsw_close = smdev_close, + .tsw_ioctl = smdev_ioctl, + .tsw_param = smdev_param, +}; + static void sm_attach_mouse(void *unused) { - struct cdev *dev; - struct tty *tp; - - dev = make_dev(&sm_cdevsw, SC_MOUSE, UID_ROOT, GID_WHEEL, 0600, - "sysmouse"); - dev->si_tty = tp = ttyalloc(); - tp->t_oproc = smstart; - tp->t_param = smparam; - tp->t_stop = nottystop; - tp->t_dev = dev; - - sysmouse_tty = tp; - /* sysmouse doesn't have scr_stat */ + sysmouse_tty = tty_alloc(&smdev_ttydevsw, NULL, &Giant); + tty_makedev(sysmouse_tty, NULL, "sysmouse"); } SYSINIT(sysmouse, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, sm_attach_mouse, NULL); @@ -293,7 +221,7 @@ sysmouse_event(mouse_info_t *info) if (mouse_status.flags == 0) return 0; - if ((sysmouse_tty == NULL) || !(sysmouse_tty->t_state & TS_ISOPEN)) + if ((sysmouse_tty == NULL) || !tty_opened(sysmouse_tty)) return mouse_status.flags; /* the first five bytes are compatible with MouseSystems' */ @@ -306,7 +234,7 @@ sysmouse_event(mouse_info_t *info) buf[2] = y >> 1; buf[4] = y - buf[2]; for (i = 0; i < MOUSE_MSC_PACKETSIZE; ++i) - ttyld_rint(sysmouse_tty, buf[i]); + ttydisc_rint(sysmouse_tty, buf[i], 0); if (mouse_level >= 1) { /* extended part */ z = imax(imin(z, 127), -128); @@ -315,8 +243,9 @@ sysmouse_event(mouse_info_t *info) /* buttons 4-10 */ buf[7] = (~mouse_status.button >> 3) & 0x7f; for (i = MOUSE_MSC_PACKETSIZE; i < MOUSE_SYS_PACKETSIZE; ++i) - ttyld_rint(sysmouse_tty, buf[i]); + ttydisc_rint(sysmouse_tty, buf[i], 0); } + ttydisc_rint_done(sysmouse_tty); return mouse_status.flags; } diff --git a/sys/dev/uart/uart_core.c b/sys/dev/uart/uart_core.c index 3b0d64c..932c08a 100644 --- a/sys/dev/uart/uart_core.c +++ b/sys/dev/uart/uart_core.c @@ -46,7 +46,6 @@ __FBSDID("$FreeBSD$"); #include <machine/bus.h> #include <sys/rman.h> #include <sys/termios.h> -#include <sys/tty.h> #include <machine/resource.h> #include <machine/stdarg.h> @@ -466,7 +465,7 @@ uart_bus_attach(device_t dev) sc->sc_polled = 1; } - sc->sc_rxbufsz = IBUFSIZ; + sc->sc_rxbufsz = 384; sc->sc_rxbuf = malloc(sc->sc_rxbufsz * sizeof(*sc->sc_rxbuf), M_UART, M_WAITOK); sc->sc_txbuf = malloc(sc->sc_txfifosz * sizeof(*sc->sc_txbuf), diff --git a/sys/dev/uart/uart_tty.c b/sys/dev/uart/uart_tty.c index 1971049..9b37bb3 100644 --- a/sys/dev/uart/uart_tty.c +++ b/sys/dev/uart/uart_tty.c @@ -123,11 +123,11 @@ uart_cngetc(struct consdev *cp) } static int -uart_tty_open(struct tty *tp, struct cdev *dev) +uart_tty_open(struct tty *tp) { struct uart_softc *sc; - sc = tp->t_sc; + sc = tty_softc(tp); if (sc == NULL || sc->sc_leaving) return (ENXIO); @@ -141,7 +141,7 @@ uart_tty_close(struct tty *tp) { struct uart_softc *sc; - sc = tp->t_sc; + sc = tty_softc(tp); if (sc == NULL || sc->sc_leaving || !sc->sc_opened) return; @@ -158,11 +158,11 @@ uart_tty_close(struct tty *tp) } static void -uart_tty_oproc(struct tty *tp) +uart_tty_outwakeup(struct tty *tp) { struct uart_softc *sc; - sc = tp->t_sc; + sc = tty_softc(tp); if (sc == NULL || sc->sc_leaving) return; @@ -173,30 +173,45 @@ uart_tty_oproc(struct tty *tp) * de-assert RTS for us. In that situation we're completely stuffed. * Without hardware support, we need to toggle RTS ourselves. */ - if ((tp->t_cflag & CRTS_IFLOW) && !sc->sc_hwiflow) { - if ((tp->t_state & TS_TBLOCK) && + if ((tp->t_termios.c_cflag & CRTS_IFLOW) && !sc->sc_hwiflow) { +#if 0 + /*if ((tp->t_state & TS_TBLOCK) && (sc->sc_hwsig & SER_RTS)) UART_SETSIG(sc, SER_DRTS); - else if (!(tp->t_state & TS_TBLOCK) && + else */ if (/*!(tp->t_state & TS_TBLOCK) &&*/ !(sc->sc_hwsig & SER_RTS)) UART_SETSIG(sc, SER_DRTS|SER_RTS); +#endif + /* XXX: we should use inwakeup to implement this! */ + if (!(sc->sc_hwsig & SER_RTS)) + UART_SETSIG(sc, SER_DRTS|SER_RTS); } - if (tp->t_state & TS_TTSTOP) + if (sc->sc_txbusy) return; - if ((tp->t_state & TS_BUSY) || sc->sc_txbusy) - return; + sc->sc_txdatasz = ttydisc_getc(tp, sc->sc_txbuf, sc->sc_txfifosz); + if (sc->sc_txdatasz != 0) + UART_TRANSMIT(sc); +} - if (tp->t_outq.c_cc == 0) { - ttwwakeup(tp); - return; - } +static int +uart_tty_ioctl(struct tty *tp, u_long cmd, caddr_t data, struct thread *td) +{ + struct uart_softc *sc; + + sc = tty_softc(tp); - sc->sc_txdatasz = q_to_b(&tp->t_outq, sc->sc_txbuf, sc->sc_txfifosz); - tp->t_state |= TS_BUSY; - UART_TRANSMIT(sc); - ttwwakeup(tp); + switch (cmd) { + case TIOCSBRK: + UART_IOCTL(sc, UART_IOCTL_BREAK, 1); + return (0); + case TIOCCBRK: + UART_IOCTL(sc, UART_IOCTL_BREAK, 0); + return (0); + default: + return pps_ioctl(cmd, data, &sc->sc_pps); + } } static int @@ -205,7 +220,7 @@ uart_tty_param(struct tty *tp, struct termios *t) struct uart_softc *sc; int databits, parity, stopbits; - sc = tp->t_sc; + sc = tty_softc(tp); if (sc == NULL || sc->sc_leaving) return (ENODEV); if (t->c_ispeed != t->c_ospeed && t->c_ospeed != 0) @@ -237,16 +252,16 @@ uart_tty_param(struct tty *tp, struct termios *t) UART_SETSIG(sc, SER_DDTR | SER_DTR); /* Set input flow control state. */ if (!sc->sc_hwiflow) { - if ((t->c_cflag & CRTS_IFLOW) && (tp->t_state & TS_TBLOCK)) + /* if ((t->c_cflag & CRTS_IFLOW) && (tp->t_state & TS_TBLOCK)) UART_SETSIG(sc, SER_DRTS); - else + else */ UART_SETSIG(sc, SER_DRTS | SER_RTS); } else UART_IOCTL(sc, UART_IOCTL_IFLOW, (t->c_cflag & CRTS_IFLOW)); /* Set output flow control state. */ if (sc->sc_hwoflow) UART_IOCTL(sc, UART_IOCTL_OFLOW, (t->c_cflag & CCTS_OFLOW)); - ttsetwater(tp); + return (0); } @@ -255,48 +270,18 @@ uart_tty_modem(struct tty *tp, int biton, int bitoff) { struct uart_softc *sc; - sc = tp->t_sc; + sc = tty_softc(tp); if (biton != 0 || bitoff != 0) UART_SETSIG(sc, SER_DELTA(bitoff|biton) | biton); return (sc->sc_hwsig); } -static void -uart_tty_break(struct tty *tp, int state) -{ - struct uart_softc *sc; - - sc = tp->t_sc; - UART_IOCTL(sc, UART_IOCTL_BREAK, state); -} - -static void -uart_tty_stop(struct tty *tp, int rw) -{ - struct uart_softc *sc; - - sc = tp->t_sc; - if (sc == NULL || sc->sc_leaving) - return; - if (rw & FWRITE) { - if (sc->sc_txbusy) { - sc->sc_txbusy = 0; - UART_FLUSH(sc, UART_FLUSH_TRANSMITTER); - } - tp->t_state &= ~TS_BUSY; - } - if (rw & FREAD) { - UART_FLUSH(sc, UART_FLUSH_RECEIVER); - sc->sc_rxget = sc->sc_rxput = 0; - } -} - void uart_tty_intr(void *arg) { struct uart_softc *sc = arg; struct tty *tp; - int c, pend, sig, xc; + int c, err = 0, pend, sig, xc; if (sc->sc_leaving) return; @@ -306,78 +291,72 @@ uart_tty_intr(void *arg) return; tp = sc->sc_u.u_tty.tp; + tty_lock(tp); if (pend & SER_INT_RXREADY) { - while (!uart_rx_empty(sc) && !(tp->t_state & TS_TBLOCK)) { + while (!uart_rx_empty(sc) /* && !(tp->t_state & TS_TBLOCK)*/) { xc = uart_rx_get(sc); c = xc & 0xff; if (xc & UART_STAT_FRAMERR) - c |= TTY_FE; + err |= TRE_FRAMING; if (xc & UART_STAT_OVERRUN) - c |= TTY_OE; + err |= TRE_OVERRUN; if (xc & UART_STAT_PARERR) - c |= TTY_PE; - ttyld_rint(tp, c); + err |= TRE_PARITY; + ttydisc_rint(tp, c, err); } } - if (pend & SER_INT_BREAK) { - if (tp != NULL && !(tp->t_iflag & IGNBRK)) - ttyld_rint(tp, 0); - } + if (pend & SER_INT_BREAK) + ttydisc_rint(tp, 0, TRE_BREAK); if (pend & SER_INT_SIGCHG) { sig = pend & SER_INT_SIGMASK; if (sig & SER_DDCD) - ttyld_modem(tp, sig & SER_DCD); - if ((sig & SER_DCTS) && (tp->t_cflag & CCTS_OFLOW) && + ttydisc_modem(tp, sig & SER_DCD); + if ((sig & SER_DCTS) && (tp->t_termios.c_cflag & CCTS_OFLOW) && !sc->sc_hwoflow) { - if (sig & SER_CTS) { - tp->t_state &= ~TS_TTSTOP; - ttyld_start(tp); - } else - tp->t_state |= TS_TTSTOP; + if (sig & SER_CTS) + uart_tty_outwakeup(tp); } } - if (pend & SER_INT_TXIDLE) { - tp->t_state &= ~TS_BUSY; - ttyld_start(tp); - } + if (pend & SER_INT_TXIDLE) + uart_tty_outwakeup(tp); + ttydisc_rint_done(tp); + tty_unlock(tp); } +static struct ttydevsw uart_tty_class = { + .tsw_flags = TF_INITLOCK|TF_CALLOUT, + .tsw_open = uart_tty_open, + .tsw_close = uart_tty_close, + .tsw_outwakeup = uart_tty_outwakeup, + .tsw_ioctl = uart_tty_ioctl, + .tsw_param = uart_tty_param, + .tsw_modem = uart_tty_modem, +}; + int uart_tty_attach(struct uart_softc *sc) { struct tty *tp; int unit; - tp = ttyalloc(); - sc->sc_u.u_tty.tp = tp; - tp->t_sc = sc; + sc->sc_u.u_tty.tp = tp = tty_alloc(&uart_tty_class, sc, NULL); unit = device_get_unit(sc->sc_dev); - tp->t_oproc = uart_tty_oproc; - tp->t_param = uart_tty_param; - tp->t_stop = uart_tty_stop; - tp->t_modem = uart_tty_modem; - tp->t_break = uart_tty_break; - tp->t_open = uart_tty_open; - tp->t_close = uart_tty_close; - - tp->t_pps = &sc->sc_pps; - if (sc->sc_sysdev != NULL && sc->sc_sysdev->type == UART_DEV_CONSOLE) { sprintf(((struct consdev *)sc->sc_sysdev->cookie)->cn_name, "ttyu%r", unit); - ttyconsolemode(tp, 0); + tty_init_console(tp, 0); } swi_add(&tty_intr_event, uart_driver_name, uart_tty_intr, sc, SWI_TTY, INTR_TYPE_TTY, &sc->sc_softih); - ttycreate(tp, TS_CALLOUT, "u%r", unit); + tty_makedev(tp, NULL, "u%r", unit); return (0); } @@ -387,10 +366,10 @@ int uart_tty_detach(struct uart_softc *sc) struct tty *tp; tp = sc->sc_u.u_tty.tp; - tp->t_pps = NULL; - ttygone(tp); + + tty_lock(tp); swi_remove(sc->sc_softih); - ttyfree(tp); + tty_rel_gone(tp); return (0); } diff --git a/sys/dev/usb/ucom.c b/sys/dev/usb/ucom.c index f99a73a..29c11ec 100644 --- a/sys/dev/usb/ucom.c +++ b/sys/dev/usb/ucom.c @@ -116,22 +116,33 @@ SYSCTL_INT(_hw_usb_ucom, OID_AUTO, debug, CTLFLAG_RW, static int ucom_modevent(module_t, int, void *); static void ucom_cleanup(struct ucom_softc *); -static int ucomparam(struct tty *, struct termios *); -static void ucomstart(struct tty *); -static void ucomstop(struct tty *, int); static void ucom_shutdown(struct ucom_softc *); static void ucom_dtr(struct ucom_softc *, int); static void ucom_rts(struct ucom_softc *, int); -static void ucombreak(struct tty *, int); +static void ucombreak(struct ucom_softc *, int); static usbd_status ucomstartread(struct ucom_softc *); static void ucomreadcb(usbd_xfer_handle, usbd_private_handle, usbd_status); static void ucomwritecb(usbd_xfer_handle, usbd_private_handle, usbd_status); static void ucomstopread(struct ucom_softc *); -static t_open_t ucomopen; -static t_close_t ucomclose; -static t_modem_t ucommodem; -static t_ioctl_t ucomioctl; +static tsw_open_t ucomtty_open; +static tsw_close_t ucomtty_close; +static tsw_outwakeup_t ucomtty_outwakeup; +static tsw_ioctl_t ucomtty_ioctl; +static tsw_param_t ucomtty_param; +static tsw_modem_t ucomtty_modem; +static tsw_free_t ucomtty_free; + +static struct ttydevsw ucomtty_class = { + .tsw_flags = TF_INITLOCK|TF_CALLOUT, + .tsw_open = ucomtty_open, + .tsw_close = ucomtty_close, + .tsw_outwakeup = ucomtty_outwakeup, + .tsw_ioctl = ucomtty_ioctl, + .tsw_param = ucomtty_param, + .tsw_modem = ucomtty_modem, + .tsw_free = ucomtty_free, +}; devclass_t ucom_devclass; @@ -160,31 +171,20 @@ ucom_modevent(module_t mod, int type, void *data) return (0); } -int -ucom_attach_tty(struct ucom_softc *sc, int flags, char* fmt, int unit) +void +ucom_attach_tty(struct ucom_softc *sc, char* fmt, int unit) { struct tty *tp; - sc->sc_tty = tp = ttyalloc(); - tp->t_sc = sc; - tp->t_oproc = ucomstart; - tp->t_param = ucomparam; - tp->t_stop = ucomstop; - tp->t_break = ucombreak; - tp->t_open = ucomopen; - tp->t_close = ucomclose; - tp->t_modem = ucommodem; - tp->t_ioctl = ucomioctl; - - return ttycreate(tp, flags, fmt, unit); + sc->sc_tty = tp = tty_alloc(&ucomtty_class, sc, &Giant); + tty_makedev(tp, NULL, fmt, unit); } int ucom_attach(struct ucom_softc *sc) { - ucom_attach_tty(sc, TS_CALLOUT, - "U%d", device_get_unit(sc->sc_dev)); + ucom_attach_tty(sc, "U%d", device_get_unit(sc->sc_dev)); DPRINTF(("ucom_attach: ttycreate: tp = %p, %s\n", sc->sc_tty, sc->sc_tty->t_dev->si_name)); @@ -195,24 +195,17 @@ ucom_attach(struct ucom_softc *sc) int ucom_detach(struct ucom_softc *sc) { - int s; - DPRINTF(("ucom_detach: sc = %p, tp = %p\n", sc, sc->sc_tty)); + tty_lock(sc->sc_tty); sc->sc_dying = 1; - ttygone(sc->sc_tty); - if (sc->sc_tty->t_state & TS_ISOPEN) - ucomclose(sc->sc_tty); if (sc->sc_bulkin_pipe != NULL) usbd_abort_pipe(sc->sc_bulkin_pipe); if (sc->sc_bulkout_pipe != NULL) usbd_abort_pipe(sc->sc_bulkout_pipe); - ttyfree(sc->sc_tty); - - s = splusb(); - splx(s); + tty_rel_gone(sc->sc_tty); return (0); } @@ -227,30 +220,30 @@ ucom_shutdown(struct ucom_softc *sc) * Hang up if necessary. Wait a bit, so the other side has time to * notice even if we immediately open the port again. */ - if (ISSET(tp->t_cflag, HUPCL)) { - (void)ucommodem(tp, 0, SER_DTR); + if (tp->t_termios.c_cflag & HUPCL) { + (void)ucomtty_modem(tp, 0, SER_DTR); +#if 0 (void)tsleep(sc, TTIPRI, "ucomsd", hz); +#endif } } static int -ucomopen(struct tty *tp, struct cdev *dev) +ucomtty_open(struct tty *tp) { - struct ucom_softc *sc; + struct ucom_softc *sc = tty_softc(tp); usbd_status err; int error; - sc = tp->t_sc; - if (sc->sc_dying) return (ENXIO); - DPRINTF(("%s: ucomopen: tp = %p\n", device_get_nameunit(sc->sc_dev), tp)); + DPRINTF(("%s: ucomtty_open: tp = %p\n", device_get_nameunit(sc->sc_dev), tp)); sc->sc_poll = 0; sc->sc_lsr = sc->sc_msr = sc->sc_mcr = 0; - (void)ucommodem(tp, SER_DTR | SER_RTS, 0); + (void)ucomtty_modem(tp, SER_DTR | SER_RTS, 0); /* Device specific open */ if (sc->sc_callback->ucom_open != NULL) { @@ -262,7 +255,7 @@ ucomopen(struct tty *tp, struct cdev *dev) } } - DPRINTF(("ucomopen: open pipes in = %d out = %d\n", + DPRINTF(("ucomtty_open: open pipes in = %d out = %d\n", sc->sc_bulkin_no, sc->sc_bulkout_no)); /* Open the bulk pipes */ @@ -328,13 +321,11 @@ fail: } static void -ucomclose(struct tty *tp) +ucomtty_close(struct tty *tp) { - struct ucom_softc *sc; - - sc = tp->t_sc; + struct ucom_softc *sc = tty_softc(tp); - DPRINTF(("%s: ucomclose \n", device_get_nameunit(sc->sc_dev))); + DPRINTF(("%s: ucomtty_close \n", device_get_nameunit(sc->sc_dev))); ucom_cleanup(sc); @@ -343,35 +334,41 @@ ucomclose(struct tty *tp) } static int -ucomioctl(struct tty *tp, u_long cmd, void *data, int flag, struct thread *p) +ucomtty_ioctl(struct tty *tp, u_long cmd, caddr_t data, struct thread *p) { - struct ucom_softc *sc; + struct ucom_softc *sc = tty_softc(tp); int error; - sc = tp->t_sc;; if (sc->sc_dying) return (EIO); DPRINTF(("ucomioctl: cmd = 0x%08lx\n", cmd)); - error = ENOTTY; + switch (cmd) { + case TIOCSBRK: + ucombreak(sc, 1); + return (0); + case TIOCCBRK: + ucombreak(sc, 0); + return (0); + } + + error = ENOIOCTL; if (sc->sc_callback->ucom_ioctl != NULL) error = sc->sc_callback->ucom_ioctl(sc->sc_parent, sc->sc_portno, - cmd, data, flag, p); + cmd, data, p); return (error); } static int -ucommodem(struct tty *tp, int sigon, int sigoff) +ucomtty_modem(struct tty *tp, int sigon, int sigoff) { - struct ucom_softc *sc; + struct ucom_softc *sc = tty_softc(tp); int mcr; int msr; int onoff; - sc = tp->t_sc; - if (sigon == 0 && sigoff == 0) { mcr = sc->sc_mcr; if (ISSET(mcr, SER_DTR)) @@ -412,12 +409,10 @@ ucommodem(struct tty *tp, int sigon, int sigoff) } static void -ucombreak(struct tty *tp, int onoff) +ucombreak(struct ucom_softc *sc, int onoff) { - struct ucom_softc *sc; - - sc = tp->t_sc; DPRINTF(("ucombreak: onoff = %d\n", onoff)); + if (sc->sc_callback->ucom_set == NULL) return; sc->sc_callback->ucom_set(sc->sc_parent, sc->sc_portno, @@ -467,47 +462,32 @@ ucom_status_change(struct ucom_softc *sc) return; onoff = ISSET(sc->sc_msr, SER_DCD) ? 1 : 0; DPRINTF(("ucom_status_change: DCD changed to %d\n", onoff)); - ttyld_modem(tp, onoff); + ttydisc_modem(tp, onoff); } } static int -ucomparam(struct tty *tp, struct termios *t) +ucomtty_param(struct tty *tp, struct termios *t) { - struct ucom_softc *sc; + struct ucom_softc *sc = tty_softc(tp); int error; usbd_status uerr; - sc = tp->t_sc; - if (sc->sc_dying) return (EIO); - DPRINTF(("ucomparam: sc = %p\n", sc)); + DPRINTF(("ucomtty_param: sc = %p\n", sc)); /* Check requested parameters. */ if (t->c_ospeed < 0) { - DPRINTF(("ucomparam: negative ospeed\n")); + DPRINTF(("ucomtty_param: negative ospeed\n")); return (EINVAL); } if (t->c_ispeed && t->c_ispeed != t->c_ospeed) { - DPRINTF(("ucomparam: mismatch ispeed and ospeed\n")); + DPRINTF(("ucomtty_param: mismatch ispeed and ospeed\n")); return (EINVAL); } - - /* - * If there were no changes, don't do anything. This avoids dropping - * input and improves performance when all we did was frob things like - * VMIN and VTIME. - */ - if (tp->t_ospeed == t->c_ospeed && - tp->t_cflag == t->c_cflag) - return (0); - - /* And copy to tty. */ - tp->t_ispeed = 0; - tp->t_ospeed = t->c_ospeed; - tp->t_cflag = t->c_cflag; + t->c_ispeed = t->c_ospeed; if (sc->sc_callback->ucom_param == NULL) return (0); @@ -516,20 +496,24 @@ ucomparam(struct tty *tp, struct termios *t) error = sc->sc_callback->ucom_param(sc->sc_parent, sc->sc_portno, t); if (error) { - DPRINTF(("ucomparam: callback: error = %d\n", error)); + DPRINTF(("ucomtty_param: callback: error = %d\n", error)); return (error); } +#if 0 ttsetwater(tp); +#endif if (t->c_cflag & CRTS_IFLOW) { sc->sc_state |= UCS_RTS_IFLOW; } else if (sc->sc_state & UCS_RTS_IFLOW) { sc->sc_state &= ~UCS_RTS_IFLOW; - (void)ucommodem(tp, SER_RTS, 0); + (void)ucomtty_modem(tp, SER_RTS, 0); } +#if 0 ttyldoptim(tp); +#endif uerr = ucomstartread(sc); if (uerr != USBD_NORMAL_COMPLETION) @@ -539,17 +523,23 @@ ucomparam(struct tty *tp, struct termios *t) } static void -ucomstart(struct tty *tp) +ucomtty_free(void *sc) +{ + /* + * Our softc gets deallocated earlier on. + * XXX: we should make sure the TTY device name doesn't get + * recycled before we end up here! + */ +} + +static void +ucomtty_outwakeup(struct tty *tp) { - struct ucom_softc *sc; - struct cblock *cbp; + struct ucom_softc *sc = tty_softc(tp); usbd_status err; - int s; - u_char *data; - int cnt; + size_t cnt; - sc = tp->t_sc; - DPRINTF(("ucomstart: sc = %p\n", sc)); + DPRINTF(("ucomtty_outwakeup: sc = %p\n", sc)); if (sc->sc_dying) return; @@ -564,90 +554,59 @@ ucomstart(struct tty *tp) if (sc->sc_oxfer == NULL) return; - s = spltty(); - + /* XXX: hardware flow control. We should use inwakeup here. */ +#if 0 if (tp->t_state & TS_TBLOCK) { if (ISSET(sc->sc_mcr, SER_RTS) && ISSET(sc->sc_state, UCS_RTS_IFLOW)) { - DPRINTF(("ucomstart: clear RTS\n")); - (void)ucommodem(tp, 0, SER_RTS); + DPRINTF(("ucomtty_outwakeup: clear RTS\n")); + (void)ucomtty_modem(tp, 0, SER_RTS); } } else { if (!ISSET(sc->sc_mcr, SER_RTS) && tp->t_rawq.c_cc <= tp->t_ilowat && ISSET(sc->sc_state, UCS_RTS_IFLOW)) { - DPRINTF(("ucomstart: set RTS\n")); - (void)ucommodem(tp, SER_RTS, 0); + DPRINTF(("ucomtty_outwakeup: set RTS\n")); + (void)ucomtty_modem(tp, SER_RTS, 0); } } +#endif - if (ISSET(tp->t_state, TS_BUSY | TS_TIMEOUT | TS_TTSTOP)) { - ttwwakeup(tp); - DPRINTF(("ucomstart: stopped\n")); - goto out; - } - - if (tp->t_outq.c_cc <= tp->t_olowat) { - if (ISSET(tp->t_state, TS_SO_OLOWAT)) { - CLR(tp->t_state, TS_SO_OLOWAT); - wakeup(TSA_OLOWAT(tp)); - } - selwakeuppri(&tp->t_wsel, TTIPRI); - if (tp->t_outq.c_cc == 0) { - 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)); - } - goto out; - } - } + if (sc->sc_state & UCS_TXBUSY) + return; - /* Grab the first contiguous region of buffer space. */ - data = tp->t_outq.c_cf; - cbp = (struct cblock *) ((intptr_t) tp->t_outq.c_cf & ~CROUND); - cnt = min((char *) (cbp+1) - tp->t_outq.c_cf, tp->t_outq.c_cc); + sc->sc_state |= UCS_TXBUSY; + if (sc->sc_callback->ucom_write != NULL) + cnt = sc->sc_callback->ucom_write(sc->sc_parent, + sc->sc_portno, tp, sc->sc_obuf, sc->sc_obufsize); + else + cnt = ttydisc_getc(tp, sc->sc_obuf, sc->sc_obufsize); if (cnt == 0) { - DPRINTF(("ucomstart: cnt == 0\n")); - goto out; - } - - SET(tp->t_state, TS_BUSY); - - if (cnt > sc->sc_obufsize) { - DPRINTF(("ucomstart: big buffer %d chars\n", cnt)); - cnt = sc->sc_obufsize; + DPRINTF(("ucomtty_outwakeup: cnt == 0\n")); + sc->sc_state &= ~UCS_TXBUSY; + return; } - if (sc->sc_callback->ucom_write != NULL) - sc->sc_callback->ucom_write(sc->sc_parent, sc->sc_portno, - sc->sc_obuf, data, &cnt); - else - memcpy(sc->sc_obuf, data, cnt); - DPRINTF(("ucomstart: %d chars\n", cnt)); + DPRINTF(("ucomtty_outwakeup: %zu chars\n", cnt)); usbd_setup_xfer(sc->sc_oxfer, sc->sc_bulkout_pipe, (usbd_private_handle)sc, sc->sc_obuf, cnt, USBD_NO_COPY, USBD_NO_TIMEOUT, ucomwritecb); /* What can we do on error? */ err = usbd_transfer(sc->sc_oxfer); - if (err != USBD_IN_PROGRESS) - printf("ucomstart: err=%s\n", usbd_errstr(err)); - - ttwwakeup(tp); - - out: - splx(s); + if (err != USBD_IN_PROGRESS) { + printf("ucomtty_outwakeup: err=%s\n", usbd_errstr(err)); + sc->sc_state &= ~UCS_TXBUSY; + } } +#if 0 static void ucomstop(struct tty *tp, int flag) { - struct ucom_softc *sc; + struct ucom_softc *sc = tty_softc(tp); int s; - sc = tp->t_sc; - DPRINTF(("ucomstop: %d\n", flag)); if ((flag & FREAD) && (sc->sc_state & UCS_RXSTOP) == 0) { @@ -658,19 +617,18 @@ ucomstop(struct tty *tp, int flag) if (flag & FWRITE) { DPRINTF(("ucomstop: write\n")); - s = spltty(); if (ISSET(tp->t_state, TS_BUSY)) { /* XXX do what? */ if (!ISSET(tp->t_state, TS_TTSTOP)) SET(tp->t_state, TS_FLUSH); } - splx(s); } - ucomstart(tp); + ucomtty_outwakeup(tp); DPRINTF(("ucomstop: done\n")); } +#endif static void ucomwritecb(usbd_xfer_handle xfer, usbd_private_handle p, usbd_status status) @@ -678,12 +636,11 @@ ucomwritecb(usbd_xfer_handle xfer, usbd_private_handle p, usbd_status status) struct ucom_softc *sc = (struct ucom_softc *)p; struct tty *tp = sc->sc_tty; u_int32_t cc; - int s; DPRINTF(("ucomwritecb: status = %d\n", status)); if (status == USBD_CANCELLED || sc->sc_dying) - goto error; + return; if (status != USBD_NORMAL_COMPLETION) { printf("%s: ucomwritecb: %s\n", @@ -691,7 +648,7 @@ ucomwritecb(usbd_xfer_handle xfer, usbd_private_handle p, usbd_status status) if (status == USBD_STALLED) usbd_clear_endpoint_stall_async(sc->sc_bulkin_pipe); /* XXX we should restart after some delay. */ - goto error; + return; } usbd_get_xfer_status(xfer, NULL, NULL, &cc, NULL); @@ -699,28 +656,21 @@ ucomwritecb(usbd_xfer_handle xfer, usbd_private_handle p, usbd_status status) if (cc <= sc->sc_opkthdrlen) { printf("%s: sent size too small, cc = %d\n", device_get_nameunit(sc->sc_dev), cc); - goto error; + return; } /* convert from USB bytes to tty bytes */ cc -= sc->sc_opkthdrlen; - s = spltty(); + sc->sc_state &= ~UCS_TXBUSY; +#if 0 CLR(tp->t_state, TS_BUSY); if (ISSET(tp->t_state, TS_FLUSH)) CLR(tp->t_state, TS_FLUSH); else ndflush(&tp->t_outq, cc); - ttyld_start(tp); - splx(s); - - return; - - error: - s = spltty(); - CLR(tp->t_state, TS_BUSY); - splx(s); - return; +#endif + ucomtty_outwakeup(tp); } static usbd_status @@ -758,8 +708,6 @@ ucomreadcb(usbd_xfer_handle xfer, usbd_private_handle p, usbd_status status) usbd_status err; u_int32_t cc; u_char *cp; - int lostcc; - int s; DPRINTF(("ucomreadcb: status = %d\n", status)); @@ -791,42 +739,20 @@ ucomreadcb(usbd_xfer_handle xfer, usbd_private_handle p, usbd_status status) } if (cc < 1) goto resubmit; - - s = spltty(); - if (tp->t_state & TS_CAN_BYPASS_L_RINT) { - if (tp->t_rawq.c_cc + cc > tp->t_ihiwat - && (sc->sc_state & UCS_RTS_IFLOW - || tp->t_iflag & IXOFF) - && !(tp->t_state & TS_TBLOCK)) - ttyblock(tp); - lostcc = b_to_q((char *)cp, cc, &tp->t_rawq); - tp->t_rawcc += cc; - ttwakeup(tp); - if (tp->t_state & TS_TTSTOP - && (tp->t_iflag & IXANY - || tp->t_cc[VSTART] == tp->t_cc[VSTOP])) { - tp->t_state &= ~TS_TTSTOP; - tp->t_lflag &= ~FLUSHO; - ucomstart(tp); - } - if (lostcc > 0) - printf("%s: lost %d chars\n", device_get_nameunit(sc->sc_dev), - lostcc); - } else { - /* Give characters to tty layer. */ - while (cc > 0) { - DPRINTFN(7, ("ucomreadcb: char = 0x%02x\n", *cp)); - if (ttyld_rint(tp, *cp) == -1) { - /* XXX what should we do? */ - printf("%s: lost %d chars\n", - device_get_nameunit(sc->sc_dev), cc); - break; - } - cc--; - cp++; + + /* Give characters to tty layer. */ + while (cc > 0) { + DPRINTFN(7, ("ucomreadcb: char = 0x%02x\n", *cp)); + if (ttydisc_rint(tp, *cp, 0) == -1) { + /* XXX what should we do? */ + printf("%s: lost %d chars\n", + device_get_nameunit(sc->sc_dev), cc); + break; } + cc--; + cp++; } - splx(s); + ttydisc_rint_done(tp); resubmit: err = ucomstartread(sc); @@ -835,9 +761,11 @@ ucomreadcb(usbd_xfer_handle xfer, usbd_private_handle p, usbd_status status) /* XXX what should we dow now? */ } +#if 0 if ((sc->sc_state & UCS_RTS_IFLOW) && !ISSET(sc->sc_mcr, SER_RTS) && !(tp->t_state & TS_TBLOCK)) - ucommodem(tp, SER_RTS, 0); + ucomtty_modem(tp, SER_RTS, 0); +#endif } static void diff --git a/sys/dev/usb/ucomvar.h b/sys/dev/usb/ucomvar.h index 4a5ddd2..7a35a0a 100644 --- a/sys/dev/usb/ucomvar.h +++ b/sys/dev/usb/ucomvar.h @@ -88,6 +88,7 @@ #define UCOM_UNK_PORTNO -1 /* XXX */ +struct tty; struct ucom_softc; struct ucom_callback { @@ -97,11 +98,11 @@ struct ucom_callback { #define UCOM_SET_RTS 2 #define UCOM_SET_BREAK 3 int (*ucom_param)(void *, int, struct termios *); - int (*ucom_ioctl)(void *, int, u_long, caddr_t, int, struct thread *); + int (*ucom_ioctl)(void *, int, u_long, caddr_t, struct thread *); int (*ucom_open)(void *, int); void (*ucom_close)(void *, int); void (*ucom_read)(void *, int, u_char **, u_int32_t *); - void (*ucom_write)(void *, int, u_char *, u_char *, u_int32_t *); + size_t (*ucom_write)(void *, int, struct tty *, u_char *, u_int32_t); }; /* line status register */ @@ -117,6 +118,7 @@ struct ucom_callback { /* ucom state declarations */ #define UCS_RXSTOP 0x0001 /* Rx stopped */ +#define UCS_TXBUSY 0x0002 /* Tx busy */ #define UCS_RTS_IFLOW 0x0008 /* use RTS input flow control */ struct ucom_softc { @@ -159,7 +161,7 @@ struct ucom_softc { extern devclass_t ucom_devclass; -int ucom_attach_tty(struct ucom_softc *, int, char*, int); +void ucom_attach_tty(struct ucom_softc *, char*, int); int ucom_attach(struct ucom_softc *); int ucom_detach(struct ucom_softc *); void ucom_status_change(struct ucom_softc *); diff --git a/sys/dev/usb/uftdi.c b/sys/dev/usb/uftdi.c index 22036e4..5a9921b 100644 --- a/sys/dev/usb/uftdi.c +++ b/sys/dev/usb/uftdi.c @@ -118,8 +118,8 @@ static void uftdi_set(void *, int, int, int); static int uftdi_param(void *, int, struct termios *); static int uftdi_open(void *sc, int portno); static void uftdi_read(void *sc, int portno, u_char **ptr,u_int32_t *count); -static void uftdi_write(void *sc, int portno, u_char *to, u_char *from, - u_int32_t *count); +static size_t uftdi_write(void *sc, int portno, struct tty *, + u_char *to, u_int32_t count); static void uftdi_break(void *sc, int portno, int onoff); static int uftdi_8u232am_getrate(speed_t speed, int *rate); @@ -486,20 +486,25 @@ uftdi_read(void *vsc, int portno, u_char **ptr, u_int32_t *count) *count -= 2; } -static void -uftdi_write(void *vsc, int portno, u_char *to, u_char *from, u_int32_t *count) +static size_t +uftdi_write(void *vsc, int portno, struct tty *tp, u_char *to, u_int32_t count) { struct uftdi_softc *sc = vsc; + size_t l; - DPRINTFN(10,("uftdi_write: sc=%p, port=%d count=%u data[0]=0x%02x\n", - vsc, portno, *count, from[0])); + DPRINTFN(10,("uftdi_write: sc=%p, port=%d tp=%p, count=%u\n", + vsc, portno, tp, count)); - /* Make length tag and copy data */ - if (sc->sc_hdrlen > 0) - *to = FTDI_OUT_TAG(*count, portno); + /* Leave space for the length tag. */ + l = ttydisc_getc(tp, to + sc->sc_hdrlen, count - sc->sc_hdrlen); + if (l == 0) + return (0); - memcpy(to + sc->sc_hdrlen, from, *count); - *count += sc->sc_hdrlen; + /* Make length tag. */ + if (sc->sc_hdrlen > 0) + *to = FTDI_OUT_TAG(l, portno); + + return (l + sc->sc_hdrlen); } static void diff --git a/sys/dev/usb/umodem.c b/sys/dev/usb/umodem.c index 6424496..89b3e05 100644 --- a/sys/dev/usb/umodem.c +++ b/sys/dev/usb/umodem.c @@ -187,7 +187,7 @@ static void umodem_rts(struct umodem_softc *, int); static void umodem_break(struct umodem_softc *, int); static void umodem_set_line_state(struct umodem_softc *); static int umodem_param(void *, int, struct termios *); -static int umodem_ioctl(void *, int, u_long, caddr_t, int, struct thread *); +static int umodem_ioctl(void *, int, u_long, caddr_t, struct thread *); static int umodem_open(void *, int portno); static void umodem_close(void *, int portno); static void umodem_intr(usbd_xfer_handle, usbd_private_handle, usbd_status); @@ -611,7 +611,7 @@ umodem_param(void *addr, int portno, struct termios *t) } int -umodem_ioctl(void *addr, int portno, u_long cmd, caddr_t data, int flag, +umodem_ioctl(void *addr, int portno, u_long cmd, caddr_t data, struct thread *p) { struct umodem_softc *sc = addr; diff --git a/sys/fs/devfs/devfs_vnops.c b/sys/fs/devfs/devfs_vnops.c index 7218119..63ab7d4 100644 --- a/sys/fs/devfs/devfs_vnops.c +++ b/sys/fs/devfs/devfs_vnops.c @@ -597,8 +597,6 @@ devfs_ioctl_f(struct file *fp, u_long com, void *data, struct ucred *cred, struc return (0); } - mtx_lock(&Giant); /* XXX TTY */ - vpold = td->td_proc->p_session->s_ttyvp; VREF(vp); SESS_LOCK(td->td_proc->p_session); @@ -610,7 +608,6 @@ devfs_ioctl_f(struct file *fp, u_long com, void *data, struct ucred *cred, struc /* Get rid of reference to old control tty */ if (vpold) vrele(vpold); - mtx_unlock(&Giant); /* XXX TTY */ } return (error); } diff --git a/sys/i386/conf/GENERIC b/sys/i386/conf/GENERIC index 587fe3e..c70d1a0 100644 --- a/sys/i386/conf/GENERIC +++ b/sys/i386/conf/GENERIC @@ -272,7 +272,7 @@ device loop # Network loopback device random # Entropy device device ether # Ethernet support device tun # Packet tunnel. -device pty # Pseudo-ttys (telnet etc) +device pty # BSD-style compatibility pseudo ttys device md # Memory "disks" device gif # IPv6 and IPv4 tunneling device faith # IPv6-to-IPv4 relaying (translation) diff --git a/sys/i386/conf/XBOX b/sys/i386/conf/XBOX index 680c7c4..eec4554 100644 --- a/sys/i386/conf/XBOX +++ b/sys/i386/conf/XBOX @@ -71,7 +71,7 @@ device io # I/O device device random # Entropy device device ether # Ethernet support #device tun # Packet tunnel. -device pty # Pseudo-ttys (telnet etc) +device pty # BSD-style compatibility pseudo ttys #device md # Memory "disks" #device gif # IPv6 and IPv4 tunneling #device faith # IPv6-to-IPv4 relaying (translation) diff --git a/sys/i386/ibcs2/ibcs2_ioctl.c b/sys/i386/ibcs2/ibcs2_ioctl.c index bbaab39..0275c98 100644 --- a/sys/i386/ibcs2/ibcs2_ioctl.c +++ b/sys/i386/ibcs2/ibcs2_ioctl.c @@ -88,6 +88,11 @@ ibcs2_stty(struct thread *td, struct ibcs2_stty_args *args) * iBCS2 ioctl calls. */ +struct speedtab { + int sp_speed; /* Speed. */ + int sp_code; /* Code. */ +}; + static struct speedtab sptab[] = { { 0, 0 }, { 50, 1 }, @@ -128,6 +133,16 @@ static u_long s2btab[] = { 38400, }; +static int +ttspeedtab(int speed, struct speedtab *table) +{ + + for ( ; table->sp_speed != -1; table++) + if (table->sp_speed == speed) + return (table->sp_code); + return (-1); +} + static void stios2btios(st, bt) struct ibcs2_termios *st; @@ -154,7 +169,7 @@ stios2btios(st, bt) l = st->c_oflag; r = 0; if (l & IBCS2_OPOST) r |= OPOST; if (l & IBCS2_ONLCR) r |= ONLCR; - if (l & IBCS2_TAB3) r |= OXTABS; + if (l & IBCS2_TAB3) r |= TAB3; bt->c_oflag = r; l = st->c_cflag; r = 0; @@ -248,7 +263,7 @@ btios2stios(bt, st) l = bt->c_oflag; r = 0; if (l & OPOST) r |= IBCS2_OPOST; if (l & ONLCR) r |= IBCS2_ONLCR; - if (l & OXTABS) r |= IBCS2_TAB3; + if (l & TAB3) r |= IBCS2_TAB3; st->c_oflag = r; l = bt->c_cflag; r = 0; diff --git a/sys/ia64/conf/GENERIC b/sys/ia64/conf/GENERIC index ae94a22..793190d 100644 --- a/sys/ia64/conf/GENERIC +++ b/sys/ia64/conf/GENERIC @@ -151,7 +151,7 @@ device faith # IPv6-to-IPv4 relaying (translation) device gif # IPv6 and IPv4 tunneling device loop # Network loopback device md # Memory "disks" -device pty # Pseudo-ttys (telnet etc) +device pty # BSD-style compatibility pseudo ttys device puc # Multi I/O cards and multi-channel UARTs device random # Entropy device device tun # Packet tunnel. diff --git a/sys/ia64/conf/SKI b/sys/ia64/conf/SKI index 377ee93..001c710 100644 --- a/sys/ia64/conf/SKI +++ b/sys/ia64/conf/SKI @@ -53,7 +53,7 @@ device pci # PCI bus support device ether # Ethernet support device loop # Network loopback device md # Memory "disks" -device pty # Pseudo-ttys (telnet etc) +device pty # BSD-style compatibility pseudo ttys device random # Entropy device device tun # Packet tunnel. diff --git a/sys/ia64/ia64/ssc.c b/sys/ia64/ia64/ssc.c index 274cc0d..04144c5 100644 --- a/sys/ia64/ia64/ssc.c +++ b/sys/ia64/ia64/ssc.c @@ -54,26 +54,22 @@ #define SSC_POLL_HZ 50 -static d_open_t ssc_open; -static d_close_t ssc_close; - -static struct cdevsw ssc_cdevsw = { - .d_version = D_VERSION, - .d_open = ssc_open, - .d_close = ssc_close, - .d_name = "ssc", - .d_flags = D_TTY | D_NEEDGIANT, +static tsw_open_t ssc_open; +static tsw_outwakeup_t ssc_outwakeup; +static tsw_close_t ssc_close; + +static struct ttydevsw ssc_class = { + .tsw_flags = TF_NOPREFIX, + .tsw_open = ssc_open, + .tsw_outwakeup = ssc_outwakeup, + .tsw_close = ssc_close, }; -static struct tty *ssc_tp = NULL; static int polltime; static struct callout_handle ssc_timeouthandle = CALLOUT_HANDLE_INITIALIZER(&ssc_timeouthandle); -static void ssc_start(struct tty *); static void ssc_timeout(void *); -static int ssc_param(struct tty *, struct termios *); -static void ssc_stop(struct tty *, int); static u_int64_t ssc(u_int64_t in0, u_int64_t in1, u_int64_t in2, u_int64_t in3, int which) @@ -90,7 +86,8 @@ ssc(u_int64_t in0, u_int64_t in1, u_int64_t in2, u_int64_t in3, int which) static void ssc_cnprobe(struct consdev *cp) { - sprintf(cp->cn_name, "ssccons"); + + strcpy(cp->cn_name, "ssccons"); cp->cn_pri = CN_INTERNAL; } @@ -107,8 +104,10 @@ ssc_cnterm(struct consdev *cp) static void ssc_cnattach(void *arg) { - make_dev(&ssc_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600, "ssccons"); - ssc_tp = ttyalloc(); + struct tty *tp; + + tp = tty_alloc(&ssc_class, NULL, NULL); + tty_makedev(tp, NULL, "ssccons"); } SYSINIT(ssc_cnattach, SI_SUB_DRIVERS, SI_ORDER_ANY, ssc_cnattach, 0); @@ -130,100 +129,39 @@ ssc_cngetc(struct consdev *cp) } static int -ssc_open(struct cdev *dev, int flag, int mode, struct thread *td) +ssc_open(struct tty *tp) { - struct tty *tp; - int s; - int error = 0, setuptimeout = 0; - - tp = dev->si_tty = ssc_tp; - - s = spltty(); - tp->t_oproc = ssc_start; - tp->t_param = ssc_param; - tp->t_stop = ssc_stop; - tp->t_dev = dev; - if ((tp->t_state & TS_ISOPEN) == 0) { - tp->t_state |= TS_CARR_ON; - ttyconsolemode(tp, 0); - - setuptimeout = 1; - } else if ((tp->t_state & TS_XCLUDE) && - priv_check(td, PRIV_TTY_EXCLUSIVE)) { - splx(s); - return EBUSY; - } - - splx(s); - error = ttyld_open(tp, dev); + polltime = hz / SSC_POLL_HZ; + if (polltime < 1) + polltime = 1; + ssc_timeouthandle = timeout(ssc_timeout, tp, polltime); - if (error == 0 && setuptimeout) { - polltime = hz / SSC_POLL_HZ; - if (polltime < 1) - polltime = 1; - ssc_timeouthandle = timeout(ssc_timeout, tp, polltime); - } - return error; + return (0); } -static int -ssc_close(struct cdev *dev, int flag, int mode, struct thread *td) +static void +ssc_close(struct tty *tp) { - int unit = minor(dev); - struct tty *tp = ssc_tp; - - if (unit != 0) - return ENXIO; untimeout(ssc_timeout, tp, ssc_timeouthandle); - ttyld_close(tp, flag); - tty_close(tp); - return 0; -} - -static int -ssc_param(struct tty *tp, struct termios *t) -{ - - return 0; } static void -ssc_start(struct tty *tp) +ssc_outwakeup(struct tty *tp) { - int s; + char buf[128]; + size_t len, c; - s = spltty(); + for (;;) { + len = ttydisc_getc(tp, buf, sizeof buf); + if (len == 0) + break; - if (tp->t_state & (TS_TIMEOUT | TS_TTSTOP)) { - ttwwakeup(tp); - splx(s); - return; + c = 0; + while (len-- > 0) + ssc_cnputc(NULL, buf[c++]); } - - tp->t_state |= TS_BUSY; - while (tp->t_outq.c_cc != 0) - ssc_cnputc(NULL, getc(&tp->t_outq)); - tp->t_state &= ~TS_BUSY; - - ttwwakeup(tp); - splx(s); -} - -/* - * Stop output on a line. - */ -static void -ssc_stop(struct tty *tp, int flag) -{ - int s; - - s = spltty(); - if (tp->t_state & TS_BUSY) - if ((tp->t_state & TS_TTSTOP) == 0) - tp->t_state |= TS_FLUSH; - splx(s); } static void @@ -232,10 +170,12 @@ ssc_timeout(void *v) struct tty *tp = v; int c; - while ((c = ssc_cngetc(NULL)) != -1) { - if (tp->t_state & TS_ISOPEN) - ttyld_rint(tp, c); - } + tty_lock(tp); + while ((c = ssc_cngetc(NULL)) != -1) + ttydisc_rint(tp, c, 0); + ttydisc_rint_done(tp); + tty_unlock(tp); + ssc_timeouthandle = timeout(ssc_timeout, tp, polltime); } diff --git a/sys/kern/init_main.c b/sys/kern/init_main.c index f726d27..3a51967 100644 --- a/sys/kern/init_main.c +++ b/sys/kern/init_main.c @@ -415,7 +415,7 @@ proc0_init(void *dummy __unused) pgrp0.pg_session = &session0; mtx_init(&session0.s_mtx, "session", NULL, MTX_DEF); - session0.s_count = 1; + refcount_init(&session0.s_count, 1); session0.s_leader = p; p->p_sysent = &null_sysvec; diff --git a/sys/kern/init_sysent.c b/sys/kern/init_sysent.c index 22db8a8..1dcfbab 100644 --- a/sys/kern/init_sysent.c +++ b/sys/kern/init_sysent.c @@ -3,7 +3,7 @@ * * DO NOT EDIT-- this file is automatically generated. * $FreeBSD$ - * created from FreeBSD: src/sys/kern/syscalls.master,v 1.242 2008/03/31 12:06:55 kib Exp + * created from FreeBSD: head/sys/kern/syscalls.master 178888 2008-05-09 23:03:00Z julian */ #include "opt_compat.h" @@ -532,4 +532,5 @@ struct sysent sysent[] = { { AS(renameat_args), (sy_call_t *)renameat, AUE_RENAMEAT, NULL, 0, 0 }, /* 501 = renameat */ { AS(symlinkat_args), (sy_call_t *)symlinkat, AUE_SYMLINKAT, NULL, 0, 0 }, /* 502 = symlinkat */ { AS(unlinkat_args), (sy_call_t *)unlinkat, AUE_UNLINKAT, NULL, 0, 0 }, /* 503 = unlinkat */ + { AS(posix_openpt_args), (sy_call_t *)posix_openpt, AUE_POSIXOPENPT, NULL, 0, 0 }, /* 504 = posix_openpt */ }; diff --git a/sys/kern/kern_acct.c b/sys/kern/kern_acct.c index 76d4c75..e505f62 100644 --- a/sys/kern/kern_acct.c +++ b/sys/kern/kern_acct.c @@ -402,12 +402,12 @@ acct_process(struct thread *td) acct.ac_gid = p->p_ucred->cr_rgid; /* (7) The terminal from which the process was started */ - SESS_LOCK(p->p_session); + sx_slock(&proctree_lock); if ((p->p_flag & P_CONTROLT) && p->p_pgrp->pg_session->s_ttyp) - acct.ac_tty = dev2udev(p->p_pgrp->pg_session->s_ttyp->t_dev); + acct.ac_tty = tty_udev(p->p_pgrp->pg_session->s_ttyp); else acct.ac_tty = NODEV; - SESS_UNLOCK(p->p_session); + sx_sunlock(&proctree_lock); /* (8) The boolean flags that tell how the process terminated, etc. */ acct.ac_flagx = p->p_acflag; diff --git a/sys/kern/kern_conf.c b/sys/kern/kern_conf.c index 4c10871..409652f 100644 --- a/sys/kern/kern_conf.c +++ b/sys/kern/kern_conf.c @@ -42,7 +42,6 @@ __FBSDID("$FreeBSD$"); #include <sys/poll.h> #include <sys/sx.h> #include <sys/ctype.h> -#include <sys/tty.h> #include <sys/ucred.h> #include <sys/taskqueue.h> #include <machine/stdarg.h> @@ -611,14 +610,6 @@ prep_cdevsw(struct cdevsw *devsw) devsw->d_kqfilter = dead_kqfilter; } - if (devsw->d_flags & D_TTY) { - if (devsw->d_ioctl == NULL) devsw->d_ioctl = ttyioctl; - if (devsw->d_read == NULL) devsw->d_read = ttyread; - if (devsw->d_write == NULL) devsw->d_write = ttywrite; - if (devsw->d_kqfilter == NULL) devsw->d_kqfilter = ttykqfilter; - if (devsw->d_poll == NULL) devsw->d_poll = ttypoll; - } - if (devsw->d_flags & D_NEEDGIANT) { if (devsw->d_gianttrick == NULL) { memcpy(dsw2, devsw, sizeof *dsw2); @@ -692,11 +683,9 @@ make_dev_credv(int flags, struct cdevsw *devsw, int minornr, } dev->si_flags |= SI_NAMED; -#ifdef MAC if (cr != NULL) dev->si_cred = crhold(cr); else -#endif dev->si_cred = NULL; dev->si_uid = uid; dev->si_gid = gid; diff --git a/sys/kern/kern_descrip.c b/sys/kern/kern_descrip.c index b7a486b..3c0858c 100644 --- a/sys/kern/kern_descrip.c +++ b/sys/kern/kern_descrip.c @@ -70,6 +70,7 @@ __FBSDID("$FreeBSD$"); #include <sys/syscallsubr.h> #include <sys/sysctl.h> #include <sys/sysproto.h> +#include <sys/tty.h> #include <sys/unistd.h> #include <sys/user.h> #include <sys/vnode.h> @@ -2564,6 +2565,7 @@ sysctl_kern_proc_filedesc(SYSCTL_HANDLER_ARGS) struct vnode *vp; struct file *fp; struct proc *p; + struct tty *tp; int vfslocked; name = (int *)arg1; @@ -2595,6 +2597,7 @@ sysctl_kern_proc_filedesc(SYSCTL_HANDLER_ARGS) kif->kf_structsize = sizeof(*kif); vp = NULL; so = NULL; + tp = NULL; kif->kf_fd = i; switch (fp->f_type) { case DTYPE_VNODE: @@ -2637,6 +2640,11 @@ sysctl_kern_proc_filedesc(SYSCTL_HANDLER_ARGS) kif->kf_type = KF_TYPE_SEM; break; + case DTYPE_PTS: + kif->kf_type = KF_TYPE_PTS; + tp = fp->f_data; + break; + default: kif->kf_type = KF_TYPE_UNKNOWN; break; @@ -2730,6 +2738,10 @@ sysctl_kern_proc_filedesc(SYSCTL_HANDLER_ARGS) kif->kf_sock_type = so->so_type; kif->kf_sock_protocol = so->so_proto->pr_protocol; } + if (tp != NULL) { + strlcpy(kif->kf_path, tty_devname(tp), + sizeof(kif->kf_path)); + } error = SYSCTL_OUT(req, kif, sizeof(*kif)); if (error) break; diff --git a/sys/kern/kern_exit.c b/sys/kern/kern_exit.c index 9e53316..f0866ed 100644 --- a/sys/kern/kern_exit.c +++ b/sys/kern/kern_exit.c @@ -119,9 +119,8 @@ void exit1(struct thread *td, int rv) { struct proc *p, *nq, *q; - struct tty *tp; - struct vnode *ttyvp; struct vnode *vtmp; + struct vnode *ttyvp = NULL; #ifdef KTRACE struct vnode *tracevp; struct ucred *tracecred; @@ -298,56 +297,48 @@ exit1(struct thread *td, int rv) vmspace_exit(td); - mtx_lock(&Giant); /* XXX TTY */ sx_xlock(&proctree_lock); if (SESS_LEADER(p)) { struct session *sp; sp = p->p_session; - if (sp->s_ttyvp) { + + SESS_LOCK(sp); + ttyvp = sp->s_ttyvp; + sp->s_ttyvp = NULL; + SESS_UNLOCK(sp); + + if (ttyvp != NULL) { /* * Controlling process. - * Signal foreground pgrp, - * drain controlling terminal - * and revoke access to controlling terminal. + * Signal foreground pgrp and revoke access to + * controlling terminal. + * + * There is no need to drain the terminal here, + * because this will be done on revocation. */ - if (sp->s_ttyp && (sp->s_ttyp->t_session == sp)) { - tp = sp->s_ttyp; - if (sp->s_ttyp->t_pgrp) { - PGRP_LOCK(sp->s_ttyp->t_pgrp); - pgsignal(sp->s_ttyp->t_pgrp, SIGHUP, 1); - PGRP_UNLOCK(sp->s_ttyp->t_pgrp); - } - /* XXX tp should be locked. */ - sx_xunlock(&proctree_lock); - (void) ttywait(tp); - sx_xlock(&proctree_lock); + if (sp->s_ttyp != NULL) { + struct tty *tp = sp->s_ttyp; + + tty_lock(tp); + tty_signal_pgrp(tp, SIGHUP); + tty_unlock(tp); + /* * The tty could have been revoked * if we blocked. */ - if (sp->s_ttyvp) { - ttyvp = sp->s_ttyvp; - SESS_LOCK(p->p_session); - sp->s_ttyvp = NULL; - SESS_UNLOCK(p->p_session); + if (ttyvp->v_type != VBAD) { sx_xunlock(&proctree_lock); VOP_LOCK(ttyvp, LK_EXCLUSIVE); VOP_REVOKE(ttyvp, REVOKEALL); - vput(ttyvp); + VOP_UNLOCK(ttyvp, 0); sx_xlock(&proctree_lock); } } - if (sp->s_ttyvp) { - ttyvp = sp->s_ttyvp; - SESS_LOCK(p->p_session); - sp->s_ttyvp = NULL; - SESS_UNLOCK(p->p_session); - vrele(ttyvp); - } /* - * s_ttyp is not zero'd; we use this to indicate - * that the session once had a controlling terminal. + * s_ttyp is not zero'd; we use this to indicate that + * the session once had a controlling terminal. * (for logging and informational purposes) */ } @@ -358,7 +349,10 @@ exit1(struct thread *td, int rv) fixjobc(p, p->p_pgrp, 0); sx_xunlock(&proctree_lock); (void)acct_process(td); - mtx_unlock(&Giant); + + /* Release the TTY now we've unlocked everything. */ + if (ttyvp != NULL) + vrele(ttyvp); #ifdef KTRACE /* * Disable tracing, then drain any pending records and release diff --git a/sys/kern/kern_proc.c b/sys/kern/kern_proc.c index 34ffaf1..25820ea 100644 --- a/sys/kern/kern_proc.c +++ b/sys/kern/kern_proc.c @@ -352,14 +352,13 @@ enterpgrp(p, pgid, pgrp, sess) * new session */ mtx_init(&sess->s_mtx, "session", NULL, MTX_DEF); - mtx_lock(&Giant); /* XXX TTY */ PROC_LOCK(p); p->p_flag &= ~P_CONTROLT; PROC_UNLOCK(p); PGRP_LOCK(pgrp); sess->s_leader = p; sess->s_sid = p->p_pid; - sess->s_count = 1; + refcount_init(&sess->s_count, 1); sess->s_ttyvp = NULL; sess->s_ttyp = NULL; bcopy(p->p_session->s_login, sess->s_login, @@ -368,11 +367,8 @@ enterpgrp(p, pgid, pgrp, sess) KASSERT(p == curproc, ("enterpgrp: mksession and p != curproc")); } else { - mtx_lock(&Giant); /* XXX TTY */ pgrp->pg_session = p->p_session; - SESS_LOCK(pgrp->pg_session); - pgrp->pg_session->s_count++; - SESS_UNLOCK(pgrp->pg_session); + sess_hold(pgrp->pg_session); PGRP_LOCK(pgrp); } pgrp->pg_id = pgid; @@ -386,7 +382,6 @@ enterpgrp(p, pgid, pgrp, sess) pgrp->pg_jobc = 0; SLIST_INIT(&pgrp->pg_sigiolst); PGRP_UNLOCK(pgrp); - mtx_unlock(&Giant); /* XXX TTY */ doenterpgrp(p, pgrp); @@ -446,7 +441,6 @@ doenterpgrp(p, pgrp) fixjobc(p, pgrp, 1); fixjobc(p, p->p_pgrp, 0); - mtx_lock(&Giant); /* XXX TTY */ PGRP_LOCK(pgrp); PGRP_LOCK(savepgrp); PROC_LOCK(p); @@ -456,7 +450,6 @@ doenterpgrp(p, pgrp) LIST_INSERT_HEAD(&pgrp->pg_members, p, p_pglist); PGRP_UNLOCK(savepgrp); PGRP_UNLOCK(pgrp); - mtx_unlock(&Giant); /* XXX TTY */ if (LIST_EMPTY(&savepgrp->pg_members)) pgdelete(savepgrp); } @@ -472,14 +465,12 @@ leavepgrp(p) sx_assert(&proctree_lock, SX_XLOCKED); savepgrp = p->p_pgrp; - mtx_lock(&Giant); /* XXX TTY */ PGRP_LOCK(savepgrp); PROC_LOCK(p); LIST_REMOVE(p, p_pglist); p->p_pgrp = NULL; PROC_UNLOCK(p); PGRP_UNLOCK(savepgrp); - mtx_unlock(&Giant); /* XXX TTY */ if (LIST_EMPTY(&savepgrp->pg_members)) pgdelete(savepgrp); return (0); @@ -493,6 +484,7 @@ pgdelete(pgrp) register struct pgrp *pgrp; { struct session *savesess; + struct tty *tp; sx_assert(&proctree_lock, SX_XLOCKED); PGRP_LOCK_ASSERT(pgrp, MA_NOTOWNED); @@ -504,18 +496,22 @@ pgdelete(pgrp) */ funsetownlst(&pgrp->pg_sigiolst); - mtx_lock(&Giant); /* XXX TTY */ PGRP_LOCK(pgrp); - if (pgrp->pg_session->s_ttyp != NULL && - pgrp->pg_session->s_ttyp->t_pgrp == pgrp) - pgrp->pg_session->s_ttyp->t_pgrp = NULL; + tp = pgrp->pg_session->s_ttyp; LIST_REMOVE(pgrp, pg_hash); savesess = pgrp->pg_session; - SESSRELE(savesess); PGRP_UNLOCK(pgrp); + + /* Remove the reference to the pgrp before deallocating it. */ + if (tp != NULL) { + tty_lock(tp); + tty_rel_pgrp(tp, pgrp); + tty_unlock(tp); + } + mtx_destroy(&pgrp->pg_mtx); FREE(pgrp, M_PGRP); - mtx_unlock(&Giant); /* XXX TTY */ + sess_release(savesess); } static void @@ -618,16 +614,21 @@ orphanpg(pg) } void -sessrele(struct session *s) +sess_hold(struct session *s) { - int i; - - SESS_LOCK(s); - i = --s->s_count; - SESS_UNLOCK(s); - if (i == 0) { - if (s->s_ttyp != NULL) - ttyrel(s->s_ttyp); + + refcount_acquire(&s->s_count); +} + +void +sess_release(struct session *s) +{ + + if (refcount_release(&s->s_count)) { + if (s->s_ttyp != NULL) { + tty_lock(s->s_ttyp); + tty_rel_sess(s->s_ttyp, s); + } mtx_destroy(&s->s_mtx); FREE(s, M_SESSION); } @@ -779,12 +780,13 @@ fill_kinfo_proc_only(struct proc *p, struct kinfo_proc *kp) kp->ki_kiflag |= KI_CTTY; if (SESS_LEADER(p)) kp->ki_kiflag |= KI_SLEADER; + /* XXX proctree_lock */ tp = sp->s_ttyp; SESS_UNLOCK(sp); } } if ((p->p_flag & P_CONTROLT) && tp != NULL) { - kp->ki_tdev = dev2udev(tp->t_dev); + kp->ki_tdev = tty_udev(tp); kp->ki_tpgid = tp->t_pgrp ? tp->t_pgrp->pg_id : NO_PID; if (tp->t_session) kp->ki_tsid = tp->t_session->s_sid; @@ -1122,9 +1124,10 @@ sysctl_kern_proc(SYSCTL_HANDLER_ARGS) PROC_UNLOCK(p); continue; } + /* XXX proctree_lock */ SESS_LOCK(p->p_session); if (p->p_session->s_ttyp == NULL || - dev2udev(p->p_session->s_ttyp->t_dev) != + tty_udev(p->p_session->s_ttyp) != (dev_t)name[0]) { SESS_UNLOCK(p->p_session); PROC_UNLOCK(p); diff --git a/sys/kern/kern_resource.c b/sys/kern/kern_resource.c index 8267c29..9a15319 100644 --- a/sys/kern/kern_resource.c +++ b/sys/kern/kern_resource.c @@ -1329,3 +1329,28 @@ chgsbsize(uip, hiwat, to, max) *hiwat = to; return (1); } + +/* + * Change the count associated with number of pseudo-terminals + * a given user is using. When 'max' is 0, don't enforce a limit + */ +int +chgptscnt(uip, diff, max) + struct uidinfo *uip; + int diff; + rlim_t max; +{ + + /* Don't allow them to exceed max, but allow subtraction. */ + if (diff > 0 && max != 0) { + if (atomic_fetchadd_long(&uip->ui_ptscnt, (long)diff) + diff > max) { + atomic_subtract_long(&uip->ui_ptscnt, (long)diff); + return (0); + } + } else { + atomic_add_long(&uip->ui_ptscnt, (long)diff); + if (uip->ui_ptscnt < 0) + printf("negative ptscnt for uid = %d\n", uip->ui_uid); + } + return (1); +} diff --git a/sys/kern/subr_prf.c b/sys/kern/subr_prf.c index a0605ab..dea4034 100644 --- a/sys/kern/subr_prf.c +++ b/sys/kern/subr_prf.c @@ -136,7 +136,7 @@ uprintf(const char *fmt, ...) if (td == NULL || TD_IS_IDLETHREAD(td)) return (0); - mtx_lock(&Giant); + sx_slock(&proctree_lock); p = td->td_proc; PROC_LOCK(p); if ((p->p_flag & P_CONTROLT) == 0) { @@ -154,10 +154,12 @@ uprintf(const char *fmt, ...) } pca.flags = TOTTY; va_start(ap, fmt); + tty_lock(pca.tty); retval = kvprintf(fmt, putchar, &pca, 10, ap); + tty_unlock(pca.tty); va_end(ap); out: - mtx_unlock(&Giant); + sx_sunlock(&proctree_lock); return (retval); } @@ -174,19 +176,17 @@ tprintf(struct proc *p, int pri, const char *fmt, ...) struct putchar_arg pca; struct session *sess = NULL; - mtx_lock(&Giant); + sx_slock(&proctree_lock); if (pri != -1) flags |= TOLOG; if (p != NULL) { PROC_LOCK(p); if (p->p_flag & P_CONTROLT && p->p_session->s_ttyvp) { sess = p->p_session; - SESS_LOCK(sess); + sess_hold(sess); PROC_UNLOCK(p); - SESSHOLD(sess); tp = sess->s_ttyp; - SESS_UNLOCK(sess); - if (ttycheckoutq(tp, 0)) + if (tp != NULL && tty_checkoutq(tp)) flags |= TOTTY; else tp = NULL; @@ -197,12 +197,16 @@ tprintf(struct proc *p, int pri, const char *fmt, ...) pca.tty = tp; pca.flags = flags; va_start(ap, fmt); + if (pca.tty != NULL) + tty_lock(pca.tty); kvprintf(fmt, putchar, &pca, 10, ap); + if (pca.tty != NULL) + tty_unlock(pca.tty); va_end(ap); if (sess != NULL) - SESSRELE(sess); + sess_release(sess); msgbuftrigger = 1; - mtx_unlock(&Giant); + sx_sunlock(&proctree_lock); } /* @@ -413,7 +417,7 @@ putchar(int c, void *arg) putcons(c, ap); } else { if ((flags & TOTTY) && tp != NULL) - tputchar(c, tp); + tty_putchar(tp, c); if (flags & TOCONS) { if (constty != NULL) msgbuf_addchar(&consmsgbuf, c); diff --git a/sys/kern/syscalls.c b/sys/kern/syscalls.c index 8fb0127..0a057ce 100644 --- a/sys/kern/syscalls.c +++ b/sys/kern/syscalls.c @@ -3,7 +3,7 @@ * * DO NOT EDIT-- this file is automatically generated. * $FreeBSD$ - * created from FreeBSD: src/sys/kern/syscalls.master,v 1.242 2008/03/31 12:06:55 kib Exp + * created from FreeBSD: head/sys/kern/syscalls.master 178888 2008-05-09 23:03:00Z julian */ const char *syscallnames[] = { @@ -511,4 +511,5 @@ const char *syscallnames[] = { "renameat", /* 501 = renameat */ "symlinkat", /* 502 = symlinkat */ "unlinkat", /* 503 = unlinkat */ + "posix_openpt", /* 504 = posix_openpt */ }; diff --git a/sys/kern/syscalls.master b/sys/kern/syscalls.master index 4cb55fa..8ed20d4 100644 --- a/sys/kern/syscalls.master +++ b/sys/kern/syscalls.master @@ -886,5 +886,6 @@ 502 AUE_SYMLINKAT STD { int symlinkat(char *path1, int fd, \ char *path2); } 503 AUE_UNLINKAT STD { int unlinkat(int fd, char *path, int flag); } +504 AUE_POSIXOPENPT STD { int posix_openpt(int flags); } ; Please copy any additions and changes to the following compatability tables: ; sys/compat/freebsd32/syscalls.master diff --git a/sys/kern/systrace_args.c b/sys/kern/systrace_args.c index 98558cc..097bf21 100644 --- a/sys/kern/systrace_args.c +++ b/sys/kern/systrace_args.c @@ -3054,6 +3054,13 @@ systrace_args(int sysnum, void *params, u_int64_t *uarg, int *n_args) *n_args = 3; break; } + /* posix_openpt */ + case 504: { + struct posix_openpt_args *p = params; + iarg[0] = p->flags; /* int */ + *n_args = 1; + break; + } default: *n_args = 0; break; @@ -4607,6 +4614,16 @@ systrace_setargdesc(int sysnum, int ndx, char *desc, size_t descsz) break; }; break; + /* setfib */ + case 175: + switch(ndx) { + case 0: + p = "int"; + break; + default: + break; + }; + break; /* ntp_adjtime */ case 176: switch(ndx) { @@ -8093,6 +8110,16 @@ systrace_setargdesc(int sysnum, int ndx, char *desc, size_t descsz) break; }; break; + /* posix_openpt */ + case 504: + switch(ndx) { + case 0: + p = "int"; + break; + default: + break; + }; + break; default: break; }; 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 */ diff --git a/sys/kern/tty_compat.c b/sys/kern/tty_compat.c index 4e2c16f..6736cfb 100644 --- a/sys/kern/tty_compat.c +++ b/sys/kern/tty_compat.c @@ -45,6 +45,11 @@ __FBSDID("$FreeBSD$"); #include <sys/kernel.h> #include <sys/sysctl.h> +struct speedtab { + int sp_speed; /* Speed. */ + int sp_code; /* Code. */ +}; + static int ttcompatgetflags(struct tty *tp); static void ttcompatsetflags(struct tty *tp, struct termios *t); static void ttcompatsetlflags(struct tty *tp, struct termios *t); @@ -102,16 +107,18 @@ ttsetcompat(struct tty *tp, u_long *com, caddr_t data, struct termios *term) if ((speed = sg->sg_ispeed) > MAX_SPEED || speed < 0) return(EINVAL); - else if (speed != ttcompatspeedtab(tp->t_ispeed, compatspeeds)) + else if (speed != ttcompatspeedtab(tp->t_termios.c_ispeed, + compatspeeds)) term->c_ispeed = compatspcodes[speed]; else - term->c_ispeed = tp->t_ispeed; + term->c_ispeed = tp->t_termios.c_ispeed; if ((speed = sg->sg_ospeed) > MAX_SPEED || speed < 0) return(EINVAL); - else if (speed != ttcompatspeedtab(tp->t_ospeed, compatspeeds)) + else if (speed != ttcompatspeedtab(tp->t_termios.c_ospeed, + compatspeeds)) term->c_ospeed = compatspcodes[speed]; else - term->c_ospeed = tp->t_ospeed; + term->c_ospeed = tp->t_termios.c_ospeed; term->c_cc[VERASE] = sg->sg_erase; term->c_cc[VKILL] = sg->sg_kill; tp->t_flags = (tp->t_flags&0xffff0000) | (sg->sg_flags&0xffff); @@ -171,7 +178,7 @@ ttsetcompat(struct tty *tp, u_long *com, caddr_t data, struct termios *term) /*ARGSUSED*/ int -ttcompat(struct tty *tp, u_long com, caddr_t data, int flag) +tty_ioctl_compat(struct tty *tp, u_long com, caddr_t data, struct thread *td) { switch (com) { case TIOCSETP: @@ -187,17 +194,19 @@ ttcompat(struct tty *tp, u_long com, caddr_t data, int flag) term = tp->t_termios; if ((error = ttsetcompat(tp, &com, data, &term)) != 0) return error; - return ttioctl(tp, com, &term, flag); + return tty_ioctl(tp, com, &term, td); } case TIOCGETP: { struct sgttyb *sg = (struct sgttyb *)data; - cc_t *cc = tp->t_cc; + cc_t *cc = tp->t_termios.c_cc; - sg->sg_ospeed = ttcompatspeedtab(tp->t_ospeed, compatspeeds); - if (tp->t_ispeed == 0) + sg->sg_ospeed = ttcompatspeedtab(tp->t_termios.c_ospeed, + compatspeeds); + if (tp->t_termios.c_ispeed == 0) sg->sg_ispeed = sg->sg_ospeed; else - sg->sg_ispeed = ttcompatspeedtab(tp->t_ispeed, compatspeeds); + sg->sg_ispeed = ttcompatspeedtab(tp->t_termios.c_ispeed, + compatspeeds); sg->sg_erase = cc[VERASE]; sg->sg_kill = cc[VKILL]; sg->sg_flags = tp->t_flags = ttcompatgetflags(tp); @@ -205,7 +214,7 @@ ttcompat(struct tty *tp, u_long com, caddr_t data, int flag) } case TIOCGETC: { struct tchars *tc = (struct tchars *)data; - cc_t *cc = tp->t_cc; + cc_t *cc = tp->t_termios.c_cc; tc->t_intrc = cc[VINTR]; tc->t_quitc = cc[VQUIT]; @@ -217,7 +226,7 @@ ttcompat(struct tty *tp, u_long com, caddr_t data, int flag) } case TIOCGLTC: { struct ltchars *ltc = (struct ltchars *)data; - cc_t *cc = tp->t_cc; + cc_t *cc = tp->t_termios.c_cc; ltc->t_suspc = cc[VSUSP]; ltc->t_dsuspc = cc[VDSUSP]; @@ -237,19 +246,19 @@ ttcompat(struct tty *tp, u_long com, caddr_t data, int flag) break; case OTIOCGETD: - *(int *)data = tp->t_line ? tp->t_line : 2; + *(int *)data = 2; break; case OTIOCSETD: { int ldisczero = 0; - return (ttioctl(tp, TIOCSETD, - *(int *)data == 2 ? (caddr_t)&ldisczero : data, flag)); + return (tty_ioctl(tp, TIOCSETD, + *(int *)data == 2 ? (caddr_t)&ldisczero : data, td)); } case OTIOCCONS: *(int *)data = 1; - return (ttioctl(tp, TIOCCONS, data, flag)); + return (tty_ioctl(tp, TIOCCONS, data, td)); default: return (ENOIOCTL); @@ -260,10 +269,10 @@ ttcompat(struct tty *tp, u_long com, caddr_t data, int flag) static int ttcompatgetflags(struct tty *tp) { - tcflag_t iflag = tp->t_iflag; - tcflag_t lflag = tp->t_lflag; - tcflag_t oflag = tp->t_oflag; - tcflag_t cflag = tp->t_cflag; + tcflag_t iflag = tp->t_termios.c_iflag; + tcflag_t lflag = tp->t_termios.c_lflag; + tcflag_t oflag = tp->t_termios.c_oflag; + tcflag_t cflag = tp->t_termios.c_cflag; int flags = 0; if (iflag&IXOFF) @@ -299,7 +308,7 @@ ttcompatgetflags(struct tty *tp) flags |= MDMBUF; if ((cflag&HUPCL) == 0) flags |= NOHANG; - if (oflag&OXTABS) + if (oflag&TAB3) flags |= XTABS; if (lflag&ECHOE) flags |= CRTERA|CRTBS; @@ -334,9 +343,9 @@ ttcompatsetflags(struct tty *tp, struct termios *t) iflag |= BRKINT|IXON|IMAXBEL; lflag |= ISIG|IEXTEN|ECHOCTL; /* XXX was echoctl on ? */ if (flags & XTABS) - oflag |= OXTABS; + oflag |= TAB3; else - oflag &= ~OXTABS; + oflag &= ~TAB3; if (flags & CBREAK) lflag &= ~ICANON; else diff --git a/sys/kern/tty_conf.c b/sys/kern/tty_conf.c deleted file mode 100644 index 77a9593..0000000 --- a/sys/kern/tty_conf.c +++ /dev/null @@ -1,205 +0,0 @@ -/*- - * Copyright (c) 2004 Poul-Henning Kamp. All rights reserved. - * Copyright (c) 1982, 1986, 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. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 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 - * 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 - * 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) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * 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_conf.c 8.4 (Berkeley) 1/21/94 - */ - -#include <sys/cdefs.h> -__FBSDID("$FreeBSD$"); - -#include "opt_compat.h" - -#include <sys/param.h> -#include <sys/systm.h> -#include <sys/tty.h> -#include <sys/conf.h> - -#ifndef MAXLDISC -#define MAXLDISC 9 -#endif - -static l_open_t l_noopen; -static l_close_t l_noclose; -static l_rint_t l_norint; -static l_start_t l_nostart; - -/* - * XXX it probably doesn't matter what the entries other than the l_open - * entry are here. The l_nullioctl and ttymodem entries still look fishy. - * Reconsider the removal of nullmodem anyway. It was too much like - * ttymodem, but a completely null version might be useful. - */ - -static struct linesw nodisc = { - .l_open = l_noopen, - .l_close = l_noclose, - .l_read = l_noread, - .l_write = l_nowrite, - .l_ioctl = l_nullioctl, - .l_rint = l_norint, - .l_start = l_nostart, - .l_modem = ttymodem -}; - -static struct linesw termios_disc = { - .l_open = tty_open, - .l_close = ttylclose, - .l_read = ttread, - .l_write = ttwrite, - .l_ioctl = l_nullioctl, - .l_rint = ttyinput, - .l_start = ttstart, - .l_modem = ttymodem -}; - -#ifdef COMPAT_43 -# define ntty_disc termios_disc -#else -# define ntty_disc nodisc -#endif - -struct linesw *linesw[MAXLDISC] = { - &termios_disc, /* 0 - termios */ - &nodisc, /* 1 - defunct */ - &ntty_disc, /* 2 - NTTYDISC */ - &nodisc, /* 3 - loadable */ - &nodisc, /* 4 - SLIPDISC */ - &nodisc, /* 5 - PPPDISC */ - &nodisc, /* 6 - NETGRAPHDISC */ - &nodisc, /* 7 - loadable */ - &nodisc, /* 8 - loadable */ -}; - -int nlinesw = sizeof (linesw) / sizeof (linesw[0]); - -#define LOADABLE_LDISC 7 - -/* - * ldisc_register: Register a line discipline. - * - * discipline: Index for discipline to load, or LDISC_LOAD for us to choose. - * linesw_p: Pointer to linesw_p. - * - * Returns: Index used or -1 on failure. - */ - -int -ldisc_register(int discipline, struct linesw *linesw_p) -{ - int slot = -1; - - if (discipline == LDISC_LOAD) { - int i; - for (i = LOADABLE_LDISC; i < MAXLDISC; i++) - if (linesw[i] == &nodisc) { - slot = i; - break; - } - } else if (discipline >= 0 && discipline < MAXLDISC) { - slot = discipline; - } - - if (slot != -1 && linesw_p) - linesw[slot] = linesw_p; - - return slot; -} - -/* - * ldisc_deregister: Deregister a line discipline obtained with - * ldisc_register. - * - * discipline: Index for discipline to unload. - */ - -void -ldisc_deregister(int discipline) -{ - - if (discipline < MAXLDISC) - linesw[discipline] = &nodisc; -} - -/* - * "no" and "null" versions of line discipline functions - */ - -static int -l_noopen(struct cdev *dev, struct tty *tp) -{ - - return (ENODEV); -} - -static int -l_noclose(struct tty *tp, int flag) -{ - - return (ENODEV); -} - -int -l_noread(struct tty *tp, struct uio *uio, int flag) -{ - - return (ENODEV); -} - -int -l_nowrite(struct tty *tp, struct uio *uio, int flag) -{ - - return (ENODEV); -} - -static int -l_norint(int c, struct tty *tp) -{ - - return (ENODEV); -} - -static int -l_nostart(struct tty *tp) -{ - - return (ENODEV); -} - -int -l_nullioctl(struct tty *tp, u_long cmd, char *data, int flags, struct thread *td) -{ - - return (ENOIOCTL); -} diff --git a/sys/kern/tty_cons.c b/sys/kern/tty_cons.c index ff57d82..81e1708 100644 --- a/sys/kern/tty_cons.c +++ b/sys/kern/tty_cons.c @@ -711,9 +711,18 @@ constty_timeout(void *arg) { int c; - while (constty != NULL && (c = msgbuf_getchar(&consmsgbuf)) != -1) { - if (tputchar(c, constty) < 0) - constty = NULL; + if (constty != NULL) { + tty_lock(constty); + while ((c = msgbuf_getchar(&consmsgbuf)) != -1) { + if (tty_putchar(constty, c) < 0) { + tty_unlock(constty); + constty = NULL; + break; + } + } + + if (constty != NULL) + tty_unlock(constty); } if (constty != NULL) { callout_reset(&conscallout, hz / constty_wakeups_per_second, diff --git a/sys/kern/tty_info.c b/sys/kern/tty_info.c index 7da932d..de06775 100644 --- a/sys/kern/tty_info.c +++ b/sys/kern/tty_info.c @@ -211,7 +211,7 @@ proc_compare(struct proc *p1, struct proc *p2) * Report on state of foreground process group. */ void -ttyinfo(struct tty *tp) +tty_info(struct tty *tp) { struct timeval utime, stime; struct proc *p, *pick; @@ -223,32 +223,27 @@ ttyinfo(struct tty *tp) char comm[MAXCOMLEN + 1]; struct rusage ru; - if (ttycheckoutq(tp,0) == 0) + tty_lock_assert(tp, MA_OWNED); + + if (tty_checkoutq(tp) == 0) return; /* Print load average. */ load = (averunnable.ldavg[0] * 100 + FSCALE / 2) >> FSHIFT; ttyprintf(tp, "load: %d.%02d ", load / 100, load % 100); - /* - * On return following a ttyprintf(), we set tp->t_rocount to 0 so - * that pending input will be retyped on BS. - */ if (tp->t_session == NULL) { ttyprintf(tp, "not a controlling terminal\n"); - tp->t_rocount = 0; return; } if (tp->t_pgrp == NULL) { ttyprintf(tp, "no foreground process group\n"); - tp->t_rocount = 0; return; } PGRP_LOCK(tp->t_pgrp); if (LIST_EMPTY(&tp->t_pgrp->pg_members)) { PGRP_UNLOCK(tp->t_pgrp); ttyprintf(tp, "empty foreground process group\n"); - tp->t_rocount = 0; return; } @@ -313,5 +308,4 @@ ttyinfo(struct tty *tp) (long)utime.tv_sec, utime.tv_usec / 10000, (long)stime.tv_sec, stime.tv_usec / 10000, pctcpu / 100, rss); - tp->t_rocount = 0; } diff --git a/sys/kern/tty_inq.c b/sys/kern/tty_inq.c new file mode 100644 index 0000000..b93dde6 --- /dev/null +++ b/sys/kern/tty_inq.c @@ -0,0 +1,502 @@ +/*- + * Copyright (c) 2008 Ed Schouten <ed@FreeBSD.org> + * All rights reserved. + * + * 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 + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 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. + * + * 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 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) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * 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. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/kernel.h> +#include <sys/lock.h> +#include <sys/queue.h> +#include <sys/sysctl.h> +#include <sys/systm.h> +#include <sys/tty.h> +#include <sys/uio.h> + +#include <vm/uma.h> + +/* + * TTY input queue buffering. + * + * Unlike the output queue, the input queue has more features that are + * needed to properly implement various features offered by the TTY + * interface: + * + * - Data can be removed from the tail of the queue, which is used to + * implement backspace. + * - Once in a while, input has to be `canonicalized'. When ICANON is + * turned on, this will be done after a CR has been inserted. + * Otherwise, it should be done after any character has been inserted. + * - The input queue can store one bit per byte, called the quoting bit. + * This bit is used by TTYDISC to make backspace work on quoted + * characters. + * + * In most cases, there is probably less input than output, so unlike + * the outq, we'll stick to 128 byte blocks here. + */ + +/* Statistics. */ +static long ttyinq_nfast = 0; +SYSCTL_LONG(_kern, OID_AUTO, tty_inq_nfast, CTLFLAG_RD, + &ttyinq_nfast, 0, "Unbuffered reads to userspace on input"); +static long ttyinq_nslow = 0; +SYSCTL_LONG(_kern, OID_AUTO, tty_inq_nslow, CTLFLAG_RD, + &ttyinq_nslow, 0, "Buffered reads to userspace on input"); + +#define TTYINQ_QUOTESIZE (TTYINQ_DATASIZE / BMSIZE) +#define BMSIZE 32 +#define GETBIT(tib,boff) \ + ((tib)->tib_quotes[(boff) / BMSIZE] & (1 << ((boff) % BMSIZE))) +#define SETBIT(tib,boff) \ + ((tib)->tib_quotes[(boff) / BMSIZE] |= (1 << ((boff) % BMSIZE))) +#define CLRBIT(tib,boff) \ + ((tib)->tib_quotes[(boff) / BMSIZE] &= ~(1 << ((boff) % BMSIZE))) + +struct ttyinq_block { + TAILQ_ENTRY(ttyinq_block) tib_list; + uint32_t tib_quotes[TTYINQ_QUOTESIZE]; + char tib_data[TTYINQ_DATASIZE]; +}; + +static uma_zone_t ttyinq_zone; + +void +ttyinq_setsize(struct ttyinq *ti, struct tty *tp, size_t size) +{ + unsigned int nblocks; + struct ttyinq_block *tib; + + nblocks = howmany(size, TTYINQ_DATASIZE); + + while (nblocks > ti->ti_nblocks) { + /* + * List is getting bigger. + * Add new blocks to the tail of the list. + * + * We must unlock the TTY temporarily, because we need + * to allocate memory. This won't be a problem, because + * in the worst case, another thread ends up here, which + * may cause us to allocate too many blocks, but this + * will be caught by the loop below. + */ + tty_unlock(tp); + tib = uma_zalloc(ttyinq_zone, M_WAITOK); + tty_lock(tp); + + if (tty_gone(tp)) + return; + + TAILQ_INSERT_TAIL(&ti->ti_list, tib, tib_list); + ti->ti_nblocks++; + } + + while (nblocks < ti->ti_nblocks) { + /* + * List is getting smaller. Remove unused blocks at the + * end. This means we cannot guarantee this routine + * shrinks buffers properly, when we need to reclaim + * more space than there is available. + * + * XXX TODO: Two solutions here: + * - Throw data away + * - Temporarily hit the watermark until enough data has + * been flushed, so we can remove the blocks. + */ + + if (ti->ti_end == 0) + tib = TAILQ_FIRST(&ti->ti_list); + else + tib = TAILQ_NEXT(ti->ti_lastblock, tib_list); + if (tib == NULL) + break; + TAILQ_REMOVE(&ti->ti_list, tib, tib_list); + uma_zfree(ttyinq_zone, tib); + ti->ti_nblocks--; + } +} + +int +ttyinq_read_uio(struct ttyinq *ti, struct tty *tp, struct uio *uio, + size_t rlen, size_t flen) +{ + + MPASS(rlen <= uio->uio_resid); + + while (rlen > 0) { + int error; + struct ttyinq_block *tib; + size_t cbegin, cend, clen; + + /* See if there still is data. */ + if (ti->ti_begin == ti->ti_linestart) + return (0); + tib = TAILQ_FIRST(&ti->ti_list); + if (tib == NULL) + return (0); + + /* + * The end address should be the lowest of these three: + * - The write pointer + * - The blocksize - we can't read beyond the block + * - The end address if we could perform the full read + */ + cbegin = ti->ti_begin; + cend = MIN(MIN(ti->ti_linestart, ti->ti_begin + rlen), + TTYINQ_DATASIZE); + clen = cend - cbegin; + MPASS(clen >= flen); + rlen -= clen; + + /* + * We can prevent buffering in some cases: + * - We need to read the block until the end. + * - We don't need to read the block until the end, but + * there is no data beyond it, which allows us to move + * the write pointer to a new block. + */ + if (cend == TTYINQ_DATASIZE || cend == ti->ti_end) { + atomic_add_long(&ttyinq_nfast, 1); + + /* + * Fast path: zero copy. Remove the first block, + * so we can unlock the TTY temporarily. + */ + TAILQ_REMOVE(&ti->ti_list, tib, tib_list); + ti->ti_nblocks--; + ti->ti_begin = 0; + + /* + * Because we remove the first block, we must + * fix up the block offsets. + */ +#define CORRECT_BLOCK(t) do { \ + if (t <= TTYINQ_DATASIZE) { \ + t = 0; \ + } else { \ + t -= TTYINQ_DATASIZE; \ + } \ +} while (0) + CORRECT_BLOCK(ti->ti_linestart); + CORRECT_BLOCK(ti->ti_reprint); + CORRECT_BLOCK(ti->ti_end); +#undef CORRECT_BLOCK + + /* + * Temporary unlock and copy the data to + * userspace. We may need to flush trailing + * bytes, like EOF characters. + */ + tty_unlock(tp); + error = uiomove(tib->tib_data + cbegin, + clen - flen, uio); + tty_lock(tp); + + if (tty_gone(tp)) { + /* Something went bad - discard this block. */ + uma_zfree(ttyinq_zone, tib); + return (ENXIO); + } + /* Block can now be readded to the list. */ + /* + * XXX: we could remove the blocks here when the + * queue was shrunk, but still in use. See + * ttyinq_setsize(). + */ + TAILQ_INSERT_TAIL(&ti->ti_list, tib, tib_list); + ti->ti_nblocks++; + if (error != 0) + return (error); + } else { + char ob[TTYINQ_DATASIZE - 1]; + atomic_add_long(&ttyinq_nslow, 1); + + /* + * Slow path: store data in a temporary buffer. + */ + memcpy(ob, tib->tib_data + cbegin, clen - flen); + ti->ti_begin += clen; + MPASS(ti->ti_begin < TTYINQ_DATASIZE); + + /* Temporary unlock and copy the data to userspace. */ + tty_unlock(tp); + error = uiomove(ob, clen - flen, uio); + tty_lock(tp); + + if (error != 0) + return (error); + if (tty_gone(tp)) + return (ENXIO); + } + } + + return (0); +} + +static __inline void +ttyinq_set_quotes(struct ttyinq_block *tib, size_t offset, + size_t length, int value) +{ + + if (value) { + /* Set the bits. */ + for (; length > 0; length--, offset++) + SETBIT(tib, offset); + } else { + /* Unset the bits. */ + for (; length > 0; length--, offset++) + CLRBIT(tib, offset); + } +} + +size_t +ttyinq_write(struct ttyinq *ti, const void *buf, size_t nbytes, int quote) +{ + const char *cbuf = buf; + struct ttyinq_block *tib; + unsigned int boff; + size_t l; + + while (nbytes > 0) { + tib = ti->ti_lastblock; + boff = ti->ti_end % TTYINQ_DATASIZE; + + if (ti->ti_end == 0) { + /* First time we're being used or drained. */ + MPASS(ti->ti_begin == 0); + tib = ti->ti_lastblock = TAILQ_FIRST(&ti->ti_list); + if (tib == NULL) { + /* Queue has no blocks. */ + break; + } + } else if (boff == 0) { + /* We reached the end of this block on last write. */ + tib = TAILQ_NEXT(tib, tib_list); + if (tib == NULL) { + /* We've reached the watermark. */ + break; + } + ti->ti_lastblock = tib; + } + + /* Don't copy more than was requested. */ + l = MIN(nbytes, TTYINQ_DATASIZE - boff); + MPASS(l > 0); + memcpy(tib->tib_data + boff, cbuf, l); + + /* Set the quoting bits for the proper region. */ + ttyinq_set_quotes(tib, boff, l, quote); + + cbuf += l; + nbytes -= l; + ti->ti_end += l; + } + + return (cbuf - (const char *)buf); +} + +int +ttyinq_write_nofrag(struct ttyinq *ti, const void *buf, size_t nbytes, int quote) +{ + size_t ret; + + if (ttyinq_bytesleft(ti) < nbytes) + return (-1); + + /* We should always be able to write it back. */ + ret = ttyinq_write(ti, buf, nbytes, quote); + MPASS(ret == nbytes); + + return (0); +} + +void +ttyinq_canonicalize(struct ttyinq *ti) +{ + + ti->ti_linestart = ti->ti_reprint = ti->ti_end; + ti->ti_startblock = ti->ti_reprintblock = ti->ti_lastblock; +} + +size_t +ttyinq_findchar(struct ttyinq *ti, const char *breakc, size_t maxlen, + char *lastc) +{ + struct ttyinq_block *tib = TAILQ_FIRST(&ti->ti_list); + unsigned int boff = ti->ti_begin; + unsigned int bend = MIN(MIN(TTYINQ_DATASIZE, ti->ti_linestart), + ti->ti_begin + maxlen); + + MPASS(maxlen > 0); + + if (tib == NULL) + return (0); + + while (boff < bend) { + if (index(breakc, tib->tib_data[boff]) && !GETBIT(tib, boff)) { + *lastc = tib->tib_data[boff]; + return (boff - ti->ti_begin + 1); + } + boff++; + } + + /* Not found - just process the entire block. */ + return (bend - ti->ti_begin); +} + +void +ttyinq_flush(struct ttyinq *ti) +{ + + ti->ti_begin = 0; + ti->ti_linestart = 0; + ti->ti_reprint = 0; + ti->ti_end = 0; +} + +#if 0 +void +ttyinq_flush_safe(struct ttyinq *ti) +{ + struct ttyinq_block *tib; + + ttyinq_flush(ti); + + /* Zero all data in the input queue to make it more safe */ + TAILQ_FOREACH(tib, &ti->ti_list, tib_list) { + bzero(&tib->tib_quotes, sizeof tib->tib_quotes); + bzero(&tib->tib_data, sizeof tib->tib_data); + } +} +#endif + +int +ttyinq_peekchar(struct ttyinq *ti, char *c, int *quote) +{ + unsigned int boff; + struct ttyinq_block *tib = ti->ti_lastblock; + + if (ti->ti_linestart == ti->ti_end) + return (-1); + + MPASS(ti->ti_end > 0); + boff = (ti->ti_end - 1) % TTYINQ_DATASIZE; + + *c = tib->tib_data[boff]; + *quote = GETBIT(tib, boff); + + return (0); +} + +void +ttyinq_unputchar(struct ttyinq *ti) +{ + + MPASS(ti->ti_linestart < ti->ti_end); + + if (--ti->ti_end % TTYINQ_DATASIZE == 0) { + /* Roll back to the previous block. */ + ti->ti_lastblock = TAILQ_PREV(ti->ti_lastblock, + ttyinq_bhead, tib_list); + /* + * This can only fail if we are unputchar()'ing the + * first character in the queue. + */ + MPASS((ti->ti_lastblock == NULL) == (ti->ti_end == 0)); + } +} + +void +ttyinq_reprintpos_set(struct ttyinq *ti) +{ + + ti->ti_reprint = ti->ti_end; + ti->ti_reprintblock = ti->ti_lastblock; +} + +void +ttyinq_reprintpos_reset(struct ttyinq *ti) +{ + + ti->ti_reprint = ti->ti_linestart; + ti->ti_reprintblock = ti->ti_startblock; +} + +static void +ttyinq_line_iterate(struct ttyinq *ti, + ttyinq_line_iterator_t *iterator, void *data, + unsigned int offset, struct ttyinq_block *tib) +{ + unsigned int boff; + + /* Use the proper block when we're at the queue head. */ + if (offset == 0) + tib = TAILQ_FIRST(&ti->ti_list); + + /* Iterate all characters and call the iterator function. */ + for (; offset < ti->ti_end; offset++) { + boff = offset % TTYINQ_DATASIZE; + MPASS(tib != NULL); + + /* Call back the iterator function. */ + iterator(data, tib->tib_data[boff], GETBIT(tib, boff)); + + /* Last byte iterated - go to the next block. */ + if (boff == TTYINQ_DATASIZE - 1) + tib = TAILQ_NEXT(tib, tib_list); + MPASS(tib != NULL); + } +} + +void +ttyinq_line_iterate_from_linestart(struct ttyinq *ti, + ttyinq_line_iterator_t *iterator, void *data) +{ + + ttyinq_line_iterate(ti, iterator, data, + ti->ti_linestart, ti->ti_startblock); +} + +void +ttyinq_line_iterate_from_reprintpos(struct ttyinq *ti, + ttyinq_line_iterator_t *iterator, void *data) +{ + + ttyinq_line_iterate(ti, iterator, data, + ti->ti_reprint, ti->ti_reprintblock); +} + +static void +ttyinq_startup(void *dummy) +{ + + ttyinq_zone = uma_zcreate("ttyinq", sizeof(struct ttyinq_block), + NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, 0); +} + +SYSINIT(ttyinq, SI_SUB_DRIVERS, SI_ORDER_FIRST, ttyinq_startup, NULL); diff --git a/sys/kern/tty_outq.c b/sys/kern/tty_outq.c new file mode 100644 index 0000000..e945cca --- /dev/null +++ b/sys/kern/tty_outq.c @@ -0,0 +1,365 @@ +/*- + * Copyright (c) 2008 Ed Schouten <ed@FreeBSD.org> + * All rights reserved. + * + * 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 + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 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. + * + * 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 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) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * 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. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/kernel.h> +#include <sys/lock.h> +#include <sys/queue.h> +#include <sys/sysctl.h> +#include <sys/systm.h> +#include <sys/tty.h> +#include <sys/uio.h> + +#include <vm/uma.h> + +/* + * TTY output queue buffering. + * + * The previous design of the TTY layer offered the so-called clists. + * These clists were used for both the input queues and the output + * queue. We don't use certain features on the output side, like quoting + * bits for parity marking and such. This mechanism is similar to the + * old clists, but only contains the features we need to buffer the + * output. + */ + +/* Statistics. */ +static long ttyoutq_nfast = 0; +SYSCTL_LONG(_kern, OID_AUTO, tty_outq_nfast, CTLFLAG_RD, + &ttyoutq_nfast, 0, "Unbuffered reads to userspace on output"); +static long ttyoutq_nslow = 0; +SYSCTL_LONG(_kern, OID_AUTO, tty_outq_nslow, CTLFLAG_RD, + &ttyoutq_nslow, 0, "Buffered reads to userspace on output"); + +struct ttyoutq_block { + STAILQ_ENTRY(ttyoutq_block) tob_list; + char tob_data[TTYOUTQ_DATASIZE]; +}; + +static uma_zone_t ttyoutq_zone; + +void +ttyoutq_flush(struct ttyoutq *to) +{ + + to->to_begin = 0; + to->to_end = 0; +} + +void +ttyoutq_setsize(struct ttyoutq *to, struct tty *tp, size_t size) +{ + unsigned int nblocks; + struct ttyoutq_block *tob; + + nblocks = howmany(size, TTYOUTQ_DATASIZE); + + while (nblocks > to->to_nblocks) { + /* + * List is getting bigger. + * Add new blocks to the tail of the list. + * + * We must unlock the TTY temporarily, because we need + * to allocate memory. This won't be a problem, because + * in the worst case, another thread ends up here, which + * may cause us to allocate too many blocks, but this + * will be caught by the loop below. + */ + tty_unlock(tp); + tob = uma_zalloc(ttyoutq_zone, M_WAITOK); + tty_lock(tp); + + if (tty_gone(tp)) + return; + + STAILQ_INSERT_TAIL(&to->to_list, tob, tob_list); + to->to_nblocks++; + } + + while (nblocks < to->to_nblocks) { + /* + * List is getting smaller. Remove unused blocks at the + * end. This means we cannot guarantee this routine + * shrinks buffers properly, when we need to reclaim + * more space than there is available. + * + * XXX TODO: Two solutions here: + * - Throw data away + * - Temporarily hit the watermark until enough data has + * been flushed, so we can remove the blocks. + */ + + if (to->to_end == 0) { + tob = STAILQ_FIRST(&to->to_list); + if (tob == NULL) + break; + STAILQ_REMOVE_HEAD(&to->to_list, tob_list); + } else { + tob = STAILQ_NEXT(to->to_lastblock, tob_list); + if (tob == NULL) + break; + STAILQ_REMOVE_NEXT(&to->to_list, to->to_lastblock, tob_list); + } + uma_zfree(ttyoutq_zone, tob); + to->to_nblocks--; + } +} + +size_t +ttyoutq_read(struct ttyoutq *to, void *buf, size_t len) +{ + char *cbuf = buf; + + while (len > 0) { + struct ttyoutq_block *tob; + size_t cbegin, cend, clen; + + /* See if there still is data. */ + if (to->to_begin == to->to_end) + break; + tob = STAILQ_FIRST(&to->to_list); + if (tob == NULL) + break; + + /* + * The end address should be the lowest of these three: + * - The write pointer + * - The blocksize - we can't read beyond the block + * - The end address if we could perform the full read + */ + cbegin = to->to_begin; + cend = MIN(MIN(to->to_end, to->to_begin + len), + TTYOUTQ_DATASIZE); + clen = cend - cbegin; + + if (cend == TTYOUTQ_DATASIZE || cend == to->to_end) { + /* Read the block until the end. */ + STAILQ_REMOVE_HEAD(&to->to_list, tob_list); + STAILQ_INSERT_TAIL(&to->to_list, tob, tob_list); + to->to_begin = 0; + if (to->to_end <= TTYOUTQ_DATASIZE) { + to->to_end = 0; + } else { + to->to_end -= TTYOUTQ_DATASIZE; + } + } else { + /* Read the block partially. */ + to->to_begin += clen; + } + + /* Copy the data out of the buffers. */ + memcpy(cbuf, tob->tob_data + cbegin, clen); + cbuf += clen; + len -= clen; + } + + return (cbuf - (char *)buf); +} + +/* + * An optimized version of ttyoutq_read() which can be used in pseudo + * TTY drivers to directly copy data from the outq to userspace, instead + * of buffering it. + * + * We can only copy data directly if we need to read the entire block + * back to the user, because we temporarily remove the block from the + * queue. Otherwise we need to copy it to a temporary buffer first, to + * make sure data remains in the correct order. + */ +int +ttyoutq_read_uio(struct ttyoutq *to, struct tty *tp, struct uio *uio) +{ + + while (uio->uio_resid > 0) { + int error; + struct ttyoutq_block *tob; + size_t cbegin, cend, clen; + + /* See if there still is data. */ + if (to->to_begin == to->to_end) + return (0); + tob = STAILQ_FIRST(&to->to_list); + if (tob == NULL) + return (0); + + /* + * The end address should be the lowest of these three: + * - The write pointer + * - The blocksize - we can't read beyond the block + * - The end address if we could perform the full read + */ + cbegin = to->to_begin; + cend = MIN(MIN(to->to_end, to->to_begin + uio->uio_resid), + TTYOUTQ_DATASIZE); + clen = cend - cbegin; + + /* + * We can prevent buffering in some cases: + * - We need to read the block until the end. + * - We don't need to read the block until the end, but + * there is no data beyond it, which allows us to move + * the write pointer to a new block. + */ + if (cend == TTYOUTQ_DATASIZE || cend == to->to_end) { + atomic_add_long(&ttyoutq_nfast, 1); + + /* + * Fast path: zero copy. Remove the first block, + * so we can unlock the TTY temporarily. + */ + STAILQ_REMOVE_HEAD(&to->to_list, tob_list); + to->to_nblocks--; + to->to_begin = 0; + if (to->to_end <= TTYOUTQ_DATASIZE) { + to->to_end = 0; + } else { + to->to_end -= TTYOUTQ_DATASIZE; + } + + /* Temporary unlock and copy the data to userspace. */ + tty_unlock(tp); + error = uiomove(tob->tob_data + cbegin, clen, uio); + tty_lock(tp); + + if (tty_gone(tp)) { + /* We lost the discipline. */ + uma_zfree(ttyoutq_zone, tob); + return (ENXIO); + } + + /* Block can now be readded to the list. */ + /* + * XXX: we could remove the blocks here when the + * queue was shrunk, but still in use. See + * ttyoutq_setsize(). + */ + STAILQ_INSERT_TAIL(&to->to_list, tob, tob_list); + to->to_nblocks++; + if (error != 0) + return (error); + } else { + char ob[TTYOUTQ_DATASIZE - 1]; + atomic_add_long(&ttyoutq_nslow, 1); + + /* + * Slow path: store data in a temporary buffer. + */ + memcpy(ob, tob->tob_data + cbegin, clen); + to->to_begin += clen; + MPASS(to->to_begin < TTYOUTQ_DATASIZE); + + /* Temporary unlock and copy the data to userspace. */ + tty_unlock(tp); + error = uiomove(ob, clen, uio); + tty_lock(tp); + + if (tty_gone(tp)) { + /* We lost the discipline. */ + return (ENXIO); + } + + if (error != 0) + return (error); + } + } + + return (0); +} + +size_t +ttyoutq_write(struct ttyoutq *to, const void *buf, size_t nbytes) +{ + const char *cbuf = buf; + struct ttyoutq_block *tob; + unsigned int boff; + size_t l; + + while (nbytes > 0) { + /* Offset in current block. */ + tob = to->to_lastblock; + boff = to->to_end % TTYOUTQ_DATASIZE; + + if (to->to_end == 0) { + /* First time we're being used or drained. */ + MPASS(to->to_begin == 0); + tob = to->to_lastblock = STAILQ_FIRST(&to->to_list); + if (tob == NULL) { + /* Queue has no blocks. */ + break; + } + } else if (boff == 0) { + /* We reached the end of this block on last write. */ + tob = STAILQ_NEXT(tob, tob_list); + if (tob == NULL) { + /* We've reached the watermark. */ + break; + } + to->to_lastblock = tob; + } + + /* Don't copy more than was requested. */ + l = MIN(nbytes, TTYOUTQ_DATASIZE - boff); + MPASS(l > 0); + memcpy(tob->tob_data + boff, cbuf, l); + + cbuf += l; + nbytes -= l; + to->to_end += l; + } + + return (cbuf - (const char *)buf); +} + +int +ttyoutq_write_nofrag(struct ttyoutq *to, const void *buf, size_t nbytes) +{ + size_t ret; + + if (ttyoutq_bytesleft(to) < nbytes) + return (-1); + + /* We should always be able to write it back. */ + ret = ttyoutq_write(to, buf, nbytes); + MPASS(ret == nbytes); + + return (0); +} + +static void +ttyoutq_startup(void *dummy) +{ + + ttyoutq_zone = uma_zcreate("ttyoutq", sizeof(struct ttyoutq_block), + NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, 0); +} + +SYSINIT(ttyoutq, SI_SUB_DRIVERS, SI_ORDER_FIRST, ttyoutq_startup, NULL); diff --git a/sys/kern/tty_pts.c b/sys/kern/tty_pts.c index ff8838e..8a51448 100644 --- a/sys/kern/tty_pts.c +++ b/sys/kern/tty_pts.c @@ -1,16 +1,9 @@ -/* - * Copyright (c) 2003 Networks Associates Technology, Inc. - * Copyright (c) 2006 Robert N. M. Watson - * Copyright (c) 2006 Olivier Houchard +/*- + * Copyright (c) 2008 Ed Schouten <ed@FreeBSD.org> * All rights reserved. * - * This software was developed for the FreeBSD Project in part by Network - * Associates Laboratories, 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. - * - * Copyright (c) 1982, 1986, 1989, 1993 - * The Regents of the University of California. All rights reserved. + * 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 @@ -20,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) @@ -35,889 +25,632 @@ * 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_pty.c 8.4 (Berkeley) 2/20/95 */ #include <sys/cdefs.h> __FBSDID("$FreeBSD$"); -/* - * Pseudo-teletype Driver - * (Actually two drivers, requiring two entries in 'cdevsw') - */ -#include "opt_compat.h" #include "opt_tty.h" + +/* Add compatibility bits for FreeBSD. */ +#define PTS_COMPAT +#ifdef DEV_PTY +/* Add /dev/ptyXX compat bits. */ +#define PTS_EXTERNAL +#endif /* DEV_PTY */ +/* Add bits to make Linux binaries work. */ +#define PTS_LINUX + #include <sys/param.h> -#include <sys/systm.h> #include <sys/lock.h> -#include <sys/mutex.h> -#include <sys/sx.h> -#if defined(COMPAT_43TTY) -#include <sys/ioctl_compat.h> -#endif -#include <sys/priv.h> -#include <sys/proc.h> -#include <sys/queue.h> -#include <sys/tty.h> +#include <sys/condvar.h> +#include <sys/conf.h> #include <sys/fcntl.h> -#include <sys/poll.h> +#include <sys/file.h> +#include <sys/filedesc.h> +#include <sys/filio.h> #include <sys/kernel.h> -#include <sys/vnode.h> -#include <sys/signalvar.h> #include <sys/malloc.h> -#include <sys/conf.h> -#include <sys/sysctl.h> -#include <sys/filio.h> - -static MALLOC_DEFINE(M_PTY, "ptys", "pty data structures"); - -static void ptsstart(struct tty *tp); -static void ptsstop(struct tty *tp, int rw); -static void ptcwakeup(struct tty *tp, int flag); - -static d_open_t ptsopen; -static d_close_t ptsclose; -static d_read_t ptsread; -static d_write_t ptswrite; -static d_ioctl_t ptsioctl; -static d_ioctl_t ptcioctl; -static d_open_t ptcopen; -static d_close_t ptcclose; -static d_read_t ptcread; -static d_write_t ptcwrite; -static d_poll_t ptcpoll; - -static struct cdevsw pts_cdevsw = { - .d_version = D_VERSION, - .d_open = ptsopen, - .d_close = ptsclose, - .d_read = ptsread, - .d_write = ptswrite, - .d_ioctl = ptsioctl, - .d_poll = ttypoll, - .d_name = "pts", - .d_flags = D_TTY | D_NEEDGIANT, - .d_kqfilter = ttykqfilter, -}; +#include <sys/poll.h> +#include <sys/proc.h> +#include <sys/resourcevar.h> +#include <sys/serial.h> +#include <sys/stat.h> +#include <sys/syscall.h> +#include <sys/syscallsubr.h> +#include <sys/sysent.h> +#include <sys/sysproto.h> +#include <sys/systm.h> +#include <sys/tty.h> +#include <sys/ttycom.h> -static struct cdevsw ptc_cdevsw = { - .d_version = D_VERSION, - .d_open = ptcopen, - .d_close = ptcclose, - .d_read = ptcread, - .d_write = ptcwrite, - .d_ioctl = ptcioctl, - .d_poll = ptcpoll, - .d_name = "ptc", - .d_flags = D_TTY | D_NEEDGIANT, - .d_kqfilter = ttykqfilter, -}; +#include <machine/stdarg.h> -#define BUFSIZ 100 /* Chunk size iomoved to/from user */ +static struct unrhdr *pts_pool; +#define MAXPTSDEVS 999 -#define TSA_PTC_READ(tp) ((void *)&(tp)->t_outq.c_cf) -#define TSA_PTC_WRITE(tp) ((void *)&(tp)->t_rawq.c_cl) -#define TSA_PTS_READ(tp) ((void *)&(tp)->t_canq) +static MALLOC_DEFINE(M_PTS, "pts", "pseudo tty device"); -#define NUM_TO_MINOR(c) ((c & 0xff) | ((c & ~0xff) << 16)) -/*- - * Once a tty is allocated, it cannot (currently) be freed. As such, - * we keep a global list of ptys that have been used so we can recycle - * them. Another list is provided for released pts, which are - * not currently allocated, permitting reuse. pt_flags holds state - * associated with a particular session, so isn't overloaded for this. - * When a pty descriptor is unused, its number is set to -1 giving - * more consistent and traditional allocation orders to pty numbers. +/* + * Per-PTS structure. * - * Locking: (p) indicates that the field is locked by the global pt_mtx. - * (c) indicates the value is constant after allocation. Other fields - * await tty locking generally, and are protected by Giant. + * List of locks + * (t) locked by tty_lock() + * (c) const until freeing */ -struct pt_desc { - int pt_num; /* (c) pty number */ - LIST_ENTRY(pt_desc) pt_list; /* (p) global pty list */ - - int pt_flags; - struct selinfo pt_selr, pt_selw; - u_char pt_send; - u_char pt_ucntl; - struct tty *pt_tty; - struct cdev *pt_devs, *pt_devc; - int pt_pts_open, pt_ptc_open; - struct prison *pt_prison; -}; +struct pts_softc { + int pts_unit; /* (c) Device unit number. */ + unsigned int pts_flags; /* (t) Device flags. */ +#define PTS_PKT 0x1 /* Packet mode. */ -static struct mtx pt_mtx; -static LIST_HEAD(,pt_desc) pt_list; -static LIST_HEAD(,pt_desc) pt_free_list; + struct cv pts_inwait; /* (t) Blocking write() on master. */ + struct selinfo pts_inpoll; /* (t) Select queue for write(). */ + struct cv pts_outwait; /* (t) Blocking read() on master. */ + struct selinfo pts_outpoll; /* (t) Select queue for read(). */ -#define PF_PKT 0x008 /* packet mode */ -#define PF_STOPPED 0x010 /* user told stopped */ -#define PF_NOSTOP 0x040 -#define PF_UCNTL 0x080 /* user control mode */ +#ifdef PTS_EXTERNAL + struct cdev *pts_cdev; /* (c) Master device node. */ +#endif /* PTS_EXTERNAL */ -static unsigned int next_avail_nb; - -static int use_pts = 0; + struct uidinfo *pts_uidinfo; /* (c) Resource limit. */ +}; -static unsigned int max_pts = 1000; +/* + * Controller-side file operations. + */ -static unsigned int nb_allocated; +static int +ptsdev_read(struct file *fp, struct uio *uio, struct ucred *active_cred, + int flags, struct thread *td) +{ + struct tty *tp = fp->f_data; + struct pts_softc *psc = tty_softc(tp); + int error, oresid; -TUNABLE_INT("kern.pts.enable", &use_pts); + if (uio->uio_resid == 0) + return (0); + + /* + * Implement packet mode. When packet mode is turned on, the + * first byte contains a bitmask of events that occured (start, + * stop, flush, window size, etc). + */ -SYSCTL_NODE(_kern, OID_AUTO, pts, CTLFLAG_RD, 0, "pts"); + if (psc->pts_flags & PTS_PKT) { + /* XXX: return proper bits. */ + error = ureadc(0, uio); + if (error != 0) + return (error); + if (uio->uio_resid == 0) + return (0); + } -SYSCTL_INT(_kern_pts, OID_AUTO, enable, CTLFLAG_RW, &use_pts, 0, - "enable pts"); + oresid = uio->uio_resid; -SYSCTL_INT(_kern_pts, OID_AUTO, max, CTLFLAG_RW, &max_pts, 0, "max pts"); + tty_lock(tp); + for (;;) { + error = ttydisc_getc_uio(tp, uio); + /* We've got data (or an error). */ + if (error != 0 || uio->uio_resid != oresid) + break; -/* - * If there's a free pty descriptor in the pty descriptor list, retrieve it. - * Otherwise, allocate a new one, initialize it, and hook it up. If there's - * not a tty number, reject. - */ -static struct pt_desc * -pty_new(void) -{ - struct pt_desc *pt; - int nb; + /* Maybe the device isn't used anyway. */ + if (tty_opened(tp) == 0) { + error = ENXIO; + break; + } - mtx_lock(&pt_mtx); - if (nb_allocated >= max_pts || nb_allocated == 0xffffff) { - mtx_unlock(&pt_mtx); - return (NULL); - } - nb_allocated++; - pt = LIST_FIRST(&pt_free_list); - if (pt) { - LIST_REMOVE(pt, pt_list); - } else { - nb = next_avail_nb++; - mtx_unlock(&pt_mtx); - pt = malloc(sizeof(*pt), M_PTY, M_WAITOK | M_ZERO); - pt->pt_tty = ttyalloc(); - mtx_lock(&pt_mtx); - pt->pt_num = nb; + /* Wait for more data. */ + if (fp->f_flag & O_NONBLOCK) { + error = EWOULDBLOCK; + break; + } + error = cv_wait_sig(&psc->pts_outwait, tp->t_mtx); + if (error != 0) + break; } - LIST_INSERT_HEAD(&pt_list, pt, pt_list); - mtx_unlock(&pt_mtx); - return (pt); -} - -/* - * Release a pty descriptor back to the pool for reuse. The pty number - * remains allocated. - */ -static void -pty_release(void *v) -{ - struct pt_desc *pt = (struct pt_desc *)v; - - mtx_lock(&pt_mtx); - KASSERT(pt->pt_ptc_open == 0 && pt->pt_pts_open == 0, - ("pty_release: pts/%d freed while open\n", pt->pt_num)); - KASSERT(pt->pt_devs == NULL && pt->pt_devc == NULL, - ("pty_release: pts/%d freed whith non-null struct cdev\n", pt->pt_num)); - nb_allocated--; - LIST_REMOVE(pt, pt_list); - LIST_INSERT_HEAD(&pt_free_list, pt, pt_list); - mtx_unlock(&pt_mtx); -} + tty_unlock(tp); -/* - * Given a pty descriptor, if both endpoints are closed, release all - * resources and destroy the device nodes to flush file system level - * state for the tty (owner, avoid races, etc). - */ -static void -pty_maybecleanup(struct pt_desc *pt) -{ - struct cdev *pt_devs, *pt_devc; - - if (pt->pt_ptc_open || pt->pt_pts_open) - return; - - if (pt->pt_tty->t_refcnt > 1) - return; - - if (bootverbose) - printf("destroying pty %d\n", pt->pt_num); - - pt_devs = pt->pt_devs; - pt_devc = pt->pt_devc; - pt->pt_devs = pt->pt_devc = NULL; - pt->pt_tty->t_dev = NULL; - pt_devc->si_drv1 = NULL; - ttyrel(pt->pt_tty); - pt->pt_tty = NULL; - destroy_dev_sched(pt_devs); - destroy_dev_sched_cb(pt_devc, pty_release, pt); + return (error); } -/*ARGSUSED*/ static int -ptsopen(struct cdev *dev, int flag, int devtype, struct thread *td) +ptsdev_write(struct file *fp, struct uio *uio, struct ucred *active_cred, + int flags, struct thread *td) { - struct tty *tp; - int error; - struct pt_desc *pt; - - pt = dev->si_drv1; - tp = dev->si_tty; - if ((tp->t_state & TS_ISOPEN) == 0) - ttyinitmode(tp, 1, 0); - else if (tp->t_state & TS_XCLUDE && priv_check(td, - PRIV_TTY_EXCLUSIVE)) { - return (EBUSY); - } else if (pt->pt_prison != td->td_ucred->cr_prison && - priv_check(td, PRIV_TTY_PRISON)) { - return (EBUSY); - } - if (tp->t_oproc) /* Ctrlr still around. */ - ttyld_modem(tp, 1); - while ((tp->t_state & TS_CARR_ON) == 0) { - if (flag & FNONBLOCK) - break; - error = ttysleep(tp, TSA_CARR_ON(tp), TTIPRI | PCATCH, - "ptsopn", 0); - if (error) - return (error); - } - error = ttyld_open(tp, dev); - if (error == 0) { - ptcwakeup(tp, FREAD|FWRITE); - pt->pt_pts_open = 1; + struct tty *tp = fp->f_data; + struct pts_softc *psc = tty_softc(tp); + char ib[256], *ibstart; + size_t iblen, rintlen; + int error = 0; + + tty_lock(tp); + + while (uio->uio_resid > 0) { + /* Temporarily unlock to buffer new characters. */ + tty_unlock(tp); + ibstart = ib; + iblen = MIN(uio->uio_resid, sizeof ib); + error = uiomove(ib, iblen, uio); + tty_lock(tp); + if (error != 0) + goto done; + + /* + * When possible, avoid the slow path. rint_bypass() + * copies all input to the input queue at once. + */ + while (iblen > 0) { + if (ttydisc_can_bypass(tp)) { + /* Store data at once. */ + rintlen = ttydisc_rint_bypass(tp, + ibstart, iblen); + ibstart += rintlen; + iblen -= rintlen; + + if (iblen == 0) { + /* All data written. */ + continue; + } + } else { + error = ttydisc_rint(tp, *ibstart, 0); + if (error == 0) { + /* Character stored successfully. */ + ibstart++; + iblen--; + continue; + } + } + + /* Maybe the device isn't used anyway. */ + if (tty_opened(tp) == 0) { + error = ENXIO; + goto done; + } + + /* Wait for more data. */ + if (fp->f_flag & O_NONBLOCK) { + error = EWOULDBLOCK; + goto done; + } + + /* Wake up users on the slave side. */ + ttydisc_rint_done(tp); + error = cv_wait_sig(&psc->pts_inwait, tp->t_mtx); + if (error != 0) + goto done; + } } + +done: ttydisc_rint_done(tp); + tty_unlock(tp); return (error); } static int -ptsclose(struct cdev *dev, int flag, int mode, struct thread *td) +ptsdev_ioctl(struct file *fp, u_long cmd, void *data, + struct ucred *active_cred, struct thread *td) { - struct pt_desc *pt = dev->si_drv1; - struct tty *tp; - int err; - - tp = dev->si_tty; - err = ttyld_close(tp, flag); - ptsstop(tp, FREAD|FWRITE); - (void) tty_close(tp); - pt->pt_pts_open = 0; - pty_maybecleanup(pt); - return (err); -} + struct tty *tp = fp->f_data; + struct pts_softc *psc = tty_softc(tp); + int error = 0, sig; -static int -ptsread(struct cdev *dev, struct uio *uio, int flag) -{ - struct tty *tp = dev->si_tty; - int error = 0; + switch (cmd) { + case FIONBIO: + /* This device supports non-blocking operation. */ + return (0); + case FIODGNAME: { + struct fiodgname_arg *fgn; + const char *p; + int i; + + /* Reverse device name lookups, for ptsname() and ttyname(). */ + fgn = data; +#ifdef PTS_EXTERNAL + if (psc->pts_cdev != NULL) + p = devtoname(psc->pts_cdev); + else +#endif /* PTS_EXTERNAL */ + p = tty_devname(tp); + i = strlen(p) + 1; + if (i > fgn->len) + return (EINVAL); + return copyout(p, fgn->buf, i); + } + + /* + * We need to implement TIOCGPGRP and TIOCGSID here again. When + * called on the pseudo-terminal master, it should not check if + * the terminal is the foreground terminal of the calling + * process. + * + * TIOCGETA is also implemented here. Various Linux PTY routines + * often call isatty(), which is implemented by tcgetattr(). + */ +#ifdef PTS_LINUX + case TIOCGETA: + /* Obtain terminal flags through tcgetattr(). */ + tty_lock(tp); + bcopy(&tp->t_termios, data, sizeof(struct termios)); + tty_unlock(tp); + return (0); +#endif /* PTS_LINUX */ + case TIOCSETAF: + case TIOCSETAW: + /* + * We must make sure we turn tcsetattr() calls of TCSAFLUSH and + * TCSADRAIN into something different. If an application would + * call TCSAFLUSH or TCSADRAIN on the master descriptor, it may + * deadlock waiting for all data to be read. + */ + cmd = TIOCSETA; + break; +#if defined(PTS_COMPAT) || defined(PTS_LINUX) + case TIOCGPTN: + /* + * Get the device unit number. + */ + if (psc->pts_unit < 0) + return (ENOTTY); + *(unsigned int *)data = psc->pts_unit; + return (0); +#endif /* PTS_COMPAT || PTS_LINUX */ + case TIOCGPGRP: + /* Get the foreground process group ID. */ + tty_lock(tp); + if (tp->t_pgrp != NULL) + *(int *)data = tp->t_pgrp->pg_id; + else + *(int *)data = NO_PID; + tty_unlock(tp); + return (0); + case TIOCGSID: + /* Get the session leader process ID. */ + tty_lock(tp); + if (tp->t_session == NULL) + error = ENOTTY; + else + *(int *)data = tp->t_session->s_sid; + tty_unlock(tp); + return (error); + case TIOCPTMASTER: + /* Yes, we are a pseudo-terminal master. */ + return (0); + case TIOCSIG: + /* Signal the foreground process group. */ + sig = *(int *)data; + if (sig < 1 || sig >= NSIG) + return (EINVAL); + + tty_lock(tp); + tty_signal_pgrp(tp, sig); + tty_unlock(tp); + return (0); + case TIOCPKT: + /* Enable/disable packet mode. */ + tty_lock(tp); + if (*(int *)data) + psc->pts_flags |= PTS_PKT; + else + psc->pts_flags &= ~PTS_PKT; + tty_unlock(tp); + return (0); + } + + /* Just redirect this ioctl to the slave device. */ + tty_lock(tp); + error = tty_ioctl(tp, cmd, data, td); + tty_unlock(tp); - if (tp->t_oproc) - error = ttyld_read(tp, uio, flag); - ptcwakeup(tp, FWRITE); return (error); } -/* - * Write to pseudo-tty. - * Wakeups of controlling tty will happen - * indirectly, when tty driver calls ptsstart. - */ static int -ptswrite(struct cdev *dev, struct uio *uio, int flag) +ptsdev_poll(struct file *fp, int events, struct ucred *active_cred, + struct thread *td) { - struct tty *tp; + struct tty *tp = fp->f_data; + struct pts_softc *psc = tty_softc(tp); + int revents = 0; - tp = dev->si_tty; - if (tp->t_oproc == 0) - return (EIO); - return (ttyld_write(tp, uio, flag)); -} + tty_lock(tp); -/* - * Start output on pseudo-tty. - * Wake up process selecting or sleeping for input from controlling tty. - */ -static void -ptsstart(struct tty *tp) -{ - struct pt_desc *pt = tp->t_dev->si_drv1; + if (tty_opened(tp) == 0) { + /* Slave device is not opened. */ + tty_unlock(tp); + return (events & + (POLLHUP|POLLIN|POLLRDNORM|POLLOUT|POLLWRNORM)); + } - if (tp->t_state & TS_TTSTOP) - return; - if (pt->pt_flags & PF_STOPPED) { - pt->pt_flags &= ~PF_STOPPED; - pt->pt_send = TIOCPKT_START; + if (events & (POLLIN|POLLRDNORM)) { + /* See if we can getc something. */ + if (ttydisc_getc_poll(tp)) + revents |= events & (POLLIN|POLLRDNORM); + } + if (events & (POLLOUT|POLLWRNORM)) { + /* See if we can rint something. */ + if (ttydisc_rint_poll(tp)) + revents |= events & (POLLOUT|POLLWRNORM); } - ptcwakeup(tp, FREAD); -} -static void -ptcwakeup(struct tty *tp, int flag) -{ - struct pt_desc *pt = tp->t_dev->si_drv1; + /* + * No need to check for POLLHUP here. This device cannot be used + * as a callout device, which means we always have a carrier, + * because the master is. + */ - if (flag & FREAD) { - selwakeup(&pt->pt_selr); - wakeup(TSA_PTC_READ(tp)); - } - if (flag & FWRITE) { - selwakeup(&pt->pt_selw); - wakeup(TSA_PTC_WRITE(tp)); + if (revents == 0) { + /* + * This code might look misleading, but the naming of + * poll events on this side is the opposite of the slave + * device. + */ + if (events & (POLLIN|POLLRDNORM)) + selrecord(td, &psc->pts_outpoll); + if (events & (POLLOUT|POLLWRNORM)) + selrecord(td, &psc->pts_inpoll); } + + tty_unlock(tp); + + return (revents); } -/* - * ptcopen implementes exclusive access to the master/control device - * as well as creating the slave device based on the credential of the - * process opening the master. By creating the slave here, we avoid - * a race to access the master in terms of having a process with access - * to an incorrectly owned slave, but it does create the possibility - * that a racing process can cause a ptmx user to get EIO if it gets - * there first. Consumers of ptmx must look for EIO and retry if it - * happens. VFS locking may actually prevent this from occurring due - * to the lookup into devfs holding the vnode lock through open, but - * it's better to be careful. - */ static int -ptcopen(struct cdev *dev, int flag, int devtype, struct thread *td) +ptsdev_stat(struct file *fp, struct stat *sb, struct ucred *active_cred, + struct thread *td) { - struct pt_desc *pt; - struct tty *tp; - struct cdev *devs; + struct tty *tp = fp->f_data; +#ifdef PTS_EXTERNAL + struct pts_softc *psc = tty_softc(tp); +#endif /* PTS_EXTERNAL */ - pt = dev->si_drv1; - if (pt == NULL) - return (EIO); /* - * In case we have destroyed the struct tty at the last connect time, - * we need to recreate it. + * According to POSIX, we must implement an fstat(). This also + * makes this implementation compatible with Linux binaries, + * because Linux calls fstat() on the pseudo-terminal master to + * obtain st_rdev. + * + * XXX: POSIX also mentions we must fill in st_dev, st_atime, + * st_ctime and st_mtime, but how? */ - if (pt->pt_tty == NULL) { - tp = ttyalloc(); - mtx_lock(&pt_mtx); - if (pt->pt_tty == NULL) { - pt->pt_tty = tp; - dev->si_tty = pt->pt_tty; - mtx_unlock(&pt_mtx); - } else { - mtx_unlock(&pt_mtx); - ttyrel(tp); - } - } - tp = dev->si_tty; - if (tp->t_oproc) - return (EIO); - /* - * XXX: Might want to make the ownership/permissions here more - * configurable. - */ - if (pt->pt_devs) - devs = pt->pt_devs; + bzero(sb, sizeof *sb); +#ifdef PTS_EXTERNAL + if (psc->pts_cdev != NULL) + sb->st_ino = sb->st_rdev = dev2udev(psc->pts_cdev); else - pt->pt_devs = devs = make_dev_cred(&pts_cdevsw, - NUM_TO_MINOR(pt->pt_num), - td->td_ucred, UID_ROOT, GID_WHEEL, 0666, "pts/%d", - pt->pt_num); - devs->si_drv1 = pt; - devs->si_tty = pt->pt_tty; - pt->pt_tty->t_dev = devs; - - tp->t_timeout = -1; - tp->t_oproc = ptsstart; - tp->t_stop = ptsstop; - ttyld_modem(tp, 1); - tp->t_lflag &= ~EXTPROC; - pt = dev->si_drv1; - pt->pt_prison = td->td_ucred->cr_prison; - pt->pt_flags = 0; - pt->pt_send = 0; - pt->pt_ucntl = 0; - pt->pt_ptc_open = 1; +#endif /* PTS_EXTERNAL */ + sb->st_ino = sb->st_rdev = tty_udev(tp); + sb->st_mode = S_IFCHR; + sb->st_uid = tp->t_dev->si_cred->cr_ruid; + sb->st_gid = GID_TTY; + return (0); } static int -ptcclose(struct cdev *dev, int flags, int fmt, struct thread *td) +ptsdev_close(struct file *fp, struct thread *td) { - struct pt_desc *pt = dev->si_drv1; - struct tty *tp; + struct tty *tp = fp->f_data; - tp = dev->si_tty; - ttyld_modem(tp, 0); + /* Deallocate TTY device. */ + tty_lock(tp); + tty_rel_gone(tp); - /* - * XXX MDMBUF makes no sense for ptys but would inhibit the above - * l_modem(). CLOCAL makes sense but isn't supported. Special - * l_modem()s that ignore carrier drop make no sense for ptys but - * may be in use because other parts of the line discipline make - * sense for ptys. Recover by doing everything that a normal - * ttymodem() would have done except for sending a SIGHUP. - */ - if (tp->t_state & TS_ISOPEN) { - tp->t_state &= ~(TS_CARR_ON | TS_CONNECTED); - tp->t_state |= TS_ZOMBIE; - ttyflush(tp, FREAD | FWRITE); - } - - tp->t_oproc = 0; /* mark closed */ - pt->pt_ptc_open = 0; - pty_maybecleanup(pt); return (0); } -static int -ptcread(struct cdev *dev, struct uio *uio, int flag) -{ - struct tty *tp = dev->si_tty; - struct pt_desc *pt = dev->si_drv1; - char buf[BUFSIZ]; - int error = 0, cc; +static struct fileops ptsdev_ops = { + .fo_read = ptsdev_read, + .fo_write = ptsdev_write, + .fo_ioctl = ptsdev_ioctl, + .fo_poll = ptsdev_poll, + .fo_stat = ptsdev_stat, + .fo_close = ptsdev_close, + .fo_flags = DFLAG_PASSABLE, +}; - /* - * We want to block until the slave - * is open, and there's something to read; - * but if we lost the slave or we're NBIO, - * then return the appropriate error instead. - */ - for (;;) { - if (tp->t_state&TS_ISOPEN) { - if (pt->pt_flags&PF_PKT && pt->pt_send) { - error = ureadc((int)pt->pt_send, uio); - if (error) - return (error); - if (pt->pt_send & TIOCPKT_IOCTL) { - cc = min(uio->uio_resid, - sizeof(tp->t_termios)); - uiomove(&tp->t_termios, cc, uio); - } - pt->pt_send = 0; - return (0); - } - if (pt->pt_flags&PF_UCNTL && pt->pt_ucntl) { - error = ureadc((int)pt->pt_ucntl, uio); - if (error) - return (error); - pt->pt_ucntl = 0; - return (0); - } - if (tp->t_outq.c_cc && (tp->t_state&TS_TTSTOP) == 0) - break; - } - if ((tp->t_state & TS_CONNECTED) == 0) - return (0); /* EOF */ - if (flag & O_NONBLOCK) - return (EWOULDBLOCK); - error = tsleep(TSA_PTC_READ(tp), TTIPRI | PCATCH, "ptcin", 0); - if (error) - return (error); - } - if (pt->pt_flags & (PF_PKT|PF_UCNTL)) - error = ureadc(0, uio); - while (uio->uio_resid > 0 && error == 0) { - cc = q_to_b(&tp->t_outq, buf, min(uio->uio_resid, BUFSIZ)); - if (cc <= 0) - break; - error = uiomove(buf, cc, uio); - } - ttwwakeup(tp); - return (error); -} +/* + * Driver-side hooks. + */ static void -ptsstop(struct tty *tp, int flush) +ptsdrv_outwakeup(struct tty *tp) { - struct pt_desc *pt = tp->t_dev->si_drv1; - int flag; - - /* note: FLUSHREAD and FLUSHWRITE already ok */ - if (flush == 0) { - flush = TIOCPKT_STOP; - pt->pt_flags |= PF_STOPPED; - } else - pt->pt_flags &= ~PF_STOPPED; - pt->pt_send |= flush; - /* change of perspective */ - flag = 0; - if (flush & FREAD) - flag |= FWRITE; - if (flush & FWRITE) - flag |= FREAD; - ptcwakeup(tp, flag); + struct pts_softc *psc = tty_softc(tp); + + cv_broadcast(&psc->pts_outwait); + selwakeup(&psc->pts_outpoll); } -static int -ptcpoll(struct cdev *dev, int events, struct thread *td) +static void +ptsdrv_inwakeup(struct tty *tp) { - struct tty *tp = dev->si_tty; - struct pt_desc *pt = dev->si_drv1; - int revents = 0; - int s; + struct pts_softc *psc = tty_softc(tp); - if ((tp->t_state & TS_CONNECTED) == 0) - return (events & - (POLLHUP | POLLIN | POLLRDNORM | POLLOUT | POLLWRNORM)); + cv_broadcast(&psc->pts_inwait); + selwakeup(&psc->pts_inpoll); +} - /* - * Need to block timeouts (ttrstart). - */ - s = spltty(); +static void +ptsdrv_close(struct tty *tp) +{ - if (events & (POLLIN | POLLRDNORM)) - if ((tp->t_state & TS_ISOPEN) && - ((tp->t_outq.c_cc && (tp->t_state & TS_TTSTOP) == 0) || - ((pt->pt_flags & PF_PKT) && pt->pt_send) || - ((pt->pt_flags & PF_UCNTL) && pt->pt_ucntl))) - revents |= events & (POLLIN | POLLRDNORM); + /* Wake up any blocked readers/writers. */ + ptsdrv_outwakeup(tp); + ptsdrv_inwakeup(tp); +} - if (events & (POLLOUT | POLLWRNORM)) - if (tp->t_state & TS_ISOPEN && - (((tp->t_rawq.c_cc + tp->t_canq.c_cc < TTYHOG - 2) || - (tp->t_canq.c_cc == 0 && (tp->t_lflag & ICANON))))) - revents |= events & (POLLOUT | POLLWRNORM); +static void +ptsdrv_free(void *softc) +{ + struct pts_softc *psc = softc; - if (events & POLLHUP) - if ((tp->t_state & TS_CARR_ON) == 0) - revents |= POLLHUP; + /* Make device number available again. */ + if (psc->pts_unit >= 0) + free_unr(pts_pool, psc->pts_unit); - if (revents == 0) { - if (events & (POLLIN | POLLRDNORM)) - selrecord(td, &pt->pt_selr); + chgptscnt(psc->pts_uidinfo, -1, 0); + uifree(psc->pts_uidinfo); - if (events & (POLLOUT | POLLWRNORM)) - selrecord(td, &pt->pt_selw); - } - splx(s); +#ifdef PTS_EXTERNAL + /* Destroy master device as well. */ + if (psc->pts_cdev != NULL) + destroy_dev_sched(psc->pts_cdev); +#endif /* PTS_EXTERNAL */ - return (revents); + free(psc, M_PTS); } +static struct ttydevsw pts_class = { + .tsw_flags = TF_NOPREFIX, + .tsw_outwakeup = ptsdrv_outwakeup, + .tsw_inwakeup = ptsdrv_inwakeup, + .tsw_close = ptsdrv_close, + .tsw_free = ptsdrv_free, +}; + static int -ptcwrite(struct cdev *dev, struct uio *uio, int flag) +pts_alloc(int fflags, struct thread *td, struct file *fp) { - struct tty *tp = dev->si_tty; - u_char *cp = 0; - int cc = 0; - u_char locbuf[BUFSIZ]; - int cnt = 0; - int error = 0; - -again: - if ((tp->t_state&TS_ISOPEN) == 0) - goto block; - while (uio->uio_resid > 0 || cc > 0) { - if (cc == 0) { - cc = min(uio->uio_resid, BUFSIZ); - cp = locbuf; - error = uiomove(cp, cc, uio); - if (error) - return (error); - /* check again for safety */ - if ((tp->t_state & TS_ISOPEN) == 0) { - /* adjust for data copied in but not written */ - uio->uio_resid += cc; - return (EIO); - } - } - while (cc > 0) { - if ((tp->t_rawq.c_cc + tp->t_canq.c_cc) >= TTYHOG - 2 && - (tp->t_canq.c_cc > 0 || !(tp->t_lflag&ICANON))) { - wakeup(TSA_HUP_OR_INPUT(tp)); - goto block; - } - ttyld_rint(tp, *cp++); - cnt++; - cc--; - } - cc = 0; + int unit, ok; + struct tty *tp; + struct pts_softc *psc; + struct proc *p = td->td_proc; + struct uidinfo *uid = td->td_ucred->cr_ruidinfo; + + /* Resource limiting. */ + PROC_LOCK(p); + ok = chgptscnt(uid, 1, lim_cur(p, RLIMIT_NPTS)); + PROC_UNLOCK(p); + if (!ok) + return (EAGAIN); + + /* Try to allocate a new pts unit number. */ + unit = alloc_unr(pts_pool); + if (unit < 0) { + chgptscnt(uid, -1, 0); + return (EAGAIN); } + + /* Allocate TTY and softc. */ + psc = malloc(sizeof(struct pts_softc), M_PTS, M_WAITOK|M_ZERO); + cv_init(&psc->pts_inwait, "pts inwait"); + cv_init(&psc->pts_outwait, "pts outwait"); + + psc->pts_unit = unit; + psc->pts_uidinfo = uid; + uihold(uid); + + tp = tty_alloc(&pts_class, psc, NULL); + + /* Expose the slave device as well. */ + tty_makedev(tp, td->td_ucred, "pts/%u", psc->pts_unit); + + finit(fp, fflags, DTYPE_PTS, tp, &ptsdev_ops); + return (0); -block: - /* - * Come here to wait for slave to open, for space - * in outq, or space in rawq, or an empty canq. - */ - if ((tp->t_state & TS_CONNECTED) == 0) { - /* adjust for data copied in but not written */ - uio->uio_resid += cc; - return (EIO); - } - if (flag & IO_NDELAY) { - /* adjust for data copied in but not written */ - uio->uio_resid += cc; - if (cnt == 0) - return (EWOULDBLOCK); - return (0); - } - error = tsleep(TSA_PTC_WRITE(tp), TTOPRI | PCATCH, "ptcout", 0); - if (error) { - /* adjust for data copied in but not written */ - uio->uio_resid += cc; - return (error); - } - goto again; } -static int -ptcioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td) +#ifdef PTS_EXTERNAL +int +pts_alloc_external(int fflags, struct thread *td, struct file *fp, + struct cdev *dev, const char *name) { - struct tty *tp = dev->si_tty; - struct pt_desc *pt = dev->si_drv1; -#if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \ - defined(COMPAT_FREEBSD4) || defined(COMPAT_43) - int ival; -#endif + int ok; + struct tty *tp; + struct pts_softc *psc; + struct proc *p = td->td_proc; + struct uidinfo *uid = td->td_ucred->cr_ruidinfo; - switch (cmd) { - - case TIOCGPGRP: - /* - * We avoid calling ttioctl on the controller since, - * in that case, tp must be the controlling terminal. - */ - *(int *)data = tp->t_pgrp ? tp->t_pgrp->pg_id : 0; - return (0); - - case TIOCPKT: - if (*(int *)data) { - if (pt->pt_flags & PF_UCNTL) - return (EINVAL); - pt->pt_flags |= PF_PKT; - } else - pt->pt_flags &= ~PF_PKT; - return (0); - - case TIOCUCNTL: - if (*(int *)data) { - if (pt->pt_flags & PF_PKT) - return (EINVAL); - pt->pt_flags |= PF_UCNTL; - } else - pt->pt_flags &= ~PF_UCNTL; - return (0); - case TIOCGPTN: - *(unsigned int *)data = pt->pt_num; - return (0); - } - - /* - * The rest of the ioctls shouldn't be called until - * the slave is open. - */ - if ((tp->t_state & TS_ISOPEN) == 0) { - if (cmd == TIOCGETA) { - /* - * TIOCGETA is used by isatty() to make sure it's - * a tty. Linux openpty() calls isatty() very early, - * before the slave is opened, so don't actually - * fill the struct termios, but just let isatty() - * know it's a tty. - */ - return (0); - } - if (cmd != FIONBIO && cmd != FIOASYNC) - return (EAGAIN); - } - - switch (cmd) { -#ifdef COMPAT_43TTY - case TIOCSETP: - case TIOCSETN: -#endif - case TIOCSETD: - case TIOCSETA: - case TIOCSETAW: - case TIOCSETAF: - /* - * IF CONTROLLER STTY THEN MUST FLUSH TO PREVENT A HANG. - * ttywflush(tp) will hang if there are characters in - * the outq. - */ - ndflush(&tp->t_outq, tp->t_outq.c_cc); - break; - -#if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \ - defined(COMPAT_FREEBSD4) || defined(COMPAT_43) - case _IO('t', 95): - ival = IOCPARM_IVAL(data); - data = (caddr_t)&ival; - /* FALLTHROUGH */ -#endif - case TIOCSIG: - if (*(unsigned int *)data >= NSIG || - *(unsigned int *)data == 0) - return(EINVAL); - if ((tp->t_lflag&NOFLSH) == 0) - ttyflush(tp, FREAD|FWRITE); - if (tp->t_pgrp != NULL) { - PGRP_LOCK(tp->t_pgrp); - pgsignal(tp->t_pgrp, *(unsigned int *)data, 1); - PGRP_UNLOCK(tp->t_pgrp); - } - if ((*(unsigned int *)data == SIGINFO) && - ((tp->t_lflag&NOKERNINFO) == 0)) - ttyinfo(tp); - return(0); - } - return (ptsioctl(dev, cmd, data, flag, td)); -} -/*ARGSUSED*/ -static int -ptsioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td) -{ - struct tty *tp = dev->si_tty; - struct pt_desc *pt = dev->si_drv1; - u_char *cc = tp->t_cc; - int stop, error; + /* Resource limiting. */ + PROC_LOCK(p); + ok = chgptscnt(uid, 1, lim_cur(p, RLIMIT_NPTS)); + PROC_UNLOCK(p); + if (!ok) + return (EAGAIN); - if (cmd == TIOCEXT) { - /* - * When the EXTPROC bit is being toggled, we need - * to send an TIOCPKT_IOCTL if the packet driver - * is turned on. - */ - if (*(int *)data) { - if (pt->pt_flags & PF_PKT) { - pt->pt_send |= TIOCPKT_IOCTL; - ptcwakeup(tp, FREAD); - } - tp->t_lflag |= EXTPROC; - } else { - if ((tp->t_lflag & EXTPROC) && - (pt->pt_flags & PF_PKT)) { - pt->pt_send |= TIOCPKT_IOCTL; - ptcwakeup(tp, FREAD); - } - tp->t_lflag &= ~EXTPROC; - } - return(0); - } - error = ttioctl(tp, cmd, data, flag); - if (error == ENOTTY) { - if (pt->pt_flags & PF_UCNTL && - (cmd & ~0xff) == UIOCCMD(0)) { - if (cmd & 0xff) { - pt->pt_ucntl = (u_char)cmd; - ptcwakeup(tp, FREAD); - } - return (0); - } - error = ENOTTY; - } - /* - * If external processing and packet mode send ioctl packet. - */ - if ((tp->t_lflag&EXTPROC) && (pt->pt_flags & PF_PKT)) { - switch(cmd) { - case TIOCSETA: - case TIOCSETAW: - case TIOCSETAF: -#ifdef COMPAT_43TTY - case TIOCSETP: - case TIOCSETN: - case TIOCSETC: - case TIOCSLTC: - case TIOCLBIS: - case TIOCLBIC: - case TIOCLSET: -#endif - pt->pt_send |= TIOCPKT_IOCTL; - ptcwakeup(tp, FREAD); - break; - default: - break; - } - } - stop = (tp->t_iflag & IXON) && CCEQ(cc[VSTOP], CTRL('s')) - && CCEQ(cc[VSTART], CTRL('q')); - if (pt->pt_flags & PF_NOSTOP) { - if (stop) { - pt->pt_send &= ~TIOCPKT_NOSTOP; - pt->pt_send |= TIOCPKT_DOSTOP; - pt->pt_flags &= ~PF_NOSTOP; - ptcwakeup(tp, FREAD); - } - } else { - if (!stop) { - pt->pt_send &= ~TIOCPKT_DOSTOP; - pt->pt_send |= TIOCPKT_NOSTOP; - pt->pt_flags |= PF_NOSTOP; - ptcwakeup(tp, FREAD); - } - } - return (error); -} + /* Allocate TTY and softc. */ + psc = malloc(sizeof(struct pts_softc), M_PTS, M_WAITOK|M_ZERO); + cv_init(&psc->pts_inwait, "pts inwait"); + cv_init(&psc->pts_outwait, "pts outwait"); -/* - * Match lookups on /dev/ptmx, find the next free pty (if any), set up - * the pty descriptor, register it, and return a reference to the master. - * - * pts == /dev/pts/xxx (oldstyle: ttyp...) - * ptc == /dev/pty/xxx (oldstyle: ptyp...) - */ -static void -pty_clone(void *arg, struct ucred *cred, char *name, int namelen, - struct cdev **dev) -{ - struct pt_desc *pt; - struct cdev *devc; + psc->pts_unit = -1; + psc->pts_cdev = dev; + psc->pts_uidinfo = uid; + uihold(uid); - if (!use_pts) - return; + tp = tty_alloc(&pts_class, psc, NULL); - if (*dev != NULL) - return; + /* Expose the slave device as well. */ + tty_makedev(tp, td->td_ucred, "%s", name); - if (strcmp(name, "ptmx") != 0) - return; + finit(fp, fflags, DTYPE_PTS, tp, &ptsdev_ops); - mtx_lock(&Giant); - pt = pty_new(); - if (pt == NULL) { - mtx_unlock(&Giant); - return; - } + return (0); +} +#endif /* PTS_EXTERNAL */ + +int +posix_openpt(struct thread *td, struct posix_openpt_args *uap) +{ + int error, fd; + struct file *fp; /* - * XXX: Lack of locking here considered worrying. We expose the - * pts/pty device nodes before they are fully initialized, although - * Giant likely protects us (unless make_dev blocks...?). - * - * XXX: If a process performs a lookup on /dev/ptmx but never an - * open, we won't GC the device node. We should have a callout - * sometime later that GC's device instances that were never - * opened, or some way to tell devfs that "this had better be for - * an open() or we won't create a device". + * POSIX states it's unspecified when other flags are passed. We + * don't allow this. */ - pt->pt_devc = devc = make_dev_credf(MAKEDEV_REF, &ptc_cdevsw, - NUM_TO_MINOR(pt->pt_num), cred, UID_ROOT, GID_WHEEL, 0666, - "pty/%d", pt->pt_num); + if (uap->flags & ~(O_RDWR|O_NOCTTY)) + return (EINVAL); + + error = falloc(td, &fp, &fd); + if (error) + return (error); + + /* Allocate the actual pseudo-TTY. */ + error = pts_alloc(FFLAGS(uap->flags & O_ACCMODE), td, fp); + if (error != 0) { + fdclose(td->td_proc->p_fd, fp, fd, td); + return (error); + } + + /* Pass it back to userspace. */ + td->td_retval[0] = fd; + fdrop(fp, td); + + return (0); +} - devc->si_drv1 = pt; - devc->si_tty = pt->pt_tty; - *dev = devc; - mtx_unlock(&Giant); +#if defined(PTS_COMPAT) || defined(PTS_LINUX) +static int +ptmx_fdopen(struct cdev *dev, int fflags, struct thread *td, struct file *fp) +{ + int error; - if (bootverbose) - printf("pty_clone: allocated pty %d to uid %d\n", pt->pt_num, - cred->cr_ruid); + error = pts_alloc(fflags & (FREAD|FWRITE), td, fp); + if (error != 0) + return (error); - return; + return (0); } +static struct cdevsw ptmx_cdevsw = { + .d_version = D_VERSION, + .d_fdopen = ptmx_fdopen, + .d_name = "ptmx", +}; +#endif /* PTS_COMPAT || PTS_LINUX */ + static void -pty_drvinit(void *unused) +pts_init(void *unused) { - mtx_init(&pt_mtx, "pt_mtx", NULL, MTX_DEF); - LIST_INIT(&pt_list); - LIST_INIT(&pt_free_list); - EVENTHANDLER_REGISTER(dev_clone, pty_clone, 0, 1000); + pts_pool = new_unrhdr(0, MAXPTSDEVS, NULL); +#if defined(PTS_COMPAT) || defined(PTS_LINUX) + make_dev(&ptmx_cdevsw, 0, UID_ROOT, GID_WHEEL, 0666, "ptmx"); +#endif /* PTS_COMPAT || PTS_LINUX */ } -SYSINIT(ptydev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE,pty_drvinit,NULL); +SYSINIT(pts, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, pts_init, NULL); diff --git a/sys/kern/tty_pty.c b/sys/kern/tty_pty.c index 3827e82..e2edd21 100644 --- a/sys/kern/tty_pty.c +++ b/sys/kern/tty_pty.c @@ -1,6 +1,9 @@ /*- - * Copyright (c) 1982, 1986, 1989, 1993 - * The Regents of the University of California. All rights reserved. + * Copyright (c) 2008 Ed Schouten <ed@FreeBSD.org> + * All rights reserved. + * + * 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 @@ -10,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) @@ -25,793 +25,101 @@ * 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_pty.c 8.4 (Berkeley) 2/20/95 */ #include <sys/cdefs.h> __FBSDID("$FreeBSD$"); -/* - * Pseudo-teletype Driver - * (Actually two drivers, requiring two entries in 'cdevsw') - */ -#include "opt_compat.h" -#include "opt_tty.h" #include <sys/param.h> -#include <sys/systm.h> -#include <sys/libkern.h> -#include <sys/lock.h> -#include <sys/mutex.h> -#include <sys/sx.h> -#if defined(COMPAT_43TTY) -#include <sys/ioctl_compat.h> -#endif -#include <sys/priv.h> -#include <sys/proc.h> -#include <sys/tty.h> #include <sys/conf.h> +#include <sys/eventhandler.h> #include <sys/fcntl.h> -#include <sys/poll.h> #include <sys/kernel.h> -#include <sys/uio.h> -#include <sys/signalvar.h> -#include <sys/malloc.h> - -static MALLOC_DEFINE(M_PTY, "ptys", "pty data structures"); - -static void ptsstart(struct tty *tp); -static void ptsstop(struct tty *tp, int rw); -static void ptcwakeup(struct tty *tp, int flag); -static struct cdev *ptyinit(struct cdev *cdev, struct thread *td); - -static d_open_t ptsopen; -static d_close_t ptsclose; -static d_read_t ptsread; -static d_write_t ptswrite; -static d_ioctl_t ptsioctl; -static d_open_t ptcopen; -static d_close_t ptcclose; -static d_read_t ptcread; -static d_ioctl_t ptcioctl; -static d_write_t ptcwrite; -static d_poll_t ptcpoll; - -static struct cdevsw pts_cdevsw = { - .d_version = D_VERSION, - .d_open = ptsopen, - .d_close = ptsclose, - .d_read = ptsread, - .d_write = ptswrite, - .d_ioctl = ptsioctl, - .d_name = "pts", - .d_flags = D_TTY | D_NEEDGIANT, -}; - -static struct cdevsw ptc_cdevsw = { - .d_version = D_VERSION, - .d_open = ptcopen, - .d_close = ptcclose, - .d_read = ptcread, - .d_write = ptcwrite, - .d_ioctl = ptcioctl, - .d_poll = ptcpoll, - .d_name = "ptc", - .d_flags = D_TTY | D_NEEDGIANT, -}; - -static struct mtx ptyinit_lock; -MTX_SYSINIT(ptyinit_lock, &ptyinit_lock, "ptyinit", MTX_DEF); - -#define BUFSIZ 100 /* Chunk size iomoved to/from user */ - -struct ptsc { - int pt_flags; - struct selinfo pt_selr, pt_selw; - u_char pt_send; - u_char pt_ucntl; - struct tty *pt_tty; - struct cdev *devs, *devc; - int pt_devs_open, pt_devc_open; - struct prison *pt_prison; -}; - -#define PF_PKT 0x08 /* packet mode */ -#define PF_STOPPED 0x10 /* user told stopped */ -#define PF_NOSTOP 0x40 -#define PF_UCNTL 0x80 /* user control mode */ - -#define TSA_PTC_READ(tp) ((void *)&(tp)->t_outq.c_cf) -#define TSA_PTC_WRITE(tp) ((void *)&(tp)->t_rawq.c_cl) -#define TSA_PTS_READ(tp) ((void *)&(tp)->t_canq) +#include <sys/proc.h> +#include <sys/systm.h> +#include <sys/tty.h> -static const char names[] = "pqrsPQRSlmnoLMNO"; /* - * This function creates and initializes a pts/ptc pair - * - * pts == /dev/tty[pqrsPQRSlmnoLMNO][0123456789abcdefghijklmnopqrstuv] - * ptc == /dev/pty[pqrsPQRSlmnoLMNO][0123456789abcdefghijklmnopqrstuv] + * This driver implements a BSD-style compatibility naming scheme for + * the pts(4) driver. We just call into pts(4) to create the actual PTY. + * To make sure we don't use the same PTY multiple times, we abuse + * si_drv1 inside the cdev to mark whether the PTY is in use. */ -static struct cdev * -ptyinit(struct cdev *devc, struct thread *td) -{ - struct ptsc *pt; - int n; - n = minor2unit(minor(devc)); +static int pty_warningcnt = 10; - /* We only allow for up to 32 ptys per char in "names". */ - if (n >= 32 * (sizeof(names) - 1)) - return (NULL); - - devc->si_flags &= ~SI_CHEAPCLONE; - - /* - * Initially do not create a slave endpoint. - */ - pt = malloc(sizeof(*pt), M_PTY, M_WAITOK | M_ZERO); - pt->devc = devc; - - pt->pt_tty = ttyalloc(); - pt->pt_tty->t_sc = pt; - mtx_lock(&ptyinit_lock); - if (devc->si_drv1 == NULL) { - devc->si_drv1 = pt; - devc->si_tty = pt->pt_tty; - mtx_unlock(&ptyinit_lock); - } else { - mtx_unlock(&ptyinit_lock); - ttyrel(pt->pt_tty); - free(pt, M_PTY); - } - return (devc); -} - -static void -pty_create_slave(struct ucred *cred, struct ptsc *pt, int m) +static int +ptydev_fdopen(struct cdev *dev, int fflags, struct thread *td, struct file *fp) { - int n; + int u, error; + char name[] = "ttyXX"; - n = minor2unit(m); - KASSERT(n >= 0 && n / 32 < sizeof(names), - ("pty_create_slave: n %d ptsc %p", n, pt)); - pt->devs = make_dev_cred(&pts_cdevsw, m, cred, UID_ROOT, GID_WHEEL, - 0666, "tty%c%r", names[n / 32], n % 32); - pt->devs->si_drv1 = pt; - pt->devs->si_tty = pt->pt_tty; - pt->pt_tty->t_dev = pt->devs; -} - -static void -pty_destroy_slave(struct ptsc *pt) -{ - - if (pt->pt_tty->t_refcnt > 1) - return; - pt->pt_tty->t_dev = NULL; - ttyrel(pt->pt_tty); - pt->pt_tty = NULL; - destroy_dev(pt->devs); - pt->devs = NULL; -} - -static void -pty_maybe_destroy_slave(struct ptsc *pt) -{ - - /* - * vfs bugs and complications near revoke() make - * it currently impossible to destroy struct cdev - */ - if (0 && pt->pt_devc_open == 0 && pt->pt_devs_open == 0) - pty_destroy_slave(pt); -} - -/*ARGSUSED*/ -static int -ptsopen(struct cdev *dev, int flag, int devtype, struct thread *td) -{ - struct tty *tp; - int error; - struct ptsc *pt; - - if (!dev->si_drv1) - return(ENXIO); - pt = dev->si_drv1; - tp = dev->si_tty; - - if ((tp->t_state & TS_ISOPEN) == 0) { - ttyinitmode(tp, 1, 0); - } else if (tp->t_state & TS_XCLUDE && priv_check(td, - PRIV_TTY_EXCLUSIVE)) - return (EBUSY); - else if (pt->pt_prison != td->td_ucred->cr_prison && - priv_check(td, PRIV_TTY_PRISON)) + if (!atomic_cmpset_ptr((uintptr_t *)&dev->si_drv1, 0, 1)) return (EBUSY); - if (tp->t_oproc) /* Ctrlr still around. */ - (void)ttyld_modem(tp, 1); - while ((tp->t_state & TS_CARR_ON) == 0) { - if (flag&FNONBLOCK) - break; - error = ttysleep(tp, TSA_CARR_ON(tp), TTIPRI | PCATCH, - "ptsopn", 0); - if (error) - return (error); - } - error = ttyld_open(tp, dev); - if (error == 0) { - ptcwakeup(tp, FREAD|FWRITE); - pt->pt_devs_open = 1; - } else - pty_maybe_destroy_slave(pt); - return (error); -} - -static int -ptsclose(struct cdev *dev, int flag, int mode, struct thread *td) -{ - struct ptsc *pti; - struct tty *tp; - int err; - - tp = dev->si_tty; - pti = dev->si_drv1; - - KASSERT(dev == pti->devs, ("ptsclose: dev != pti->devs")); - - err = ttyld_close(tp, flag); - (void) tty_close(tp); - - pti->pt_devs_open = 0; - pty_maybe_destroy_slave(pti); - - return (err); -} - -static int -ptsread(struct cdev *dev, struct uio *uio, int flag) -{ - struct tty *tp = dev->si_tty; - int error = 0; - - if (tp->t_oproc) - error = ttyld_read(tp, uio, flag); - ptcwakeup(tp, FWRITE); - return (error); -} - -/* - * Write to pseudo-tty. - * Wakeups of controlling tty will happen - * indirectly, when tty driver calls ptsstart. - */ -static int -ptswrite(struct cdev *dev, struct uio *uio, int flag) -{ - struct tty *tp; - - tp = dev->si_tty; - if (tp->t_oproc == 0) - return (EIO); - return (ttyld_write(tp, uio, flag)); -} - -/* - * Start output on pseudo-tty. - * Wake up process selecting or sleeping for input from controlling tty. - */ -static void -ptsstart(struct tty *tp) -{ - struct ptsc *pt = tp->t_sc; - - if (tp->t_state & TS_TTSTOP) - return; - if (pt->pt_flags & PF_STOPPED) { - pt->pt_flags &= ~PF_STOPPED; - pt->pt_send = TIOCPKT_START; - } - ptcwakeup(tp, FREAD); -} - -static void -ptcwakeup(struct tty *tp, int flag) -{ - struct ptsc *pt = tp->t_sc; - - if (flag & FREAD) { - selwakeuppri(&pt->pt_selr, TTIPRI); - wakeup(TSA_PTC_READ(tp)); - } - if (flag & FWRITE) { - selwakeuppri(&pt->pt_selw, TTOPRI); - wakeup(TSA_PTC_WRITE(tp)); - } -} - -static int -ptcopen(struct cdev *dev, int flag, int devtype, struct thread *td) -{ - struct tty *tp; - struct ptsc *pt; - - if (!dev->si_drv1) - ptyinit(dev, td); - if (!dev->si_drv1) - return(ENXIO); - - pt = dev->si_drv1; - /* - * In case we have destroyed the struct tty at the last connect time, - * we need to recreate it. - */ - if (pt->pt_tty == NULL) { - tp = ttyalloc(); - mtx_lock(&ptyinit_lock); - if (pt->pt_tty == NULL) { - pt->pt_tty = tp; - pt->pt_tty->t_sc = pt; - dev->si_tty = pt->pt_tty; - mtx_unlock(&ptyinit_lock); - } else { - mtx_unlock(&ptyinit_lock); - ttyrel(tp); - } - } - tp = dev->si_tty; - - if (tp->t_oproc) - return (EIO); - tp->t_timeout = -1; - tp->t_oproc = ptsstart; - tp->t_stop = ptsstop; - (void)ttyld_modem(tp, 1); - tp->t_lflag &= ~EXTPROC; - pt->pt_prison = td->td_ucred->cr_prison; - pt->pt_flags = 0; - pt->pt_send = 0; - pt->pt_ucntl = 0; - - if (!pt->devs) - pty_create_slave(td->td_ucred, pt, minor(dev)); - pt->pt_devc_open = 1; - - return (0); -} - -static int -ptcclose(struct cdev *dev, int flags, int fmt, struct thread *td) -{ - struct ptsc *pti = dev->si_drv1; - struct tty *tp; - - tp = dev->si_tty; - (void)ttyld_modem(tp, 0); - - /* - * XXX MDMBUF makes no sense for ptys but would inhibit the above - * l_modem(). CLOCAL makes sense but isn't supported. Special - * l_modem()s that ignore carrier drop make no sense for ptys but - * may be in use because other parts of the line discipline make - * sense for ptys. Recover by doing everything that a normal - * ttymodem() would have done except for sending a SIGHUP. - */ - if (tp->t_state & TS_ISOPEN) { - tp->t_state &= ~(TS_CARR_ON | TS_CONNECTED); - tp->t_state |= TS_ZOMBIE; - ttyflush(tp, FREAD | FWRITE); - } - - tp->t_oproc = 0; /* mark closed */ - pti->pt_devc_open = 0; - pty_maybe_destroy_slave(pti); - return (0); -} - -static int -ptcread(struct cdev *dev, struct uio *uio, int flag) -{ - struct tty *tp = dev->si_tty; - struct ptsc *pt = dev->si_drv1; - char buf[BUFSIZ]; - int error = 0, cc; - - /* - * We want to block until the slave - * is open, and there's something to read; - * but if we lost the slave or we're NBIO, - * then return the appropriate error instead. - */ - for (;;) { - if (tp->t_state&TS_ISOPEN) { - if (pt->pt_flags&PF_PKT && pt->pt_send) { - error = ureadc((int)pt->pt_send, uio); - if (error) - return (error); - if (pt->pt_send & TIOCPKT_IOCTL) { - cc = min(uio->uio_resid, - sizeof(tp->t_termios)); - uiomove(&tp->t_termios, cc, uio); - } - pt->pt_send = 0; - return (0); - } - if (pt->pt_flags&PF_UCNTL && pt->pt_ucntl) { - error = ureadc((int)pt->pt_ucntl, uio); - if (error) - return (error); - pt->pt_ucntl = 0; - return (0); - } - if (tp->t_outq.c_cc && (tp->t_state&TS_TTSTOP) == 0) - break; - } - if ((tp->t_state & TS_CONNECTED) == 0) - return (0); /* EOF */ - if (flag & O_NONBLOCK) - return (EWOULDBLOCK); - error = tsleep(TSA_PTC_READ(tp), TTIPRI | PCATCH, "ptcin", 0); - if (error) - return (error); - } - if (pt->pt_flags & (PF_PKT|PF_UCNTL)) - error = ureadc(0, uio); - while (uio->uio_resid > 0 && error == 0) { - cc = q_to_b(&tp->t_outq, buf, min(uio->uio_resid, BUFSIZ)); - if (cc <= 0) - break; - error = uiomove(buf, cc, uio); - } - ttwwakeup(tp); - return (error); -} - -static void -ptsstop(struct tty *tp, int flush) -{ - struct ptsc *pt = tp->t_sc; - int flag; - - /* note: FLUSHREAD and FLUSHWRITE already ok */ - if (flush == 0) { - flush = TIOCPKT_STOP; - pt->pt_flags |= PF_STOPPED; - } else - pt->pt_flags &= ~PF_STOPPED; - pt->pt_send |= flush; - /* change of perspective */ - flag = 0; - if (flush & FREAD) - flag |= FWRITE; - if (flush & FWRITE) - flag |= FREAD; - ptcwakeup(tp, flag); -} - -static int -ptcpoll(struct cdev *dev, int events, struct thread *td) -{ - struct tty *tp = dev->si_tty; - struct ptsc *pt = dev->si_drv1; - int revents = 0; - int s; - - if ((tp->t_state & TS_CONNECTED) == 0) - return (events & - (POLLHUP | POLLIN | POLLRDNORM | POLLOUT | POLLWRNORM)); - /* - * Need to block timeouts (ttrstart). - */ - s = spltty(); + /* Generate device name and create PTY. */ + u = dev2unit(dev); + name[3] = u >> 8; + name[4] = u; - if (events & (POLLIN | POLLRDNORM)) - if ((tp->t_state & TS_ISOPEN) && - ((tp->t_outq.c_cc && (tp->t_state & TS_TTSTOP) == 0) || - ((pt->pt_flags & PF_PKT) && pt->pt_send) || - ((pt->pt_flags & PF_UCNTL) && pt->pt_ucntl))) - revents |= events & (POLLIN | POLLRDNORM); - - if (events & (POLLOUT | POLLWRNORM)) - if (tp->t_state & TS_ISOPEN && - (((tp->t_rawq.c_cc + tp->t_canq.c_cc < TTYHOG - 2) || - (tp->t_canq.c_cc == 0 && (tp->t_lflag & ICANON))))) - revents |= events & (POLLOUT | POLLWRNORM); - - if (events & POLLHUP) - if ((tp->t_state & TS_CARR_ON) == 0) - revents |= POLLHUP; - - if (revents == 0) { - if (events & (POLLIN | POLLRDNORM)) - selrecord(td, &pt->pt_selr); - - if (events & (POLLOUT | POLLWRNORM)) - selrecord(td, &pt->pt_selw); - } - splx(s); - - return (revents); -} - -static int -ptcwrite(struct cdev *dev, struct uio *uio, int flag) -{ - struct tty *tp = dev->si_tty; - u_char *cp = 0; - int cc = 0; - u_char locbuf[BUFSIZ]; - int cnt = 0; - int error = 0; - -again: - if ((tp->t_state&TS_ISOPEN) == 0) - goto block; - while (uio->uio_resid > 0 || cc > 0) { - if (cc == 0) { - cc = min(uio->uio_resid, BUFSIZ); - cp = locbuf; - error = uiomove(cp, cc, uio); - if (error) - return (error); - /* check again for safety */ - if ((tp->t_state & TS_ISOPEN) == 0) { - /* adjust for data copied in but not written */ - uio->uio_resid += cc; - return (EIO); - } - } - while (cc > 0) { - if ((tp->t_rawq.c_cc + tp->t_canq.c_cc) >= TTYHOG - 2 && - (tp->t_canq.c_cc > 0 || !(tp->t_lflag&ICANON))) { - wakeup(TSA_HUP_OR_INPUT(tp)); - goto block; - } - ttyld_rint(tp, *cp++); - cnt++; - cc--; - } - cc = 0; - } - return (0); -block: - /* - * Come here to wait for slave to open, for space - * in outq, or space in rawq, or an empty canq. - */ - if ((tp->t_state & TS_CONNECTED) == 0) { - /* adjust for data copied in but not written */ - uio->uio_resid += cc; - return (EIO); - } - if (flag & O_NONBLOCK) { - /* adjust for data copied in but not written */ - uio->uio_resid += cc; - if (cnt == 0) - return (EWOULDBLOCK); - return (0); - } - error = tsleep(TSA_PTC_WRITE(tp), TTOPRI | PCATCH, "ptcout", 0); - if (error) { - /* adjust for data copied in but not written */ - uio->uio_resid += cc; + error = pts_alloc_external(fflags & (FREAD|FWRITE), td, fp, dev, name); + if (error != 0) { + destroy_dev_sched(dev); return (error); } - goto again; -} - -/*ARGSUSED*/ -static int -ptcioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td) -{ - struct tty *tp = dev->si_tty; - struct ptsc *pt = dev->si_drv1; -#if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \ - defined(COMPAT_FREEBSD4) || defined(COMPAT_43) - int ival; -#endif - - switch (cmd) { - - case TIOCGPGRP: - /* - * We avoid calling ttioctl on the controller since, - * in that case, tp must be the controlling terminal. - */ - *(int *)data = tp->t_pgrp ? tp->t_pgrp->pg_id : 0; - return (0); - - case TIOCPKT: - if (*(int *)data) { - if (pt->pt_flags & PF_UCNTL) - return (EINVAL); - pt->pt_flags |= PF_PKT; - } else - pt->pt_flags &= ~PF_PKT; - return (0); - - case TIOCUCNTL: - if (*(int *)data) { - if (pt->pt_flags & PF_PKT) - return (EINVAL); - pt->pt_flags |= PF_UCNTL; - } else - pt->pt_flags &= ~PF_UCNTL; - return (0); - } - - /* - * The rest of the ioctls shouldn't be called until - * the slave is open. - */ - if ((tp->t_state & TS_ISOPEN) == 0) - return (EAGAIN); - - switch (cmd) { -#ifdef COMPAT_43TTY - case TIOCSETP: - case TIOCSETN: -#endif - case TIOCSETD: - case TIOCSETA: - case TIOCSETAW: - case TIOCSETAF: - /* - * IF CONTROLLER STTY THEN MUST FLUSH TO PREVENT A HANG. - * ttywflush(tp) will hang if there are characters in - * the outq. - */ - ndflush(&tp->t_outq, tp->t_outq.c_cc); - break; -#if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \ - defined(COMPAT_FREEBSD4) || defined(COMPAT_43) - case _IO('t', 95): - ival = IOCPARM_IVAL(data); - data = (caddr_t)&ival; - /* FALLTHROUGH */ -#endif - case TIOCSIG: - if (*(unsigned int *)data >= NSIG || - *(unsigned int *)data == 0) - return(EINVAL); - if ((tp->t_lflag&NOFLSH) == 0) - ttyflush(tp, FREAD|FWRITE); - if (tp->t_pgrp != NULL) { - PGRP_LOCK(tp->t_pgrp); - pgsignal(tp->t_pgrp, *(unsigned int *)data, 1); - PGRP_UNLOCK(tp->t_pgrp); - } - if ((*(unsigned int *)data == SIGINFO) && - ((tp->t_lflag&NOKERNINFO) == 0)) - ttyinfo(tp); - return(0); + /* Raise a warning when a legacy PTY has been allocated. */ + if (pty_warningcnt > 0) { + pty_warningcnt--; + printf("pid %d (%s) is using legacy pty devices%s\n", + td->td_proc->p_pid, td->td_name, + pty_warningcnt ? "" : " - not logging anymore"); } - return (ptsioctl(dev, cmd, data, flag, td)); + return (0); } -/*ARGSUSED*/ -static int -ptsioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td) -{ - struct tty *tp = dev->si_tty; - struct ptsc *pt = dev->si_drv1; - u_char *cc = tp->t_cc; - int stop, error; - - if (cmd == TIOCEXT) { - /* - * When the EXTPROC bit is being toggled, we need - * to send an TIOCPKT_IOCTL if the packet driver - * is turned on. - */ - if (*(int *)data) { - if (pt->pt_flags & PF_PKT) { - pt->pt_send |= TIOCPKT_IOCTL; - ptcwakeup(tp, FREAD); - } - tp->t_lflag |= EXTPROC; - } else { - if ((tp->t_lflag & EXTPROC) && - (pt->pt_flags & PF_PKT)) { - pt->pt_send |= TIOCPKT_IOCTL; - ptcwakeup(tp, FREAD); - } - tp->t_lflag &= ~EXTPROC; - } - return(0); - } - error = ttyioctl(dev, cmd, data, flag, td); - if (error == ENOTTY) { - if (pt->pt_flags & PF_UCNTL && - (cmd & ~0xff) == UIOCCMD(0)) { - if (cmd & 0xff) { - pt->pt_ucntl = (u_char)cmd; - ptcwakeup(tp, FREAD); - } - return (0); - } - error = ENOTTY; - } - /* - * If external processing and packet mode send ioctl packet. - */ - if ((tp->t_lflag&EXTPROC) && (pt->pt_flags & PF_PKT)) { - switch(cmd) { - case TIOCSETA: - case TIOCSETAW: - case TIOCSETAF: -#ifdef COMPAT_43TTY - case TIOCSETP: - case TIOCSETN: - case TIOCSETC: - case TIOCSLTC: - case TIOCLBIS: - case TIOCLBIC: - case TIOCLSET: -#endif - pt->pt_send |= TIOCPKT_IOCTL; - ptcwakeup(tp, FREAD); - break; - default: - break; - } - } - stop = (tp->t_iflag & IXON) && CCEQ(cc[VSTOP], CTRL('s')) - && CCEQ(cc[VSTART], CTRL('q')); - if (pt->pt_flags & PF_NOSTOP) { - if (stop) { - pt->pt_send &= ~TIOCPKT_NOSTOP; - pt->pt_send |= TIOCPKT_DOSTOP; - pt->pt_flags &= ~PF_NOSTOP; - ptcwakeup(tp, FREAD); - } - } else { - if (!stop) { - pt->pt_send &= ~TIOCPKT_DOSTOP; - pt->pt_send |= TIOCPKT_NOSTOP; - pt->pt_flags |= PF_NOSTOP; - ptcwakeup(tp, FREAD); - } - } - return (error); -} +static struct cdevsw ptydev_cdevsw = { + .d_version = D_VERSION, + .d_fdopen = ptydev_fdopen, + .d_name = "ptydev", +}; static void pty_clone(void *arg, struct ucred *cr, char *name, int namelen, struct cdev **dev) { - char *cp; int u; + /* Cloning is already satisfied. */ if (*dev != NULL) return; - if (bcmp(name, "pty", 3) != 0) - return; - if (name[5] != '\0' || name[3] == '\0') + + /* Only catch /dev/ptyXX. */ + if (namelen != 5 || bcmp(name, "pty", 3) != 0) return; - cp = index(names, name[3]); - if (cp == NULL) + + /* Only catch /dev/pty[l-sL-S]X. */ + if (!(name[3] >= 'l' && name[3] <= 's') && + !(name[3] >= 'L' && name[3] <= 'S')) return; - u = (cp - names) * 32; - if (name[4] >= '0' && name[4] <= '9') - u += name[4] - '0'; - else if (name[4] >= 'a' && name[4] <= 'v') - u += name[4] - 'a' + 10; - else + + /* Only catch /dev/pty[l-sL-S][0-9a-v]. */ + if (!(name[4] >= '0' && name[4] <= '9') && + !(name[4] >= 'a' && name[4] <= 'v')) return; - *dev = make_dev_credf(MAKEDEV_REF, &ptc_cdevsw, unit2minor(u), cr, - UID_ROOT, GID_WHEEL, 0666, "pty%c%r", names[u / 32], u % 32); - (*dev)->si_flags |= SI_CHEAPCLONE; - return; + + /* Create the controller device node. */ + u = (unsigned int)name[3] << 8 | name[4]; + *dev = make_dev_credf(MAKEDEV_REF, &ptydev_cdevsw, u, + NULL, UID_ROOT, GID_WHEEL, 0666, name); } static void -ptc_drvinit(void *unused) +pty_init(void *unused) { EVENTHANDLER_REGISTER(dev_clone, pty_clone, 0, 1000); } -SYSINIT(ptcdev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE,ptc_drvinit,NULL); +SYSINIT(pty, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, pty_init, NULL); diff --git a/sys/kern/tty_ttydisc.c b/sys/kern/tty_ttydisc.c new file mode 100644 index 0000000..b52fc18 --- /dev/null +++ b/sys/kern/tty_ttydisc.c @@ -0,0 +1,1131 @@ +/*- + * Copyright (c) 2008 Ed Schouten <ed@FreeBSD.org> + * All rights reserved. + * + * 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 + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 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. + * + * 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 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) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * 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. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/fcntl.h> +#include <sys/filio.h> +#include <sys/kernel.h> +#include <sys/signal.h> +#include <sys/sysctl.h> +#include <sys/systm.h> +#include <sys/tty.h> +#include <sys/ttycom.h> +#include <sys/ttydefaults.h> +#include <sys/uio.h> +#include <sys/vnode.h> + +/* + * Standard TTYDISC `termios' line discipline. + */ + +/* Statistics. */ +static long tty_nin = 0; +SYSCTL_LONG(_kern, OID_AUTO, tty_nin, CTLFLAG_RD, + &tty_nin, 0, "Total amount of bytes received"); +static long tty_nout = 0; +SYSCTL_LONG(_kern, OID_AUTO, tty_nout, CTLFLAG_RD, + &tty_nout, 0, "Total amount of bytes transmitted"); + +/* termios comparison macro's. */ +#define CMP_CC(v,c) (tp->t_termios.c_cc[v] != _POSIX_VDISABLE && \ + tp->t_termios.c_cc[v] == (c)) +#define CMP_FLAG(field,opt) (tp->t_termios.c_ ## field ## flag & (opt)) + +/* Characters that cannot be modified through c_cc. */ +#define CTAB '\t' +#define CNL '\n' +#define CCR '\r' + +/* Character is a control character. */ +#define CTL_VALID(c) ((c) == 0x7f || (unsigned char)(c) < 0x20) +/* Control character should be processed on echo. */ +#define CTL_ECHO(c,q) (!(q) && ((c) == CERASE2 || (c) == CTAB || \ + (c) == CNL || (c) == CCR)) +/* Control character should be printed using ^X notation. */ +#define CTL_PRINT(c,q) ((c) == 0x7f || ((unsigned char)(c) < 0x20 && \ + ((q) || ((c) != CTAB && (c) != CNL)))) +/* Character is whitespace. */ +#define CTL_WHITE(c) ((c) == ' ' || (c) == CTAB) +/* Character is alphanumeric. */ +#define CTL_ALNUM(c) (((c) >= '0' && (c) <= '9') || \ + ((c) >= 'a' && (c) <= 'z') || ((c) >= 'A' && (c) <= 'Z')) + +void +ttydisc_open(struct tty *tp) +{ + ttydisc_optimize(tp); +} + +void +ttydisc_close(struct tty *tp) +{ + + /* Clean up our flags when leaving the discipline. */ + tp->t_flags &= ~(TF_STOPPED|TF_HIWAT|TF_ZOMBIE); + + /* POSIX states we should flush when close() is called. */ + ttyinq_flush(&tp->t_inq); + ttyoutq_flush(&tp->t_outq); + + if (!tty_gone(tp)) { + ttydevsw_inwakeup(tp); + ttydevsw_outwakeup(tp); + } +} + +static int +ttydisc_read_canonical(struct tty *tp, struct uio *uio, int ioflag) +{ + char breakc[4] = { CNL }; /* enough to hold \n, VEOF and VEOL. */ + int error; + size_t clen, flen = 0, n = 1; + unsigned char lastc = _POSIX_VDISABLE; + +#define BREAK_ADD(c) do { \ + if (tp->t_termios.c_cc[c] != _POSIX_VDISABLE) \ + breakc[n++] = tp->t_termios.c_cc[c]; \ +} while (0) + /* Determine which characters we should trigger on. */ + BREAK_ADD(VEOF); + BREAK_ADD(VEOL); +#undef BREAK_ADD + breakc[n] = '\0'; + + do { + /* + * Quite a tricky case: unlike the old TTY + * implementation, this implementation copies data back + * to userspace in large chunks. Unfortunately, we can't + * calculate the line length on beforehand if it crosses + * ttyinq_block boundaries, because multiple reads could + * then make this code read beyond the newline. + * + * This is why we limit the read to: + * - The size the user has requested + * - The blocksize (done in tty_inq.c) + * - The amount of bytes until the newline + * + * This causes the line length to be recalculated after + * each block has been copied to userspace. This will + * cause the TTY layer to return data in chunks using + * the blocksize (except the first and last blocks). + */ + clen = ttyinq_findchar(&tp->t_inq, breakc, uio->uio_resid, + &lastc); + + /* No more data. */ + if (clen == 0) { + if (ioflag & IO_NDELAY) + return (EWOULDBLOCK); + else if (tp->t_flags & TF_ZOMBIE) + return (0); + + error = tty_wait(tp, &tp->t_inwait); + if (error) + return (error); + continue; + } + + /* Don't send the EOF char back to userspace. */ + if (CMP_CC(VEOF, lastc)) + flen = 1; + + MPASS(flen <= clen); + + /* Read and throw away the EOF character. */ + error = ttyinq_read_uio(&tp->t_inq, tp, uio, clen, flen); + if (error) + return (error); + + } while (uio->uio_resid > 0 && lastc == _POSIX_VDISABLE); + + return (0); +} + +static int +ttydisc_read_raw_no_timer(struct tty *tp, struct uio *uio, int ioflag) +{ + size_t vmin = tp->t_termios.c_cc[VMIN]; + int oresid = uio->uio_resid; + int error; + + MPASS(tp->t_termios.c_cc[VTIME] == 0); + + /* + * This routine implements the easy cases of read()s while in + * non-canonical mode, namely case B and D, where we don't have + * any timers at all. + */ + + for (;;) { + error = ttyinq_read_uio(&tp->t_inq, tp, uio, + uio->uio_resid, 0); + if (error) + return (error); + if (uio->uio_resid == 0 || (oresid - uio->uio_resid) >= vmin) + return (0); + + /* We have to wait for more. */ + if (ioflag & IO_NDELAY) + return (EWOULDBLOCK); + else if (tp->t_flags & TF_ZOMBIE) + return (0); + + error = tty_wait(tp, &tp->t_inwait); + if (error) + return (error); + } +} + +static int +ttydisc_read_raw_read_timer(struct tty *tp, struct uio *uio, int ioflag, + int oresid) +{ + size_t vmin = MAX(tp->t_termios.c_cc[VMIN], 1); + unsigned int vtime = tp->t_termios.c_cc[VTIME]; + struct timeval end, now, left; + int error, hz; + + MPASS(tp->t_termios.c_cc[VTIME] != 0); + + /* Determine when the read should be expired. */ + end.tv_sec = vtime / 10; + end.tv_usec = (vtime % 10) * 100000; + getmicrotime(&now); + timevaladd(&end, &now); + + for (;;) { + error = ttyinq_read_uio(&tp->t_inq, tp, uio, + uio->uio_resid, 0); + if (error) + return (error); + if (uio->uio_resid == 0 || (oresid - uio->uio_resid) >= vmin) + return (0); + + /* Calculate how long we should wait. */ + getmicrotime(&now); + if (timevalcmp(&now, &end, >)) + return (0); + left = end; + timevalsub(&left, &now); + hz = tvtohz(&left); + + /* + * We have to wait for more. If the timer expires, we + * should return a 0-byte read. + */ + if (ioflag & IO_NDELAY) + return (EWOULDBLOCK); + else if (tp->t_flags & TF_ZOMBIE) + return (0); + + error = tty_timedwait(tp, &tp->t_inwait, hz); + if (error) + return (error == EWOULDBLOCK ? 0 : error); + } + + return (0); +} + +static int +ttydisc_read_raw_interbyte_timer(struct tty *tp, struct uio *uio, int ioflag) +{ + size_t vmin = tp->t_termios.c_cc[VMIN]; + int oresid = uio->uio_resid; + int error; + + MPASS(tp->t_termios.c_cc[VMIN] != 0); + MPASS(tp->t_termios.c_cc[VTIME] != 0); + + /* + * When using the interbyte timer, the timer should be started + * after the first byte has been received. We just call into the + * generic read timer code after we've received the first byte. + */ + + for (;;) { + error = ttyinq_read_uio(&tp->t_inq, tp, uio, + uio->uio_resid, 0); + if (error) + return (error); + if (uio->uio_resid == 0 || (oresid - uio->uio_resid) >= vmin) + return (0); + + /* + * Not enough data, but we did receive some, which means + * we'll now start using the interbyte timer. + */ + if (oresid != uio->uio_resid) + break; + + /* We have to wait for more. */ + if (ioflag & IO_NDELAY) + return (EWOULDBLOCK); + else if (tp->t_flags & TF_ZOMBIE) + return (0); + + error = tty_wait(tp, &tp->t_inwait); + if (error) + return (error); + } + + return ttydisc_read_raw_read_timer(tp, uio, ioflag, oresid); +} + +int +ttydisc_read(struct tty *tp, struct uio *uio, int ioflag) +{ + int error; + size_t c; + + tty_lock_assert(tp, MA_OWNED); + + if (uio->uio_resid == 0) + return (0); + + if (CMP_FLAG(l, ICANON)) + error = ttydisc_read_canonical(tp, uio, ioflag); + else if (tp->t_termios.c_cc[VTIME] == 0) + error = ttydisc_read_raw_no_timer(tp, uio, ioflag); + else if (tp->t_termios.c_cc[VMIN] == 0) + error = ttydisc_read_raw_read_timer(tp, uio, ioflag, + uio->uio_resid); + else + error = ttydisc_read_raw_interbyte_timer(tp, uio, ioflag); + + c = ttyinq_bytesleft(&tp->t_inq); + if (c >= tp->t_inlow) { + /* Unset the input watermark when we've got enough space. */ + tty_hiwat_in_unblock(tp); + } + + return (error); +} + +static __inline unsigned int +ttydisc_findchar(const char *obstart, unsigned int oblen) +{ + const char *c = obstart; + + while (oblen--) { + if (CTL_VALID(*c)) + break; + c++; + } + + return (c - obstart); +} + +static int +ttydisc_write_oproc(struct tty *tp, char c) +{ + unsigned int scnt, error; + + MPASS(CMP_FLAG(o, OPOST)); + MPASS(CTL_VALID(c)); + +#define PRINT_NORMAL() ttyoutq_write_nofrag(&tp->t_outq, &c, 1) + switch (c) { + case CEOF: + /* End-of-text dropping. */ + if (CMP_FLAG(o, ONOEOT)) + return (0); + return PRINT_NORMAL(); + + case CERASE2: + /* Handle backspace to fix tab expansion. */ + if (PRINT_NORMAL() != 0) + return (-1); + if (tp->t_column > 0) + tp->t_column--; + return (0); + + case CTAB: + /* Tab expansion. */ + scnt = 8 - (tp->t_column & 7); + if (CMP_FLAG(o, TAB3)) { + error = ttyoutq_write_nofrag(&tp->t_outq, + " ", scnt); + } else { + error = PRINT_NORMAL(); + } + if (error) + return (-1); + + tp->t_column += scnt; + MPASS((tp->t_column % 8) == 0); + return (0); + + case CNL: + /* Newline conversion. */ + if (CMP_FLAG(o, ONLCR)) { + /* Convert \n to \r\n. */ + error = ttyoutq_write_nofrag(&tp->t_outq, "\r\n", 2); + } else { + error = PRINT_NORMAL(); + } + if (error) + return (-1); + + if (CMP_FLAG(o, ONLCR|ONLRET)) { + tp->t_column = tp->t_writepos = 0; + ttyinq_reprintpos_set(&tp->t_inq); + } + return (0); + + case CCR: + /* Carriage return to newline conversion. */ + if (CMP_FLAG(o, OCRNL)) + c = CNL; + /* Omit carriage returns on column 0. */ + if (CMP_FLAG(o, ONOCR) && tp->t_column == 0) + return (0); + if (PRINT_NORMAL() != 0) + return (-1); + + tp->t_column = tp->t_writepos = 0; + ttyinq_reprintpos_set(&tp->t_inq); + return (0); + } + + /* + * Invisible control character. Print it, but don't + * increase the column count. + */ + return PRINT_NORMAL(); +#undef PRINT_NORMAL +} + +/* + * Just like the old TTY implementation, we need to copy data in chunks + * into a temporary buffer. One of the reasons why we need to do this, + * is because output processing (only TAB3 though) may allow the buffer + * to grow eight times. + */ +int +ttydisc_write(struct tty *tp, struct uio *uio, int ioflag) +{ + char ob[256]; + char *obstart; + int error = 0; + unsigned int oblen = 0; + + tty_lock_assert(tp, MA_OWNED); + + if (tp->t_flags & TF_ZOMBIE) + return (EIO); + + /* + * We don't need to check whether the process is the foreground + * process group or if we have a carrier. This is already done + * in ttydev_write(). + */ + + while (uio->uio_resid > 0) { + unsigned int nlen; + + MPASS(oblen == 0); + + /* Step 1: read data. */ + + tty_unlock(tp); + + obstart = ob; + nlen = MIN(uio->uio_resid, sizeof ob); + error = uiomove(ob, nlen, uio); + if (error != 0) + break; + oblen = nlen; + + tty_lock(tp); + if (tty_gone(tp)) { + error = ENXIO; + break; + } + + MPASS(oblen > 0); + + /* Step 2: process data. */ + do { + unsigned int plen, wlen; + + /* Search for special characters for post processing. */ + if (CMP_FLAG(o, OPOST)) { + plen = ttydisc_findchar(obstart, oblen); + } else { + plen = oblen; + } + + if (plen == 0) { + /* + * We're going to process a character + * that needs processing + */ + if (ttydisc_write_oproc(tp, *obstart) == 0) { + obstart++; + oblen--; + + tp->t_writepos = tp->t_column; + ttyinq_reprintpos_set(&tp->t_inq); + continue; + } + } else { + /* We're going to write regular data. */ + wlen = ttyoutq_write(&tp->t_outq, obstart, plen); + obstart += wlen; + oblen -= wlen; + tp->t_column += wlen; + + tp->t_writepos = tp->t_column; + ttyinq_reprintpos_set(&tp->t_inq); + + if (wlen == plen) + continue; + } + + /* Watermark reached. Try to sleep. */ + tp->t_flags |= TF_HIWAT_OUT; + + if (ioflag & IO_NDELAY) { + error = EWOULDBLOCK; + goto done; + } + + /* + * The driver may write back the data + * synchronously. Be sure to check the high + * water mark before going to sleep. + */ + ttydevsw_outwakeup(tp); + if ((tp->t_flags & TF_HIWAT_OUT) == 0) + continue; + + error = tty_wait(tp, &tp->t_outwait); + if (error) + goto done; + + if (tp->t_flags & TF_ZOMBIE) { + error = EIO; + goto done; + } + } while (oblen > 0); + } + +done: + ttydevsw_outwakeup(tp); + + /* + * Add the amount of bytes that we didn't process back to the + * uio counters. We need to do this to make sure write() doesn't + * count the bytes we didn't store in the queue. + */ + uio->uio_resid += oblen; + return (error); +} + +void +ttydisc_optimize(struct tty *tp) +{ + tty_lock_assert(tp, MA_OWNED); + + if (!CMP_FLAG(i, ICRNL|IGNCR|IMAXBEL|INLCR|ISTRIP|IXON) && + (!CMP_FLAG(i, BRKINT) || CMP_FLAG(i, IGNBRK)) && + (!CMP_FLAG(i, PARMRK) || + CMP_FLAG(i, IGNPAR|IGNBRK) == (IGNPAR|IGNBRK)) && + !CMP_FLAG(l, ECHO|ICANON|IEXTEN|ISIG|PENDIN)) { + tp->t_flags |= TF_BYPASS; + } else { + tp->t_flags &= ~TF_BYPASS; + } +} + +void +ttydisc_modem(struct tty *tp, int open) +{ + + tty_lock_assert(tp, MA_OWNED); + + if (open) + cv_broadcast(&tp->t_dcdwait); + + /* + * Ignore modem status lines when CLOCAL is turned on, but don't + * enter the zombie state when the TTY isn't opened, because + * that would cause the TTY to be in zombie state after being + * opened. + */ + if (!tty_opened(tp) || CMP_FLAG(c, CLOCAL)) + return; + + if (open == 0) { + /* + * Lost carrier. + */ + tp->t_flags |= TF_ZOMBIE; + + tty_signal_sessleader(tp, SIGHUP); + tty_flush(tp, FREAD|FWRITE); + } else { + /* + * Carrier is back again. + */ + + /* XXX: what should we do here? */ + } +} + +static int +ttydisc_echo_force(struct tty *tp, char c, int quote) +{ + + if (CMP_FLAG(o, OPOST) && CTL_ECHO(c, quote)) { + /* + * Only perform postprocessing when OPOST is turned on + * and the character is an unquoted BS/TB/NL/CR. + */ + return ttydisc_write_oproc(tp, c); + } else if (CMP_FLAG(l, ECHOCTL) && CTL_PRINT(c, quote)) { + /* + * Only use ^X notation when ECHOCTL is turned on and + * we've got an quoted control character. + */ + char ob[2] = { '^', '?' }; + + /* Print ^X notation. */ + if (c != 0x7f) + ob[1] = c + 'A' - 1; + + tp->t_column += 2; + return ttyoutq_write_nofrag(&tp->t_outq, ob, 2); + } else { + /* Can just be printed. */ + tp->t_column++; + return ttyoutq_write_nofrag(&tp->t_outq, &c, 1); + } +} + +static int +ttydisc_echo(struct tty *tp, char c, int quote) +{ + + /* + * Only echo characters when ECHO is turned on, or ECHONL when + * the character is an unquoted newline. + */ + if (!CMP_FLAG(l, ECHO) && + (!CMP_FLAG(l, ECHONL) || c != CNL || quote)) + return (0); + + return ttydisc_echo_force(tp, c, quote); +} + + +static void +ttydisc_reprint_char(void *d, char c, int quote) +{ + struct tty *tp = d; + + ttydisc_echo(tp, c, quote); +} + +static void +ttydisc_reprint(struct tty *tp) +{ + cc_t c; + + /* Print ^R\n, followed by the line. */ + c = tp->t_termios.c_cc[VREPRINT]; + if (c != _POSIX_VDISABLE) + ttydisc_echo(tp, c, 0); + ttydisc_echo(tp, CNL, 0); + ttyinq_reprintpos_reset(&tp->t_inq); + + ttyinq_line_iterate_from_linestart(&tp->t_inq, ttydisc_reprint_char, tp); +} + +struct ttydisc_recalc_length { + struct tty *tp; + unsigned int curlen; +}; + +static void +ttydisc_recalc_charlength(void *d, char c, int quote) +{ + struct ttydisc_recalc_length *data = d; + struct tty *tp = data->tp; + + if (CTL_PRINT(c, quote)) { + if (CMP_FLAG(l, ECHOCTL)) + data->curlen += 2; + } else if (c == CTAB) { + data->curlen += 8 - (data->curlen & 7); + } else { + data->curlen++; + } +} + +static unsigned int +ttydisc_recalc_linelength(struct tty *tp) +{ + struct ttydisc_recalc_length data = { tp, tp->t_writepos }; + + ttyinq_line_iterate_from_reprintpos(&tp->t_inq, + ttydisc_recalc_charlength, &data); + return (data.curlen); +} + +static int +ttydisc_rubchar(struct tty *tp) +{ + char c; + int quote; + unsigned int prevpos, tablen; + + if (ttyinq_peekchar(&tp->t_inq, &c, "e) != 0) + return (-1); + ttyinq_unputchar(&tp->t_inq); + + if (CMP_FLAG(l, ECHO)) { + /* + * Remove the character from the screen. This is even + * safe for characters that span multiple characters + * (tabs, quoted, etc). + */ + if (tp->t_writepos >= tp->t_column) { + /* Retype the sentence. */ + ttydisc_reprint(tp); + } else if (CMP_FLAG(l, ECHOE)) { + if (CTL_PRINT(c, quote)) { + /* Remove ^X formatted chars. */ + if (CMP_FLAG(l, ECHOCTL)) { + tp->t_column -= 2; + ttyoutq_write_nofrag(&tp->t_outq, + "\b\b \b\b", 6); + } + } else if (c == ' ') { + /* Space character needs no rubbing. */ + tp->t_column -= 1; + ttyoutq_write_nofrag(&tp->t_outq, "\b", 1); + } else if (c == CTAB) { + /* + * Making backspace work with tabs is + * quite hard. Recalculate the length of + * this character and remove it. + * + * Because terminal settings could be + * changed while the line is being + * inserted, the calculations don't have + * to be correct. Make sure we keep the + * tab length within proper bounds. + */ + prevpos = ttydisc_recalc_linelength(tp); + if (prevpos >= tp->t_column) + tablen = 1; + else + tablen = tp->t_column - prevpos; + if (tablen > 8) + tablen = 8; + + tp->t_column = prevpos; + ttyoutq_write_nofrag(&tp->t_outq, + "\b\b\b\b\b\b\b\b", tablen); + return (0); + } else { + /* + * Remove a regular character by + * punching a space over it. + */ + tp->t_column -= 1; + ttyoutq_write_nofrag(&tp->t_outq, "\b \b", 3); + } + } else { + /* Don't print spaces. */ + ttydisc_echo(tp, tp->t_termios.c_cc[VERASE], 0); + } + } + + return (0); +} + +static void +ttydisc_rubword(struct tty *tp) +{ + char c; + int quote, alnum; + + /* Strip whitespace first. */ + for (;;) { + if (ttyinq_peekchar(&tp->t_inq, &c, "e) != 0) + return; + if (!CTL_WHITE(c)) + break; + ttydisc_rubchar(tp); + } + + /* + * Record whether the last character from the previous iteration + * was alphanumeric or not. We need this to implement ALTWERASE. + */ + alnum = CTL_ALNUM(c); + for (;;) { + ttydisc_rubchar(tp); + + if (ttyinq_peekchar(&tp->t_inq, &c, "e) != 0) + return; + if (CTL_WHITE(c)) + return; + if (CMP_FLAG(l, ALTWERASE) && CTL_ALNUM(c) != alnum) + return; + } +} + +int +ttydisc_rint(struct tty *tp, char c, int flags) +{ + int signal, quote = 0; + char ob[3] = { 0xff, 0x00 }; + size_t ol; + + tty_lock_assert(tp, MA_OWNED); + + atomic_add_long(&tty_nin, 1); + + if (tp->t_flags & TF_BYPASS) + goto processed; + + if (flags) { + if (flags & TRE_BREAK) { + if (CMP_FLAG(i, IGNBRK)) { + /* Ignore break characters. */ + return (0); + } else if (CMP_FLAG(i, BRKINT)) { + /* Generate SIGINT on break. */ + tty_flush(tp, FREAD|FWRITE); + tty_signal_pgrp(tp, SIGINT); + return (0); + } else { + /* Just print it. */ + goto parmrk; + } + } else if (flags & TRE_FRAMING || + (flags & TRE_PARITY && CMP_FLAG(i, INPCK))) { + if (CMP_FLAG(i, IGNPAR)) { + /* Ignore bad characters. */ + return (0); + } else { + /* Just print it. */ + goto parmrk; + } + } + } + + /* Allow any character to perform a wakeup. */ + if (CMP_FLAG(i, IXANY)) + tp->t_flags &= ~TF_STOPPED; + + /* Remove the top bit. */ + if (CMP_FLAG(i, ISTRIP)) + c &= ~0x80; + + /* Skip input processing when we want to print it literally. */ + if (tp->t_flags & TF_LITERAL) { + tp->t_flags &= ~TF_LITERAL; + quote = 1; + goto processed; + } + + /* Special control characters that are implementation dependent. */ + if (CMP_FLAG(l, IEXTEN)) { + /* Accept the next character as literal. */ + if (CMP_CC(VLNEXT, c)) { + if (CMP_FLAG(l, ECHO)) { + if (CMP_FLAG(l, ECHOE)) + ttyoutq_write_nofrag(&tp->t_outq, "^\b", 2); + else + ttydisc_echo(tp, c, 0); + } + tp->t_flags |= TF_LITERAL; + return (0); + } + } + + /* + * Handle signal processing. + */ + if (CMP_FLAG(l, ISIG)) { + if (CMP_FLAG(l, ICANON|IEXTEN) == (ICANON|IEXTEN)) { + if (CMP_CC(VSTATUS, c)) { + tty_signal_pgrp(tp, SIGINFO); + return (0); + } + } + + /* + * When compared to the old implementation, this + * implementation also flushes the output queue. POSIX + * is really brief about this, but does makes us assume + * we have to do so. + */ + signal = 0; + if (CMP_CC(VINTR, c)) { + signal = SIGINT; + } else if (CMP_CC(VQUIT, c)) { + signal = SIGQUIT; + } else if (CMP_CC(VSUSP, c)) { + signal = SIGTSTP; + } + + if (signal != 0) { + /* + * Echo the character before signalling the + * processes. + */ + if (!CMP_FLAG(l, NOFLSH)) + tty_flush(tp, FREAD|FWRITE); + ttydisc_echo(tp, c, 0); + tty_signal_pgrp(tp, signal); + return (0); + } + } + + /* + * Handle start/stop characters. + */ + if (CMP_FLAG(i, IXON)) { + if (CMP_CC(VSTOP, c)) { + /* Stop it if we aren't stopped yet. */ + if ((tp->t_flags & TF_STOPPED) == 0) { + tp->t_flags |= TF_STOPPED; + return (0); + } + /* + * Fallthrough: + * When VSTART == VSTOP, we should make this key + * toggle it. + */ + if (!CMP_CC(VSTART, c)) + return (0); + } + if (CMP_CC(VSTART, c)) { + tp->t_flags &= ~TF_STOPPED; + return (0); + } + } + + /* Conversion of CR and NL. */ + switch (c) { + case CCR: + if (CMP_FLAG(i, IGNCR)) + return (0); + if (CMP_FLAG(i, ICRNL)) + c = CNL; + break; + case CNL: + if (CMP_FLAG(i, INLCR)) + c = CCR; + break; + } + + /* Canonical line editing. */ + if (CMP_FLAG(l, ICANON)) { + if (CMP_CC(VERASE, c) || CMP_CC(VERASE2, c)) { + ttydisc_rubchar(tp); + return (0); + } else if (CMP_CC(VKILL, c)) { + while (ttydisc_rubchar(tp) == 0); + return (0); + } else if (CMP_FLAG(l, IEXTEN)) { + if (CMP_CC(VWERASE, c)) { + ttydisc_rubword(tp); + return (0); + } else if (CMP_CC(VREPRINT, c)) { + ttydisc_reprint(tp); + return (0); + } + } + } + +processed: + if (CMP_FLAG(i, PARMRK) && (unsigned char)c == 0xff) { + /* Print 0xff 0xff. */ + ob[1] = 0xff; + ol = 2; + quote = 1; + } else { + ob[0] = c; + ol = 1; + } + + goto print; + +parmrk: + if (CMP_FLAG(i, PARMRK)) { + /* Prepend 0xff 0x00 0x.. */ + ob[2] = c; + ol = 3; + quote = 1; + } else { + ob[0] = c; + ol = 1; + } + +print: + /* See if we can store this on the input queue. */ + if (ttyinq_write_nofrag(&tp->t_inq, ob, ol, quote) != 0) { + /* We cannot. Enable the input watermark. */ + tty_hiwat_in_block(tp); + return (-1); + } + + /* + * In raw mode, we canonicalize after receiving a single + * character. Otherwise, we canonicalize when we receive a + * newline, VEOL or VEOF, but only when it isn't quoted. + */ + if (!CMP_FLAG(l, ICANON) || + (!quote && (c == CNL || CMP_CC(VEOL, c) || CMP_CC(VEOF, c)))) { + ttyinq_canonicalize(&tp->t_inq); + } + + ttydisc_echo(tp, c, quote); + + return (0); +} + +size_t +ttydisc_rint_bypass(struct tty *tp, char *buf, size_t len) +{ + size_t ret; + + tty_lock_assert(tp, MA_OWNED); + + MPASS(tp->t_flags & TF_BYPASS); + + atomic_add_long(&tty_nin, len); + + ret = ttyinq_write(&tp->t_inq, buf, len, 0); + ttyinq_canonicalize(&tp->t_inq); + + return (ret); +} + +void +ttydisc_rint_done(struct tty *tp) +{ + + tty_lock_assert(tp, MA_OWNED); + + /* Wake up readers. */ + tty_wakeup(tp, FREAD); + /* Wake up driver for echo. */ + ttydevsw_outwakeup(tp); +} + +static void +ttydisc_wakeup_watermark(struct tty *tp) +{ + size_t c; + + c = ttyoutq_bytesleft(&tp->t_outq); + if (tp->t_flags & TF_HIWAT_OUT) { + /* Only allow us to run when we're below the watermark. */ + if (c < tp->t_outlow) + return; + + /* Reset the watermark. */ + tp->t_flags &= ~TF_HIWAT_OUT; + } else { + /* Only run when we have data at all. */ + if (c == 0) + return; + } + tty_wakeup(tp, FWRITE); +} + +size_t +ttydisc_getc(struct tty *tp, void *buf, size_t len) +{ + int ret; + + tty_lock_assert(tp, MA_OWNED); + + if (tp->t_flags & TF_STOPPED) + return (0); + + ret = ttyoutq_read(&tp->t_outq, buf, len); + ttydisc_wakeup_watermark(tp); + + atomic_add_long(&tty_nout, ret); + + return (ret); +} + +int +ttydisc_getc_uio(struct tty *tp, struct uio *uio) +{ + int error; + int obytes = uio->uio_resid; + + tty_lock_assert(tp, MA_OWNED); + + if (tp->t_flags & TF_STOPPED) + return (0); + + error = ttyoutq_read_uio(&tp->t_outq, tp, uio); + ttydisc_wakeup_watermark(tp); + + atomic_add_long(&tty_nout, obytes - uio->uio_resid); + + return (error); +} + +/* + * XXX: not really related to the TTYDISC, but we'd better put + * tty_putchar() here, because we need to perform proper output + * processing. + */ + +int +tty_putchar(struct tty *tp, char c) +{ + tty_lock_assert(tp, MA_OWNED); + + if (tty_gone(tp)) + return (-1); + + ttydisc_echo_force(tp, c, 0); + tp->t_writepos = tp->t_column; + ttyinq_reprintpos_set(&tp->t_inq); + + ttydevsw_outwakeup(tp); + return (0); +} diff --git a/sys/modules/nmdm/Makefile b/sys/modules/nmdm/Makefile index b788334..25e9470 100644 --- a/sys/modules/nmdm/Makefile +++ b/sys/modules/nmdm/Makefile @@ -4,14 +4,5 @@ KMOD= nmdm SRCS= nmdm.c -SRCS+= opt_compat.h opt_tty.h vnode_if.h - -.if !defined(KERNBUILDDIR) -opt_compat.h: - echo "#define COMPAT_43 1" >opt_compat.h - -opt_tty.h: - echo "#define TTYHOG 8192" >opt_tty.h -.endif .include <bsd.kmod.mk> diff --git a/sys/modules/rc/Makefile b/sys/modules/rc/Makefile index cc8d228..ffa6163 100644 --- a/sys/modules/rc/Makefile +++ b/sys/modules/rc/Makefile @@ -3,11 +3,6 @@ .PATH: ${.CURDIR}/../../dev/rc KMOD= rc -SRCS= rc.c device_if.h bus_if.h isa_if.h opt_tty.h - -.if !defined(KERNBUILDDIR) -opt_tty.h: - echo "#define TTYHOG 8192" >opt_tty.h -.endif +SRCS= rc.c device_if.h bus_if.h isa_if.h .include <bsd.kmod.mk> diff --git a/sys/pc98/cbus/scterm-sck.c b/sys/pc98/cbus/scterm-sck.c index 9b45490..ced4a92 100644 --- a/sys/pc98/cbus/scterm-sck.c +++ b/sys/pc98/cbus/scterm-sck.c @@ -1110,7 +1110,7 @@ ascii_end: static int scterm_ioctl(scr_stat *scp, struct tty *tp, u_long cmd, caddr_t data, - int flag, struct thread *td) + struct thread *td) { term_stat *tcp = scp->ts; vid_info_t *vi; diff --git a/sys/pc98/conf/GENERIC b/sys/pc98/conf/GENERIC index f55a98c..25576a7 100644 --- a/sys/pc98/conf/GENERIC +++ b/sys/pc98/conf/GENERIC @@ -154,7 +154,7 @@ device cardbus # CardBus (32-bit) bus # Serial (COM) ports options COM_MULTIPORT #options COM_ESP # ESP98 -device sio # 8250, 16[45]50, 8251 based serial ports +#device sio # 8250, 16[45]50, 8251 based serial ports device mse #device joy @@ -231,7 +231,7 @@ device loop # Network loopback device random # Entropy device device ether # Ethernet support device tun # Packet tunnel. -device pty # Pseudo-ttys (telnet etc) +device pty # BSD-style compatibility pseudo ttys device md # Memory "disks" device gif # IPv6 and IPv4 tunneling device faith # IPv6-to-IPv4 relaying (translation) diff --git a/sys/pc98/conf/NOTES b/sys/pc98/conf/NOTES index 4a5786e..efa651e 100644 --- a/sys/pc98/conf/NOTES +++ b/sys/pc98/conf/NOTES @@ -203,7 +203,7 @@ options BPF_JITTER # sio: serial ports (see sio(4)), including support for various # PC Card devices, such as Modem and NICs # -device sio +#device sio hint.sio.0.at="isa" hint.sio.0.port="0x3F8" hint.sio.0.flags="0x10" diff --git a/sys/powerpc/conf/GENERIC b/sys/powerpc/conf/GENERIC index 86e9687..d1de70e 100644 --- a/sys/powerpc/conf/GENERIC +++ b/sys/powerpc/conf/GENERIC @@ -117,7 +117,7 @@ device loop # Network loopback device random # Entropy device device ether # Ethernet support device tun # Packet tunnel. -device pty # Pseudo-ttys (telnet etc) +device pty # BSD-style compatibility pseudo ttys device md # Memory "disks" device ofwd # Open Firmware disks device gif # IPv6 and IPv4 tunneling diff --git a/sys/sparc64/conf/GENERIC b/sys/sparc64/conf/GENERIC index 91a95a3..b2a0bdf 100644 --- a/sys/sparc64/conf/GENERIC +++ b/sys/sparc64/conf/GENERIC @@ -206,7 +206,7 @@ device loop # Network loopback device random # Entropy device device ether # Ethernet support device tun # Packet tunnel. -device pty # Pseudo-ttys (telnet etc) +device pty # BSD-style compatibility pseudo ttys device md # Memory "disks" device gif # IPv6 and IPv4 tunneling device faith # IPv6-to-IPv4 relaying (translation) diff --git a/sys/sun4v/conf/GENERIC b/sys/sun4v/conf/GENERIC index 4e93a06..1a3f695 100644 --- a/sys/sun4v/conf/GENERIC +++ b/sys/sun4v/conf/GENERIC @@ -170,7 +170,7 @@ device loop # Network loopback device random # Entropy device device ether # Ethernet support device tun # Packet tunnel. -device pty # Pseudo-ttys (telnet etc) +device pty # BSD-style compatibility pseudo ttys device md # Memory "disks" device gif # IPv6 and IPv4 tunneling device faith # IPv6-to-IPv4 relaying (translation) diff --git a/sys/sun4v/sun4v/hvcons.c b/sys/sun4v/sun4v/hvcons.c index 0217a85..314d15c 100644 --- a/sys/sun4v/sun4v/hvcons.c +++ b/sys/sun4v/sun4v/hvcons.c @@ -53,16 +53,14 @@ __FBSDID("$FreeBSD$"); #define HVCN_POLL_FREQ 10 - -static d_open_t hvcn_open; -static d_close_t hvcn_close; - -static struct cdevsw hvcn_cdevsw = { - .d_version = D_VERSION, - .d_open = hvcn_open, - .d_close = hvcn_close, - .d_name = "hvcn", - .d_flags = D_TTY | D_NEEDGIANT, +static tsw_open_t hvcn_open; +static tsw_outwakeup_t hvcn_outwakeup; +static tsw_close_t hvcn_close; + +static struct ttydevsw hvcn_class = { + .tsw_open = hvcn_open, + .tsw_outwakeup = hvcn_outwakeup, + .tsw_close = hvcn_close, }; #define PCBURST 16 @@ -81,9 +79,6 @@ static struct callout_handle hvcn_timeouthandle static int alt_break_state; #endif -static void hvcn_tty_start(struct tty *); -static int hvcn_tty_param(struct tty *, struct termios *); -static void hvcn_tty_stop(struct tty *, int); static void hvcn_timeout(void *); static cn_probe_t hvcn_cnprobe; @@ -113,67 +108,27 @@ hv_cnputs(char *p) } static int -hvcn_open(struct cdev *dev, int flag, int mode, struct thread *td) +hvcn_open(struct tty *tp) { - struct tty *tp; - int error, setuptimeout; - - setuptimeout = 0; - - if (dev->si_tty == NULL) { - hvcn_tp = ttyalloc(); - dev->si_tty = hvcn_tp; - hvcn_tp->t_dev = dev; - } - tp = dev->si_tty; - - tp->t_oproc = hvcn_tty_start; - tp->t_param = hvcn_tty_param; - tp->t_stop = hvcn_tty_stop; - if ((tp->t_state & TS_ISOPEN) == 0) { - tp->t_state |= TS_CARR_ON; - ttyconsolemode(tp, 0); - - setuptimeout = 1; - } else if ((tp->t_state & TS_XCLUDE) && priv_check(td, - PRIV_TTY_EXCLUSIVE)) { - return (EBUSY); - } - - error = ttyld_open(tp, dev); -#if defined(SIMULATOR) || 1 - if (error == 0 && setuptimeout) { - int polltime; + /* + * Set up timeout to trigger fake interrupts to transmit + * trailing data. + */ + polltime = hz / HVCN_POLL_FREQ; + if (polltime < 1) + polltime = 1; + hvcn_timeouthandle = timeout(hvcn_timeout, tp, polltime); - polltime = hz / HVCN_POLL_FREQ; - if (polltime < 1) { - polltime = 1; - } + buflen = 0; - hvcn_timeouthandle = timeout(hvcn_timeout, tp, polltime); - } -#endif - return (error); + return (0); } -static int -hvcn_close(struct cdev *dev, int flag, int mode, struct thread *td) +static void +hvcn_close(struct tty *tp) { - int unit; - struct tty *tp; - - unit = minor(dev); - tp = dev->si_tty; - - if (unit != 0) - return (ENXIO); - untimeout(hvcn_timeout, tp, hvcn_timeouthandle); - ttyld_close(tp, flag); - tty_close(tp); - - return (0); } static void @@ -210,7 +165,8 @@ done: static void hvcn_cninit(struct consdev *cp) { - sprintf(cp->cn_name, "hvcn"); + + strcpy(cp->cn_name, "hvcn"); } static int @@ -295,64 +251,43 @@ hvcn_cnputc(struct consdev *cp, int c) } while (error == H_EWOULDBLOCK); } -static int -hvcn_tty_param(struct tty *tp, struct termios *t) -{ - tp->t_ispeed = t->c_ispeed; - tp->t_ospeed = t->c_ospeed; - tp->t_cflag = t->c_cflag; - - return (0); -} - static void -hvcn_tty_start(struct tty *tp) +hvcn_outwakeup(struct tty *tp) { - if (!(tp->t_state & (TS_TIMEOUT | TS_BUSY | TS_TTSTOP))) { - tp->t_state |= TS_BUSY; + for (;;) { + /* Refill the input buffer. */ + if (buflen == 0) { + buflen = ttydisc_getc(tp, buf, PCBURST); + bufindex = 0; + } - do { - if (buflen == 0) { - buflen = q_to_b(&tp->t_outq, buf, PCBURST); - bufindex = 0; - } - while (buflen) { - if (hv_cons_putchar(buf[bufindex]) == H_EWOULDBLOCK) - goto done; - bufindex++; - buflen--; - } - } while (tp->t_outq.c_cc != 0); - done: - tp->t_state &= ~TS_BUSY; - ttwwakeup(tp); + /* Transmit the input buffer. */ + while (buflen) { + if (hv_cons_putchar(buf[bufindex]) == H_EWOULDBLOCK) + return; + bufindex++; + buflen--; + } } } static void -hvcn_tty_stop(struct tty *tp, int flag) -{ - if ((tp->t_state & TS_BUSY) && !(tp->t_state & TS_TTSTOP)) - tp->t_state |= TS_FLUSH; - - -} - -static void hvcn_intr(void *v) { - struct tty *tp; + struct tty *tp = v; int c; - tp = (struct tty *)v; + tty_lock(tp); + /* Receive data. */ while ((c = hvcn_cncheckc(NULL)) != -1) - if (tp->t_state & TS_ISOPEN) - ttyld_rint(tp, c); + ttydisc_rint(tp, c, 0); + ttydisc_rint_done(tp); - if (tp->t_outq.c_cc != 0 || buflen != 0) - hvcn_tty_start(tp); + /* Transmit trailing data. */ + hvcn_outwakeup(tp); + tty_unlock(tp); } static void @@ -381,7 +316,7 @@ static int hvcn_dev_attach(device_t dev) { - struct cdev *cdev; + struct tty *tp; int error, rid; /* belongs in attach - but attach is getting called multiple times @@ -392,8 +327,9 @@ hvcn_dev_attach(device_t dev) hvcn_consdev.cn_name[0] == '\0') return (ENXIO); - cdev = make_dev(&hvcn_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600, "ttyv%r", 1); - make_dev_alias(cdev, "hvcn"); + tp = tty_alloc(&hvcn_class, NULL, NULL); + tty_makedev(tp, NULL, "v%r", 1); + tty_makealias(tp, "hvcn"); rid = 0; diff --git a/sys/sys/conf.h b/sys/sys/conf.h index 739c3fe..8128fa7 100644 --- a/sys/sys/conf.h +++ b/sys/sys/conf.h @@ -46,7 +46,6 @@ #include <sys/queue.h> #endif -struct tty; struct snapdata; struct devfs_dirent; struct cdevsw; @@ -85,13 +84,11 @@ struct cdev { u_long si_usecount; u_long si_threadcount; union { - struct tty *__sit_tty; struct snapdata *__sid_snapdata; } __si_u; char __si_namebuf[SPECNAMELEN + 1]; }; -#define si_tty __si_u.__sit_tty #define si_snapdata __si_u.__sid_snapdata #ifdef _KERNEL diff --git a/sys/sys/file.h b/sys/sys/file.h index 6f7dc00..461b7e8 100644 --- a/sys/sys/file.h +++ b/sys/sys/file.h @@ -62,6 +62,7 @@ struct socket; #define DTYPE_MQUEUE 7 /* posix message queue */ #define DTYPE_SHM 8 /* swap-backed shared memory */ #define DTYPE_SEM 9 /* posix semaphore */ +#define DTYPE_PTS 10 /* pseudo teletype master device */ #ifdef _KERNEL diff --git a/sys/sys/linedisc.h b/sys/sys/linedisc.h deleted file mode 100644 index 659aa4b..0000000 --- a/sys/sys/linedisc.h +++ /dev/null @@ -1,141 +0,0 @@ -/*- - * Copyright (c) 1990, 1993 - * The Regents of the University of California. All rights reserved. - * Copyright (c) 2004 - * Poul-Henning Kamp. 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. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 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 - * 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 - * 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) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * 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. - * - * @(#)conf.h 8.5 (Berkeley) 1/9/95 - * $FreeBSD$ - */ - -#ifndef _SYS_LINEDISC_H_ -#define _SYS_LINEDISC_H_ - -#ifdef _KERNEL - -struct tty; - -typedef int l_open_t(struct cdev *dev, struct tty *tp); -typedef int l_close_t(struct tty *tp, int flag); -typedef int l_read_t(struct tty *tp, struct uio *uio, int flag); -typedef int l_write_t(struct tty *tp, struct uio *uio, int flag); -typedef int l_ioctl_t(struct tty *tp, u_long cmd, caddr_t data, - int flag, struct thread *td); -typedef int l_rint_t(int c, struct tty *tp); -typedef int l_start_t(struct tty *tp); -typedef int l_modem_t(struct tty *tp, int flag); - -/* - * Line discipline switch table - */ -struct linesw { - l_open_t *l_open; - l_close_t *l_close; - l_read_t *l_read; - l_write_t *l_write; - l_ioctl_t *l_ioctl; - l_rint_t *l_rint; - l_start_t *l_start; - l_modem_t *l_modem; -}; - -extern struct linesw *linesw[]; -extern int nlinesw; - -int ldisc_register(int , struct linesw *); -void ldisc_deregister(int); -#define LDISC_LOAD -1 /* Loadable line discipline */ - -l_read_t l_noread; -l_write_t l_nowrite; -l_ioctl_t l_nullioctl; - -static __inline int -ttyld_open(struct tty *tp, struct cdev *dev) -{ - - return ((*linesw[tp->t_line]->l_open)(dev, tp)); -} - -static __inline int -ttyld_close(struct tty *tp, int flag) -{ - - return ((*linesw[tp->t_line]->l_close)(tp, flag)); -} - -static __inline int -ttyld_read(struct tty *tp, struct uio *uio, int flag) -{ - - return ((*linesw[tp->t_line]->l_read)(tp, uio, flag)); -} - -static __inline int -ttyld_write(struct tty *tp, struct uio *uio, int flag) -{ - - return ((*linesw[tp->t_line]->l_write)(tp, uio, flag)); -} - -static __inline int -ttyld_ioctl(struct tty *tp, u_long cmd, caddr_t data, int flag, - struct thread *td) -{ - - return ((*linesw[tp->t_line]->l_ioctl)(tp, cmd, data, flag, td)); -} - -static __inline int -ttyld_rint(struct tty *tp, int c) -{ - - return ((*linesw[tp->t_line]->l_rint)(c, tp)); -} - -static __inline int -ttyld_start(struct tty *tp) -{ - - return ((*linesw[tp->t_line]->l_start)(tp)); -} - -static __inline int -ttyld_modem(struct tty *tp, int flag) -{ - - return ((*linesw[tp->t_line]->l_modem)(tp, flag)); -} - -#endif /* _KERNEL */ - -#endif /* !_SYS_LINEDISC_H_ */ diff --git a/sys/sys/param.h b/sys/sys/param.h index 773f24f..c0415b0 100644 --- a/sys/sys/param.h +++ b/sys/sys/param.h @@ -57,7 +57,7 @@ * is created, otherwise 1. */ #undef __FreeBSD_version -#define __FreeBSD_version 800044 /* Master, propagated to newvers */ +#define __FreeBSD_version 800045 /* Master, propagated to newvers */ #ifndef LOCORE #include <sys/types.h> diff --git a/sys/sys/proc.h b/sys/sys/proc.h index 3779591..0b919b2 100644 --- a/sys/sys/proc.h +++ b/sys/sys/proc.h @@ -72,10 +72,10 @@ * (c) const until freeing */ struct session { - int s_count; /* (m) Ref cnt; pgrps in session. */ + u_int s_count; /* Ref cnt; pgrps in session - atomic. */ struct proc *s_leader; /* (m + e) Session leader. */ struct vnode *s_ttyvp; /* (m) Vnode of controlling tty. */ - struct tty *s_ttyp; /* (m) Controlling tty. */ + struct tty *s_ttyp; /* (e) Controlling tty. */ pid_t s_sid; /* (c) Session ID. */ /* (m) Setlogin() name: */ char s_login[roundup(MAXLOGNAME, sizeof(long))]; @@ -644,8 +644,6 @@ MALLOC_DECLARE(M_ZOMBIE); #define NO_PID 100000 #define SESS_LEADER(p) ((p)->p_session->s_leader == (p)) -#define SESSHOLD(s) ((s)->s_count++) -#define SESSRELE(s) sessrele(s) #define STOPEVENT(p, e, v) do { \ @@ -807,7 +805,8 @@ void pstats_fork(struct pstats *src, struct pstats *dst); void pstats_free(struct pstats *ps); int securelevel_ge(struct ucred *cr, int level); int securelevel_gt(struct ucred *cr, int level); -void sessrele(struct session *); +void sess_hold(struct session *); +void sess_release(struct session *); int setrunnable(struct thread *); void setsugid(struct proc *p); int sigonstack(size_t sp); diff --git a/sys/sys/resource.h b/sys/sys/resource.h index d01b0bd..03fbd70 100644 --- a/sys/sys/resource.h +++ b/sys/sys/resource.h @@ -93,8 +93,9 @@ struct rusage { #define RLIMIT_SBSIZE 9 /* maximum size of all socket buffers */ #define RLIMIT_VMEM 10 /* virtual process size (inclusive of mmap) */ #define RLIMIT_AS RLIMIT_VMEM /* standard name for RLIMIT_VMEM */ +#define RLIMIT_NPTS 11 /* pseudo-terminals */ -#define RLIM_NLIMITS 11 /* number of resource limits */ +#define RLIM_NLIMITS 12 /* number of resource limits */ #define RLIM_INFINITY ((rlim_t)(((uint64_t)1 << 63) - 1)) /* XXX Missing: RLIM_SAVED_MAX, RLIM_SAVED_CUR */ diff --git a/sys/sys/resourcevar.h b/sys/sys/resourcevar.h index cd80cd4..0a867d1 100644 --- a/sys/sys/resourcevar.h +++ b/sys/sys/resourcevar.h @@ -91,6 +91,7 @@ struct uidinfo { LIST_ENTRY(uidinfo) ui_hash; /* (c) hash chain of uidinfos */ long ui_sbsize; /* (b) socket buffer space consumed */ long ui_proccnt; /* (b) number of processes */ + long ui_ptscnt; /* (b) number of pseudo-terminals */ uid_t ui_uid; /* (a) uid */ u_int ui_ref; /* (b) reference count */ }; @@ -106,6 +107,7 @@ void calcru(struct proc *p, struct timeval *up, struct timeval *sp); int chgproccnt(struct uidinfo *uip, int diff, rlim_t maxval); int chgsbsize(struct uidinfo *uip, u_int *hiwat, u_int to, rlim_t maxval); +int chgptscnt(struct uidinfo *uip, int diff, rlim_t maxval); int fuswintr(void *base); struct plimit *lim_alloc(void); diff --git a/sys/sys/syscall.h b/sys/sys/syscall.h index 3b1b2c1..a92fc9f 100644 --- a/sys/sys/syscall.h +++ b/sys/sys/syscall.h @@ -3,7 +3,7 @@ * * DO NOT EDIT-- this file is automatically generated. * $FreeBSD$ - * created from FreeBSD: src/sys/kern/syscalls.master,v 1.242 2008/03/31 12:06:55 kib Exp + * created from FreeBSD: head/sys/kern/syscalls.master 178888 2008-05-09 23:03:00Z julian */ #define SYS_syscall 0 @@ -419,4 +419,5 @@ #define SYS_renameat 501 #define SYS_symlinkat 502 #define SYS_unlinkat 503 -#define SYS_MAXSYSCALL 504 +#define SYS_posix_openpt 504 +#define SYS_MAXSYSCALL 505 diff --git a/sys/sys/syscall.mk b/sys/sys/syscall.mk index b0172f4..b09938a 100644 --- a/sys/sys/syscall.mk +++ b/sys/sys/syscall.mk @@ -1,7 +1,7 @@ # FreeBSD system call names. # DO NOT EDIT-- this file is automatically generated. # $FreeBSD$ -# created from FreeBSD: src/sys/kern/syscalls.master,v 1.242 2008/03/31 12:06:55 kib Exp +# created from FreeBSD: head/sys/kern/syscalls.master 178888 2008-05-09 23:03:00Z julian MIASM = \ syscall.o \ exit.o \ @@ -367,4 +367,5 @@ MIASM = \ readlinkat.o \ renameat.o \ symlinkat.o \ - unlinkat.o + unlinkat.o \ + posix_openpt.o diff --git a/sys/sys/sysproto.h b/sys/sys/sysproto.h index feb582a..fe2a229 100644 --- a/sys/sys/sysproto.h +++ b/sys/sys/sysproto.h @@ -3,7 +3,7 @@ * * DO NOT EDIT-- this file is automatically generated. * $FreeBSD$ - * created from FreeBSD: src/sys/kern/syscalls.master,v 1.242 2008/03/31 12:06:55 kib Exp + * created from FreeBSD: head/sys/kern/syscalls.master 178888 2008-05-09 23:03:00Z julian */ #ifndef _SYS_SYSPROTO_H_ @@ -1630,6 +1630,9 @@ struct unlinkat_args { char path_l_[PADL_(char *)]; char * path; char path_r_[PADR_(char *)]; char flag_l_[PADL_(int)]; int flag; char flag_r_[PADR_(int)]; }; +struct posix_openpt_args { + char flags_l_[PADL_(int)]; int flags; char flags_r_[PADR_(int)]; +}; int nosys(struct thread *, struct nosys_args *); void sys_exit(struct thread *, struct sys_exit_args *); int fork(struct thread *, struct fork_args *); @@ -1987,6 +1990,7 @@ int readlinkat(struct thread *, struct readlinkat_args *); int renameat(struct thread *, struct renameat_args *); int symlinkat(struct thread *, struct symlinkat_args *); int unlinkat(struct thread *, struct unlinkat_args *); +int posix_openpt(struct thread *, struct posix_openpt_args *); #ifdef COMPAT_43 @@ -2569,6 +2573,7 @@ int freebsd4_sigreturn(struct thread *, struct freebsd4_sigreturn_args *); #define SYS_AUE_renameat AUE_RENAMEAT #define SYS_AUE_symlinkat AUE_SYMLINKAT #define SYS_AUE_unlinkat AUE_UNLINKAT +#define SYS_AUE_posix_openpt AUE_POSIXOPENPT #undef PAD_ #undef PADL_ diff --git a/sys/sys/termios.h b/sys/sys/termios.h index 00259f1..7defbd3 100644 --- a/sys/sys/termios.h +++ b/sys/sys/termios.h @@ -83,10 +83,6 @@ typedef __pid_t pid_t; #define _POSIX_VDISABLE 0xff -#ifndef _POSIX_SOURCE -#define CCEQ(val, c) ((c) == (val) ? (val) != _POSIX_VDISABLE : 0) -#endif - /* * Input flags - software input processing */ @@ -112,7 +108,12 @@ typedef __pid_t pid_t; #define OPOST 0x00000001 /* enable following output processing */ #ifndef _POSIX_SOURCE #define ONLCR 0x00000002 /* map NL to CR-NL (ala CRMOD) */ -#define OXTABS 0x00000004 /* expand tabs to spaces */ +#define TABDLY 0x00000004 /* tab delay mask */ +#define TAB0 0x00000000 /* no tab delay and expansion */ +#define TAB3 0x00000004 /* expand tabs to spaces */ +#ifndef _KERNEL +#define OXTABS TAB3 +#endif /* !_KERNEL */ #define ONOEOT 0x00000008 /* discard EOT's (^D) on output) */ #define OCRNL 0x00000010 /* map CR to NL on output */ #define ONOCR 0x00000020 /* no CR output at column 0 */ @@ -143,7 +144,9 @@ typedef __pid_t pid_t; #define CDTR_IFLOW 0x00040000 /* DTR flow control of input */ #define CDSR_OFLOW 0x00080000 /* DSR flow control of output */ #define CCAR_OFLOW 0x00100000 /* DCD flow control of output */ -#define MDMBUF 0x00100000 /* old name for CCAR_OFLOW */ +#ifndef _KERNEL +#define MDMBUF CCAR_OFLOW +#endif /* !_KERNEL */ #endif @@ -230,6 +233,10 @@ struct termios { #ifndef _KERNEL +#ifndef _POSIX_SOURCE +#define CCEQ(val, c) ((c) != _POSIX_VDISABLE && (c) == (val)) +#endif + /* * Commands passed to tcsetattr() for setting the termios structure. */ diff --git a/sys/sys/tty.h b/sys/sys/tty.h index e887a2a..8e0e77d 100644 --- a/sys/sys/tty.h +++ b/sys/sys/tty.h @@ -1,19 +1,9 @@ /*- - * Copyright (c) 1982, 1986, 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) diff --git a/sys/sys/ttycom.h b/sys/sys/ttycom.h index 1d97ab3..60b6145 100644 --- a/sys/sys/ttycom.h +++ b/sys/sys/ttycom.h @@ -73,7 +73,8 @@ struct winsize { /* 23-25 obsolete or unused */ #define TIOCGETD _IOR('t', 26, int) /* get line discipline */ #define TIOCSETD _IOW('t', 27, int) /* set line discipline */ - /* 28-69 free */ +#define TIOCPTMASTER _IO('t', 28) /* pts master validation */ + /* 29-69 free */ /* 80-84 slip */ #define TIOCGDRAINWAIT _IOR('t', 86, int) /* get ttywait timeout */ #define TIOCSDRAINWAIT _IOW('t', 87, int) /* set ttywait timeout */ diff --git a/sys/sys/ttydefaults.h b/sys/sys/ttydefaults.h index 5813bda..d120fb0 100644 --- a/sys/sys/ttydefaults.h +++ b/sys/sys/ttydefaults.h @@ -52,6 +52,7 @@ #define TTYDEF_LFLAG TTYDEF_LFLAG_ECHO #define TTYDEF_CFLAG (CREAD | CS8 | HUPCL) #define TTYDEF_SPEED (B9600) +#define TTYDEF_SPEED_PSEUDO (B38400) /* * Control Character Defaults diff --git a/sys/sys/ttydevsw.h b/sys/sys/ttydevsw.h new file mode 100644 index 0000000..a4f5afb --- /dev/null +++ b/sys/sys/ttydevsw.h @@ -0,0 +1,158 @@ +/*- + * Copyright (c) 2008 Ed Schouten <ed@FreeBSD.org> + * All rights reserved. + * + * 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 + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 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. + * + * 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 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) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * 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. + * + * $FreeBSD$ + */ + +#ifndef _SYS_TTYDEVSW_H_ +#define _SYS_TTYDEVSW_H_ + +#ifndef _SYS_TTY_H_ +#error "can only be included through <sys/tty.h>" +#endif /* !_SYS_TTY_H_ */ + +/* + * Driver routines that are called from the line discipline to adjust + * hardware parameters and such. + */ +typedef int tsw_open_t(struct tty *); +typedef void tsw_close_t(struct tty *); +typedef void tsw_outwakeup_t(struct tty *); +typedef void tsw_inwakeup_t(struct tty *); +typedef int tsw_ioctl_t(struct tty *, u_long, caddr_t, struct thread *); +typedef int tsw_param_t(struct tty *, struct termios *); +typedef int tsw_modem_t(struct tty *, int, int); +typedef int tsw_mmap_t(struct tty *, vm_offset_t, vm_paddr_t *, int); +typedef void tsw_free_t(void *); + +struct ttydevsw { + unsigned int tsw_flags; /* Default TTY flags. */ + + tsw_open_t *tsw_open; /* Device opening. */ + tsw_close_t *tsw_close; /* Device closure. */ + + tsw_outwakeup_t *tsw_outwakeup; /* Output available. */ + tsw_inwakeup_t *tsw_inwakeup; /* Input can be stored again. */ + + tsw_ioctl_t *tsw_ioctl; /* ioctl() hooks. */ + tsw_param_t *tsw_param; /* TIOCSETA device parameter setting. */ + tsw_modem_t *tsw_modem; /* Modem sigon/sigoff. */ + + tsw_mmap_t *tsw_mmap; /* mmap() hooks. */ + + tsw_free_t *tsw_free; /* Destructor. */ +}; + +static __inline int +ttydevsw_open(struct tty *tp) +{ + tty_lock_assert(tp, MA_OWNED); + MPASS(!tty_gone(tp)); + + return tp->t_devsw->tsw_open(tp); +} + +static __inline void +ttydevsw_close(struct tty *tp) +{ + tty_lock_assert(tp, MA_OWNED); + MPASS(!tty_gone(tp)); + + tp->t_devsw->tsw_close(tp); +} + +static __inline void +ttydevsw_outwakeup(struct tty *tp) +{ + tty_lock_assert(tp, MA_OWNED); + MPASS(!tty_gone(tp)); + + /* Prevent spurious wakeups. */ + if (tp->t_flags & TF_STOPPED) + return; + if (ttyoutq_bytesused(&tp->t_outq) == 0) + return; + + tp->t_devsw->tsw_outwakeup(tp); +} + +static __inline void +ttydevsw_inwakeup(struct tty *tp) +{ + tty_lock_assert(tp, MA_OWNED); + MPASS(!tty_gone(tp)); + + /* Prevent spurious wakeups. */ + if (tp->t_flags & TF_HIWAT_IN) + return; + + tp->t_devsw->tsw_inwakeup(tp); +} + +static __inline int +ttydevsw_ioctl(struct tty *tp, u_long cmd, caddr_t data, struct thread *td) +{ + tty_lock_assert(tp, MA_OWNED); + MPASS(!tty_gone(tp)); + + return tp->t_devsw->tsw_ioctl(tp, cmd, data, td); +} + +static __inline int +ttydevsw_param(struct tty *tp, struct termios *t) +{ + MPASS(!tty_gone(tp)); + + return tp->t_devsw->tsw_param(tp, t); +} + +static __inline int +ttydevsw_modem(struct tty *tp, int sigon, int sigoff) +{ + MPASS(!tty_gone(tp)); + + return tp->t_devsw->tsw_modem(tp, sigon, sigoff); +} + +static __inline int +ttydevsw_mmap(struct tty *tp, vm_offset_t offset, vm_paddr_t *paddr, int nprot) +{ + MPASS(!tty_gone(tp)); + + return tp->t_devsw->tsw_mmap(tp, offset, paddr, nprot); +} + +static __inline void +ttydevsw_free(struct tty *tp) +{ + MPASS(tty_gone(tp)); + + tp->t_devsw->tsw_free(tty_softc(tp)); +} + +#endif /* !_SYS_TTYDEVSW_H_ */ diff --git a/sys/sys/ttydisc.h b/sys/sys/ttydisc.h new file mode 100644 index 0000000..dd170e6 --- /dev/null +++ b/sys/sys/ttydisc.h @@ -0,0 +1,105 @@ +/*- + * Copyright (c) 2008 Ed Schouten <ed@FreeBSD.org> + * All rights reserved. + * + * 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 + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 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. + * + * 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 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) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * 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. + * + * $FreeBSD$ + */ + +#ifndef _SYS_TTYDISC_H_ +#define _SYS_TTYDISC_H_ + +#ifndef _SYS_TTY_H_ +#error "can only be included through <sys/tty.h>" +#endif /* !_SYS_TTY_H_ */ + +struct cv; +struct thread; +struct tty; +struct uio; + +/* Top half routines. */ +void ttydisc_open(struct tty *); +void ttydisc_close(struct tty *); +int ttydisc_read(struct tty *, struct uio *, int); +int ttydisc_write(struct tty *, struct uio *, int); +void ttydisc_optimize(struct tty *); + +/* Bottom half routines. */ +void ttydisc_modem(struct tty *, int); +#define ttydisc_can_bypass(tp) ((tp)->t_flags & TF_BYPASS) +int ttydisc_rint(struct tty *, char, int); +size_t ttydisc_rint_bypass(struct tty *, char *, size_t); +void ttydisc_rint_done(struct tty *); +size_t ttydisc_getc(struct tty *, void *buf, size_t); +int ttydisc_getc_uio(struct tty *, struct uio *); + +/* Error codes for ttydisc_rint(). */ +#define TRE_FRAMING 0x01 +#define TRE_PARITY 0x02 +#define TRE_OVERRUN 0x04 +#define TRE_BREAK 0x08 + +static __inline size_t +ttydisc_read_poll(struct tty *tp) +{ + + tty_lock_assert(tp, MA_OWNED); + + return ttyinq_bytescanonicalized(&tp->t_inq); +} + +static __inline size_t +ttydisc_write_poll(struct tty *tp) +{ + + tty_lock_assert(tp, MA_OWNED); + + return ttyoutq_bytesleft(&tp->t_outq); +} + +static __inline size_t +ttydisc_rint_poll(struct tty *tp) +{ + + tty_lock_assert(tp, MA_OWNED); + + return ttyinq_bytesleft(&tp->t_inq); +} + +static __inline size_t +ttydisc_getc_poll(struct tty *tp) +{ + + tty_lock_assert(tp, MA_OWNED); + + if (tp->t_flags & TF_STOPPED) + return (0); + + return ttyoutq_bytesused(&tp->t_outq); +} + +#endif /* !_SYS_TTYDISC_H_ */ diff --git a/sys/sys/ttyqueue.h b/sys/sys/ttyqueue.h new file mode 100644 index 0000000..3e72fef --- /dev/null +++ b/sys/sys/ttyqueue.h @@ -0,0 +1,157 @@ +/*- + * Copyright (c) 2008 Ed Schouten <ed@FreeBSD.org> + * All rights reserved. + * + * 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 + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 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. + * + * 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 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) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * 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. + * + * $FreeBSD$ + */ + +#ifndef _SYS_TTYQUEUE_H_ +#define _SYS_TTYQUEUE_H_ + +#ifndef _SYS_TTY_H_ +#error "can only be included through <sys/tty.h>" +#endif /* !_SYS_TTY_H_ */ + +struct tty; +struct ttyinq_block; +struct ttyoutq_block; +struct uio; + +/* Data input queue. */ +struct ttyinq { + TAILQ_HEAD(ttyinq_bhead, ttyinq_block) ti_list; + struct ttyinq_block *ti_startblock; + struct ttyinq_block *ti_reprintblock; + struct ttyinq_block *ti_lastblock; + unsigned int ti_begin; + unsigned int ti_linestart; + unsigned int ti_reprint; + unsigned int ti_end; + unsigned int ti_nblocks; +}; +#define TTYINQ_DATASIZE 128 + +/* Data output queue. */ +struct ttyoutq { + STAILQ_HEAD(, ttyoutq_block) to_list; + struct ttyoutq_block *to_lastblock; + unsigned int to_begin; + unsigned int to_end; + unsigned int to_nblocks; +}; +#define TTYOUTQ_DATASIZE (256 - sizeof(STAILQ_ENTRY(ttyoutq_block))) + +#ifdef _KERNEL +/* Input queue handling routines. */ +void ttyinq_setsize(struct ttyinq *, struct tty *, size_t); +int ttyinq_read_uio(struct ttyinq *, struct tty *, struct uio *, + size_t, size_t); +size_t ttyinq_write(struct ttyinq *, const void *, size_t, int); +int ttyinq_write_nofrag(struct ttyinq *, const void *, size_t, int); +void ttyinq_canonicalize(struct ttyinq *); +size_t ttyinq_findchar(struct ttyinq *, const char *, size_t, char *); +void ttyinq_flush(struct ttyinq *); +int ttyinq_peekchar(struct ttyinq *, char *, int *); +void ttyinq_unputchar(struct ttyinq *); +void ttyinq_reprintpos_set(struct ttyinq *); +void ttyinq_reprintpos_reset(struct ttyinq *); + +static __inline size_t +ttyinq_getsize(struct ttyinq *ti) +{ + return (ti->ti_nblocks * TTYINQ_DATASIZE); +} + +static __inline size_t +ttyinq_bytesleft(struct ttyinq *ti) +{ + size_t len; + + /* Make sure the usage never exceeds the length. */ + len = ti->ti_nblocks * TTYINQ_DATASIZE; + MPASS(len >= ti->ti_end); + + return (len - ti->ti_end); +} + +static __inline size_t +ttyinq_bytescanonicalized(struct ttyinq *ti) +{ + MPASS(ti->ti_begin <= ti->ti_linestart); + + return (ti->ti_linestart - ti->ti_begin); +} + +static __inline size_t +ttyinq_bytesline(struct ttyinq *ti) +{ + MPASS(ti->ti_linestart <= ti->ti_end); + + return (ti->ti_end - ti->ti_linestart); +} + +/* Input buffer iteration. */ +typedef void ttyinq_line_iterator_t(void *, char, int); +void ttyinq_line_iterate_from_linestart(struct ttyinq *, + ttyinq_line_iterator_t *, void *); +void ttyinq_line_iterate_from_reprintpos(struct ttyinq *, + ttyinq_line_iterator_t *, void *); + +/* Output queue handling routines. */ +void ttyoutq_flush(struct ttyoutq *); +void ttyoutq_setsize(struct ttyoutq *, struct tty *, size_t); +size_t ttyoutq_read(struct ttyoutq *, void *, size_t); +int ttyoutq_read_uio(struct ttyoutq *, struct tty *, struct uio *); +size_t ttyoutq_write(struct ttyoutq *, const void *, size_t); +int ttyoutq_write_nofrag(struct ttyoutq *, const void *, size_t); + +static __inline size_t +ttyoutq_getsize(struct ttyoutq *to) +{ + return (to->to_nblocks * TTYOUTQ_DATASIZE); +} + +static __inline size_t +ttyoutq_bytesleft(struct ttyoutq *to) +{ + size_t len; + + /* Make sure the usage never exceeds the length. */ + len = to->to_nblocks * TTYOUTQ_DATASIZE; + MPASS(len >= to->to_end); + + return (len - to->to_end); +} + +static __inline size_t +ttyoutq_bytesused(struct ttyoutq *to) +{ + return (to->to_end - to->to_begin); +} +#endif /* _KERNEL */ + +#endif /* !_SYS_TTYQUEUE_H_ */ diff --git a/sys/sys/user.h b/sys/sys/user.h index e8b6a1f..0af8528 100644 --- a/sys/sys/user.h +++ b/sys/sys/user.h @@ -250,6 +250,7 @@ struct user { #define KF_TYPE_MQUEUE 7 #define KF_TYPE_SHM 8 #define KF_TYPE_SEM 9 +#define KF_TYPE_PTS 10 #define KF_TYPE_UNKNOWN 255 #define KF_VTYPE_VNON 0 |