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:
parent
2bda9238e5
commit
64308260f6
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=182764
@ -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.
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
};
|
||||
|
||||
|
@ -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)
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user