pty.c revision 135298
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 135298 2004-09-16 12:07:25Z 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);
157135298Sphk	pt->pt_tty->t_sc = pt;
15849536Sphk	devs->si_drv1 = devc->si_drv1 = pt;
159130054Sphk	devs->si_tty = devc->si_tty = pt->pt_tty;
160130054Sphk	pt->pt_tty->t_dev = devs;
16164880Sphk	return (devc);
16216322Sgpalmer}
1631541Srgrimes
1641541Srgrimes/*ARGSUSED*/
16512675Sjulianstatic	int
166130585Sphkptsopen(struct cdev *dev, int flag, int devtype, struct thread *td)
1671541Srgrimes{
168111742Sdes	struct tty *tp;
1691541Srgrimes	int error;
170130263Sphk	struct ptsc *pt;
1711541Srgrimes
17249536Sphk	if (!dev->si_drv1)
173111742Sdes		return(ENXIO);
174130263Sphk	pt = dev->si_drv1;
17550652Sphk	tp = dev->si_tty;
1761541Srgrimes	if ((tp->t_state & TS_ISOPEN) == 0) {
1771541Srgrimes		ttychars(tp);		/* Set up default chars */
1781541Srgrimes		tp->t_iflag = TTYDEF_IFLAG;
1791541Srgrimes		tp->t_oflag = TTYDEF_OFLAG;
1801541Srgrimes		tp->t_lflag = TTYDEF_LFLAG;
1811541Srgrimes		tp->t_cflag = TTYDEF_CFLAG;
1821541Srgrimes		tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED;
183125839Srwatson	} else if (tp->t_state & TS_XCLUDE && suser(td))
1841541Srgrimes		return (EBUSY);
185130263Sphk	else if (pt->pt_prison != td->td_ucred->cr_prison)
18657070Srwatson		return (EBUSY);
1871541Srgrimes	if (tp->t_oproc)			/* Ctrlr still around. */
188130077Sphk		(void)ttyld_modem(tp, 1);
1891541Srgrimes	while ((tp->t_state & TS_CARR_ON) == 0) {
1901541Srgrimes		if (flag&FNONBLOCK)
1911541Srgrimes			break;
1929639Sbde		error = ttysleep(tp, TSA_CARR_ON(tp), TTIPRI | PCATCH,
1939624Sbde				 "ptsopn", 0);
1943308Sphk		if (error)
1951541Srgrimes			return (error);
1961541Srgrimes	}
197130077Sphk	error = ttyld_open(tp, dev);
1987724Sache	if (error == 0)
1997724Sache		ptcwakeup(tp, FREAD|FWRITE);
2001541Srgrimes	return (error);
2011541Srgrimes}
2021541Srgrimes
20312675Sjulianstatic	int
204130585Sphkptsclose(struct cdev *dev, int flag, int mode, struct thread *td)
2051541Srgrimes{
206111742Sdes	struct tty *tp;
2071541Srgrimes	int err;
2081541Srgrimes
20950652Sphk	tp = dev->si_tty;
210130077Sphk	err = ttyld_close(tp, flag);
211132226Sphk	(void) tty_close(tp);
2121541Srgrimes	return (err);
2131541Srgrimes}
2141541Srgrimes
21512675Sjulianstatic	int
216130585Sphkptsread(struct cdev *dev, struct uio *uio, int flag)
2171541Srgrimes{
218111742Sdes	struct tty *tp = dev->si_tty;
2191541Srgrimes	int error = 0;
2201541Srgrimes
221131114Sphk	if (tp->t_oproc)
222131114Sphk		error = ttyld_read(tp, uio, flag);
2231541Srgrimes	ptcwakeup(tp, FWRITE);
2241541Srgrimes	return (error);
2251541Srgrimes}
2261541Srgrimes
2271541Srgrimes/*
2281541Srgrimes * Write to pseudo-tty.
2291541Srgrimes * Wakeups of controlling tty will happen
2301541Srgrimes * indirectly, when tty driver calls ptsstart.
2311541Srgrimes */
23212675Sjulianstatic	int
233130585Sphkptswrite(struct cdev *dev, struct uio *uio, int flag)
2341541Srgrimes{
235111742Sdes	struct tty *tp;
2361541Srgrimes
23750652Sphk	tp = dev->si_tty;
2381541Srgrimes	if (tp->t_oproc == 0)
2391541Srgrimes		return (EIO);
240130077Sphk	return (ttyld_write(tp, uio, flag));
2411541Srgrimes}
2421541Srgrimes
2431541Srgrimes/*
2441541Srgrimes * Start output on pseudo-tty.
2451541Srgrimes * Wake up process selecting or sleeping for input from controlling tty.
2461541Srgrimes */
24712819Sphkstatic void
248130262Sphkptsstart(struct tty *tp)
2491541Srgrimes{
250135298Sphk	struct ptsc *pt = tp->t_sc;
2511541Srgrimes
2521541Srgrimes	if (tp->t_state & TS_TTSTOP)
2531541Srgrimes		return;
254130263Sphk	if (pt->pt_flags & PF_STOPPED) {
255130263Sphk		pt->pt_flags &= ~PF_STOPPED;
256130263Sphk		pt->pt_send = TIOCPKT_START;
2571541Srgrimes	}
2581541Srgrimes	ptcwakeup(tp, FREAD);
2591541Srgrimes}
2601541Srgrimes
26112819Sphkstatic void
262130262Sphkptcwakeup(struct tty *tp, int flag)
2631541Srgrimes{
264135298Sphk	struct ptsc *pt = tp->t_sc;
2651541Srgrimes
2661541Srgrimes	if (flag & FREAD) {
267130263Sphk		selwakeuppri(&pt->pt_selr, TTIPRI);
2689639Sbde		wakeup(TSA_PTC_READ(tp));
2691541Srgrimes	}
2701541Srgrimes	if (flag & FWRITE) {
271130263Sphk		selwakeuppri(&pt->pt_selw, TTOPRI);
2729639Sbde		wakeup(TSA_PTC_WRITE(tp));
2731541Srgrimes	}
2741541Srgrimes}
2751541Srgrimes
27612675Sjulianstatic	int
277130585Sphkptcopen(struct cdev *dev, int flag, int devtype, struct thread *td)
2781541Srgrimes{
279111742Sdes	struct tty *tp;
280130263Sphk	struct ptsc *pt;
2811541Srgrimes
28249536Sphk	if (!dev->si_drv1)
28377176Sphk		ptyinit(dev);
28449536Sphk	if (!dev->si_drv1)
285111742Sdes		return(ENXIO);
28650652Sphk	tp = dev->si_tty;
2871541Srgrimes	if (tp->t_oproc)
2881541Srgrimes		return (EIO);
28959818Sache	tp->t_timeout = -1;
2901541Srgrimes	tp->t_oproc = ptsstart;
29151654Sphk	tp->t_stop = ptsstop;
292130077Sphk	(void)ttyld_modem(tp, 1);
2931541Srgrimes	tp->t_lflag &= ~EXTPROC;
294130263Sphk	pt = dev->si_drv1;
295130263Sphk	pt->pt_prison = td->td_ucred->cr_prison;
296130263Sphk	pt->pt_flags = 0;
297130263Sphk	pt->pt_send = 0;
298130263Sphk	pt->pt_ucntl = 0;
2991541Srgrimes	return (0);
3001541Srgrimes}
3011541Srgrimes
30212675Sjulianstatic	int
303130585Sphkptcclose(struct cdev *dev, int flags, int fmt, struct thread *td)
3041541Srgrimes{
305111742Sdes	struct tty *tp;
3061541Srgrimes
30750652Sphk	tp = dev->si_tty;
308130077Sphk	(void)ttyld_modem(tp, 0);
3099824Sbde
3109824Sbde	/*
3119824Sbde	 * XXX MDMBUF makes no sense for ptys but would inhibit the above
3129824Sbde	 * l_modem().  CLOCAL makes sense but isn't supported.   Special
3139824Sbde	 * l_modem()s that ignore carrier drop make no sense for ptys but
3149824Sbde	 * may be in use because other parts of the line discipline make
3159824Sbde	 * sense for ptys.  Recover by doing everything that a normal
3169824Sbde	 * ttymodem() would have done except for sending a SIGHUP.
3179824Sbde	 */
3189850Sbde	if (tp->t_state & TS_ISOPEN) {
3199850Sbde		tp->t_state &= ~(TS_CARR_ON | TS_CONNECTED);
3209850Sbde		tp->t_state |= TS_ZOMBIE;
3219850Sbde		ttyflush(tp, FREAD | FWRITE);
3229850Sbde	}
3239824Sbde
3241541Srgrimes	tp->t_oproc = 0;		/* mark closed */
3251541Srgrimes	return (0);
3261541Srgrimes}
3271541Srgrimes
32812675Sjulianstatic	int
329130585Sphkptcread(struct cdev *dev, struct uio *uio, int flag)
3301541Srgrimes{
331111742Sdes	struct tty *tp = dev->si_tty;
332130263Sphk	struct ptsc *pt = dev->si_drv1;
3331541Srgrimes	char buf[BUFSIZ];
3341541Srgrimes	int error = 0, cc;
3351541Srgrimes
3361541Srgrimes	/*
3371541Srgrimes	 * We want to block until the slave
3381541Srgrimes	 * is open, and there's something to read;
3391541Srgrimes	 * but if we lost the slave or we're NBIO,
3401541Srgrimes	 * then return the appropriate error instead.
3411541Srgrimes	 */
3421541Srgrimes	for (;;) {
3431541Srgrimes		if (tp->t_state&TS_ISOPEN) {
344130263Sphk			if (pt->pt_flags&PF_PKT && pt->pt_send) {
345130263Sphk				error = ureadc((int)pt->pt_send, uio);
3461541Srgrimes				if (error)
3471541Srgrimes					return (error);
348130263Sphk				if (pt->pt_send & TIOCPKT_IOCTL) {
3491541Srgrimes					cc = min(uio->uio_resid,
3501541Srgrimes						sizeof(tp->t_termios));
351111741Sdes					uiomove(&tp->t_termios, cc, uio);
3521541Srgrimes				}
353130263Sphk				pt->pt_send = 0;
3541541Srgrimes				return (0);
3551541Srgrimes			}
356130263Sphk			if (pt->pt_flags&PF_UCNTL && pt->pt_ucntl) {
357130263Sphk				error = ureadc((int)pt->pt_ucntl, uio);
3581541Srgrimes				if (error)
3591541Srgrimes					return (error);
360130263Sphk				pt->pt_ucntl = 0;
3611541Srgrimes				return (0);
3621541Srgrimes			}
3631541Srgrimes			if (tp->t_outq.c_cc && (tp->t_state&TS_TTSTOP) == 0)
3641541Srgrimes				break;
3651541Srgrimes		}
3669824Sbde		if ((tp->t_state & TS_CONNECTED) == 0)
3671541Srgrimes			return (0);	/* EOF */
3681541Srgrimes		if (flag & IO_NDELAY)
3691541Srgrimes			return (EWOULDBLOCK);
3709639Sbde		error = tsleep(TSA_PTC_READ(tp), TTIPRI | PCATCH, "ptcin", 0);
3713308Sphk		if (error)
3721541Srgrimes			return (error);
3731541Srgrimes	}
374130263Sphk	if (pt->pt_flags & (PF_PKT|PF_UCNTL))
3751541Srgrimes		error = ureadc(0, uio);
3761541Srgrimes	while (uio->uio_resid > 0 && error == 0) {
3771541Srgrimes		cc = q_to_b(&tp->t_outq, buf, min(uio->uio_resid, BUFSIZ));
3781541Srgrimes		if (cc <= 0)
3791541Srgrimes			break;
3801541Srgrimes		error = uiomove(buf, cc, uio);
3811541Srgrimes	}
3829626Sbde	ttwwakeup(tp);
3831541Srgrimes	return (error);
3841541Srgrimes}
3851541Srgrimes
38612675Sjulianstatic	void
387130262Sphkptsstop(struct tty *tp, int flush)
3881541Srgrimes{
389135298Sphk	struct ptsc *pt = tp->t_sc;
3901541Srgrimes	int flag;
3911541Srgrimes
3921541Srgrimes	/* note: FLUSHREAD and FLUSHWRITE already ok */
3931541Srgrimes	if (flush == 0) {
3941541Srgrimes		flush = TIOCPKT_STOP;
395130263Sphk		pt->pt_flags |= PF_STOPPED;
3961541Srgrimes	} else
397130263Sphk		pt->pt_flags &= ~PF_STOPPED;
398130263Sphk	pt->pt_send |= flush;
3991541Srgrimes	/* change of perspective */
4001541Srgrimes	flag = 0;
4011541Srgrimes	if (flush & FREAD)
4021541Srgrimes		flag |= FWRITE;
4031541Srgrimes	if (flush & FWRITE)
4041541Srgrimes		flag |= FREAD;
4051541Srgrimes	ptcwakeup(tp, flag);
4061541Srgrimes}
4071541Srgrimes
40812675Sjulianstatic	int
409130585Sphkptcpoll(struct cdev *dev, int events, struct thread *td)
4101541Srgrimes{
411111742Sdes	struct tty *tp = dev->si_tty;
412130263Sphk	struct ptsc *pt = dev->si_drv1;
41329354Speter	int revents = 0;
4141541Srgrimes	int s;
4151541Srgrimes
4169824Sbde	if ((tp->t_state & TS_CONNECTED) == 0)
417120513Sphk		return (events &
418120513Sphk		   (POLLHUP | POLLIN | POLLRDNORM | POLLOUT | POLLWRNORM));
4191541Srgrimes
42029354Speter	/*
42129354Speter	 * Need to block timeouts (ttrstart).
42229354Speter	 */
42329354Speter	s = spltty();
4241541Srgrimes
42529354Speter	if (events & (POLLIN | POLLRDNORM))
42629354Speter		if ((tp->t_state & TS_ISOPEN) &&
42729354Speter		    ((tp->t_outq.c_cc && (tp->t_state & TS_TTSTOP) == 0) ||
428130263Sphk		     ((pt->pt_flags & PF_PKT) && pt->pt_send) ||
429130263Sphk		     ((pt->pt_flags & PF_UCNTL) && pt->pt_ucntl)))
43029354Speter			revents |= events & (POLLIN | POLLRDNORM);
4311541Srgrimes
43229354Speter	if (events & (POLLOUT | POLLWRNORM))
43329354Speter		if (tp->t_state & TS_ISOPEN &&
434131114Sphk		    (((tp->t_rawq.c_cc + tp->t_canq.c_cc < TTYHOG - 2) ||
43590831Sdillon		      (tp->t_canq.c_cc == 0 && (tp->t_lflag & ICANON)))))
43629354Speter			revents |= events & (POLLOUT | POLLWRNORM);
4371541Srgrimes
43829354Speter	if (events & POLLHUP)
43929354Speter		if ((tp->t_state & TS_CARR_ON) == 0)
44029354Speter			revents |= POLLHUP;
4411541Srgrimes
44229354Speter	if (revents == 0) {
44329354Speter		if (events & (POLLIN | POLLRDNORM))
444130263Sphk			selrecord(td, &pt->pt_selr);
44529354Speter
446111742Sdes		if (events & (POLLOUT | POLLWRNORM))
447130263Sphk			selrecord(td, &pt->pt_selw);
4481541Srgrimes	}
44929354Speter	splx(s);
45029354Speter
45129354Speter	return (revents);
4521541Srgrimes}
4531541Srgrimes
45412675Sjulianstatic	int
455130585Sphkptcwrite(struct cdev *dev, struct uio *uio, int flag)
4561541Srgrimes{
457111742Sdes	struct tty *tp = dev->si_tty;
458111742Sdes	u_char *cp = 0;
459111742Sdes	int cc = 0;
4601541Srgrimes	u_char locbuf[BUFSIZ];
4611541Srgrimes	int cnt = 0;
4621541Srgrimes	int error = 0;
4631541Srgrimes
4641541Srgrimesagain:
4651541Srgrimes	if ((tp->t_state&TS_ISOPEN) == 0)
4661541Srgrimes		goto block;
46711789Sbde	while (uio->uio_resid > 0 || cc > 0) {
4681541Srgrimes		if (cc == 0) {
4691541Srgrimes			cc = min(uio->uio_resid, BUFSIZ);
4701541Srgrimes			cp = locbuf;
471111741Sdes			error = uiomove(cp, cc, uio);
4721541Srgrimes			if (error)
4731541Srgrimes				return (error);
4741541Srgrimes			/* check again for safety */
47511789Sbde			if ((tp->t_state & TS_ISOPEN) == 0) {
47611789Sbde				/* adjust for data copied in but not written */
47711789Sbde				uio->uio_resid += cc;
4781541Srgrimes				return (EIO);
47911789Sbde			}
4801541Srgrimes		}
4811541Srgrimes		while (cc > 0) {
4821541Srgrimes			if ((tp->t_rawq.c_cc + tp->t_canq.c_cc) >= TTYHOG - 2 &&
48390831Sdillon			   (tp->t_canq.c_cc > 0 || !(tp->t_lflag&ICANON))) {
4849824Sbde				wakeup(TSA_HUP_OR_INPUT(tp));
4851541Srgrimes				goto block;
4861541Srgrimes			}
487130077Sphk			ttyld_rint(tp, *cp++);
4881541Srgrimes			cnt++;
4891541Srgrimes			cc--;
4901541Srgrimes		}
4911541Srgrimes		cc = 0;
4921541Srgrimes	}
4931541Srgrimes	return (0);
4941541Srgrimesblock:
4951541Srgrimes	/*
4961541Srgrimes	 * Come here to wait for slave to open, for space
49715199Sbde	 * in outq, or space in rawq, or an empty canq.
4981541Srgrimes	 */
49911789Sbde	if ((tp->t_state & TS_CONNECTED) == 0) {
50011789Sbde		/* adjust for data copied in but not written */
50111789Sbde		uio->uio_resid += cc;
5021541Srgrimes		return (EIO);
50311789Sbde	}
5041541Srgrimes	if (flag & IO_NDELAY) {
5051541Srgrimes		/* adjust for data copied in but not written */
5061541Srgrimes		uio->uio_resid += cc;
5071541Srgrimes		if (cnt == 0)
5081541Srgrimes			return (EWOULDBLOCK);
5091541Srgrimes		return (0);
5101541Srgrimes	}
5119639Sbde	error = tsleep(TSA_PTC_WRITE(tp), TTOPRI | PCATCH, "ptcout", 0);
5123308Sphk	if (error) {
5131541Srgrimes		/* adjust for data copied in but not written */
5141541Srgrimes		uio->uio_resid += cc;
5151541Srgrimes		return (error);
5161541Srgrimes	}
5171541Srgrimes	goto again;
5181541Srgrimes}
5191541Srgrimes
5201541Srgrimes/*ARGSUSED*/
52112675Sjulianstatic	int
522130585Sphkptyioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td)
5231541Srgrimes{
524111742Sdes	struct tty *tp = dev->si_tty;
525130263Sphk	struct ptsc *pt = dev->si_drv1;
526111742Sdes	u_char *cc = tp->t_cc;
5271541Srgrimes	int stop, error;
5281541Srgrimes
52947301Sluoqi	if (devsw(dev)->d_open == ptcopen) {
5301541Srgrimes		switch (cmd) {
5311541Srgrimes
5321541Srgrimes		case TIOCGPGRP:
5331541Srgrimes			/*
53433826Sbde			 * We avoid calling ttioctl on the controller since,
5351541Srgrimes			 * in that case, tp must be the controlling terminal.
5361541Srgrimes			 */
5371541Srgrimes			*(int *)data = tp->t_pgrp ? tp->t_pgrp->pg_id : 0;
5381541Srgrimes			return (0);
5391541Srgrimes
5401541Srgrimes		case TIOCPKT:
5411541Srgrimes			if (*(int *)data) {
542130263Sphk				if (pt->pt_flags & PF_UCNTL)
5431541Srgrimes					return (EINVAL);
544130263Sphk				pt->pt_flags |= PF_PKT;
5451541Srgrimes			} else
546130263Sphk				pt->pt_flags &= ~PF_PKT;
5471541Srgrimes			return (0);
5481541Srgrimes
5491541Srgrimes		case TIOCUCNTL:
5501541Srgrimes			if (*(int *)data) {
551130263Sphk				if (pt->pt_flags & PF_PKT)
5521541Srgrimes					return (EINVAL);
553130263Sphk				pt->pt_flags |= PF_UCNTL;
5541541Srgrimes			} else
555130263Sphk				pt->pt_flags &= ~PF_UCNTL;
5561541Srgrimes			return (0);
55747203Sluoqi		}
5581541Srgrimes
55947203Sluoqi		/*
560111742Sdes		 * The rest of the ioctls shouldn't be called until
56147301Sluoqi		 * the slave is open.
56247203Sluoqi		 */
56347203Sluoqi		if ((tp->t_state & TS_ISOPEN) == 0)
56447301Sluoqi			return (EAGAIN);
56547203Sluoqi
56647203Sluoqi		switch (cmd) {
567130892Sphk#ifndef BURN_BRIDGES
5681541Srgrimes#ifdef COMPAT_43
5698876Srgrimes		case TIOCSETP:
5701541Srgrimes		case TIOCSETN:
5711541Srgrimes#endif
572130892Sphk#endif
5731541Srgrimes		case TIOCSETD:
5741541Srgrimes		case TIOCSETA:
5751541Srgrimes		case TIOCSETAW:
5769858Sache		case TIOCSETAF:
57747301Sluoqi			/*
57847301Sluoqi			 * IF CONTROLLER STTY THEN MUST FLUSH TO PREVENT A HANG.
57947301Sluoqi			 * ttywflush(tp) will hang if there are characters in
58047301Sluoqi			 * the outq.
58147301Sluoqi			 */
5821541Srgrimes			ndflush(&tp->t_outq, tp->t_outq.c_cc);
5831541Srgrimes			break;
5841541Srgrimes
5851541Srgrimes		case TIOCSIG:
58627770Sjmg			if (*(unsigned int *)data >= NSIG ||
58727770Sjmg			    *(unsigned int *)data == 0)
5881541Srgrimes				return(EINVAL);
5891541Srgrimes			if ((tp->t_lflag&NOFLSH) == 0)
5901541Srgrimes				ttyflush(tp, FREAD|FWRITE);
59191140Stanimura			if (tp->t_pgrp != NULL) {
59291140Stanimura				PGRP_LOCK(tp->t_pgrp);
59391140Stanimura				pgsignal(tp->t_pgrp, *(unsigned int *)data, 1);
59491140Stanimura				PGRP_UNLOCK(tp->t_pgrp);
59591140Stanimura			}
5961541Srgrimes			if ((*(unsigned int *)data == SIGINFO) &&
5971541Srgrimes			    ((tp->t_lflag&NOKERNINFO) == 0))
5981541Srgrimes				ttyinfo(tp);
5991541Srgrimes			return(0);
6001541Srgrimes		}
60147203Sluoqi	}
60247301Sluoqi	if (cmd == TIOCEXT) {
60347301Sluoqi		/*
60447301Sluoqi		 * When the EXTPROC bit is being toggled, we need
60547301Sluoqi		 * to send an TIOCPKT_IOCTL if the packet driver
60647301Sluoqi		 * is turned on.
60747301Sluoqi		 */
60847301Sluoqi		if (*(int *)data) {
609130263Sphk			if (pt->pt_flags & PF_PKT) {
610130263Sphk				pt->pt_send |= TIOCPKT_IOCTL;
61147301Sluoqi				ptcwakeup(tp, FREAD);
61247301Sluoqi			}
61347301Sluoqi			tp->t_lflag |= EXTPROC;
61447301Sluoqi		} else {
61547301Sluoqi			if ((tp->t_lflag & EXTPROC) &&
616130263Sphk			    (pt->pt_flags & PF_PKT)) {
617130263Sphk				pt->pt_send |= TIOCPKT_IOCTL;
61847301Sluoqi				ptcwakeup(tp, FREAD);
61947301Sluoqi			}
62047301Sluoqi			tp->t_lflag &= ~EXTPROC;
62147301Sluoqi		}
62247301Sluoqi		return(0);
62347301Sluoqi	}
624130054Sphk	error = ttyioctl(dev, cmd, data, flag, td);
625130054Sphk	if (error == ENOTTY) {
626130263Sphk		if (pt->pt_flags & PF_UCNTL &&
6271541Srgrimes		    (cmd & ~0xff) == UIOCCMD(0)) {
6281541Srgrimes			if (cmd & 0xff) {
629130263Sphk				pt->pt_ucntl = (u_char)cmd;
6301541Srgrimes				ptcwakeup(tp, FREAD);
6311541Srgrimes			}
6321541Srgrimes			return (0);
6331541Srgrimes		}
6341541Srgrimes		error = ENOTTY;
6351541Srgrimes	}
6361541Srgrimes	/*
6371541Srgrimes	 * If external processing and packet mode send ioctl packet.
6381541Srgrimes	 */
639130263Sphk	if ((tp->t_lflag&EXTPROC) && (pt->pt_flags & PF_PKT)) {
6401541Srgrimes		switch(cmd) {
6411541Srgrimes		case TIOCSETA:
6421541Srgrimes		case TIOCSETAW:
6431541Srgrimes		case TIOCSETAF:
644130892Sphk#ifndef BURN_BRIDGES
6451541Srgrimes#ifdef COMPAT_43
6461541Srgrimes		case TIOCSETP:
6471541Srgrimes		case TIOCSETN:
6481541Srgrimes		case TIOCSETC:
6491541Srgrimes		case TIOCSLTC:
6501541Srgrimes		case TIOCLBIS:
6511541Srgrimes		case TIOCLBIC:
6521541Srgrimes		case TIOCLSET:
6531541Srgrimes#endif
654130892Sphk#endif
655130263Sphk			pt->pt_send |= TIOCPKT_IOCTL;
6561541Srgrimes			ptcwakeup(tp, FREAD);
657115463Sphk			break;
6581541Srgrimes		default:
6591541Srgrimes			break;
6601541Srgrimes		}
6611541Srgrimes	}
6628876Srgrimes	stop = (tp->t_iflag & IXON) && CCEQ(cc[VSTOP], CTRL('s'))
6631541Srgrimes		&& CCEQ(cc[VSTART], CTRL('q'));
664130263Sphk	if (pt->pt_flags & PF_NOSTOP) {
6651541Srgrimes		if (stop) {
666130263Sphk			pt->pt_send &= ~TIOCPKT_NOSTOP;
667130263Sphk			pt->pt_send |= TIOCPKT_DOSTOP;
668130263Sphk			pt->pt_flags &= ~PF_NOSTOP;
6691541Srgrimes			ptcwakeup(tp, FREAD);
6701541Srgrimes		}
6711541Srgrimes	} else {
6721541Srgrimes		if (!stop) {
673130263Sphk			pt->pt_send &= ~TIOCPKT_DOSTOP;
674130263Sphk			pt->pt_send |= TIOCPKT_NOSTOP;
675130263Sphk			pt->pt_flags |= PF_NOSTOP;
6761541Srgrimes			ptcwakeup(tp, FREAD);
6771541Srgrimes		}
6781541Srgrimes	}
6791541Srgrimes	return (error);
6801541Srgrimes}
68112517Sjulian
68212675Sjulianstatic void
683130585Sphkpty_clone(void *arg, char *name, int namelen, struct cdev **dev)
68464880Sphk{
68564880Sphk	int u;
68664880Sphk
687130640Sphk	if (*dev != NULL)
68864880Sphk		return;
68964880Sphk	if (bcmp(name, "pty", 3) != 0)
69064880Sphk		return;
69164880Sphk	if (name[5] != '\0')
69264880Sphk		return;
69364880Sphk	switch (name[3]) {
69464880Sphk	case 'p': u =   0; break;
69564880Sphk	case 'q': u =  32; break;
69664880Sphk	case 'r': u =  64; break;
69764880Sphk	case 's': u =  96; break;
69864880Sphk	case 'P': u = 128; break;
69964880Sphk	case 'Q': u = 160; break;
70064880Sphk	case 'R': u = 192; break;
70164880Sphk	case 'S': u = 224; break;
70264880Sphk	default: return;
70364880Sphk	}
70464880Sphk	if (name[4] >= '0' && name[4] <= '9')
70564880Sphk		u += name[4] - '0';
70664880Sphk	else if (name[4] >= 'a' && name[4] <= 'v')
70764880Sphk		u += name[4] - 'a' + 10;
70864880Sphk	else
70964880Sphk		return;
71077176Sphk	*dev = make_dev(&ptc_cdevsw, u,
71177176Sphk	    UID_ROOT, GID_WHEEL, 0666, "pty%c%r", names[u / 32], u % 32);
71277176Sphk	(*dev)->si_flags |= SI_CHEAPCLONE;
71364880Sphk	return;
71464880Sphk}
71564880Sphk
71664880Sphkstatic void
717130262Sphkptc_drvinit(void *unused)
71812517Sjulian{
719108363Sphk
72065374Sphk	EVENTHANDLER_REGISTER(dev_clone, pty_clone, 0, 1000);
72112517Sjulian}
72212517Sjulian
72312517SjulianSYSINIT(ptcdev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR_C,ptc_drvinit,NULL)
724