1
0
mirror of https://git.FreeBSD.org/src.git synced 2024-12-25 11:37:56 +00:00

Implement pts(4) packet mode.

As reported by several users on the mailing lists, applications like
screen(1) fail to properly handle ^S and ^Q characters. This was because
MPSAFE TTY didn't implement packet mode (TIOCPKT) yet. Add basic packet
mode support to make these applications work again.

Obtained from:	//depot/projects/mpsafetty/...
This commit is contained in:
Ed Schouten 2008-09-04 16:39:02 +00:00
parent 2bda9238e5
commit 64308260f6
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=182764
4 changed files with 118 additions and 35 deletions

View File

@ -173,9 +173,3 @@ In
it was replaced with the
.Nm
driver.
.Sh BUGS
Packet mode has not been properly implemented in this version of
.Fx .
When enabled, it will always prepend
.Dv TIOCPKT_DATA ,
even if other events have been triggered.

View File

@ -815,6 +815,11 @@ ttydevsw_defmmap(struct tty *tp, vm_offset_t offset, vm_paddr_t *paddr,
return (-1);
}
static void
ttydevsw_defpktnotify(struct tty *tp, char event)
{
}
static void
ttydevsw_deffree(void *softc)
{
@ -846,6 +851,7 @@ tty_alloc(struct ttydevsw *tsw, void *sc, struct mtx *mutex)
PATCH_FUNC(param);
PATCH_FUNC(modem);
PATCH_FUNC(mmap);
PATCH_FUNC(pktnotify);
PATCH_FUNC(free);
#undef PATCH_FUNC
@ -1226,11 +1232,13 @@ tty_flush(struct tty *tp, int flags)
tp->t_flags &= ~TF_HIWAT_OUT;
ttyoutq_flush(&tp->t_outq);
tty_wakeup(tp, FWRITE);
ttydevsw_pktnotify(tp, TIOCPKT_FLUSHWRITE);
}
if (flags & FREAD) {
tty_hiwat_in_unblock(tp);
ttyinq_flush(&tp->t_inq);
ttydevsw_inwakeup(tp);
ttydevsw_pktnotify(tp, TIOCPKT_FLUSHREAD);
}
}
@ -1372,6 +1380,17 @@ tty_generic_ioctl(struct tty *tp, u_long cmd, void *data, struct thread *td)
ttyinq_canonicalize(&tp->t_inq);
tty_wakeup(tp, FREAD);
}
/*
* For packet mode: notify the PTY consumer that VSTOP
* and VSTART may have been changed.
*/
if (tp->t_termios.c_iflag & IXON &&
tp->t_termios.c_cc[VSTOP] == CTRL('S') &&
tp->t_termios.c_cc[VSTART] == CTRL('Q'))
ttydevsw_pktnotify(tp, TIOCPKT_DOSTOP);
else
ttydevsw_pktnotify(tp, TIOCPKT_NOSTOP);
return (0);
}
case TIOCGETD:
@ -1562,10 +1581,12 @@ tty_generic_ioctl(struct tty *tp, u_long cmd, void *data, struct thread *td)
return (0);
case TIOCSTOP:
tp->t_flags |= TF_STOPPED;
ttydevsw_pktnotify(tp, TIOCPKT_STOP);
return (0);
case TIOCSTART:
tp->t_flags &= ~TF_STOPPED;
ttydevsw_outwakeup(tp);
ttydevsw_pktnotify(tp, TIOCPKT_START);
return (0);
case TIOCSTAT:
tty_info(tp);

View File

@ -82,6 +82,7 @@ struct pts_softc {
int pts_unit; /* (c) Device unit number. */
unsigned int pts_flags; /* (t) Device flags. */
#define PTS_PKT 0x1 /* Packet mode. */
char pts_pkt; /* (t) Unread packet mode data. */
struct cv pts_inwait; /* (t) Blocking write() on master. */
struct selinfo pts_inpoll; /* (t) Select queue for write(). */
@ -105,34 +106,54 @@ ptsdev_read(struct file *fp, struct uio *uio, struct ucred *active_cred,
{
struct tty *tp = fp->f_data;
struct pts_softc *psc = tty_softc(tp);
int error, oresid;
int error = 0;
char pkt;
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).
*/
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);
}
oresid = uio->uio_resid;
tty_lock(tp);
for (;;) {
error = ttydisc_getc_uio(tp, uio);
/* We've got data (or an error). */
if (error != 0 || uio->uio_resid != oresid)
/*
* 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).
*/
if (psc->pts_flags & PTS_PKT && psc->pts_pkt) {
pkt = psc->pts_pkt;
psc->pts_pkt = 0;
tty_unlock(tp);
error = ureadc(pkt, uio);
return (error);
}
/*
* Transmit regular data.
*
* XXX: We shouldn't use ttydisc_getc_poll()! Even
* though in this implementation, there is likely going
* to be data, we should just call ttydisc_getc_uio()
* and use its return value to sleep.
*/
if (ttydisc_getc_poll(tp)) {
if (psc->pts_flags & PTS_PKT) {
/*
* XXX: Small race. Fortunately PTY
* consumers aren't multithreaded.
*/
tty_unlock(tp);
error = ureadc(TIOCPKT_DATA, uio);
if (error)
return (error);
tty_lock(tp);
}
error = ttydisc_getc_uio(tp, uio);
break;
}
/* Maybe the device isn't used anyway. */
if (tty_opened(tp) == 0)
@ -147,6 +168,7 @@ ptsdev_read(struct file *fp, struct uio *uio, struct ucred *active_cred,
if (error != 0)
break;
}
tty_unlock(tp);
return (error);
@ -162,14 +184,14 @@ ptsdev_write(struct file *fp, struct uio *uio, struct ucred *active_cred,
size_t iblen, rintlen;
int error = 0;
tty_lock(tp);
if (uio->uio_resid == 0)
return (0);
while (uio->uio_resid > 0) {
/* Temporarily unlock to buffer new characters. */
tty_unlock(tp);
for (;;) {
ibstart = ib;
iblen = MIN(uio->uio_resid, sizeof ib);
error = uiomove(ib, iblen, uio);
tty_lock(tp);
if (error != 0)
goto done;
@ -178,7 +200,8 @@ ptsdev_write(struct file *fp, struct uio *uio, struct ucred *active_cred,
* When possible, avoid the slow path. rint_bypass()
* copies all input to the input queue at once.
*/
while (iblen > 0) {
MPASS(iblen > 0);
do {
if (ttydisc_can_bypass(tp)) {
/* Store data at once. */
rintlen = ttydisc_rint_bypass(tp,
@ -188,7 +211,7 @@ ptsdev_write(struct file *fp, struct uio *uio, struct ucred *active_cred,
if (iblen == 0) {
/* All data written. */
continue;
break;
}
} else {
error = ttydisc_rint(tp, *ibstart, 0);
@ -217,7 +240,11 @@ ptsdev_write(struct file *fp, struct uio *uio, struct ucred *active_cred,
error = cv_wait_sig(&psc->pts_inwait, tp->t_mtx);
if (error != 0)
goto done;
}
} while (iblen > 0);
if (uio->uio_resid == 0)
break;
tty_unlock(tp);
}
done: ttydisc_rint_done(tp);
@ -362,7 +389,8 @@ ptsdev_poll(struct file *fp, int events, struct ucred *active_cred,
if (events & (POLLIN|POLLRDNORM)) {
/* See if we can getc something. */
if (ttydisc_getc_poll(tp))
if (ttydisc_getc_poll(tp) ||
(psc->pts_flags & PTS_PKT && psc->pts_pkt))
revents |= events & (POLLIN|POLLRDNORM);
}
if (events & (POLLOUT|POLLWRNORM)) {
@ -480,6 +508,34 @@ ptsdrv_close(struct tty *tp)
ptsdrv_inwakeup(tp);
}
static void
ptsdrv_pktnotify(struct tty *tp, char event)
{
struct pts_softc *psc = tty_softc(tp);
/*
* Clear conflicting flags.
*/
switch (event) {
case TIOCPKT_STOP:
psc->pts_pkt &= ~TIOCPKT_START;
break;
case TIOCPKT_START:
psc->pts_pkt &= ~TIOCPKT_STOP;
break;
case TIOCPKT_NOSTOP:
psc->pts_pkt &= ~TIOCPKT_DOSTOP;
break;
case TIOCPKT_DOSTOP:
psc->pts_pkt &= ~TIOCPKT_NOSTOP;
break;
}
psc->pts_pkt |= event;
ptsdrv_outwakeup(tp);
}
static void
ptsdrv_free(void *softc)
{
@ -506,6 +562,7 @@ static struct ttydevsw pts_class = {
.tsw_outwakeup = ptsdrv_outwakeup,
.tsw_inwakeup = ptsdrv_inwakeup,
.tsw_close = ptsdrv_close,
.tsw_pktnotify = ptsdrv_pktnotify,
.tsw_free = ptsdrv_free,
};

View File

@ -48,6 +48,7 @@ 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_pktnotify_t(struct tty *, char);
typedef void tsw_free_t(void *);
struct ttydevsw {
@ -64,6 +65,7 @@ struct ttydevsw {
tsw_modem_t *tsw_modem; /* Modem sigon/sigoff. */
tsw_mmap_t *tsw_mmap; /* mmap() hooks. */
tsw_pktnotify_t *tsw_pktnotify; /* TIOCPKT events. */
tsw_free_t *tsw_free; /* Destructor. */
};
@ -147,6 +149,15 @@ ttydevsw_mmap(struct tty *tp, vm_offset_t offset, vm_paddr_t *paddr, int nprot)
return tp->t_devsw->tsw_mmap(tp, offset, paddr, nprot);
}
static __inline void
ttydevsw_pktnotify(struct tty *tp, char event)
{
tty_lock_assert(tp, MA_OWNED);
MPASS(!tty_gone(tp));
tp->t_devsw->tsw_pktnotify(tp, event);
}
static __inline void
ttydevsw_free(struct tty *tp)
{