Index: share/man/man4/pts.4 =================================================================== --- share/man/man4/pts.4 (revision 182681) +++ share/man/man4/pts.4 (working copy) @@ -173,9 +173,3 @@ 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. Index: sys/kern/tty_pts.c =================================================================== --- sys/kern/tty_pts.c (revision 182681) +++ sys/kern/tty_pts.c (working copy) @@ -82,6 +82,7 @@ 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 @@ { 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) + tty_lock(tp); + + for (;;) { + /* + * 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); - if (uio->uio_resid == 0) - return (0); - } + } - oresid = uio->uio_resid; + /* + * 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_lock(tp); - for (;;) { - error = ttydisc_getc_uio(tp, uio); - /* We've got data (or an error). */ - if (error != 0 || uio->uio_resid != oresid) + 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 @@ if (error != 0) break; } + tty_unlock(tp); return (error); @@ -162,14 +184,14 @@ 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 @@ * 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 @@ if (iblen == 0) { /* All data written. */ - continue; + break; } } else { error = ttydisc_rint(tp, *ibstart, 0); @@ -217,7 +240,11 @@ 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 @@ 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)) { @@ -481,6 +509,34 @@ } 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) { struct pts_softc *psc = softc; @@ -506,6 +562,7 @@ .tsw_outwakeup = ptsdrv_outwakeup, .tsw_inwakeup = ptsdrv_inwakeup, .tsw_close = ptsdrv_close, + .tsw_pktnotify = ptsdrv_pktnotify, .tsw_free = ptsdrv_free, }; Index: sys/kern/tty.c =================================================================== --- sys/kern/tty.c (revision 182681) +++ sys/kern/tty.c (working copy) @@ -816,6 +816,11 @@ } static void +ttydevsw_defpktnotify(struct tty *tp, char event) +{ +} + +static void ttydevsw_deffree(void *softc) { @@ -846,6 +851,7 @@ PATCH_FUNC(param); PATCH_FUNC(modem); PATCH_FUNC(mmap); + PATCH_FUNC(pktnotify); PATCH_FUNC(free); #undef PATCH_FUNC @@ -1226,11 +1232,13 @@ 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 @@ 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 @@ 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); Index: sys/sys/ttydevsw.h =================================================================== --- sys/sys/ttydevsw.h (revision 182681) +++ sys/sys/ttydevsw.h (working copy) @@ -48,6 +48,7 @@ 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 @@ 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. */ }; @@ -148,6 +150,15 @@ } 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) { MPASS(tty_gone(tp));