* Copyright (c) 1982, 1986, 1989 The Regents of the University of California.
* %sccs.include.redist.c%
* @(#)tty_pty.c 8.1 (Berkeley) %G%
* (Actually two drivers, requiring two entries in 'cdevsw')
#include "pty.h" /* XXX */
#define NPTY 32 /* crude XXX */
#define BUFSIZ 100 /* Chunk size iomoved to/from user */
struct tty pt_tty
[NPTY
]; /* XXX */
struct selinfo pt_selr
, pt_selw
;
} pt_ioctl
[NPTY
]; /* XXX */
int npty
= NPTY
; /* for pstat -t */
#define PF_PKT 0x0008 /* packet mode */
#define PF_STOPPED 0x0010 /* user told stopped */
#define PF_REMOTE 0x0020 /* remote and flow controlled input */
#define PF_UCNTL 0x0080 /* user control mode */
#define PF_TIOC 0x0100 /* transparent control mode */
#define PF_LIOC 0x0200 /* transparent control locked */
#define PF_WIOC 0x0400 /* waiting for PF_LIOC to clear */
#define PF_BLOCK 0x0800 /* block writes to slave */
#define PF_OWAIT 0x1000 /* waiting for PF_BLOCK to clear */
void ptsstop
__P((struct tty
*, int));
* Establish n (or default if n is 1) ptys in the system.
* XXX cdevsw & pstat require the array `pty[]' to be an array
/* maybe should allow 0 => none? */
ntb
= n
* sizeof(struct tty
);
mem
= malloc(ntb
+ ALIGNBYTES
+ n
* sizeof(struct pt_ioctl
),
pt_tty
= (struct tty
*)mem
;
mem
= (char *)ALIGN(mem
+ ntb
);
pt_ioctl
= (struct pt_ioctl
*)mem
;
ptsopen(dev
, flag
, devtype
, p
)
tp
= &pt_tty
[minor(dev
)];
if ((tp
->t_state
& TS_ISOPEN
) == 0) {
ttychars(tp
); /* Set up default chars */
tp
->t_iflag
= TTYDEF_IFLAG
;
tp
->t_oflag
= TTYDEF_OFLAG
;
tp
->t_lflag
= TTYDEF_LFLAG
;
tp
->t_cflag
= TTYDEF_CFLAG
;
tp
->t_ispeed
= tp
->t_ospeed
= TTYDEF_SPEED
;
ttsetwater(tp
); /* would be done in xxparam() */
} else if (tp
->t_state
&TS_XCLUDE
&& p
->p_ucred
->cr_uid
!= 0)
if (tp
->t_oproc
) /* Ctrlr still around. */
tp
->t_state
|= TS_CARR_ON
;
while ((tp
->t_state
& TS_CARR_ON
) == 0) {
if (error
= ttysleep(tp
, (caddr_t
)&tp
->t_rawq
, TTIPRI
| PCATCH
,
error
= (*linesw
[tp
->t_line
].l_open
)(dev
, tp
);
ptcwakeup(tp
, FREAD
|FWRITE
);
ptsclose(dev
, flag
, mode
, p
)
tp
= &pt_tty
[minor(dev
)];
err
= (*linesw
[tp
->t_line
].l_close
)(tp
, flag
);
ptcwakeup(tp
, FREAD
|FWRITE
);
struct proc
*p
= curproc
;
register struct tty
*tp
= &pt_tty
[minor(dev
)];
register struct pt_ioctl
*pti
= &pt_ioctl
[minor(dev
)];
if (pti
->pt_flags
& PF_REMOTE
) {
while (isbackground(p
, tp
)) {
if ((p
->p_sigignore
& sigmask(SIGTTIN
)) ||
(p
->p_sigmask
& sigmask(SIGTTIN
)) ||
p
->p_pgrp
->pg_jobc
== 0 ||
pgsignal(p
->p_pgrp
, SIGTTIN
, 1);
if (error
= ttysleep(tp
, (caddr_t
)&lbolt
,
TTIPRI
| PCATCH
, ttybg
, 0))
if (tp
->t_canq
.c_cc
== 0) {
if (error
= ttysleep(tp
, (caddr_t
)&tp
->t_canq
,
TTIPRI
| PCATCH
, ttyin
, 0))
while (tp
->t_canq
.c_cc
> 1 && uio
->uio_resid
> 0)
if (ureadc(getc(&tp
->t_canq
), uio
) < 0) {
if (tp
->t_canq
.c_cc
== 1)
(void) getc(&tp
->t_canq
);
error
= (*linesw
[tp
->t_line
].l_read
)(tp
, uio
, flag
);
* Wakeups of controlling tty will happen
* indirectly, when tty driver calls ptsstart.
register struct tty
*tp
= &pt_tty
[minor(dev
)];
register struct pt_ioctl
*pti
= &pt_ioctl
[minor(dev
)];
while (pti
->pt_flags
& PF_BLOCK
) {
pti
->pt_flags
|= PF_OWAIT
;
sleep((caddr_t
)pti
+ 1, TTOPRI
);
return ((*linesw
[tp
->t_line
].l_write
)(tp
, uio
, flag
));
* Start output on pseudo-tty.
* Wake up process selecting or sleeping for input from controlling tty.
register struct pt_ioctl
*pti
= &pt_ioctl
[minor(tp
->t_dev
)];
if (tp
->t_state
& TS_TTSTOP
)
if (pti
->pt_flags
& PF_STOPPED
) {
pti
->pt_flags
&= ~PF_STOPPED
;
pti
->pt_send
= TIOCPKT_START
;
struct pt_ioctl
*pti
= &pt_ioctl
[minor(tp
->t_dev
)];
selwakeup(&pti
->pt_selr
);
wakeup((caddr_t
)&tp
->t_outq
.c_cf
);
selwakeup(&pti
->pt_selw
);
wakeup((caddr_t
)&tp
->t_rawq
.c_cf
);
ptcopen(dev_t dev
, int flag
, int devtype
, struct proc
*p
)
ptcopen(dev
, flag
, devtype
, p
)
tp
= &pt_tty
[minor(dev
)];
(void)(*linesw
[tp
->t_line
].l_modem
)(tp
, 1);
pti
= &pt_ioctl
[minor(dev
)];
tp
= &pt_tty
[minor(dev
)];
(void)(*linesw
[tp
->t_line
].l_modem
)(tp
, 0);
tp
->t_state
&= ~TS_CARR_ON
;
tp
->t_oproc
= 0; /* mark closed */
register struct tty
*tp
= &pt_tty
[minor(dev
)];
struct pt_ioctl
*pti
= &pt_ioctl
[minor(dev
)];
* 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.
if (tp
->t_state
&TS_ISOPEN
) {
if (pti
->pt_flags
&PF_PKT
&& pti
->pt_send
) {
error
= ureadc((int)pti
->pt_send
, uio
);
if (pti
->pt_send
& TIOCPKT_IOCTL
) {
uiomove(&tp
->t_termios
, cc
, uio
);
if (pti
->pt_flags
&PF_UCNTL
&& pti
->pt_ucntl
) {
error
= ureadc((int)pti
->pt_ucntl
, uio
);
if (pti
->pt_flags
&PF_TIOC
&& pti
->pt_ioc
.c_cc
) {
if (uio
->uio_resid
< pti
->pt_ioc
.c_cc
+ 1)
error
= ureadc(TIOCPKT_TIOC
, uio
);
while (error
== 0 && pti
->pt_ioc
.c_cc
> 0) {
cc
= q_to_b(&pti
->pt_ioc
, buf
,
MIN(pti
->pt_ioc
.c_cc
, BUFSIZ
));
if (cc
<= 0) /* impossible? */
error
= uiomove(buf
, cc
, UIO_READ
, uio
);
if (tp
->t_outq
.c_cc
&& (tp
->t_state
&TS_TTSTOP
) == 0)
if ((tp
->t_state
&TS_CARR_ON
) == 0)
if (error
= tsleep((caddr_t
)&tp
->t_outq
.c_cf
, TTIPRI
| PCATCH
,
if (pti
->pt_flags
& (PF_PKT
|PF_UCNTL
|PF_TIOC
))
while (uio
->uio_resid
> 0 && error
== 0) {
cc
= q_to_b(&tp
->t_outq
, buf
, min(uio
->uio_resid
, BUFSIZ
));
error
= uiomove(buf
, cc
, uio
);
if (tp
->t_outq
.c_cc
<= TTLOWAT(tp
) && !(pti
->pt_flags
& PF_BLOCK
))
if (tp
->t_state
&TS_ASLEEP
) {
tp
->t_state
&= ~TS_ASLEEP
;
wakeup((caddr_t
)&tp
->t_outq
);
selwakeup(tp
->t_wsel
, tp
->t_state
& TS_WCOLL
);
tp
->t_state
&= ~TS_WCOLL
;
struct pt_ioctl
*pti
= &pt_ioctl
[minor(tp
->t_dev
)];
/* note: FLUSHREAD and FLUSHWRITE already ok */
pti
->pt_flags
|= PF_STOPPED
;
pti
->pt_flags
&= ~PF_STOPPED
;
/* change of perspective */
register struct tty
*tp
= &pt_tty
[minor(dev
)];
struct pt_ioctl
*pti
= &pt_ioctl
[minor(dev
)];
if ((tp
->t_state
&TS_CARR_ON
) == 0)
* Need to block timeouts (ttrstart).
if ((tp
->t_state
&TS_ISOPEN
) &&
tp
->t_outq
.c_cc
&& (tp
->t_state
&TS_TTSTOP
) == 0) {
case 0: /* exceptional */
if ((tp
->t_state
&TS_ISOPEN
) &&
(pti
->pt_flags
&PF_PKT
&& pti
->pt_send
||
pti
->pt_flags
&PF_TIOC
&& pti
->pt_ioc
.c_cc
||
pti
->pt_flags
&PF_UCNTL
&& pti
->pt_ucntl
))
selrecord(p
, &pti
->pt_selr
);
if (tp
->t_state
&TS_ISOPEN
) {
if (pti
->pt_flags
& PF_REMOTE
) {
if (tp
->t_canq
.c_cc
== 0)
if (tp
->t_rawq
.c_cc
+ tp
->t_canq
.c_cc
< TTYHOG
-2)
if (tp
->t_canq
.c_cc
== 0 && (tp
->t_iflag
&ICANON
))
selrecord(p
, &pti
->pt_selw
);
register struct uio
*uio
;
register struct tty
*tp
= &pt_tty
[minor(dev
)];
struct pt_ioctl
*pti
= &pt_ioctl
[minor(dev
)];
if ((tp
->t_state
&TS_ISOPEN
) == 0)
if (pti
->pt_flags
& PF_REMOTE
) {
while (uio
->uio_resid
> 0 && tp
->t_canq
.c_cc
< TTYHOG
- 1) {
cc
= min(uio
->uio_resid
, BUFSIZ
);
cc
= min(cc
, TTYHOG
- 1 - tp
->t_canq
.c_cc
);
error
= uiomove((caddr_t
)cp
, cc
, uio
);
/* check again for safety */
if ((tp
->t_state
&TS_ISOPEN
) == 0)
(void) b_to_q((char *)cp
, cc
, &tp
->t_canq
);
(void) putc(0, &tp
->t_canq
);
wakeup((caddr_t
)&tp
->t_canq
);
while (uio
->uio_resid
> 0) {
cc
= min(uio
->uio_resid
, BUFSIZ
);
error
= uiomove((caddr_t
)cp
, cc
, uio
);
/* check again for safety */
if ((tp
->t_state
&TS_ISOPEN
) == 0)
if ((tp
->t_rawq
.c_cc
+ tp
->t_canq
.c_cc
) >= TTYHOG
- 2 &&
(tp
->t_canq
.c_cc
> 0 || !(tp
->t_iflag
&ICANON
))) {
wakeup((caddr_t
)&tp
->t_rawq
);
(*linesw
[tp
->t_line
].l_rint
)(*cp
++, tp
);
* Come here to wait for slave to open, for space
* in outq, or space in rawq.
if ((tp
->t_state
&TS_CARR_ON
) == 0)
/* adjust for data copied in but not written */
if (error
= tsleep((caddr_t
)&tp
->t_rawq
.c_cf
, TTOPRI
| PCATCH
,
/* adjust for data copied in but not written */
ptyioctl(dev
, cmd
, data
, flag
, p
)
register struct tty
*tp
= &pt_tty
[minor(dev
)];
register struct pt_ioctl
*pti
= &pt_ioctl
[minor(dev
)];
register u_char
*cc
= tp
->t_cc
;
* IF CONTROLLER STTY THEN MUST FLUSH TO PREVENT A HANG.
* ttywflush(tp) will hang if there are characters in the outq.
* When the EXTPROC bit is being toggled, we need
* to send an TIOCPKT_IOCTL if the packet driver
if (pti
->pt_flags
& PF_PKT
) {
pti
->pt_send
|= TIOCPKT_IOCTL
;
if ((tp
->t_state
& EXTPROC
) &&
(pti
->pt_flags
& PF_PKT
)) {
pti
->pt_send
|= TIOCPKT_IOCTL
;
if (cdevsw
[major(dev
)].d_open
== ptcopen
) {
if ((cmd
& 0xffff) == (TIOCIOANS(0) & 0xffff)) {
if (!(pti
->pt_flags
& PF_LIOC
) || pti
->pt_ioc
.c_cc
)
(void) b_to_q(data
, IOCPARM_LEN(cmd
), &pti
->pt_ioc
);
wakeup((caddr_t
)&pti
->pt_ioc
);
* We aviod 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;
if (pti
->pt_flags
& PF_UCNTL
)
pti
->pt_flags
&= ~PF_PKT
;
if (pti
->pt_flags
& PF_PKT
)
pti
->pt_flags
|= PF_UCNTL
;
pti
->pt_flags
&= ~PF_UCNTL
;
if (pti
->pt_flags
& PF_UCNTL
)
pti
->pt_flags
|= PF_TIOC
;
pti
->pt_flags
&= ~(PF_TIOC
|PF_LIOC
|PF_WIOC
);
(void) getc(&pti
->pt_ioc
);
wakeup((caddr_t
)&pti
->pt_ioc
);
pti
->pt_flags
|= PF_BLOCK
;
if (pti
->pt_flags
& PF_OWAIT
)
wakeup((caddr_t
)pti
+ 1);
pti
->pt_flags
&= ~(PF_BLOCK
|PF_OWAIT
);
pti
->pt_flags
|= PF_REMOTE
;
pti
->pt_flags
&= ~PF_REMOTE
;
ttyflush(tp
, FREAD
|FWRITE
);
*(int *)data
= tp
->t_outq
.c_cc
;
ndflush(&tp
->t_outq
, tp
->t_outq
.c_cc
);
if (*(unsigned int *)data
>= NSIG
)
if ((tp
->t_lflag
&NOFLSH
) == 0)
ttyflush(tp
, FREAD
|FWRITE
);
pgsignal(tp
->t_pgrp
, *(unsigned int *)data
, 1);
if ((*(unsigned int *)data
== SIGINFO
) &&
((tp
->t_lflag
&NOKERNINFO
) == 0))
} else if (pti
->pt_flags
& PF_TIOC
) {
while (pti
->pt_flags
& PF_LIOC
) {
pti
->pt_flags
|= PF_WIOC
;
switch (tsleep((caddr_t
)&pti
->pt_flags
,TTIPRI
-1,5*hz
)) {
pti
->pt_flags
|= PF_LIOC
| PF_BLOCK
;
(void) getc(&pti
->pt_ioc
);
(void) b_to_q(&cmd
, sizeof cmd
, &pti
->pt_ioc
);
(void) b_to_q(data
, IOCPARM_LEN(cmd
), &pti
->pt_ioc
);
switch (tsleep((caddr_t
)&pti
->pt_ioc
, TTIPRI
-1, 5*hz
)) {
(void) getc(&pti
->pt_ioc
);
if (pti
->pt_flags
& PF_WIOC
)
wakeup((caddr_t
)&pti
->pt_flags
);
if (pti
->pt_flags
& PF_OWAIT
)
wakeup((caddr_t
)pti
+ 1);
pti
->pt_flags
&= ~(PF_LIOC
|PF_WIOC
|PF_BLOCK
|PF_OWAIT
);
if (pti
->pt_ioc
.c_cc
== 0) {
if (pti
->pt_flags
& PF_WIOC
)
wakeup((caddr_t
)&pti
->pt_flags
);
if (pti
->pt_flags
& PF_OWAIT
)
wakeup((caddr_t
)pti
+ 1);
pti
->pt_flags
&= ~(PF_LIOC
|PF_WIOC
|PF_BLOCK
|PF_OWAIT
);
if (q_to_b(&pti
->pt_ioc
, &error
, sizeof error
) != sizeof error
)
if (error
== 0 && cmd
& IOC_OUT
) {
if (IOCPARM_LEN(cmd
) != pti
->pt_ioc
.c_cc
)
(void) q_to_b(&pti
->pt_ioc
, data
,
(void) getc(&pti
->pt_ioc
);
if (pti
->pt_flags
& PF_WIOC
)
wakeup((caddr_t
)&pti
->pt_flags
);
if (pti
->pt_flags
& PF_OWAIT
)
wakeup((caddr_t
)pti
+ 1);
pti
->pt_flags
&= ~(PF_LIOC
|PF_WIOC
|PF_BLOCK
|PF_OWAIT
);
error
= (*linesw
[tp
->t_line
].l_ioctl
)(tp
, cmd
, data
, flag
, p
);
error
= ttioctl(tp
, cmd
, data
, flag
);
if (pti
->pt_flags
& PF_UCNTL
&&
(cmd
& ~0xff) == UIOCCMD(0)) {
pti
->pt_ucntl
= (u_char
)cmd
;
* If external processing and packet mode send ioctl packet.
if ((tp
->t_lflag
&EXTPROC
) && (pti
->pt_flags
& PF_PKT
)) {
#if defined(COMPAT_43) || defined(COMPAT_SUNOS)
pti
->pt_send
|= TIOCPKT_IOCTL
;
stop
= (tp
->t_iflag
& IXON
) && CCEQ(cc
[VSTOP
], CTRL('s'))
&& CCEQ(cc
[VSTART
], CTRL('q'));
if (pti
->pt_flags
& PF_NOSTOP
) {
pti
->pt_send
&= ~TIOCPKT_NOSTOP
;
pti
->pt_send
|= TIOCPKT_DOSTOP
;
pti
->pt_flags
&= ~PF_NOSTOP
;
pti
->pt_send
&= ~TIOCPKT_DOSTOP
;
pti
->pt_send
|= TIOCPKT_NOSTOP
;
pti
->pt_flags
|= PF_NOSTOP
;