summaryrefslogtreecommitdiffstats
path: root/sys
diff options
context:
space:
mode:
Diffstat (limited to 'sys')
-rw-r--r--sys/amd64/conf/GENERIC2
-rw-r--r--sys/bsm/audit_kevents.h1
-rw-r--r--sys/compat/freebsd32/freebsd32_syscall.h3
-rw-r--r--sys/compat/freebsd32/freebsd32_syscalls.c1
-rw-r--r--sys/compat/freebsd32/freebsd32_sysent.c1
-rw-r--r--sys/compat/freebsd32/syscalls.master1
-rw-r--r--sys/compat/linux/linux_ioctl.c9
-rw-r--r--sys/compat/linux/linux_stats.c12
-rw-r--r--sys/conf/NOTES9
-rw-r--r--sys/conf/files8
-rw-r--r--sys/conf/options2
-rw-r--r--sys/dev/dcons/dcons.h2
-rw-r--r--sys/dev/dcons/dcons_os.c393
-rw-r--r--sys/dev/nmdm/nmdm.c412
-rw-r--r--sys/dev/ofw/ofw_console.c143
-rw-r--r--sys/dev/syscons/schistory.c7
-rw-r--r--sys/dev/syscons/scmouse.c5
-rw-r--r--sys/dev/syscons/scterm-sc.c2
-rw-r--r--sys/dev/syscons/scvesactl.c12
-rw-r--r--sys/dev/syscons/scvidctl.c18
-rw-r--r--sys/dev/syscons/syscons.c327
-rw-r--r--sys/dev/syscons/syscons.h24
-rw-r--r--sys/dev/syscons/sysmouse.c135
-rw-r--r--sys/dev/uart/uart_core.c3
-rw-r--r--sys/dev/uart/uart_tty.c165
-rw-r--r--sys/dev/usb/ucom.c334
-rw-r--r--sys/dev/usb/ucomvar.h8
-rw-r--r--sys/dev/usb/uftdi.c27
-rw-r--r--sys/dev/usb/umodem.c4
-rw-r--r--sys/fs/devfs/devfs_vnops.c3
-rw-r--r--sys/i386/conf/GENERIC2
-rw-r--r--sys/i386/conf/XBOX2
-rw-r--r--sys/i386/ibcs2/ibcs2_ioctl.c19
-rw-r--r--sys/ia64/conf/GENERIC2
-rw-r--r--sys/ia64/conf/SKI2
-rw-r--r--sys/ia64/ia64/ssc.c138
-rw-r--r--sys/kern/init_main.c2
-rw-r--r--sys/kern/init_sysent.c3
-rw-r--r--sys/kern/kern_acct.c6
-rw-r--r--sys/kern/kern_conf.c11
-rw-r--r--sys/kern/kern_descrip.c12
-rw-r--r--sys/kern/kern_exit.c62
-rw-r--r--sys/kern/kern_proc.c59
-rw-r--r--sys/kern/kern_resource.c25
-rw-r--r--sys/kern/subr_prf.c24
-rw-r--r--sys/kern/syscalls.c3
-rw-r--r--sys/kern/syscalls.master1
-rw-r--r--sys/kern/systrace_args.c27
-rw-r--r--sys/kern/tty.c4182
-rw-r--r--sys/kern/tty_compat.c55
-rw-r--r--sys/kern/tty_conf.c205
-rw-r--r--sys/kern/tty_cons.c15
-rw-r--r--sys/kern/tty_info.c14
-rw-r--r--sys/kern/tty_inq.c502
-rw-r--r--sys/kern/tty_outq.c365
-rw-r--r--sys/kern/tty_pts.c1303
-rw-r--r--sys/kern/tty_pty.c810
-rw-r--r--sys/kern/tty_ttydisc.c1131
-rw-r--r--sys/modules/nmdm/Makefile9
-rw-r--r--sys/modules/rc/Makefile7
-rw-r--r--sys/pc98/cbus/scterm-sck.c2
-rw-r--r--sys/pc98/conf/GENERIC4
-rw-r--r--sys/pc98/conf/NOTES2
-rw-r--r--sys/powerpc/conf/GENERIC2
-rw-r--r--sys/sparc64/conf/GENERIC2
-rw-r--r--sys/sun4v/conf/GENERIC2
-rw-r--r--sys/sun4v/sun4v/hvcons.c162
-rw-r--r--sys/sys/conf.h3
-rw-r--r--sys/sys/file.h1
-rw-r--r--sys/sys/linedisc.h141
-rw-r--r--sys/sys/param.h2
-rw-r--r--sys/sys/proc.h9
-rw-r--r--sys/sys/resource.h3
-rw-r--r--sys/sys/resourcevar.h2
-rw-r--r--sys/sys/syscall.h5
-rw-r--r--sys/sys/syscall.mk5
-rw-r--r--sys/sys/sysproto.h7
-rw-r--r--sys/sys/termios.h19
-rw-r--r--sys/sys/tty.h23
-rw-r--r--sys/sys/ttycom.h3
-rw-r--r--sys/sys/ttydefaults.h1
-rw-r--r--sys/sys/ttydevsw.h158
-rw-r--r--sys/sys/ttydisc.h105
-rw-r--r--sys/sys/ttyqueue.h157
-rw-r--r--sys/sys/user.h1
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, &quote) != 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, &quote) != 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, &quote) != 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
OpenPOWER on IntegriCloud