pty.c revision 132226
11541Srgrimes/*
21541Srgrimes * Copyright (c) 1982, 1986, 1989, 1993
31541Srgrimes *	The Regents of the University of California.  All rights reserved.
41541Srgrimes *
51541Srgrimes * Redistribution and use in source and binary forms, with or without
61541Srgrimes * modification, are permitted provided that the following conditions
71541Srgrimes * are met:
81541Srgrimes * 1. Redistributions of source code must retain the above copyright
91541Srgrimes *    notice, this list of conditions and the following disclaimer.
101541Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
111541Srgrimes *    notice, this list of conditions and the following disclaimer in the
121541Srgrimes *    documentation and/or other materials provided with the distribution.
131541Srgrimes * 4. Neither the name of the University nor the names of its contributors
141541Srgrimes *    may be used to endorse or promote products derived from this software
151541Srgrimes *    without specific prior written permission.
161541Srgrimes *
171541Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
181541Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
191541Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
201541Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
211541Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
221541Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
231541Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
241541Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
251541Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
261541Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
271541Srgrimes * SUCH DAMAGE.
281541Srgrimes *
2914511Shsu *	@(#)tty_pty.c	8.4 (Berkeley) 2/20/95
301541Srgrimes */
311541Srgrimes
32116182Sobrien#include <sys/cdefs.h>
33116182Sobrien__FBSDID("$FreeBSD: head/sys/kern/tty_pty.c 132226 2004-07-15 20:47:41Z phk $");
34116182Sobrien
351541Srgrimes/*
361541Srgrimes * Pseudo-teletype Driver
371541Srgrimes * (Actually two drivers, requiring two entries in 'cdevsw')
381541Srgrimes */
3931778Seivind#include "opt_compat.h"
40111899Sdas#include "opt_tty.h"
411541Srgrimes#include <sys/param.h>
421541Srgrimes#include <sys/systm.h>
4391140Stanimura#include <sys/lock.h>
4491140Stanimura#include <sys/mutex.h>
4591140Stanimura#include <sys/sx.h>
46130892Sphk#ifndef BURN_BRIDGES
47130344Sphk#if defined(COMPAT_43)
4824207Sbde#include <sys/ioctl_compat.h>
4924207Sbde#endif
50130892Sphk#endif
511541Srgrimes#include <sys/proc.h>
521541Srgrimes#include <sys/tty.h>
531541Srgrimes#include <sys/conf.h>
5424131Sbde#include <sys/fcntl.h>
5529354Speter#include <sys/poll.h>
561541Srgrimes#include <sys/kernel.h>
571541Srgrimes#include <sys/vnode.h>
583308Sphk#include <sys/signalvar.h>
5949536Sphk#include <sys/malloc.h>
601541Srgrimes
6169774Sphkstatic MALLOC_DEFINE(M_PTY, "ptys", "pty data structures");
6212517Sjulian
6392723Salfredstatic void ptsstart(struct tty *tp);
6492723Salfredstatic void ptsstop(struct tty *tp, int rw);
6592723Salfredstatic void ptcwakeup(struct tty *tp, int flag);
66130585Sphkstatic struct cdev *ptyinit(struct cdev *cdev);
6711789Sbde
6812675Sjulianstatic	d_open_t	ptsopen;
6912675Sjulianstatic	d_close_t	ptsclose;
7012675Sjulianstatic	d_read_t	ptsread;
7112675Sjulianstatic	d_write_t	ptswrite;
7212675Sjulianstatic	d_ioctl_t	ptyioctl;
7312675Sjulianstatic	d_open_t	ptcopen;
7412675Sjulianstatic	d_close_t	ptcclose;
7512675Sjulianstatic	d_read_t	ptcread;
7612675Sjulianstatic	d_write_t	ptcwrite;
7729354Speterstatic	d_poll_t	ptcpoll;
7812675Sjulian
7938485Sbde#define	CDEV_MAJOR_S	5
8047625Sphkstatic struct cdevsw pts_cdevsw = {
81126080Sphk	.d_version =	D_VERSION,
82111815Sphk	.d_open =	ptsopen,
83111815Sphk	.d_close =	ptsclose,
84111815Sphk	.d_read =	ptsread,
85111815Sphk	.d_write =	ptswrite,
86111815Sphk	.d_ioctl =	ptyioctl,
87111815Sphk	.d_name =	"pts",
88111815Sphk	.d_maj =	CDEV_MAJOR_S,
89126080Sphk	.d_flags =	D_TTY | D_NEEDGIANT,
9038485Sbde};
9112675Sjulian
9238485Sbde#define	CDEV_MAJOR_C	6
9347625Sphkstatic struct cdevsw ptc_cdevsw = {
94126080Sphk	.d_version =	D_VERSION,
95111815Sphk	.d_open =	ptcopen,
96111815Sphk	.d_close =	ptcclose,
97111815Sphk	.d_read =	ptcread,
98111815Sphk	.d_write =	ptcwrite,
99111815Sphk	.d_ioctl =	ptyioctl,
100111815Sphk	.d_poll =	ptcpoll,
101111815Sphk	.d_name =	"ptc",
102111815Sphk	.d_maj =	CDEV_MAJOR_C,
103126080Sphk	.d_flags =	D_TTY | D_NEEDGIANT,
10438485Sbde};
10512675Sjulian
1061541Srgrimes#define BUFSIZ 100		/* Chunk size iomoved to/from user */
1071541Srgrimes
108130263Sphkstruct	ptsc {
1091541Srgrimes	int	pt_flags;
1101541Srgrimes	struct	selinfo pt_selr, pt_selw;
1111541Srgrimes	u_char	pt_send;
1121541Srgrimes	u_char	pt_ucntl;
113130054Sphk	struct tty *pt_tty;
114130585Sphk	struct cdev *devs, *devc;
11557070Srwatson	struct	prison *pt_prison;
11649536Sphk};
1171541Srgrimes
1181541Srgrimes#define	PF_PKT		0x08		/* packet mode */
1191541Srgrimes#define	PF_STOPPED	0x10		/* user told stopped */
1201541Srgrimes#define	PF_NOSTOP	0x40
1211541Srgrimes#define PF_UCNTL	0x80		/* user control mode */
1221541Srgrimes
123130259Sphk#define	TSA_PTC_READ(tp)	((void *)&(tp)->t_outq.c_cf)
124130259Sphk#define	TSA_PTC_WRITE(tp)	((void *)&(tp)->t_rawq.c_cl)
125130259Sphk#define	TSA_PTS_READ(tp)	((void *)&(tp)->t_canq)
126130259Sphk
12777176Sphkstatic char *names = "pqrsPQRS";
1281541Srgrimes/*
12949536Sphk * This function creates and initializes a pts/ptc pair
1301541Srgrimes *
13149536Sphk * pts == /dev/tty[pqrsPQRS][0123456789abcdefghijklmnopqrstuv]
13249536Sphk * ptc == /dev/pty[pqrsPQRS][0123456789abcdefghijklmnopqrstuv]
13349536Sphk *
134111742Sdes * XXX: define and add mapping of upper minor bits to allow more
13549536Sphk *      than 256 ptys.
1361541Srgrimes */
137130585Sphkstatic struct cdev *
138130585Sphkptyinit(struct cdev *devc)
1391541Srgrimes{
140130585Sphk	struct cdev *devs;
141130263Sphk	struct ptsc *pt;
14277176Sphk	int n;
1431541Srgrimes
14477176Sphk	n = minor(devc);
14549536Sphk	/* For now we only map the lower 8 bits of the minor */
14649536Sphk	if (n & ~0xff)
147130640Sphk		return (NULL);
14849536Sphk
14978405Sbrian	devc->si_flags &= ~SI_CHEAPCLONE;
15078405Sbrian
151111119Simp	pt = malloc(sizeof(*pt), M_PTY, M_WAITOK | M_ZERO);
15250092Sjulian	pt->devs = devs = make_dev(&pts_cdevsw, n,
15366067Sphk	    UID_ROOT, GID_WHEEL, 0666, "tty%c%r", names[n / 32], n % 32);
15477176Sphk	pt->devc = devc;
15549536Sphk
156130054Sphk	pt->pt_tty = ttymalloc(pt->pt_tty);
15749536Sphk	devs->si_drv1 = devc->si_drv1 = pt;
158130054Sphk	devs->si_tty = devc->si_tty = pt->pt_tty;
159130054Sphk	pt->pt_tty->t_dev = devs;
16064880Sphk	return (devc);
16116322Sgpalmer}
1621541Srgrimes
1631541Srgrimes/*ARGSUSED*/
16412675Sjulianstatic	int
165130585Sphkptsopen(struct cdev *dev, int flag, int devtype, struct thread *td)
1661541Srgrimes{
167111742Sdes	struct tty *tp;
1681541Srgrimes	int error;
169130263Sphk	struct ptsc *pt;
1701541Srgrimes
17149536Sphk	if (!dev->si_drv1)
172111742Sdes		return(ENXIO);
173130263Sphk	pt = dev->si_drv1;
17450652Sphk	tp = dev->si_tty;
1751541Srgrimes	if ((tp->t_state & TS_ISOPEN) == 0) {
1761541Srgrimes		ttychars(tp);		/* Set up default chars */
1771541Srgrimes		tp->t_iflag = TTYDEF_IFLAG;
1781541Srgrimes		tp->t_oflag = TTYDEF_OFLAG;
1791541Srgrimes		tp->t_lflag = TTYDEF_LFLAG;
1801541Srgrimes		tp->t_cflag = TTYDEF_CFLAG;
1811541Srgrimes		tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED;
182125839Srwatson	} else if (tp->t_state & TS_XCLUDE && suser(td))
1831541Srgrimes		return (EBUSY);
184130263Sphk	else if (pt->pt_prison != td->td_ucred->cr_prison)
18557070Srwatson		return (EBUSY);
1861541Srgrimes	if (tp->t_oproc)			/* Ctrlr still around. */
187130077Sphk		(void)ttyld_modem(tp, 1);
1881541Srgrimes	while ((tp->t_state & TS_CARR_ON) == 0) {
1891541Srgrimes		if (flag&FNONBLOCK)
1901541Srgrimes			break;
1919639Sbde		error = ttysleep(tp, TSA_CARR_ON(tp), TTIPRI | PCATCH,
1929624Sbde				 "ptsopn", 0);
1933308Sphk		if (error)
1941541Srgrimes			return (error);
1951541Srgrimes	}
196130077Sphk	error = ttyld_open(tp, dev);
1977724Sache	if (error == 0)
1987724Sache		ptcwakeup(tp, FREAD|FWRITE);
1991541Srgrimes	return (error);
2001541Srgrimes}
2011541Srgrimes
20212675Sjulianstatic	int
203130585Sphkptsclose(struct cdev *dev, int flag, int mode, struct thread *td)
2041541Srgrimes{
205111742Sdes	struct tty *tp;
2061541Srgrimes	int err;
2071541Srgrimes
20850652Sphk	tp = dev->si_tty;
209130077Sphk	err = ttyld_close(tp, flag);
210132226Sphk	(void) tty_close(tp);
2111541Srgrimes	return (err);
2121541Srgrimes}
2131541Srgrimes
21412675Sjulianstatic	int
215130585Sphkptsread(struct cdev *dev, struct uio *uio, int flag)
2161541Srgrimes{
217111742Sdes	struct tty *tp = dev->si_tty;
2181541Srgrimes	int error = 0;
2191541Srgrimes
220131114Sphk	if (tp->t_oproc)
221131114Sphk		error = ttyld_read(tp, uio, flag);
2221541Srgrimes	ptcwakeup(tp, FWRITE);
2231541Srgrimes	return (error);
2241541Srgrimes}
2251541Srgrimes
2261541Srgrimes/*
2271541Srgrimes * Write to pseudo-tty.
2281541Srgrimes * Wakeups of controlling tty will happen
2291541Srgrimes * indirectly, when tty driver calls ptsstart.
2301541Srgrimes */
23112675Sjulianstatic	int
232130585Sphkptswrite(struct cdev *dev, struct uio *uio, int flag)
2331541Srgrimes{
234111742Sdes	struct tty *tp;
2351541Srgrimes
23650652Sphk	tp = dev->si_tty;
2371541Srgrimes	if (tp->t_oproc == 0)
2381541Srgrimes		return (EIO);
239130077Sphk	return (ttyld_write(tp, uio, flag));
2401541Srgrimes}
2411541Srgrimes
2421541Srgrimes/*
2431541Srgrimes * Start output on pseudo-tty.
2441541Srgrimes * Wake up process selecting or sleeping for input from controlling tty.
2451541Srgrimes */
24612819Sphkstatic void
247130262Sphkptsstart(struct tty *tp)
2481541Srgrimes{
249130263Sphk	struct ptsc *pt = tp->t_dev->si_drv1;
2501541Srgrimes
2511541Srgrimes	if (tp->t_state & TS_TTSTOP)
2521541Srgrimes		return;
253130263Sphk	if (pt->pt_flags & PF_STOPPED) {
254130263Sphk		pt->pt_flags &= ~PF_STOPPED;
255130263Sphk		pt->pt_send = TIOCPKT_START;
2561541Srgrimes	}
2571541Srgrimes	ptcwakeup(tp, FREAD);
2581541Srgrimes}
2591541Srgrimes
26012819Sphkstatic void
261130262Sphkptcwakeup(struct tty *tp, int flag)
2621541Srgrimes{
263130263Sphk	struct ptsc *pt = tp->t_dev->si_drv1;
2641541Srgrimes
2651541Srgrimes	if (flag & FREAD) {
266130263Sphk		selwakeuppri(&pt->pt_selr, TTIPRI);
2679639Sbde		wakeup(TSA_PTC_READ(tp));
2681541Srgrimes	}
2691541Srgrimes	if (flag & FWRITE) {
270130263Sphk		selwakeuppri(&pt->pt_selw, TTOPRI);
2719639Sbde		wakeup(TSA_PTC_WRITE(tp));
2721541Srgrimes	}
2731541Srgrimes}
2741541Srgrimes
27512675Sjulianstatic	int
276130585Sphkptcopen(struct cdev *dev, int flag, int devtype, struct thread *td)
2771541Srgrimes{
278111742Sdes	struct tty *tp;
279130263Sphk	struct ptsc *pt;
2801541Srgrimes
28149536Sphk	if (!dev->si_drv1)
28277176Sphk		ptyinit(dev);
28349536Sphk	if (!dev->si_drv1)
284111742Sdes		return(ENXIO);
28550652Sphk	tp = dev->si_tty;
2861541Srgrimes	if (tp->t_oproc)
2871541Srgrimes		return (EIO);
28859818Sache	tp->t_timeout = -1;
2891541Srgrimes	tp->t_oproc = ptsstart;
29051654Sphk	tp->t_stop = ptsstop;
291130077Sphk	(void)ttyld_modem(tp, 1);
2921541Srgrimes	tp->t_lflag &= ~EXTPROC;
293130263Sphk	pt = dev->si_drv1;
294130263Sphk	pt->pt_prison = td->td_ucred->cr_prison;
295130263Sphk	pt->pt_flags = 0;
296130263Sphk	pt->pt_send = 0;
297130263Sphk	pt->pt_ucntl = 0;
2981541Srgrimes	return (0);
2991541Srgrimes}
3001541Srgrimes
30112675Sjulianstatic	int
302130585Sphkptcclose(struct cdev *dev, int flags, int fmt, struct thread *td)
3031541Srgrimes{
304111742Sdes	struct tty *tp;
3051541Srgrimes
30650652Sphk	tp = dev->si_tty;
307130077Sphk	(void)ttyld_modem(tp, 0);
3089824Sbde
3099824Sbde	/*
3109824Sbde	 * XXX MDMBUF makes no sense for ptys but would inhibit the above
3119824Sbde	 * l_modem().  CLOCAL makes sense but isn't supported.   Special
3129824Sbde	 * l_modem()s that ignore carrier drop make no sense for ptys but
3139824Sbde	 * may be in use because other parts of the line discipline make
3149824Sbde	 * sense for ptys.  Recover by doing everything that a normal
3159824Sbde	 * ttymodem() would have done except for sending a SIGHUP.
3169824Sbde	 */
3179850Sbde	if (tp->t_state & TS_ISOPEN) {
3189850Sbde		tp->t_state &= ~(TS_CARR_ON | TS_CONNECTED);
3199850Sbde		tp->t_state |= TS_ZOMBIE;
3209850Sbde		ttyflush(tp, FREAD | FWRITE);
3219850Sbde	}
3229824Sbde
3231541Srgrimes	tp->t_oproc = 0;		/* mark closed */
3241541Srgrimes	return (0);
3251541Srgrimes}
3261541Srgrimes
32712675Sjulianstatic	int
328130585Sphkptcread(struct cdev *dev, struct uio *uio, int flag)
3291541Srgrimes{
330111742Sdes	struct tty *tp = dev->si_tty;
331130263Sphk	struct ptsc *pt = dev->si_drv1;
3321541Srgrimes	char buf[BUFSIZ];
3331541Srgrimes	int error = 0, cc;
3341541Srgrimes
3351541Srgrimes	/*
3361541Srgrimes	 * We want to block until the slave
3371541Srgrimes	 * is open, and there's something to read;
3381541Srgrimes	 * but if we lost the slave or we're NBIO,
3391541Srgrimes	 * then return the appropriate error instead.
3401541Srgrimes	 */
3411541Srgrimes	for (;;) {
3421541Srgrimes		if (tp->t_state&TS_ISOPEN) {
343130263Sphk			if (pt->pt_flags&PF_PKT && pt->pt_send) {
344130263Sphk				error = ureadc((int)pt->pt_send, uio);
3451541Srgrimes				if (error)
3461541Srgrimes					return (error);
347130263Sphk				if (pt->pt_send & TIOCPKT_IOCTL) {
3481541Srgrimes					cc = min(uio->uio_resid,
3491541Srgrimes						sizeof(tp->t_termios));
350111741Sdes					uiomove(&tp->t_termios, cc, uio);
3511541Srgrimes				}
352130263Sphk				pt->pt_send = 0;
3531541Srgrimes				return (0);
3541541Srgrimes			}
355130263Sphk			if (pt->pt_flags&PF_UCNTL && pt->pt_ucntl) {
356130263Sphk				error = ureadc((int)pt->pt_ucntl, uio);
3571541Srgrimes				if (error)
3581541Srgrimes					return (error);
359130263Sphk				pt->pt_ucntl = 0;
3601541Srgrimes				return (0);
3611541Srgrimes			}
3621541Srgrimes			if (tp->t_outq.c_cc && (tp->t_state&TS_TTSTOP) == 0)
3631541Srgrimes				break;
3641541Srgrimes		}
3659824Sbde		if ((tp->t_state & TS_CONNECTED) == 0)
3661541Srgrimes			return (0);	/* EOF */
3671541Srgrimes		if (flag & IO_NDELAY)
3681541Srgrimes			return (EWOULDBLOCK);
3699639Sbde		error = tsleep(TSA_PTC_READ(tp), TTIPRI | PCATCH, "ptcin", 0);
3703308Sphk		if (error)
3711541Srgrimes			return (error);
3721541Srgrimes	}
373130263Sphk	if (pt->pt_flags & (PF_PKT|PF_UCNTL))
3741541Srgrimes		error = ureadc(0, uio);
3751541Srgrimes	while (uio->uio_resid > 0 && error == 0) {
3761541Srgrimes		cc = q_to_b(&tp->t_outq, buf, min(uio->uio_resid, BUFSIZ));
3771541Srgrimes		if (cc <= 0)
3781541Srgrimes			break;
3791541Srgrimes		error = uiomove(buf, cc, uio);
3801541Srgrimes	}
3819626Sbde	ttwwakeup(tp);
3821541Srgrimes	return (error);
3831541Srgrimes}
3841541Srgrimes
38512675Sjulianstatic	void
386130262Sphkptsstop(struct tty *tp, int flush)
3871541Srgrimes{
388130263Sphk	struct ptsc *pt = tp->t_dev->si_drv1;
3891541Srgrimes	int flag;
3901541Srgrimes
3911541Srgrimes	/* note: FLUSHREAD and FLUSHWRITE already ok */
3921541Srgrimes	if (flush == 0) {
3931541Srgrimes		flush = TIOCPKT_STOP;
394130263Sphk		pt->pt_flags |= PF_STOPPED;
3951541Srgrimes	} else
396130263Sphk		pt->pt_flags &= ~PF_STOPPED;
397130263Sphk	pt->pt_send |= flush;
3981541Srgrimes	/* change of perspective */
3991541Srgrimes	flag = 0;
4001541Srgrimes	if (flush & FREAD)
4011541Srgrimes		flag |= FWRITE;
4021541Srgrimes	if (flush & FWRITE)
4031541Srgrimes		flag |= FREAD;
4041541Srgrimes	ptcwakeup(tp, flag);
4051541Srgrimes}
4061541Srgrimes
40712675Sjulianstatic	int
408130585Sphkptcpoll(struct cdev *dev, int events, struct thread *td)
4091541Srgrimes{
410111742Sdes	struct tty *tp = dev->si_tty;
411130263Sphk	struct ptsc *pt = dev->si_drv1;
41229354Speter	int revents = 0;
4131541Srgrimes	int s;
4141541Srgrimes
4159824Sbde	if ((tp->t_state & TS_CONNECTED) == 0)
416120513Sphk		return (events &
417120513Sphk		   (POLLHUP | POLLIN | POLLRDNORM | POLLOUT | POLLWRNORM));
4181541Srgrimes
41929354Speter	/*
42029354Speter	 * Need to block timeouts (ttrstart).
42129354Speter	 */
42229354Speter	s = spltty();
4231541Srgrimes
42429354Speter	if (events & (POLLIN | POLLRDNORM))
42529354Speter		if ((tp->t_state & TS_ISOPEN) &&
42629354Speter		    ((tp->t_outq.c_cc && (tp->t_state & TS_TTSTOP) == 0) ||
427130263Sphk		     ((pt->pt_flags & PF_PKT) && pt->pt_send) ||
428130263Sphk		     ((pt->pt_flags & PF_UCNTL) && pt->pt_ucntl)))
42929354Speter			revents |= events & (POLLIN | POLLRDNORM);
4301541Srgrimes
43129354Speter	if (events & (POLLOUT | POLLWRNORM))
43229354Speter		if (tp->t_state & TS_ISOPEN &&
433131114Sphk		    (((tp->t_rawq.c_cc + tp->t_canq.c_cc < TTYHOG - 2) ||
43490831Sdillon		      (tp->t_canq.c_cc == 0 && (tp->t_lflag & ICANON)))))
43529354Speter			revents |= events & (POLLOUT | POLLWRNORM);
4361541Srgrimes
43729354Speter	if (events & POLLHUP)
43829354Speter		if ((tp->t_state & TS_CARR_ON) == 0)
43929354Speter			revents |= POLLHUP;
4401541Srgrimes
44129354Speter	if (revents == 0) {
44229354Speter		if (events & (POLLIN | POLLRDNORM))
443130263Sphk			selrecord(td, &pt->pt_selr);
44429354Speter
445111742Sdes		if (events & (POLLOUT | POLLWRNORM))
446130263Sphk			selrecord(td, &pt->pt_selw);
4471541Srgrimes	}
44829354Speter	splx(s);
44929354Speter
45029354Speter	return (revents);
4511541Srgrimes}
4521541Srgrimes
45312675Sjulianstatic	int
454130585Sphkptcwrite(struct cdev *dev, struct uio *uio, int flag)
4551541Srgrimes{
456111742Sdes	struct tty *tp = dev->si_tty;
457111742Sdes	u_char *cp = 0;
458111742Sdes	int cc = 0;
4591541Srgrimes	u_char locbuf[BUFSIZ];
4601541Srgrimes	int cnt = 0;
4611541Srgrimes	int error = 0;
4621541Srgrimes
4631541Srgrimesagain:
4641541Srgrimes	if ((tp->t_state&TS_ISOPEN) == 0)
4651541Srgrimes		goto block;
46611789Sbde	while (uio->uio_resid > 0 || cc > 0) {
4671541Srgrimes		if (cc == 0) {
4681541Srgrimes			cc = min(uio->uio_resid, BUFSIZ);
4691541Srgrimes			cp = locbuf;
470111741Sdes			error = uiomove(cp, cc, uio);
4711541Srgrimes			if (error)
4721541Srgrimes				return (error);
4731541Srgrimes			/* check again for safety */
47411789Sbde			if ((tp->t_state & TS_ISOPEN) == 0) {
47511789Sbde				/* adjust for data copied in but not written */
47611789Sbde				uio->uio_resid += cc;
4771541Srgrimes				return (EIO);
47811789Sbde			}
4791541Srgrimes		}
4801541Srgrimes		while (cc > 0) {
4811541Srgrimes			if ((tp->t_rawq.c_cc + tp->t_canq.c_cc) >= TTYHOG - 2 &&
48290831Sdillon			   (tp->t_canq.c_cc > 0 || !(tp->t_lflag&ICANON))) {
4839824Sbde				wakeup(TSA_HUP_OR_INPUT(tp));
4841541Srgrimes				goto block;
4851541Srgrimes			}
486130077Sphk			ttyld_rint(tp, *cp++);
4871541Srgrimes			cnt++;
4881541Srgrimes			cc--;
4891541Srgrimes		}
4901541Srgrimes		cc = 0;
4911541Srgrimes	}
4921541Srgrimes	return (0);
4931541Srgrimesblock:
4941541Srgrimes	/*
4951541Srgrimes	 * Come here to wait for slave to open, for space
49615199Sbde	 * in outq, or space in rawq, or an empty canq.
4971541Srgrimes	 */
49811789Sbde	if ((tp->t_state & TS_CONNECTED) == 0) {
49911789Sbde		/* adjust for data copied in but not written */
50011789Sbde		uio->uio_resid += cc;
5011541Srgrimes		return (EIO);
50211789Sbde	}
5031541Srgrimes	if (flag & IO_NDELAY) {
5041541Srgrimes		/* adjust for data copied in but not written */
5051541Srgrimes		uio->uio_resid += cc;
5061541Srgrimes		if (cnt == 0)
5071541Srgrimes			return (EWOULDBLOCK);
5081541Srgrimes		return (0);
5091541Srgrimes	}
5109639Sbde	error = tsleep(TSA_PTC_WRITE(tp), TTOPRI | PCATCH, "ptcout", 0);
5113308Sphk	if (error) {
5121541Srgrimes		/* adjust for data copied in but not written */
5131541Srgrimes		uio->uio_resid += cc;
5141541Srgrimes		return (error);
5151541Srgrimes	}
5161541Srgrimes	goto again;
5171541Srgrimes}
5181541Srgrimes
5191541Srgrimes/*ARGSUSED*/
52012675Sjulianstatic	int
521130585Sphkptyioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td)
5221541Srgrimes{
523111742Sdes	struct tty *tp = dev->si_tty;
524130263Sphk	struct ptsc *pt = dev->si_drv1;
525111742Sdes	u_char *cc = tp->t_cc;
5261541Srgrimes	int stop, error;
5271541Srgrimes
52847301Sluoqi	if (devsw(dev)->d_open == ptcopen) {
5291541Srgrimes		switch (cmd) {
5301541Srgrimes
5311541Srgrimes		case TIOCGPGRP:
5321541Srgrimes			/*
53333826Sbde			 * We avoid calling ttioctl on the controller since,
5341541Srgrimes			 * in that case, tp must be the controlling terminal.
5351541Srgrimes			 */
5361541Srgrimes			*(int *)data = tp->t_pgrp ? tp->t_pgrp->pg_id : 0;
5371541Srgrimes			return (0);
5381541Srgrimes
5391541Srgrimes		case TIOCPKT:
5401541Srgrimes			if (*(int *)data) {
541130263Sphk				if (pt->pt_flags & PF_UCNTL)
5421541Srgrimes					return (EINVAL);
543130263Sphk				pt->pt_flags |= PF_PKT;
5441541Srgrimes			} else
545130263Sphk				pt->pt_flags &= ~PF_PKT;
5461541Srgrimes			return (0);
5471541Srgrimes
5481541Srgrimes		case TIOCUCNTL:
5491541Srgrimes			if (*(int *)data) {
550130263Sphk				if (pt->pt_flags & PF_PKT)
5511541Srgrimes					return (EINVAL);
552130263Sphk				pt->pt_flags |= PF_UCNTL;
5531541Srgrimes			} else
554130263Sphk				pt->pt_flags &= ~PF_UCNTL;
5551541Srgrimes			return (0);
55647203Sluoqi		}
5571541Srgrimes
55847203Sluoqi		/*
559111742Sdes		 * The rest of the ioctls shouldn't be called until
56047301Sluoqi		 * the slave is open.
56147203Sluoqi		 */
56247203Sluoqi		if ((tp->t_state & TS_ISOPEN) == 0)
56347301Sluoqi			return (EAGAIN);
56447203Sluoqi
56547203Sluoqi		switch (cmd) {
566130892Sphk#ifndef BURN_BRIDGES
5671541Srgrimes#ifdef COMPAT_43
5688876Srgrimes		case TIOCSETP:
5691541Srgrimes		case TIOCSETN:
5701541Srgrimes#endif
571130892Sphk#endif
5721541Srgrimes		case TIOCSETD:
5731541Srgrimes		case TIOCSETA:
5741541Srgrimes		case TIOCSETAW:
5759858Sache		case TIOCSETAF:
57647301Sluoqi			/*
57747301Sluoqi			 * IF CONTROLLER STTY THEN MUST FLUSH TO PREVENT A HANG.
57847301Sluoqi			 * ttywflush(tp) will hang if there are characters in
57947301Sluoqi			 * the outq.
58047301Sluoqi			 */
5811541Srgrimes			ndflush(&tp->t_outq, tp->t_outq.c_cc);
5821541Srgrimes			break;
5831541Srgrimes
5841541Srgrimes		case TIOCSIG:
58527770Sjmg			if (*(unsigned int *)data >= NSIG ||
58627770Sjmg			    *(unsigned int *)data == 0)
5871541Srgrimes				return(EINVAL);
5881541Srgrimes			if ((tp->t_lflag&NOFLSH) == 0)
5891541Srgrimes				ttyflush(tp, FREAD|FWRITE);
59091140Stanimura			if (tp->t_pgrp != NULL) {
59191140Stanimura				PGRP_LOCK(tp->t_pgrp);
59291140Stanimura				pgsignal(tp->t_pgrp, *(unsigned int *)data, 1);
59391140Stanimura				PGRP_UNLOCK(tp->t_pgrp);
59491140Stanimura			}
5951541Srgrimes			if ((*(unsigned int *)data == SIGINFO) &&
5961541Srgrimes			    ((tp->t_lflag&NOKERNINFO) == 0))
5971541Srgrimes				ttyinfo(tp);
5981541Srgrimes			return(0);
5991541Srgrimes		}
60047203Sluoqi	}
60147301Sluoqi	if (cmd == TIOCEXT) {
60247301Sluoqi		/*
60347301Sluoqi		 * When the EXTPROC bit is being toggled, we need
60447301Sluoqi		 * to send an TIOCPKT_IOCTL if the packet driver
60547301Sluoqi		 * is turned on.
60647301Sluoqi		 */
60747301Sluoqi		if (*(int *)data) {
608130263Sphk			if (pt->pt_flags & PF_PKT) {
609130263Sphk				pt->pt_send |= TIOCPKT_IOCTL;
61047301Sluoqi				ptcwakeup(tp, FREAD);
61147301Sluoqi			}
61247301Sluoqi			tp->t_lflag |= EXTPROC;
61347301Sluoqi		} else {
61447301Sluoqi			if ((tp->t_lflag & EXTPROC) &&
615130263Sphk			    (pt->pt_flags & PF_PKT)) {
616130263Sphk				pt->pt_send |= TIOCPKT_IOCTL;
61747301Sluoqi				ptcwakeup(tp, FREAD);
61847301Sluoqi			}
61947301Sluoqi			tp->t_lflag &= ~EXTPROC;
62047301Sluoqi		}
62147301Sluoqi		return(0);
62247301Sluoqi	}
623130054Sphk	error = ttyioctl(dev, cmd, data, flag, td);
624130054Sphk	if (error == ENOTTY) {
625130263Sphk		if (pt->pt_flags & PF_UCNTL &&
6261541Srgrimes		    (cmd & ~0xff) == UIOCCMD(0)) {
6271541Srgrimes			if (cmd & 0xff) {
628130263Sphk				pt->pt_ucntl = (u_char)cmd;
6291541Srgrimes				ptcwakeup(tp, FREAD);
6301541Srgrimes			}
6311541Srgrimes			return (0);
6321541Srgrimes		}
6331541Srgrimes		error = ENOTTY;
6341541Srgrimes	}
6351541Srgrimes	/*
6361541Srgrimes	 * If external processing and packet mode send ioctl packet.
6371541Srgrimes	 */
638130263Sphk	if ((tp->t_lflag&EXTPROC) && (pt->pt_flags & PF_PKT)) {
6391541Srgrimes		switch(cmd) {
6401541Srgrimes		case TIOCSETA:
6411541Srgrimes		case TIOCSETAW:
6421541Srgrimes		case TIOCSETAF:
643130892Sphk#ifndef BURN_BRIDGES
6441541Srgrimes#ifdef COMPAT_43
6451541Srgrimes		case TIOCSETP:
6461541Srgrimes		case TIOCSETN:
6471541Srgrimes		case TIOCSETC:
6481541Srgrimes		case TIOCSLTC:
6491541Srgrimes		case TIOCLBIS:
6501541Srgrimes		case TIOCLBIC:
6511541Srgrimes		case TIOCLSET:
6521541Srgrimes#endif
653130892Sphk#endif
654130263Sphk			pt->pt_send |= TIOCPKT_IOCTL;
6551541Srgrimes			ptcwakeup(tp, FREAD);
656115463Sphk			break;
6571541Srgrimes		default:
6581541Srgrimes			break;
6591541Srgrimes		}
6601541Srgrimes	}
6618876Srgrimes	stop = (tp->t_iflag & IXON) && CCEQ(cc[VSTOP], CTRL('s'))
6621541Srgrimes		&& CCEQ(cc[VSTART], CTRL('q'));
663130263Sphk	if (pt->pt_flags & PF_NOSTOP) {
6641541Srgrimes		if (stop) {
665130263Sphk			pt->pt_send &= ~TIOCPKT_NOSTOP;
666130263Sphk			pt->pt_send |= TIOCPKT_DOSTOP;
667130263Sphk			pt->pt_flags &= ~PF_NOSTOP;
6681541Srgrimes			ptcwakeup(tp, FREAD);
6691541Srgrimes		}
6701541Srgrimes	} else {
6711541Srgrimes		if (!stop) {
672130263Sphk			pt->pt_send &= ~TIOCPKT_DOSTOP;
673130263Sphk			pt->pt_send |= TIOCPKT_NOSTOP;
674130263Sphk			pt->pt_flags |= PF_NOSTOP;
6751541Srgrimes			ptcwakeup(tp, FREAD);
6761541Srgrimes		}
6771541Srgrimes	}
6781541Srgrimes	return (error);
6791541Srgrimes}
68012517Sjulian
68112675Sjulianstatic void
682130585Sphkpty_clone(void *arg, char *name, int namelen, struct cdev **dev)
68364880Sphk{
68464880Sphk	int u;
68564880Sphk
686130640Sphk	if (*dev != NULL)
68764880Sphk		return;
68864880Sphk	if (bcmp(name, "pty", 3) != 0)
68964880Sphk		return;
69064880Sphk	if (name[5] != '\0')
69164880Sphk		return;
69264880Sphk	switch (name[3]) {
69364880Sphk	case 'p': u =   0; break;
69464880Sphk	case 'q': u =  32; break;
69564880Sphk	case 'r': u =  64; break;
69664880Sphk	case 's': u =  96; break;
69764880Sphk	case 'P': u = 128; break;
69864880Sphk	case 'Q': u = 160; break;
69964880Sphk	case 'R': u = 192; break;
70064880Sphk	case 'S': u = 224; break;
70164880Sphk	default: return;
70264880Sphk	}
70364880Sphk	if (name[4] >= '0' && name[4] <= '9')
70464880Sphk		u += name[4] - '0';
70564880Sphk	else if (name[4] >= 'a' && name[4] <= 'v')
70664880Sphk		u += name[4] - 'a' + 10;
70764880Sphk	else
70864880Sphk		return;
70977176Sphk	*dev = make_dev(&ptc_cdevsw, u,
71077176Sphk	    UID_ROOT, GID_WHEEL, 0666, "pty%c%r", names[u / 32], u % 32);
71177176Sphk	(*dev)->si_flags |= SI_CHEAPCLONE;
71264880Sphk	return;
71364880Sphk}
71464880Sphk
71564880Sphkstatic void
716130262Sphkptc_drvinit(void *unused)
71712517Sjulian{
718108363Sphk
71965374Sphk	EVENTHANDLER_REGISTER(dev_clone, pty_clone, 0, 1000);
72012517Sjulian}
72112517Sjulian
72212517SjulianSYSINIT(ptcdev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR_C,ptc_drvinit,NULL)
723