* Copyright (c) 1982, 1986, 1989 The Regents of the University of California.
* Redistribution and use in source and binary forms are permitted
* provided that the above copyright notice and this paragraph are
* duplicated in all such forms and that any documentation,
* advertising materials, and other materials related to such
* distribution and use acknowledge that the software was developed
* by the University of California, Berkeley. The name of the
* University may not be used to endorse or promote products derived
* from this software without specific prior written permission.
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
* @(#)tty_pty.c 7.7 (Berkeley) %G%
* (Actually two drivers, requiring two entries in 'cdevsw')
#define NPTY 32 /* crude XXX */
#define BUFSIZ 100 /* Chunk size iomoved to/from user */
struct proc
*pt_selr
, *pt_selw
;
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 */
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
&& u
.u_uid
!= 0)
if (tp
->t_oproc
) /* Ctrlr still around. */
tp
->t_state
|= TS_CARR_ON
;
while ((tp
->t_state
& TS_CARR_ON
) == 0) {
sleep((caddr_t
)&tp
->t_rawq
, TTIPRI
);
error
= (*linesw
[tp
->t_line
].l_open
)(dev
, tp
, flag
);
ptcwakeup(tp
, FREAD
|FWRITE
);
tp
= &pt_tty
[minor(dev
)];
(*linesw
[tp
->t_line
].l_close
)(tp
);
ptcwakeup(tp
, FREAD
|FWRITE
);
register struct tty
*tp
= &pt_tty
[minor(dev
)];
register struct pt_ioctl
*pti
= &pt_ioctl
[minor(dev
)];
if (pti
->pt_flags
& PF_REMOTE
) {
u
.u_procp
->p_pgrp
->pg_id
!= tp
->t_pgid
){
if ((u
.u_procp
->p_sigignore
& sigmask(SIGTTIN
)) ||
(u
.u_procp
->p_sigmask
& sigmask(SIGTTIN
)) ||
!u
.u_procp
->p_pgrp
->pg_jobc
||
u
.u_procp
->p_flag
&SVFORK
)
pgsignal(u
.u_procp
->p_pgrp
, SIGTTIN
);
sleep((caddr_t
)&lbolt
, TTIPRI
);
if (tp
->t_canq
.c_cc
== 0) {
sleep((caddr_t
)&tp
->t_canq
, TTIPRI
);
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
, pti
->pt_flags
& PF_RCOLL
);
pti
->pt_flags
&= ~PF_RCOLL
;
wakeup((caddr_t
)&tp
->t_outq
.c_cf
);
selwakeup(pti
->pt_selw
, pti
->pt_flags
& PF_WCOLL
);
pti
->pt_flags
&= ~PF_WCOLL
;
if (ptydebug
) printf("WAKEUP c_cf %d\n", u
.u_procp
->p_pid
);
wakeup((caddr_t
)&tp
->t_rawq
.c_cf
);
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_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 (ptydebug
) printf("SLEEP(1) c_cf %d\n", u
.u_procp
->p_pid
);
sleep((caddr_t
)&tp
->t_outq
.c_cf
, TTIPRI
);
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
))
if ((p
= pti
->pt_selr
) && p
->p_wchan
== (caddr_t
)&selwait
)
pti
->pt_flags
|= PF_RCOLL
;
pti
->pt_selr
= u
.u_procp
;
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
))
if ((p
= pti
->pt_selw
) && p
->p_wchan
== (caddr_t
)&selwait
)
pti
->pt_flags
|= PF_WCOLL
;
pti
->pt_selw
= u
.u_procp
;
register struct uio
*uio
;
register struct tty
*tp
= &pt_tty
[minor(dev
)];
register struct iovec
*iov
;
struct pt_ioctl
*pti
= &pt_ioctl
[minor(dev
)];
if ((tp
->t_state
&TS_ISOPEN
) == 0)
if (pti
->pt_flags
& PF_REMOTE
) {
while (uio
->uio_iovcnt
> 0 && tp
->t_canq
.c_cc
< TTYHOG
- 1) {
cc
= MIN(iov
->iov_len
, BUFSIZ
);
cc
= MIN(cc
, TTYHOG
- 1 - tp
->t_canq
.c_cc
);
error
= uiomove(cp
, cc
, uio
);
/* check again for safety */
if ((tp
->t_state
&TS_ISOPEN
) == 0)
(void) b_to_q(cp
, cc
, &tp
->t_canq
);
(void) putc(0, &tp
->t_canq
);
wakeup((caddr_t
)&tp
->t_canq
);
while (uio
->uio_iovcnt
> 0) {
cc
= MIN(iov
->iov_len
, BUFSIZ
);
error
= uiomove(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
++&0377, 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)
if ((pti
->pt_flags
& PF_NBIO
) || (flag
& IO_NDELAY
)) {
if (ptydebug
) printf("SLEEP(2) c_cf %d\n", u
.u_procp
->p_pid
);
sleep((caddr_t
)&tp
->t_rawq
.c_cf
, TTOPRI
);
ptyioctl(dev
, cmd
, data
, flag
)
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.
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
);
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
);
pti
->pt_flags
|= PF_NBIO
;
pti
->pt_flags
&= ~PF_NBIO
;
*(int *)data
= tp
->t_outq
.c_cc
;
while (getc(&tp
->t_outq
) >= 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
);
error
= ttioctl(tp
, cmd
, data
, flag
);
* Since we use the tty queues internally,
* pty's can't be switched to disciplines which overwrite
* the queues. We can't tell anything about the discipline
* Nb: this is not really good enough, the line disc open routine
* may have done anything at all, no guarantees that close
* will fix it. This also has the effect of losing the
* previous discipline, which an error on a TIOCSETD shouldn't
* do... Sometime it should be done via an explicit check
* for TIOCSETD, then check to see what linesw[new_number].l_rint
if (linesw
[tp
->t_line
].l_rint
!= ttyinput
) {
(*linesw
[tp
->t_line
].l_close
)(tp
);
(void)(*linesw
[tp
->t_line
].l_open
)(dev
, tp
, flag
);
if (pti
->pt_flags
& PF_UCNTL
&&
(cmd
& ~0xff) == UIOCCMD(0)) {
pti
->pt_ucntl
= (u_char
)cmd
;
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
;