Changeset View
Changeset View
Standalone View
Standalone View
sys/kern/tty.c
| Show First 20 Lines • Show All 120 Lines • ▼ Show 20 Lines | |||||
| * while unlocked. | * while unlocked. | ||||
| */ | */ | ||||
| static int | static int | ||||
| tty_watermarks(struct tty *tp) | tty_watermarks(struct tty *tp) | ||||
| { | { | ||||
| size_t bs = 0; | size_t bs = 0; | ||||
| int error; | int error; | ||||
| tty_assert_locked(tp); | |||||
| ttydisc_assert_locked(tp); | |||||
| /* Provide an input buffer for 2 seconds of data. */ | /* Provide an input buffer for 2 seconds of data. */ | ||||
| if (tp->t_termios.c_cflag & CREAD) | if (tp->t_termios.c_cflag & CREAD) | ||||
| bs = MIN(tp->t_termios.c_ispeed / 5, TTYBUF_MAX); | bs = MIN(tp->t_termios.c_ispeed / 5, TTYBUF_MAX); | ||||
| error = ttyinq_setsize(&tp->t_inq, tp, bs); | error = ttyinq_setsize(&tp->t_inq, tp, bs); | ||||
| if (error != 0) | if (error != 0) | ||||
| return (error); | goto out; | ||||
| /* Set low watermark at 10% (when 90% is available). */ | /* Set low watermark at 10% (when 90% is available). */ | ||||
| tp->t_inlow = (ttyinq_getallocatedsize(&tp->t_inq) * 9) / 10; | tp->t_inlow = (ttyinq_getallocatedsize(&tp->t_inq) * 9) / 10; | ||||
| /* Provide an output buffer for 2 seconds of data. */ | /* Provide an output buffer for 2 seconds of data. */ | ||||
| bs = MIN(tp->t_termios.c_ospeed / 5, TTYBUF_MAX); | bs = MIN(tp->t_termios.c_ospeed / 5, TTYBUF_MAX); | ||||
| error = ttyoutq_setsize(&tp->t_outq, tp, bs); | error = ttyoutq_setsize(&tp->t_outq, tp, bs); | ||||
| if (error != 0) | if (error != 0) | ||||
| return (error); | goto out; | ||||
| /* Set low watermark at 10% (when 90% is available). */ | /* Set low watermark at 10% (when 90% is available). */ | ||||
| tp->t_outlow = (ttyoutq_getallocatedsize(&tp->t_outq) * 9) / 10; | tp->t_outlow = (ttyoutq_getallocatedsize(&tp->t_outq) * 9) / 10; | ||||
| return (0); | out: | ||||
| return (error); | |||||
| } | } | ||||
| static int | static int | ||||
| tty_drain(struct tty *tp, int leaving) | tty_drain(struct tty *tp, int leaving) | ||||
| { | { | ||||
| sbintime_t timeout_at; | sbintime_t timeout_at; | ||||
| size_t bytes; | size_t bytes; | ||||
| int error; | int error; | ||||
| ttydisc_assert_locked(tp); | |||||
| if (ttyhook_hashook(tp, getc_inject)) | if (ttyhook_hashook(tp, getc_inject)) | ||||
| /* buffer is inaccessible */ | /* buffer is inaccessible */ | ||||
| return (0); | return (0); | ||||
| /* | /* | ||||
| * For close(), use the recent historic timeout of "1 second without | * For close(), use the recent historic timeout of "1 second without | ||||
| * making progress". For tcdrain(), use t_drainwait as the timeout, | * making progress". For tcdrain(), use t_drainwait as the timeout, | ||||
| * with zero meaning "no timeout" which gives POSIX behavior. | * with zero meaning "no timeout" which gives POSIX behavior. | ||||
| ▲ Show 20 Lines • Show All 58 Lines • ▼ Show 20 Lines | ttydev_enter(struct tty *tp) | ||||
| return (0); | return (0); | ||||
| } | } | ||||
| static void | static void | ||||
| ttydev_leave(struct tty *tp) | ttydev_leave(struct tty *tp) | ||||
| { | { | ||||
| /* | |||||
| * The locking in this function, like elsewhere, is also a nightmare | |||||
| * that will be corrected when the tty lock gets converted to a | |||||
| * sleepable lock. | |||||
| */ | |||||
| tty_assert_locked(tp); | tty_assert_locked(tp); | ||||
| if (tty_opened(tp) || tp->t_flags & TF_OPENCLOSE) { | if (tty_opened(tp) || tp->t_flags & TF_OPENCLOSE) { | ||||
| /* Device is still opened somewhere. */ | /* Device is still opened somewhere. */ | ||||
| if (ttydisc_lock_owned(tp)) | |||||
| ttydisc_unlock(tp); | |||||
| tty_unlock(tp); | tty_unlock(tp); | ||||
| return; | return; | ||||
| } | } | ||||
| if (!ttydisc_lock_owned(tp)) | |||||
| ttydisc_lock(tp); | |||||
| tp->t_flags |= TF_OPENCLOSE; | tp->t_flags |= TF_OPENCLOSE; | ||||
| /* Remove console TTY. */ | /* Remove console TTY. */ | ||||
| constty_clear(tp); | constty_clear(tp); | ||||
| /* Drain any output. */ | /* | ||||
| * Drain any output. Pick up ttydisc_lock in advance of tty_drain, | |||||
| * which will perhaps want to sleep and can't handle having both locks. | |||||
| */ | |||||
| if (!tty_gone(tp)) | if (!tty_gone(tp)) | ||||
| tty_drain(tp, 1); | tty_drain(tp, 1); | ||||
| ttydisc_close(tp); | ttydisc_close(tp); | ||||
| /* Free i/o queues now since they might be large. */ | /* Free i/o queues now since they might be large. */ | ||||
| ttyinq_free(&tp->t_inq); | ttyinq_free(&tp->t_inq); | ||||
| tp->t_inlow = 0; | tp->t_inlow = 0; | ||||
| ttyoutq_free(&tp->t_outq); | ttyoutq_free(&tp->t_outq); | ||||
| tp->t_outlow = 0; | tp->t_outlow = 0; | ||||
| if (!tty_gone(tp)) | if (!tty_gone(tp)) | ||||
| ttydevsw_close(tp); | ttydevsw_close(tp); | ||||
| tp->t_flags &= ~TF_OPENCLOSE; | tp->t_flags &= ~TF_OPENCLOSE; | ||||
| cv_broadcast(&tp->t_dcdwait); | cv_broadcast(&tp->t_dcdwait); | ||||
| ttydisc_unlock(tp); | |||||
| tty_rel_free(tp); | tty_rel_free(tp); | ||||
| } | } | ||||
| /* | /* | ||||
| * Operations that are exposed through the character device in /dev. | * Operations that are exposed through the character device in /dev. | ||||
| */ | */ | ||||
| static int | static int | ||||
| ttydev_open(struct cdev *dev, int oflags, int devtype __unused, | ttydev_open(struct cdev *dev, int oflags, int devtype __unused, | ||||
| Show All 10 Lines | if (tty_gone(tp)) { | ||||
| tty_unlock(tp); | tty_unlock(tp); | ||||
| return (ENXIO); | return (ENXIO); | ||||
| } | } | ||||
| /* | /* | ||||
| * Block when other processes are currently opening or closing | * Block when other processes are currently opening or closing | ||||
| * the TTY. | * the TTY. | ||||
| */ | */ | ||||
| ttydisc_lock(tp); | |||||
| while (tp->t_flags & TF_OPENCLOSE) { | while (tp->t_flags & TF_OPENCLOSE) { | ||||
| error = tty_wait(tp, &tp->t_dcdwait); | error = tty_wait(tp, &tp->t_dcdwait); | ||||
| if (error != 0) { | if (error != 0) { | ||||
| tty_unlock(tp); | tty_unlock(tp); | ||||
| return (error); | return (error); | ||||
| } | } | ||||
| } | } | ||||
| tp->t_flags |= TF_OPENCLOSE; | tp->t_flags |= TF_OPENCLOSE; | ||||
| ttydisc_unlock(tp); | |||||
| /* | /* | ||||
| * Make sure the "tty" and "cua" device cannot be opened at the | * Make sure the "tty" and "cua" device cannot be opened at the | ||||
| * same time. The console is a "tty" device. | * same time. The console is a "tty" device. | ||||
| */ | */ | ||||
| if (TTY_CALLOUT(tp, dev)) { | if (TTY_CALLOUT(tp, dev)) { | ||||
| if (tp->t_flags & (TF_OPENED_CONS | TF_OPENED_IN)) { | if (tp->t_flags & (TF_OPENED_CONS | TF_OPENED_IN)) { | ||||
| error = EBUSY; | error = EBUSY; | ||||
| goto done; | goto done; | ||||
| } | } | ||||
| } else { | } else { | ||||
| if (tp->t_flags & TF_OPENED_OUT) { | if (tp->t_flags & TF_OPENED_OUT) { | ||||
| error = EBUSY; | error = EBUSY; | ||||
| goto done; | goto done; | ||||
| } | } | ||||
| } | } | ||||
| if (tp->t_flags & TF_EXCLUDE && priv_check(td, PRIV_TTY_EXCLUSIVE)) { | if (tp->t_flags & TF_EXCLUDE && priv_check(td, PRIV_TTY_EXCLUSIVE)) { | ||||
| error = EBUSY; | error = EBUSY; | ||||
| goto done; | goto done; | ||||
| } | } | ||||
| if (!tty_opened(tp)) { | if (!tty_opened(tp)) { | ||||
| /* Set proper termios flags. */ | /* | ||||
| * Set proper termios flags. Further mutations of t_termios | |||||
| * will require the ttydisc lock. | |||||
| */ | |||||
| if (TTY_CALLOUT(tp, dev)) | if (TTY_CALLOUT(tp, dev)) | ||||
| tp->t_termios = tp->t_termios_init_out; | tp->t_termios = tp->t_termios_init_out; | ||||
| else | else | ||||
| tp->t_termios = tp->t_termios_init_in; | tp->t_termios = tp->t_termios_init_in; | ||||
| ttydisc_lock(tp); | |||||
| ttydevsw_param(tp, &tp->t_termios); | ttydevsw_param(tp, &tp->t_termios); | ||||
| /* Prevent modem control on callout devices and /dev/console. */ | /* Prevent modem control on callout devices and /dev/console. */ | ||||
| if (TTY_CALLOUT(tp, dev) || dev == dev_console) | if (TTY_CALLOUT(tp, dev) || dev == dev_console) | ||||
| tp->t_termios.c_cflag |= CLOCAL; | tp->t_termios.c_cflag |= CLOCAL; | ||||
| if ((tp->t_termios.c_cflag & CNO_RTSDTR) == 0) | if ((tp->t_termios.c_cflag & CNO_RTSDTR) == 0) | ||||
| ttydevsw_modem(tp, SER_DTR|SER_RTS, 0); | ttydevsw_modem(tp, SER_DTR|SER_RTS, 0); | ||||
| error = ttydevsw_open(tp); | error = ttydevsw_open(tp); | ||||
| if (error != 0) | if (error != 0) | ||||
| goto done; | goto done; | ||||
| ttydisc_open(tp); | ttydisc_open(tp); | ||||
| error = tty_watermarks(tp); | error = tty_watermarks(tp); | ||||
| /* tty_watermarks dropped the ttydisc lock. */ | |||||
| if (error != 0) | if (error != 0) | ||||
| goto done; | goto done; | ||||
| } else { | |||||
| ttydisc_lock(tp); | |||||
| } | } | ||||
| /* Wait for Carrier Detect. */ | /* Wait for Carrier Detect. */ | ||||
| if ((oflags & O_NONBLOCK) == 0 && | if ((oflags & O_NONBLOCK) == 0 && | ||||
| (tp->t_termios.c_cflag & CLOCAL) == 0) { | (tp->t_termios.c_cflag & CLOCAL) == 0) { | ||||
| while ((ttydevsw_modem(tp, 0, 0) & SER_DCD) == 0) { | while ((ttydevsw_modem(tp, 0, 0) & SER_DCD) == 0) { | ||||
| error = tty_wait(tp, &tp->t_dcdwait); | error = tty_wait(tp, &tp->t_dcdwait); | ||||
| if (error != 0) | if (error != 0) | ||||
| goto done; | goto done; | ||||
| } | } | ||||
| } | } | ||||
| if (dev == dev_console) | if (dev == dev_console) | ||||
| tp->t_flags |= TF_OPENED_CONS; | tp->t_flags |= TF_OPENED_CONS; | ||||
| else if (TTY_CALLOUT(tp, dev)) | else if (TTY_CALLOUT(tp, dev)) | ||||
| tp->t_flags |= TF_OPENED_OUT; | tp->t_flags |= TF_OPENED_OUT; | ||||
| else | else | ||||
| tp->t_flags |= TF_OPENED_IN; | tp->t_flags |= TF_OPENED_IN; | ||||
| MPASS((tp->t_flags & (TF_OPENED_CONS | TF_OPENED_IN)) == 0 || | MPASS((tp->t_flags & (TF_OPENED_CONS | TF_OPENED_IN)) == 0 || | ||||
| (tp->t_flags & TF_OPENED_OUT) == 0); | (tp->t_flags & TF_OPENED_OUT) == 0); | ||||
| done: tp->t_flags &= ~TF_OPENCLOSE; | done: | ||||
| if (!ttydisc_lock_owned(tp)) | |||||
| ttydisc_lock(tp); | |||||
| tp->t_flags &= ~TF_OPENCLOSE; | |||||
| cv_broadcast(&tp->t_dcdwait); | cv_broadcast(&tp->t_dcdwait); | ||||
| ttydev_leave(tp); | ttydev_leave(tp); | ||||
| return (error); | return (error); | ||||
| } | } | ||||
| static int | static int | ||||
| ttydev_close(struct cdev *dev, int fflag, int devtype __unused, | ttydev_close(struct cdev *dev, int fflag, int devtype __unused, | ||||
| struct thread *td) | struct thread *td) | ||||
| { | { | ||||
| struct tty *tp = dev->si_drv1; | struct tty *tp = dev->si_drv1; | ||||
| tty_lock(tp); | tty_lock(tp); | ||||
| ttydisc_lock(tp); | |||||
| /* | /* | ||||
| * Don't actually close the device if it is being used as the | * Don't actually close the device if it is being used as the | ||||
| * console. | * console. | ||||
| */ | */ | ||||
| MPASS((tp->t_flags & (TF_OPENED_CONS | TF_OPENED_IN)) == 0 || | MPASS((tp->t_flags & (TF_OPENED_CONS | TF_OPENED_IN)) == 0 || | ||||
| (tp->t_flags & TF_OPENED_OUT) == 0); | (tp->t_flags & TF_OPENED_OUT) == 0); | ||||
| if (dev == dev_console) | if (dev == dev_console) | ||||
| tp->t_flags &= ~TF_OPENED_CONS; | tp->t_flags &= ~TF_OPENED_CONS; | ||||
| else | else | ||||
| tp->t_flags &= ~(TF_OPENED_IN|TF_OPENED_OUT); | tp->t_flags &= ~(TF_OPENED_IN|TF_OPENED_OUT); | ||||
| if (tp->t_flags & TF_OPENED) { | if (tp->t_flags & TF_OPENED) { | ||||
| ttydisc_unlock(tp); | |||||
| tty_unlock(tp); | tty_unlock(tp); | ||||
| return (0); | return (0); | ||||
| } | } | ||||
| /* If revoking, flush output now to avoid draining it later. */ | /* If revoking, flush output now to avoid draining it later. */ | ||||
| if ((fflag & FREVOKE) != 0) { | if ((fflag & FREVOKE) != 0) { | ||||
| tty_flush(tp, FWRITE); | tty_flush(tp, FWRITE); | ||||
| knlist_delete(&tp->t_inpoll.si_note, td, 1); | knlist_delete(&tp->t_inpoll.si_note, td, 1); | ||||
| Show All 12 Lines | ttydev_close(struct cdev *dev, int fflag, int devtype __unused, | ||||
| return (0); | return (0); | ||||
| } | } | ||||
| static __inline int | static __inline int | ||||
| tty_is_ctty(struct tty *tp, struct proc *p) | tty_is_ctty(struct tty *tp, struct proc *p) | ||||
| { | { | ||||
| tty_assert_locked(tp); | KASSERT(tty_lock_owned(tp) || ttydisc_lock_owned(tp), | ||||
| ("neither ttymtx nor ttydiscmtx owned")); | |||||
| return (p->p_session == tp->t_session && p->p_flag & P_CONTROLT); | return (p->p_session == tp->t_session && p->p_flag & P_CONTROLT); | ||||
| } | } | ||||
| int | int | ||||
| tty_wait_background(struct tty *tp, struct thread *td, int sig) | tty_wait_background(struct tty *tp, struct thread *td, int sig) | ||||
| { | { | ||||
| struct proc *p; | struct proc *p; | ||||
| struct pgrp *pg; | struct pgrp *pg; | ||||
| ksiginfo_t ksi; | ksiginfo_t ksi; | ||||
| int error; | int error; | ||||
| MPASS(sig == SIGTTIN || sig == SIGTTOU); | MPASS(sig == SIGTTIN || sig == SIGTTOU); | ||||
| tty_assert_locked(tp); | ttydisc_assert_locked(tp); | ||||
| p = td->td_proc; | p = td->td_proc; | ||||
| for (;;) { | for (;;) { | ||||
| pg = p->p_pgrp; | pg = p->p_pgrp; | ||||
| PGRP_LOCK(pg); | PGRP_LOCK(pg); | ||||
| PROC_LOCK(p); | PROC_LOCK(p); | ||||
| /* | /* | ||||
| ▲ Show 20 Lines • Show All 48 Lines • ▼ Show 20 Lines | if (sig != 0) { | ||||
| ksi.ksi_code = SI_KERNEL; | ksi.ksi_code = SI_KERNEL; | ||||
| ksi.ksi_signo = sig; | ksi.ksi_signo = sig; | ||||
| sig = 0; | sig = 0; | ||||
| } | } | ||||
| pgsignal(pg, ksi.ksi_signo, 1, &ksi); | pgsignal(pg, ksi.ksi_signo, 1, &ksi); | ||||
| PGRP_UNLOCK(pg); | PGRP_UNLOCK(pg); | ||||
| error = tty_wait(tp, &tp->t_bgwait); | error = tty_wait(tp, &tp->t_bgwait); | ||||
kevans: This is also fixed... this patch grew organically as I worked out the intricacies of tty bits… | |||||
| if (error) | if (error) | ||||
| return (error); | return (error); | ||||
| } | } | ||||
| } | } | ||||
| static int | static int | ||||
| ttydev_read(struct cdev *dev, struct uio *uio, int ioflag) | ttydev_read(struct cdev *dev, struct uio *uio, int ioflag) | ||||
| { | { | ||||
| struct tty *tp = dev->si_drv1; | struct tty *tp = dev->si_drv1; | ||||
| int error; | int error; | ||||
| /* | |||||
| * We'll pick up the tty lock just long enough to ensure we're alive and | |||||
| * well, then we'll pick up the ttydisc lock and drop the tty lock -- we | |||||
| * won't be needing it for the rest of this as we serialize I/O on the | |||||
| * ttydisc lock. | |||||
| */ | |||||
| error = ttydev_enter(tp); | error = ttydev_enter(tp); | ||||
| if (error) | if (error) | ||||
| goto done; | goto done; | ||||
| ttydisc_lock(tp); | |||||
| error = ttydisc_read(tp, uio, ioflag); | error = ttydisc_read(tp, uio, ioflag); | ||||
| ttydisc_unlock(tp); | |||||
| tty_unlock(tp); | tty_unlock(tp); | ||||
| /* | /* | ||||
| * The read() call should not throw an error when the device is | * The read() call should not throw an error when the device is | ||||
| * being destroyed. Silently convert it to an EOF. | * being destroyed. Silently convert it to an EOF. | ||||
| */ | */ | ||||
| done: if (error == ENXIO) | done: if (error == ENXIO) | ||||
| error = 0; | error = 0; | ||||
| return (error); | return (error); | ||||
| } | } | ||||
| static int | static int | ||||
| ttydev_write(struct cdev *dev, struct uio *uio, int ioflag) | ttydev_write(struct cdev *dev, struct uio *uio, int ioflag) | ||||
| { | { | ||||
| struct tty *tp = dev->si_drv1; | struct tty *tp = dev->si_drv1; | ||||
| int defer, error; | int defer, error; | ||||
| error = ttydev_enter(tp); | error = ttydev_enter(tp); | ||||
| if (error) | if (error) | ||||
| return (error); | return (error); | ||||
| ttydisc_lock(tp); | |||||
| if (tp->t_termios.c_lflag & TOSTOP) { | if (tp->t_termios.c_lflag & TOSTOP) { | ||||
| error = tty_wait_background(tp, curthread, SIGTTOU); | error = tty_wait_background(tp, curthread, SIGTTOU); | ||||
| if (error) | if (error) | ||||
| goto done; | goto done; | ||||
| } | } | ||||
| if (ioflag & IO_NDELAY && tp->t_flags & TF_BUSY_OUT) { | if (ioflag & IO_NDELAY && tp->t_flags & TF_BUSY_OUT) { | ||||
| Show All 10 Lines | if (ioflag & IO_NDELAY && tp->t_flags & TF_BUSY_OUT) { | ||||
| tp->t_flags |= TF_BUSY_OUT; | tp->t_flags |= TF_BUSY_OUT; | ||||
| defer = sigdeferstop(SIGDEFERSTOP_ERESTART); | defer = sigdeferstop(SIGDEFERSTOP_ERESTART); | ||||
| error = ttydisc_write(tp, uio, ioflag); | error = ttydisc_write(tp, uio, ioflag); | ||||
| sigallowstop(defer); | sigallowstop(defer); | ||||
| tp->t_flags &= ~TF_BUSY_OUT; | tp->t_flags &= ~TF_BUSY_OUT; | ||||
| cv_signal(&tp->t_outserwait); | cv_signal(&tp->t_outserwait); | ||||
| } | } | ||||
| done: tty_unlock(tp); | done: ttydisc_unlock(tp); | ||||
| tty_unlock(tp); | |||||
| return (error); | return (error); | ||||
| } | } | ||||
| static int | static int | ||||
| ttydev_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, | ttydev_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, | ||||
| struct thread *td) | struct thread *td) | ||||
| { | { | ||||
| struct tty *tp = dev->si_drv1; | struct tty *tp = dev->si_drv1; | ||||
| Show All 34 Lines | #ifdef COMPAT_43TTY | ||||
| case TIOCSETN: | case TIOCSETN: | ||||
| case TIOCSETP: | case TIOCSETP: | ||||
| case TIOCSLTC: | case TIOCSLTC: | ||||
| #endif /* COMPAT_43TTY */ | #endif /* COMPAT_43TTY */ | ||||
| /* | /* | ||||
| * If the ioctl() causes the TTY to be modified, let it | * If the ioctl() causes the TTY to be modified, let it | ||||
| * wait in the background. | * wait in the background. | ||||
| */ | */ | ||||
| ttydisc_lock(tp); | |||||
| error = tty_wait_background(tp, curthread, SIGTTOU); | error = tty_wait_background(tp, curthread, SIGTTOU); | ||||
| ttydisc_unlock(tp); | |||||
| if (error) | if (error) | ||||
| goto done; | goto done; | ||||
| } | } | ||||
| if (cmd == TIOCSETA || cmd == TIOCSETAW || cmd == TIOCSETAF) { | if (cmd == TIOCSETA || cmd == TIOCSETAW || cmd == TIOCSETAF) { | ||||
| struct termios *old = &tp->t_termios; | struct termios *old = &tp->t_termios; | ||||
| struct termios *new = (struct termios *)data; | struct termios *new = (struct termios *)data; | ||||
| struct termios *lock = TTY_CALLOUT(tp, dev) ? | struct termios *lock = TTY_CALLOUT(tp, dev) ? | ||||
| Show All 32 Lines | |||||
| { | { | ||||
| struct tty *tp = dev->si_drv1; | struct tty *tp = dev->si_drv1; | ||||
| int error, revents = 0; | int error, revents = 0; | ||||
| error = ttydev_enter(tp); | error = ttydev_enter(tp); | ||||
| if (error) | if (error) | ||||
| return ((events & (POLLIN|POLLRDNORM)) | POLLHUP); | return ((events & (POLLIN|POLLRDNORM)) | POLLHUP); | ||||
| ttydisc_lock(tp); | |||||
| if (events & (POLLIN|POLLRDNORM)) { | if (events & (POLLIN|POLLRDNORM)) { | ||||
| /* See if we can read something. */ | /* See if we can read something. */ | ||||
| if (ttydisc_read_poll(tp) > 0) | if (ttydisc_read_poll(tp) > 0) | ||||
| revents |= events & (POLLIN|POLLRDNORM); | revents |= events & (POLLIN|POLLRDNORM); | ||||
| } | } | ||||
| if (tp->t_flags & TF_ZOMBIE) { | if (tp->t_flags & TF_ZOMBIE) { | ||||
| /* Hangup flag on zombie state. */ | /* Hangup flag on zombie state. */ | ||||
| revents |= POLLHUP; | revents |= POLLHUP; | ||||
| } else if (events & (POLLOUT|POLLWRNORM)) { | } else if (events & (POLLOUT|POLLWRNORM)) { | ||||
| /* See if we can write something. */ | /* See if we can write something. */ | ||||
| if (ttydisc_write_poll(tp) > 0) | if (ttydisc_write_poll(tp) > 0) | ||||
| revents |= events & (POLLOUT|POLLWRNORM); | revents |= events & (POLLOUT|POLLWRNORM); | ||||
| } | } | ||||
| ttydisc_unlock(tp); | |||||
| if (revents == 0) { | if (revents == 0) { | ||||
| if (events & (POLLIN|POLLRDNORM)) | if (events & (POLLIN|POLLRDNORM)) | ||||
| selrecord(td, &tp->t_inpoll); | selrecord(td, &tp->t_inpoll); | ||||
| if (events & (POLLOUT|POLLWRNORM)) | if (events & (POLLOUT|POLLWRNORM)) | ||||
| selrecord(td, &tp->t_outpoll); | selrecord(td, &tp->t_outpoll); | ||||
| } | } | ||||
| Show All 32 Lines | tty_kqops_read_detach(struct knote *kn) | ||||
| knlist_remove(&tp->t_inpoll.si_note, kn, 0); | knlist_remove(&tp->t_inpoll.si_note, kn, 0); | ||||
| } | } | ||||
| static int | static int | ||||
| tty_kqops_read_event(struct knote *kn, long hint __unused) | tty_kqops_read_event(struct knote *kn, long hint __unused) | ||||
| { | { | ||||
| struct tty *tp = kn->kn_hook; | struct tty *tp = kn->kn_hook; | ||||
| tty_assert_locked(tp); | ttydisc_assert_locked(tp); | ||||
| if (tty_gone(tp) || tp->t_flags & TF_ZOMBIE) { | if (tty_gone(tp) || tp->t_flags & TF_ZOMBIE) { | ||||
| kn->kn_flags |= EV_EOF; | kn->kn_flags |= EV_EOF; | ||||
| return (1); | return (1); | ||||
| } else { | } else { | ||||
| kn->kn_data = ttydisc_read_poll(tp); | kn->kn_data = ttydisc_read_poll(tp); | ||||
| return (kn->kn_data > 0); | return (kn->kn_data > 0); | ||||
| } | } | ||||
| } | } | ||||
| static void | static void | ||||
| tty_kqops_write_detach(struct knote *kn) | tty_kqops_write_detach(struct knote *kn) | ||||
| { | { | ||||
| struct tty *tp = kn->kn_hook; | struct tty *tp = kn->kn_hook; | ||||
| knlist_remove(&tp->t_outpoll.si_note, kn, 0); | knlist_remove(&tp->t_outpoll.si_note, kn, 0); | ||||
| } | } | ||||
| static int | static int | ||||
| tty_kqops_write_event(struct knote *kn, long hint __unused) | tty_kqops_write_event(struct knote *kn, long hint __unused) | ||||
| { | { | ||||
| struct tty *tp = kn->kn_hook; | struct tty *tp = kn->kn_hook; | ||||
| tty_assert_locked(tp); | ttydisc_assert_locked(tp); | ||||
| if (tty_gone(tp)) { | if (tty_gone(tp)) { | ||||
| kn->kn_flags |= EV_EOF; | kn->kn_flags |= EV_EOF; | ||||
| return (1); | return (1); | ||||
| } else { | } else { | ||||
| kn->kn_data = ttydisc_write_poll(tp); | kn->kn_data = ttydisc_write_poll(tp); | ||||
| return (kn->kn_data > 0); | return (kn->kn_data > 0); | ||||
| } | } | ||||
| Show All 20 Lines | ttydev_kqfilter(struct cdev *dev, struct knote *kn) | ||||
| error = ttydev_enter(tp); | error = ttydev_enter(tp); | ||||
| if (error) | if (error) | ||||
| return (error); | return (error); | ||||
| switch (kn->kn_filter) { | switch (kn->kn_filter) { | ||||
| case EVFILT_READ: | case EVFILT_READ: | ||||
| kn->kn_hook = tp; | kn->kn_hook = tp; | ||||
| kn->kn_fop = &tty_kqops_read; | kn->kn_fop = &tty_kqops_read; | ||||
| ttydisc_lock(tp); | |||||
| knlist_add(&tp->t_inpoll.si_note, kn, 1); | knlist_add(&tp->t_inpoll.si_note, kn, 1); | ||||
| ttydisc_unlock(tp); | |||||
| break; | break; | ||||
| case EVFILT_WRITE: | case EVFILT_WRITE: | ||||
| kn->kn_hook = tp; | kn->kn_hook = tp; | ||||
| kn->kn_fop = &tty_kqops_write; | kn->kn_fop = &tty_kqops_write; | ||||
| ttydisc_lock(tp); | |||||
| knlist_add(&tp->t_outpoll.si_note, kn, 1); | knlist_add(&tp->t_outpoll.si_note, kn, 1); | ||||
| ttydisc_unlock(tp); | |||||
| break; | break; | ||||
| default: | default: | ||||
| error = EINVAL; | error = EINVAL; | ||||
| break; | break; | ||||
| } | } | ||||
| tty_unlock(tp); | tty_unlock(tp); | ||||
| return (error); | return (error); | ||||
| ▲ Show 20 Lines • Show All 253 Lines • ▼ Show 20 Lines | |||||
| struct tty * | struct tty * | ||||
| tty_alloc(struct ttydevsw *tsw, void *sc) | tty_alloc(struct ttydevsw *tsw, void *sc) | ||||
| { | { | ||||
| return (tty_alloc_mutex(tsw, sc, NULL)); | return (tty_alloc_mutex(tsw, sc, NULL)); | ||||
| } | } | ||||
| struct tty * | struct tty * | ||||
| tty_alloc_mutex(struct ttydevsw *tsw, void *sc, struct mtx *mutex) | tty_alloc_mutex(struct ttydevsw *tsw, void *sc, struct mtx *discmtx) | ||||
| { | { | ||||
| struct tty *tp; | struct tty *tp; | ||||
| /* Make sure the driver defines all routines. */ | /* Make sure the driver defines all routines. */ | ||||
| #define PATCH_FUNC(x) do { \ | #define PATCH_FUNC(x) do { \ | ||||
| if (tsw->tsw_ ## x == NULL) \ | if (tsw->tsw_ ## x == NULL) \ | ||||
| tsw->tsw_ ## x = ttydevsw_def ## x; \ | tsw->tsw_ ## x = ttydevsw_def ## x; \ | ||||
| } while (0) | } while (0) | ||||
| Show All 22 Lines | #undef PATCH_FUNC | ||||
| tty_init_termios(tp); | tty_init_termios(tp); | ||||
| cv_init(&tp->t_inwait, "ttyin"); | cv_init(&tp->t_inwait, "ttyin"); | ||||
| cv_init(&tp->t_outwait, "ttyout"); | cv_init(&tp->t_outwait, "ttyout"); | ||||
| cv_init(&tp->t_outserwait, "ttyosr"); | cv_init(&tp->t_outserwait, "ttyosr"); | ||||
| cv_init(&tp->t_bgwait, "ttybg"); | cv_init(&tp->t_bgwait, "ttybg"); | ||||
| cv_init(&tp->t_dcdwait, "ttydcd"); | cv_init(&tp->t_dcdwait, "ttydcd"); | ||||
| /* Allow drivers to use a custom mutex to lock the TTY. */ | if (discmtx == &Giant) { | ||||
| if (mutex != NULL) { | /* | ||||
| tp->t_mtx = mutex; | * This should go away when the syscons/Giant problem is | ||||
| * resolved. For now, we still have the one driver locked by | |||||
| * Giant, so we have to special-case it and maintain the old | |||||
| * behavior. It will get a separate discmtx and maintain Giant | |||||
| * as the main tty lock. | |||||
| */ | |||||
| tp->t_mtx = discmtx; | |||||
| discmtx = NULL; | |||||
| } else { | } else { | ||||
| tp->t_mtx = &tp->t_mtxobj; | sx_init(&tp->t_sxobj, "ttysx"); | ||||
| mtx_init(&tp->t_mtxobj, "ttymtx", NULL, MTX_DEF); | |||||
| } | } | ||||
| knlist_init_mtx(&tp->t_inpoll.si_note, tp->t_mtx); | if (discmtx != NULL) { | ||||
| knlist_init_mtx(&tp->t_outpoll.si_note, tp->t_mtx); | tp->t_discmtx = discmtx; | ||||
| } else { | |||||
| tp->t_discmtx = &tp->t_discmtxobj; | |||||
| mtx_init(&tp->t_discmtxobj, "ttydiscmtx", NULL, MTX_DEF); | |||||
| } | |||||
| knlist_init_mtx(&tp->t_inpoll.si_note, tp->t_discmtx); | |||||
| knlist_init_mtx(&tp->t_outpoll.si_note, tp->t_discmtx); | |||||
| return (tp); | return (tp); | ||||
| } | } | ||||
| static void | static void | ||||
| tty_dealloc(void *arg) | tty_dealloc(void *arg) | ||||
| { | { | ||||
| struct tty *tp = arg; | struct tty *tp = arg; | ||||
| Show All 14 Lines | tty_dealloc(void *arg) | ||||
| knlist_destroy(&tp->t_outpoll.si_note); | knlist_destroy(&tp->t_outpoll.si_note); | ||||
| cv_destroy(&tp->t_inwait); | cv_destroy(&tp->t_inwait); | ||||
| cv_destroy(&tp->t_outwait); | cv_destroy(&tp->t_outwait); | ||||
| cv_destroy(&tp->t_bgwait); | cv_destroy(&tp->t_bgwait); | ||||
| cv_destroy(&tp->t_dcdwait); | cv_destroy(&tp->t_dcdwait); | ||||
| cv_destroy(&tp->t_outserwait); | cv_destroy(&tp->t_outserwait); | ||||
| if (tp->t_mtx == &tp->t_mtxobj) | /* We didn't bother initializing the sx if we were given Giant. */ | ||||
| mtx_destroy(&tp->t_mtxobj); | if (tp->t_mtx == NULL) | ||||
| sx_destroy(&tp->t_sxobj); | |||||
| if (tp->t_discmtx == &tp->t_discmtxobj) | |||||
| mtx_destroy(&tp->t_discmtxobj); | |||||
| ttydevsw_free(tp); | ttydevsw_free(tp); | ||||
| free(tp, M_TTY); | free(tp, M_TTY); | ||||
| } | } | ||||
| static void | static void | ||||
| tty_rel_free(struct tty *tp) | tty_rel_free(struct tty *tp) | ||||
| { | { | ||||
| struct cdev *dev; | struct cdev *dev; | ||||
| Show All 26 Lines | |||||
| void | void | ||||
| tty_rel_pgrp(struct tty *tp, struct pgrp *pg) | tty_rel_pgrp(struct tty *tp, struct pgrp *pg) | ||||
| { | { | ||||
| MPASS(tp->t_sessioncnt > 0); | MPASS(tp->t_sessioncnt > 0); | ||||
| tty_assert_locked(tp); | tty_assert_locked(tp); | ||||
| ttydisc_lock(tp); | |||||
| if (tp->t_pgrp == pg) | if (tp->t_pgrp == pg) | ||||
| tp->t_pgrp = NULL; | tp->t_pgrp = NULL; | ||||
| ttydisc_unlock(tp); | |||||
| tty_unlock(tp); | tty_unlock(tp); | ||||
| } | } | ||||
| void | void | ||||
| tty_rel_sess(struct tty *tp, struct session *sess) | tty_rel_sess(struct tty *tp, struct session *sess) | ||||
| { | { | ||||
| tty_assert_locked(tp); | |||||
| MPASS(tp->t_sessioncnt > 0); | MPASS(tp->t_sessioncnt > 0); | ||||
| ttydisc_lock(tp); | |||||
| /* Current session has left. */ | /* Current session has left. */ | ||||
| if (tp->t_session == sess) { | if (tp->t_session == sess) { | ||||
| tp->t_session = NULL; | tp->t_session = NULL; | ||||
| MPASS(tp->t_pgrp == NULL); | MPASS(tp->t_pgrp == NULL); | ||||
| } | } | ||||
| tp->t_sessioncnt--; | tp->t_sessioncnt--; | ||||
| ttydisc_unlock(tp); | |||||
| tty_rel_free(tp); | tty_rel_free(tp); | ||||
| } | } | ||||
| void | void | ||||
| tty_rel_gone(struct tty *tp) | tty_rel_gone(struct tty *tp) | ||||
| { | { | ||||
| tty_assert_locked(tp); | tty_assert_locked(tp); | ||||
| MPASS(!tty_gone(tp)); | MPASS(!tty_gone(tp)); | ||||
| if (!ttydisc_lock_owned(tp)) | |||||
| ttydisc_lock(tp); | |||||
| /* Simulate carrier removal. */ | /* Simulate carrier removal. */ | ||||
| ttydisc_modem(tp, 0); | ttydisc_modem(tp, 0); | ||||
| /* Wake up all blocked threads. */ | /* Wake up all blocked threads. */ | ||||
| tty_wakeup(tp, FREAD|FWRITE); | tty_wakeup(tp, FREAD|FWRITE); | ||||
| cv_broadcast(&tp->t_bgwait); | cv_broadcast(&tp->t_bgwait); | ||||
| cv_broadcast(&tp->t_dcdwait); | cv_broadcast(&tp->t_dcdwait); | ||||
| tp->t_flags |= TF_GONE; | tp->t_flags |= TF_GONE; | ||||
| ttydisc_unlock(tp); | |||||
| tty_rel_free(tp); | tty_rel_free(tp); | ||||
| } | } | ||||
| static int | static int | ||||
| tty_drop_ctty(struct tty *tp, struct proc *p) | tty_drop_ctty(struct tty *tp, struct proc *p) | ||||
| { | { | ||||
| struct session *session; | struct session *session; | ||||
| struct vnode *vp; | struct vnode *vp; | ||||
| Show All 25 Lines | if (session->s_ttyp == NULL || session->s_ttyp != tp) { | ||||
| return (ENOTTY); | return (ENOTTY); | ||||
| } | } | ||||
| if (!SESS_LEADER(p)) { | if (!SESS_LEADER(p)) { | ||||
| sx_xunlock(&proctree_lock); | sx_xunlock(&proctree_lock); | ||||
| return (EPERM); | return (EPERM); | ||||
| } | } | ||||
| ttydisc_lock(tp); | |||||
| tp->t_sessioncnt--; | |||||
| ttydisc_unlock(tp); | |||||
| PROC_LOCK(p); | PROC_LOCK(p); | ||||
| SESS_LOCK(session); | SESS_LOCK(session); | ||||
| vp = session->s_ttyvp; | vp = session->s_ttyvp; | ||||
| session->s_ttyp = NULL; | session->s_ttyp = NULL; | ||||
| session->s_ttyvp = NULL; | session->s_ttyvp = NULL; | ||||
| session->s_ttydp = NULL; | session->s_ttydp = NULL; | ||||
| SESS_UNLOCK(session); | SESS_UNLOCK(session); | ||||
| tp->t_sessioncnt--; | |||||
| p->p_flag &= ~P_CONTROLT; | p->p_flag &= ~P_CONTROLT; | ||||
| PROC_UNLOCK(p); | PROC_UNLOCK(p); | ||||
| sx_xunlock(&proctree_lock); | sx_xunlock(&proctree_lock); | ||||
| /* | /* | ||||
| * If we did have a vnode, release our reference. Ordinarily we manage | * If we did have a vnode, release our reference. Ordinarily we manage | ||||
| * these at the devfs layer, but we can't necessarily know that we were | * these at the devfs layer, but we can't necessarily know that we were | ||||
| * invoked on the vnode referenced in the session (i.e. the vnode we | * invoked on the vnode referenced in the session (i.e. the vnode we | ||||
| ▲ Show 20 Lines • Show All 222 Lines • ▼ Show 20 Lines | |||||
| */ | */ | ||||
| void | void | ||||
| tty_signal_sessleader(struct tty *tp, int sig) | tty_signal_sessleader(struct tty *tp, int sig) | ||||
| { | { | ||||
| struct proc *p; | struct proc *p; | ||||
| struct session *s; | struct session *s; | ||||
| tty_assert_locked(tp); | ttydisc_assert_locked(tp); | ||||
| MPASS(sig >= 1 && sig < NSIG); | MPASS(sig >= 1 && sig < NSIG); | ||||
| /* Make signals start output again. */ | /* Make signals start output again. */ | ||||
| tp->t_flags &= ~TF_STOPPED; | tp->t_flags &= ~TF_STOPPED; | ||||
| tp->t_termios.c_lflag &= ~FLUSHO; | tp->t_termios.c_lflag &= ~FLUSHO; | ||||
| /* | /* | ||||
| * Load s_leader exactly once to avoid race where s_leader is | * Load s_leader exactly once to avoid race where s_leader is | ||||
| Show All 9 Lines | tty_signal_sessleader(struct tty *tp, int sig) | ||||
| } | } | ||||
| } | } | ||||
| void | void | ||||
| tty_signal_pgrp(struct tty *tp, int sig) | tty_signal_pgrp(struct tty *tp, int sig) | ||||
| { | { | ||||
| ksiginfo_t ksi; | ksiginfo_t ksi; | ||||
| tty_assert_locked(tp); | ttydisc_assert_locked(tp); | ||||
| MPASS(sig >= 1 && sig < NSIG); | MPASS(sig >= 1 && sig < NSIG); | ||||
| /* Make signals start output again. */ | /* Make signals start output again. */ | ||||
| tp->t_flags &= ~TF_STOPPED; | tp->t_flags &= ~TF_STOPPED; | ||||
| tp->t_termios.c_lflag &= ~FLUSHO; | tp->t_termios.c_lflag &= ~FLUSHO; | ||||
| if (sig == SIGINFO && !(tp->t_termios.c_lflag & NOKERNINFO)) | if (sig == SIGINFO && !(tp->t_termios.c_lflag & NOKERNINFO)) | ||||
| tty_info(tp); | tty_info(tp); | ||||
| if (tp->t_pgrp != NULL) { | if (tp->t_pgrp != NULL) { | ||||
| ksiginfo_init(&ksi); | ksiginfo_init(&ksi); | ||||
| ksi.ksi_signo = sig; | ksi.ksi_signo = sig; | ||||
| ksi.ksi_code = SI_KERNEL; | ksi.ksi_code = SI_KERNEL; | ||||
| PGRP_LOCK(tp->t_pgrp); | PGRP_LOCK(tp->t_pgrp); | ||||
| pgsignal(tp->t_pgrp, sig, 1, &ksi); | pgsignal(tp->t_pgrp, sig, 1, &ksi); | ||||
| PGRP_UNLOCK(tp->t_pgrp); | PGRP_UNLOCK(tp->t_pgrp); | ||||
| } | } | ||||
| } | } | ||||
| void | void | ||||
| tty_wakeup(struct tty *tp, int flags) | tty_wakeup(struct tty *tp, int flags) | ||||
| { | { | ||||
| ttydisc_assert_locked(tp); | |||||
| if (tp->t_flags & TF_ASYNC && tp->t_sigio != NULL) | if (tp->t_flags & TF_ASYNC && tp->t_sigio != NULL) | ||||
| pgsigio(&tp->t_sigio, SIGIO, (tp->t_session != NULL)); | pgsigio(&tp->t_sigio, SIGIO, (tp->t_session != NULL)); | ||||
| if (flags & FWRITE) { | if (flags & FWRITE) { | ||||
| cv_broadcast(&tp->t_outwait); | cv_broadcast(&tp->t_outwait); | ||||
| selwakeup(&tp->t_outpoll); | selwakeup(&tp->t_outpoll); | ||||
| KNOTE_LOCKED(&tp->t_outpoll.si_note, 0); | KNOTE_LOCKED(&tp->t_outpoll.si_note, 0); | ||||
| } | } | ||||
| if (flags & FREAD) { | if (flags & FREAD) { | ||||
| cv_broadcast(&tp->t_inwait); | cv_broadcast(&tp->t_inwait); | ||||
| selwakeup(&tp->t_inpoll); | selwakeup(&tp->t_inpoll); | ||||
| KNOTE_LOCKED(&tp->t_inpoll.si_note, 0); | KNOTE_LOCKED(&tp->t_inpoll.si_note, 0); | ||||
| } | } | ||||
| } | } | ||||
| int | int | ||||
| tty_wait(struct tty *tp, struct cv *cv) | tty_wait(struct tty *tp, struct cv *cv) | ||||
| { | { | ||||
| int error; | int error; | ||||
| int revokecnt = tp->t_revokecnt; | int revokecnt = tp->t_revokecnt; | ||||
| bool locktty; | |||||
| tty_lock_assert(tp, MA_OWNED|MA_NOTRECURSED); | ttydisc_lock_assert(tp, MA_OWNED | MA_NOTRECURSED); | ||||
| MPASS(!tty_gone(tp)); | MPASS(!tty_gone(tp)); | ||||
| error = cv_wait_sig(cv, tp->t_mtx); | locktty = tty_lock_owned(tp); | ||||
| if (locktty) | |||||
| tty_unlock(tp); | |||||
| error = cv_wait_sig(cv, ttydisc_getlock(tp)); | |||||
| /* If we had the tty lock coming in, relock it. */ | |||||
| if (locktty) { | |||||
| ttydisc_unlock(tp); | |||||
| tty_lock(tp); | |||||
| ttydisc_lock(tp); | |||||
| } | |||||
| /* Bail out when the device slipped away. */ | /* Bail out when the device slipped away. */ | ||||
| if (tty_gone(tp)) | if (tty_gone(tp)) | ||||
| return (ENXIO); | return (ENXIO); | ||||
| /* Restart the system call when we may have been revoked. */ | /* Restart the system call when we may have been revoked. */ | ||||
| if (tp->t_revokecnt != revokecnt) | if (tp->t_revokecnt != revokecnt) | ||||
| return (ERESTART); | return (ERESTART); | ||||
| return (error); | return (error); | ||||
| } | } | ||||
| int | int | ||||
| tty_timedwait(struct tty *tp, struct cv *cv, int hz) | tty_timedwait(struct tty *tp, struct cv *cv, int hz) | ||||
| { | { | ||||
| int error; | int error; | ||||
| int revokecnt = tp->t_revokecnt; | int revokecnt = tp->t_revokecnt; | ||||
| bool locktty; | |||||
| tty_lock_assert(tp, MA_OWNED|MA_NOTRECURSED); | ttydisc_lock_assert(tp, MA_OWNED | MA_NOTRECURSED); | ||||
| MPASS(!tty_gone(tp)); | MPASS(!tty_gone(tp)); | ||||
| error = cv_timedwait_sig(cv, tp->t_mtx, hz); | locktty = tty_lock_owned(tp); | ||||
| if (locktty) | |||||
| tty_unlock(tp); | |||||
| error = cv_timedwait_sig(cv, ttydisc_getlock(tp), hz); | |||||
| /* If we had the tty lock coming in, relock it. */ | |||||
| if (locktty) { | |||||
| ttydisc_unlock(tp); | |||||
| tty_lock(tp); | |||||
| ttydisc_lock(tp); | |||||
| } | |||||
| /* Bail out when the device slipped away. */ | /* Bail out when the device slipped away. */ | ||||
| if (tty_gone(tp)) | if (tty_gone(tp)) | ||||
| return (ENXIO); | return (ENXIO); | ||||
| /* Restart the system call when we may have been revoked. */ | /* Restart the system call when we may have been revoked. */ | ||||
| if (tp->t_revokecnt != revokecnt) | if (tp->t_revokecnt != revokecnt) | ||||
| return (ERESTART); | return (ERESTART); | ||||
| return (error); | return (error); | ||||
| } | } | ||||
| void | void | ||||
| tty_flush(struct tty *tp, int flags) | tty_flush(struct tty *tp, int flags) | ||||
| { | { | ||||
| ttydisc_assert_locked(tp); | |||||
| if (flags & FWRITE) { | if (flags & FWRITE) { | ||||
| tp->t_flags &= ~TF_HIWAT_OUT; | tp->t_flags &= ~TF_HIWAT_OUT; | ||||
| ttyoutq_flush(&tp->t_outq); | ttyoutq_flush(&tp->t_outq); | ||||
| tty_wakeup(tp, FWRITE); | tty_wakeup(tp, FWRITE); | ||||
| if (!tty_gone(tp)) { | if (!tty_gone(tp)) { | ||||
| ttydevsw_outwakeup(tp); | ttydevsw_outwakeup(tp); | ||||
| ttydevsw_pktnotify(tp, TIOCPKT_FLUSHWRITE); | ttydevsw_pktnotify(tp, TIOCPKT_FLUSHWRITE); | ||||
| } | } | ||||
| } | } | ||||
| if (flags & FREAD) { | if (flags & FREAD) { | ||||
| tty_hiwat_in_unblock(tp); | tty_hiwat_in_unblock(tp); | ||||
| ttyinq_flush(&tp->t_inq); | ttyinq_flush(&tp->t_inq); | ||||
| tty_wakeup(tp, FREAD); | tty_wakeup(tp, FREAD); | ||||
| if (!tty_gone(tp)) { | if (!tty_gone(tp)) { | ||||
| ttydevsw_inwakeup(tp); | ttydevsw_inwakeup(tp); | ||||
| ttydevsw_pktnotify(tp, TIOCPKT_FLUSHREAD); | ttydevsw_pktnotify(tp, TIOCPKT_FLUSHREAD); | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| void | void | ||||
| tty_set_winsize(struct tty *tp, const struct winsize *wsz) | tty_set_winsize(struct tty *tp, const struct winsize *wsz) | ||||
| { | { | ||||
| ttydisc_assert_unlocked(tp); | |||||
| tty_assert_locked(tp); | |||||
| if (memcmp(&tp->t_winsize, wsz, sizeof(*wsz)) == 0) | if (memcmp(&tp->t_winsize, wsz, sizeof(*wsz)) == 0) | ||||
| return; | return; | ||||
| tp->t_winsize = *wsz; | tp->t_winsize = *wsz; | ||||
| ttydisc_lock(tp); | |||||
| tty_signal_pgrp(tp, SIGWINCH); | tty_signal_pgrp(tp, SIGWINCH); | ||||
| ttydisc_unlock(tp); | |||||
| } | } | ||||
| static int | static int | ||||
| tty_generic_ioctl(struct tty *tp, u_long cmd, void *data, int fflag, | tty_generic_ioctl(struct tty *tp, u_long cmd, void *data, int fflag, | ||||
| struct thread *td) | struct thread *td) | ||||
| { | { | ||||
| int error; | int error; | ||||
| switch (cmd) { | switch (cmd) { | ||||
| /* | /* | ||||
| * Modem commands. | * Modem commands. | ||||
| * The SER_* and TIOCM_* flags are the same, but one bit | * The SER_* and TIOCM_* flags are the same, but one bit | ||||
| * shifted. I don't know why. | * shifted. I don't know why. | ||||
| */ | */ | ||||
| case TIOCSDTR: | case TIOCSDTR: | ||||
| ttydisc_lock(tp); | |||||
| ttydevsw_modem(tp, SER_DTR, 0); | ttydevsw_modem(tp, SER_DTR, 0); | ||||
| ttydisc_unlock(tp); | |||||
| return (0); | return (0); | ||||
| case TIOCCDTR: | case TIOCCDTR: | ||||
| ttydisc_lock(tp); | |||||
| ttydevsw_modem(tp, 0, SER_DTR); | ttydevsw_modem(tp, 0, SER_DTR); | ||||
| ttydisc_unlock(tp); | |||||
| return (0); | return (0); | ||||
| case TIOCMSET: { | case TIOCMSET: { | ||||
| int bits = *(int *)data; | int bits = *(int *)data; | ||||
| ttydisc_lock(tp); | |||||
| ttydevsw_modem(tp, | ttydevsw_modem(tp, | ||||
| (bits & (TIOCM_DTR | TIOCM_RTS)) >> 1, | (bits & (TIOCM_DTR | TIOCM_RTS)) >> 1, | ||||
| ((~bits) & (TIOCM_DTR | TIOCM_RTS)) >> 1); | ((~bits) & (TIOCM_DTR | TIOCM_RTS)) >> 1); | ||||
| ttydisc_unlock(tp); | |||||
| return (0); | return (0); | ||||
| } | } | ||||
| case TIOCMBIS: { | case TIOCMBIS: { | ||||
| int bits = *(int *)data; | int bits = *(int *)data; | ||||
| ttydisc_lock(tp); | |||||
| ttydevsw_modem(tp, (bits & (TIOCM_DTR | TIOCM_RTS)) >> 1, 0); | ttydevsw_modem(tp, (bits & (TIOCM_DTR | TIOCM_RTS)) >> 1, 0); | ||||
| ttydisc_unlock(tp); | |||||
| return (0); | return (0); | ||||
| } | } | ||||
| case TIOCMBIC: { | case TIOCMBIC: { | ||||
| int bits = *(int *)data; | int bits = *(int *)data; | ||||
| ttydisc_lock(tp); | |||||
| ttydevsw_modem(tp, 0, (bits & (TIOCM_DTR | TIOCM_RTS)) >> 1); | ttydevsw_modem(tp, 0, (bits & (TIOCM_DTR | TIOCM_RTS)) >> 1); | ||||
| ttydisc_unlock(tp); | |||||
| return (0); | return (0); | ||||
| } | } | ||||
| case TIOCMGET: | case TIOCMGET: | ||||
| ttydisc_lock(tp); | |||||
| *(int *)data = TIOCM_LE + (ttydevsw_modem(tp, 0, 0) << 1); | *(int *)data = TIOCM_LE + (ttydevsw_modem(tp, 0, 0) << 1); | ||||
| ttydisc_unlock(tp); | |||||
| return (0); | return (0); | ||||
| case FIOASYNC: | case FIOASYNC: | ||||
| if (*(int *)data) | if (*(int *)data) | ||||
| tp->t_flags |= TF_ASYNC; | tp->t_flags |= TF_ASYNC; | ||||
| else | else | ||||
| tp->t_flags &= ~TF_ASYNC; | tp->t_flags &= ~TF_ASYNC; | ||||
| return (0); | return (0); | ||||
| Show All 30 Lines | case TIOCGETA: | ||||
| *(struct termios*)data = tp->t_termios; | *(struct termios*)data = tp->t_termios; | ||||
| return (0); | return (0); | ||||
| case TIOCSETA: | case TIOCSETA: | ||||
| case TIOCSETAW: | case TIOCSETAW: | ||||
| case TIOCSETAF: { | case TIOCSETAF: { | ||||
| struct termios *t = data; | struct termios *t = data; | ||||
| bool canonicalize = false; | bool canonicalize = false; | ||||
| ttydisc_lock(tp); | |||||
| /* | /* | ||||
| * Who makes up these funny rules? According to POSIX, | * Who makes up these funny rules? According to POSIX, | ||||
| * input baud rate is set equal to the output baud rate | * input baud rate is set equal to the output baud rate | ||||
| * when zero. | * when zero. | ||||
| */ | */ | ||||
| if (t->c_ispeed == 0) | if (t->c_ispeed == 0) | ||||
| t->c_ispeed = t->c_ospeed; | t->c_ispeed = t->c_ospeed; | ||||
| /* Discard any unsupported bits. */ | /* Discard any unsupported bits. */ | ||||
| t->c_iflag &= TTYSUP_IFLAG; | t->c_iflag &= TTYSUP_IFLAG; | ||||
| t->c_oflag &= TTYSUP_OFLAG; | t->c_oflag &= TTYSUP_OFLAG; | ||||
| t->c_lflag &= TTYSUP_LFLAG; | t->c_lflag &= TTYSUP_LFLAG; | ||||
| t->c_cflag &= TTYSUP_CFLAG; | t->c_cflag &= TTYSUP_CFLAG; | ||||
| /* Set terminal flags through tcsetattr(). */ | /* Set terminal flags through tcsetattr(). */ | ||||
| if (cmd == TIOCSETAW || cmd == TIOCSETAF) { | if (cmd == TIOCSETAW || cmd == TIOCSETAF) { | ||||
| error = tty_drain(tp, 0); | error = tty_drain(tp, 0); | ||||
| if (error) | if (error) { | ||||
| ttydisc_unlock(tp); | |||||
| return (error); | return (error); | ||||
| } | |||||
| if (cmd == TIOCSETAF) | if (cmd == TIOCSETAF) | ||||
| tty_flush(tp, FREAD); | tty_flush(tp, FREAD); | ||||
| } | } | ||||
| /* | /* | ||||
| * Only call param() when the flags really change. | * Only call param() when the flags really change. | ||||
| */ | */ | ||||
| if ((t->c_cflag & CIGNORE) == 0 && | if ((t->c_cflag & CIGNORE) == 0 && | ||||
| (tp->t_termios.c_cflag != t->c_cflag || | (tp->t_termios.c_cflag != t->c_cflag || | ||||
| ((tp->t_termios.c_iflag ^ t->c_iflag) & | ((tp->t_termios.c_iflag ^ t->c_iflag) & | ||||
| (IXON|IXOFF|IXANY)) || | (IXON|IXOFF|IXANY)) || | ||||
| tp->t_termios.c_ispeed != t->c_ispeed || | tp->t_termios.c_ispeed != t->c_ispeed || | ||||
| tp->t_termios.c_ospeed != t->c_ospeed)) { | tp->t_termios.c_ospeed != t->c_ospeed)) { | ||||
| error = ttydevsw_param(tp, t); | error = ttydevsw_param(tp, t); | ||||
| if (error) | if (error) { | ||||
| ttydisc_unlock(tp); | |||||
| return (error); | return (error); | ||||
| } | |||||
| /* XXX: CLOCAL? */ | /* XXX: CLOCAL? */ | ||||
| tp->t_termios.c_cflag = t->c_cflag & ~CIGNORE; | tp->t_termios.c_cflag = t->c_cflag & ~CIGNORE; | ||||
| tp->t_termios.c_ispeed = t->c_ispeed; | tp->t_termios.c_ispeed = t->c_ispeed; | ||||
| tp->t_termios.c_ospeed = t->c_ospeed; | tp->t_termios.c_ospeed = t->c_ospeed; | ||||
| /* Baud rate has changed - update watermarks. */ | /* Baud rate has changed - update watermarks. */ | ||||
| error = tty_watermarks(tp); | error = tty_watermarks(tp); | ||||
| if (error) | if (error) { | ||||
| ttydisc_unlock(tp); | |||||
| return (error); | return (error); | ||||
| } | } | ||||
| } | |||||
| /* | /* | ||||
| * We'll canonicalize any partial input if we're transitioning | * We'll canonicalize any partial input if we're transitioning | ||||
| * ICANON one way or the other. If we're going from -ICANON -> | * ICANON one way or the other. If we're going from -ICANON -> | ||||
| * ICANON, then in the worst case scenario we're in the middle | * ICANON, then in the worst case scenario we're in the middle | ||||
| * of a line but both ttydisc_read() and FIONREAD will search | * of a line but both ttydisc_read() and FIONREAD will search | ||||
| * for one of our line terminals. | * for one of our line terminals. | ||||
| */ | */ | ||||
| Show All 28 Lines | case TIOCSETAF: { | ||||
| * and VSTART may have been changed. | * and VSTART may have been changed. | ||||
| */ | */ | ||||
| if (tp->t_termios.c_iflag & IXON && | if (tp->t_termios.c_iflag & IXON && | ||||
| tp->t_termios.c_cc[VSTOP] == CTRL('S') && | tp->t_termios.c_cc[VSTOP] == CTRL('S') && | ||||
| tp->t_termios.c_cc[VSTART] == CTRL('Q')) | tp->t_termios.c_cc[VSTART] == CTRL('Q')) | ||||
| ttydevsw_pktnotify(tp, TIOCPKT_DOSTOP); | ttydevsw_pktnotify(tp, TIOCPKT_DOSTOP); | ||||
| else | else | ||||
| ttydevsw_pktnotify(tp, TIOCPKT_NOSTOP); | ttydevsw_pktnotify(tp, TIOCPKT_NOSTOP); | ||||
| ttydisc_unlock(tp); | |||||
| return (0); | return (0); | ||||
| } | } | ||||
| case TIOCGETD: | case TIOCGETD: | ||||
| /* For compatibility - we only support TTYDISC. */ | /* For compatibility - we only support TTYDISC. */ | ||||
| *(int *)data = TTYDISC; | *(int *)data = TTYDISC; | ||||
| return (0); | return (0); | ||||
| case TIOCGPGRP: | case TIOCGPGRP: | ||||
| if (!tty_is_ctty(tp, td->td_proc)) | if (!tty_is_ctty(tp, td->td_proc)) | ||||
| ▲ Show 20 Lines • Show All 47 Lines • ▼ Show 20 Lines | if (p->p_session->s_ttyp != NULL || | ||||
| * TTYs of which the session leader has been | * TTYs of which the session leader has been | ||||
| * killed or the TTY revoked. | * killed or the TTY revoked. | ||||
| */ | */ | ||||
| sx_xunlock(&proctree_lock); | sx_xunlock(&proctree_lock); | ||||
| return (EPERM); | return (EPERM); | ||||
| } | } | ||||
| /* Connect the session to the TTY. */ | /* Connect the session to the TTY. */ | ||||
| ttydisc_lock(tp); | |||||
| tp->t_session = p->p_session; | tp->t_session = p->p_session; | ||||
| tp->t_session->s_ttyp = tp; | tp->t_session->s_ttyp = tp; | ||||
| tp->t_sessioncnt++; | tp->t_sessioncnt++; | ||||
| /* Assign foreground process group. */ | /* Assign foreground process group. */ | ||||
| tp->t_pgrp = p->p_pgrp; | tp->t_pgrp = p->p_pgrp; | ||||
| ttydisc_unlock(tp); | |||||
| PROC_LOCK(p); | PROC_LOCK(p); | ||||
| p->p_flag |= P_CONTROLT; | p->p_flag |= P_CONTROLT; | ||||
| PROC_UNLOCK(p); | PROC_UNLOCK(p); | ||||
| sx_xunlock(&proctree_lock); | sx_xunlock(&proctree_lock); | ||||
| return (0); | return (0); | ||||
| } | } | ||||
| case TIOCSPGRP: { | case TIOCSPGRP: { | ||||
| Show All 19 Lines | case TIOCSPGRP: { | ||||
| /* | /* | ||||
| * Determine if this TTY is the controlling TTY after | * Determine if this TTY is the controlling TTY after | ||||
| * relocking the TTY. | * relocking the TTY. | ||||
| */ | */ | ||||
| if (!tty_is_ctty(tp, td->td_proc)) { | if (!tty_is_ctty(tp, td->td_proc)) { | ||||
| sx_sunlock(&proctree_lock); | sx_sunlock(&proctree_lock); | ||||
| return (ENOTTY); | return (ENOTTY); | ||||
| } | } | ||||
| ttydisc_lock(tp); | |||||
| tp->t_pgrp = pg; | tp->t_pgrp = pg; | ||||
| ttydisc_unlock(tp); | |||||
| sx_sunlock(&proctree_lock); | sx_sunlock(&proctree_lock); | ||||
| /* Wake up the background process groups. */ | /* Wake up the background process groups. */ | ||||
| cv_broadcast(&tp->t_bgwait); | cv_broadcast(&tp->t_bgwait); | ||||
| return (0); | return (0); | ||||
| } | } | ||||
| case TIOCFLUSH: { | case TIOCFLUSH: { | ||||
| int flags = *(int *)data; | int flags = *(int *)data; | ||||
| if (flags == 0) | if (flags == 0) | ||||
| flags = (FREAD|FWRITE); | flags = (FREAD|FWRITE); | ||||
| else | else | ||||
| flags &= (FREAD|FWRITE); | flags &= (FREAD|FWRITE); | ||||
| ttydisc_lock(tp); | |||||
| tty_flush(tp, flags); | tty_flush(tp, flags); | ||||
| ttydisc_unlock(tp); | |||||
| return (0); | return (0); | ||||
| } | } | ||||
| case TIOCDRAIN: | case TIOCDRAIN: | ||||
| /* Drain TTY output. */ | /* Drain TTY output. */ | ||||
| return tty_drain(tp, 0); | ttydisc_lock(tp); | ||||
| error = tty_drain(tp, 0); | |||||
| ttydisc_unlock(tp); | |||||
| return (error); | |||||
| case TIOCGDRAINWAIT: | case TIOCGDRAINWAIT: | ||||
| ttydisc_lock(tp); | |||||
| *(int *)data = tp->t_drainwait; | *(int *)data = tp->t_drainwait; | ||||
| ttydisc_unlock(tp); | |||||
| return (0); | return (0); | ||||
| case TIOCSDRAINWAIT: | case TIOCSDRAINWAIT: | ||||
| error = priv_check(td, PRIV_TTY_DRAINWAIT); | error = priv_check(td, PRIV_TTY_DRAINWAIT); | ||||
| if (error == 0) | if (error == 0) | ||||
| tp->t_drainwait = *(int *)data; | tp->t_drainwait = *(int *)data; | ||||
| return (error); | return (error); | ||||
| case TIOCCONS: | case TIOCCONS: | ||||
| /* Set terminal as console TTY. */ | /* Set terminal as console TTY. */ | ||||
| Show All 10 Lines | case TIOCGWINSZ: | ||||
| /* Obtain window size. */ | /* Obtain window size. */ | ||||
| *(struct winsize*)data = tp->t_winsize; | *(struct winsize*)data = tp->t_winsize; | ||||
| return (0); | return (0); | ||||
| case TIOCSWINSZ: | case TIOCSWINSZ: | ||||
| /* Set window size. */ | /* Set window size. */ | ||||
| tty_set_winsize(tp, data); | tty_set_winsize(tp, data); | ||||
| return (0); | return (0); | ||||
| case TIOCEXCL: | case TIOCEXCL: | ||||
| ttydisc_lock(tp); | |||||
| tp->t_flags |= TF_EXCLUDE; | tp->t_flags |= TF_EXCLUDE; | ||||
| ttydisc_unlock(tp); | |||||
| return (0); | return (0); | ||||
| case TIOCNXCL: | case TIOCNXCL: | ||||
| ttydisc_lock(tp); | |||||
| tp->t_flags &= ~TF_EXCLUDE; | tp->t_flags &= ~TF_EXCLUDE; | ||||
| ttydisc_unlock(tp); | |||||
| return (0); | return (0); | ||||
| case TIOCSTOP: | case TIOCSTOP: | ||||
| ttydisc_lock(tp); | |||||
| tp->t_flags |= TF_STOPPED; | tp->t_flags |= TF_STOPPED; | ||||
| ttydevsw_pktnotify(tp, TIOCPKT_STOP); | ttydevsw_pktnotify(tp, TIOCPKT_STOP); | ||||
| ttydisc_unlock(tp); | |||||
| return (0); | return (0); | ||||
| case TIOCSTART: | case TIOCSTART: | ||||
| ttydisc_lock(tp); | |||||
| tp->t_flags &= ~TF_STOPPED; | tp->t_flags &= ~TF_STOPPED; | ||||
| tp->t_termios.c_lflag &= ~FLUSHO; | tp->t_termios.c_lflag &= ~FLUSHO; | ||||
| ttydevsw_outwakeup(tp); | ttydevsw_outwakeup(tp); | ||||
| ttydevsw_pktnotify(tp, TIOCPKT_START); | ttydevsw_pktnotify(tp, TIOCPKT_START); | ||||
| ttydisc_unlock(tp); | |||||
| return (0); | return (0); | ||||
| case TIOCSTAT: | case TIOCSTAT: | ||||
| ttydisc_lock(tp); | |||||
| tty_info(tp); | tty_info(tp); | ||||
| ttydisc_unlock(tp); | |||||
| return (0); | return (0); | ||||
| case TIOCSTI: | case TIOCSTI: | ||||
| if ((fflag & FREAD) == 0 && priv_check(td, PRIV_TTY_STI)) | if ((fflag & FREAD) == 0 && priv_check(td, PRIV_TTY_STI)) | ||||
| return (EPERM); | return (EPERM); | ||||
| if (!tty_is_ctty(tp, td->td_proc) && | if (!tty_is_ctty(tp, td->td_proc) && | ||||
| priv_check(td, PRIV_TTY_STI)) | priv_check(td, PRIV_TTY_STI)) | ||||
| return (EACCES); | return (EACCES); | ||||
| ttydisc_lock(tp); | |||||
| ttydisc_rint(tp, *(char *)data, 0); | ttydisc_rint(tp, *(char *)data, 0); | ||||
| ttydisc_rint_done(tp); | ttydisc_rint_done(tp); | ||||
| ttydisc_unlock(tp); | |||||
| return (0); | return (0); | ||||
| } | } | ||||
| #ifdef COMPAT_43TTY | #ifdef COMPAT_43TTY | ||||
| return tty_ioctl_compat(tp, cmd, data, fflag, td); | return tty_ioctl_compat(tp, cmd, data, fflag, td); | ||||
| #else /* !COMPAT_43TTY */ | #else /* !COMPAT_43TTY */ | ||||
| return (ENOIOCTL); | return (ENOIOCTL); | ||||
| #endif /* COMPAT_43TTY */ | #endif /* COMPAT_43TTY */ | ||||
| } | } | ||||
| int | int | ||||
| tty_ioctl(struct tty *tp, u_long cmd, void *data, int fflag, struct thread *td) | tty_ioctl(struct tty *tp, u_long cmd, void *data, int fflag, struct thread *td) | ||||
| { | { | ||||
| int error; | int error; | ||||
| tty_assert_locked(tp); | tty_assert_locked(tp); | ||||
| if (tty_gone(tp)) | if (tty_gone(tp)) | ||||
| return (ENXIO); | return (ENXIO); | ||||
| /* tty device driver may change parameters related to I/O. */ | |||||
| ttydisc_lock(tp); | |||||
| error = ttydevsw_ioctl(tp, cmd, data, td); | error = ttydevsw_ioctl(tp, cmd, data, td); | ||||
| ttydisc_unlock(tp); | |||||
| if (error == ENOIOCTL) | if (error == ENOIOCTL) | ||||
| error = tty_generic_ioctl(tp, cmd, data, fflag, td); | error = tty_generic_ioctl(tp, cmd, data, fflag, td); | ||||
| return (error); | return (error); | ||||
| } | } | ||||
| dev_t | dev_t | ||||
| tty_udev(struct tty *tp) | tty_udev(struct tty *tp) | ||||
| { | { | ||||
| if (tp->t_dev) | if (tp->t_dev) | ||||
| return (dev2udev(tp->t_dev)); | return (dev2udev(tp->t_dev)); | ||||
| else | else | ||||
| return (NODEV); | return (NODEV); | ||||
| } | } | ||||
| int | int | ||||
| tty_checkoutq(struct tty *tp) | tty_checkoutq(struct tty *tp) | ||||
| { | { | ||||
| ttydisc_assert_locked(tp); | |||||
| /* 256 bytes should be enough to print a log message. */ | /* 256 bytes should be enough to print a log message. */ | ||||
| return (ttyoutq_bytesleft(&tp->t_outq) >= 256); | return (ttyoutq_bytesleft(&tp->t_outq) >= 256); | ||||
| } | } | ||||
| void | void | ||||
| tty_hiwat_in_block(struct tty *tp) | tty_hiwat_in_block(struct tty *tp) | ||||
| { | { | ||||
| ttydisc_assert_locked(tp); | |||||
| if ((tp->t_flags & TF_HIWAT_IN) == 0 && | if ((tp->t_flags & TF_HIWAT_IN) == 0 && | ||||
| tp->t_termios.c_iflag & IXOFF && | tp->t_termios.c_iflag & IXOFF && | ||||
| tp->t_termios.c_cc[VSTOP] != _POSIX_VDISABLE) { | tp->t_termios.c_cc[VSTOP] != _POSIX_VDISABLE) { | ||||
| /* | /* | ||||
| * Input flow control. Only enter the high watermark when we | * Input flow control. Only enter the high watermark when we | ||||
| * can successfully store the VSTOP character. | * can successfully store the VSTOP character. | ||||
| */ | */ | ||||
| if (ttyoutq_write_nofrag(&tp->t_outq, | if (ttyoutq_write_nofrag(&tp->t_outq, | ||||
| &tp->t_termios.c_cc[VSTOP], 1) == 0) | &tp->t_termios.c_cc[VSTOP], 1) == 0) | ||||
| tp->t_flags |= TF_HIWAT_IN; | tp->t_flags |= TF_HIWAT_IN; | ||||
| } else { | } else { | ||||
| /* No input flow control. */ | /* No input flow control. */ | ||||
| tp->t_flags |= TF_HIWAT_IN; | tp->t_flags |= TF_HIWAT_IN; | ||||
| } | } | ||||
| } | } | ||||
| void | void | ||||
| tty_hiwat_in_unblock(struct tty *tp) | tty_hiwat_in_unblock(struct tty *tp) | ||||
| { | { | ||||
| ttydisc_assert_locked(tp); | |||||
| if (tp->t_flags & TF_HIWAT_IN && | if (tp->t_flags & TF_HIWAT_IN && | ||||
| tp->t_termios.c_iflag & IXOFF && | tp->t_termios.c_iflag & IXOFF && | ||||
| tp->t_termios.c_cc[VSTART] != _POSIX_VDISABLE) { | tp->t_termios.c_cc[VSTART] != _POSIX_VDISABLE) { | ||||
| /* | /* | ||||
| * Input flow control. Only leave the high watermark when we | * Input flow control. Only leave the high watermark when we | ||||
| * can successfully store the VSTART character. | * can successfully store the VSTART character. | ||||
| */ | */ | ||||
| if (ttyoutq_write_nofrag(&tp->t_outq, | if (ttyoutq_write_nofrag(&tp->t_outq, | ||||
| ▲ Show 20 Lines • Show All 83 Lines • ▼ Show 20 Lines | ttyhook_register(struct tty **rtp, struct proc *p, int fd, struct ttyhook *th, | ||||
| /* Try to attach the hook to the TTY. */ | /* Try to attach the hook to the TTY. */ | ||||
| error = EBUSY; | error = EBUSY; | ||||
| tty_lock(tp); | tty_lock(tp); | ||||
| MPASS((tp->t_hook == NULL) == ((tp->t_flags & TF_HOOK) == 0)); | MPASS((tp->t_hook == NULL) == ((tp->t_flags & TF_HOOK) == 0)); | ||||
| if (tp->t_flags & TF_HOOK) | if (tp->t_flags & TF_HOOK) | ||||
| goto done3; | goto done3; | ||||
| ttydisc_lock(tp); | |||||
| tp->t_flags |= TF_HOOK; | tp->t_flags |= TF_HOOK; | ||||
| tp->t_hook = th; | tp->t_hook = th; | ||||
| tp->t_hooksoftc = softc; | tp->t_hooksoftc = softc; | ||||
| *rtp = tp; | *rtp = tp; | ||||
| error = 0; | error = 0; | ||||
| /* Maybe we can switch into bypass mode now. */ | /* Maybe we can switch into bypass mode now. */ | ||||
| ttydisc_optimize(tp); | ttydisc_optimize(tp); | ||||
| /* Silently convert rint() calls to rint_bypass() when possible. */ | /* Silently convert rint() calls to rint_bypass() when possible. */ | ||||
| if (!ttyhook_hashook(tp, rint) && ttyhook_hashook(tp, rint_bypass)) | if (!ttyhook_hashook(tp, rint) && ttyhook_hashook(tp, rint_bypass)) | ||||
| th->th_rint = ttyhook_defrint; | th->th_rint = ttyhook_defrint; | ||||
| ttydisc_unlock(tp); | |||||
| done3: tty_unlock(tp); | done3: tty_unlock(tp); | ||||
| done2: dev_relthread(dev, ref); | done2: dev_relthread(dev, ref); | ||||
| done1: fdrop(fp, curthread); | done1: fdrop(fp, curthread); | ||||
| return (error); | return (error); | ||||
| } | } | ||||
| void | void | ||||
| ttyhook_unregister(struct tty *tp) | ttyhook_unregister(struct tty *tp) | ||||
| { | { | ||||
| tty_assert_locked(tp); | tty_assert_locked(tp); | ||||
| MPASS(tp->t_flags & TF_HOOK); | MPASS(tp->t_flags & TF_HOOK); | ||||
| /* Disconnect the hook. */ | /* Disconnect the hook. */ | ||||
| ttydisc_lock(tp); | |||||
| tp->t_flags &= ~TF_HOOK; | tp->t_flags &= ~TF_HOOK; | ||||
| tp->t_hook = NULL; | tp->t_hook = NULL; | ||||
| /* Maybe we need to leave bypass mode. */ | /* Maybe we need to leave bypass mode. */ | ||||
| ttydisc_optimize(tp); | ttydisc_optimize(tp); | ||||
| ttydisc_unlock(tp); | |||||
| /* Maybe deallocate the TTY as well. */ | /* Maybe deallocate the TTY as well. */ | ||||
| tty_rel_free(tp); | tty_rel_free(tp); | ||||
| } | } | ||||
| /* | /* | ||||
| * /dev/console handling. | * /dev/console handling. | ||||
| */ | */ | ||||
| ▲ Show 20 Lines • Show All 282 Lines • Show Last 20 Lines | |||||
This is also fixed... this patch grew organically as I worked out the intricacies of tty bits, and I suspect at some point I was dropping the ttydisc lock coming into this loop then picking it back up here. It no longer makes any sense, though.