+ return sessp == tp->t_session && p->p_flag & P_CONTROLT;
+}
+
+
+static int filt_ttyattach(struct knote *kn, struct kevent_qos_s *kev);
+static void filt_ttydetach(struct knote *kn);
+static int filt_ttyevent(struct knote *kn, long hint);
+static int filt_ttytouch(struct knote *kn, struct kevent_qos_s *kev);
+static int filt_ttyprocess(struct knote *kn, struct kevent_qos_s *kev);
+
+SECURITY_READ_ONLY_EARLY(struct filterops) tty_filtops = {
+ .f_isfd = 1,
+ .f_attach = filt_ttyattach,
+ .f_detach = filt_ttydetach,
+ .f_event = filt_ttyevent,
+ .f_touch = filt_ttytouch,
+ .f_process = filt_ttyprocess
+};
+
+/*
+ * Called with struct tty locked. Returns non-zero if there is data to be read
+ * or written.
+ */
+static int
+filt_tty_common(struct knote *kn, struct kevent_qos_s *kev, struct tty *tp)
+{
+ int retval = 0;
+ int64_t data = 0;
+
+ TTY_LOCK_OWNED(tp); /* debug assert */
+
+ switch (kn->kn_filter) {
+ case EVFILT_READ:
+ /*
+ * ttnread can change the tty state,
+ * hence must be done upfront, before any other check.
+ */
+ data = ttnread(tp);
+ retval = (data != 0);
+ break;
+ case EVFILT_WRITE:
+ if ((tp->t_outq.c_cc <= tp->t_lowat) &&
+ (tp->t_state & TS_CONNECTED)) {
+ data = tp->t_hiwat - tp->t_outq.c_cc;
+ retval = (data != 0);
+ }
+ break;
+ default:
+ panic("tty kevent: unexpected filter: %d, kn = %p, tty = %p",
+ kn->kn_filter, kn, tp);
+ break;
+ }
+
+ /*
+ * TODO(mwidmann, jandrus): For native knote low watermark support,
+ * check the kn_sfflags for NOTE_LOWAT and check against kn_sdata.
+ *
+ * res = ((kn->kn_sfflags & NOTE_LOWAT) != 0) ?
+ * (kn->kn_data >= kn->kn_sdata) : kn->kn_data;
+ */
+
+ if (tp->t_state & TS_ZOMBIE) {
+ kn->kn_flags |= EV_EOF;
+ }
+ if (kn->kn_flags & EV_EOF) {
+ retval = 1;
+ }
+ if (retval && kev) {
+ knote_fill_kevent(kn, kev, data);
+ }
+
+ return retval;
+}
+
+/*
+ * Find the struct tty from a waitq, which is a member of one of the two struct
+ * selinfos inside the struct tty. Use the seltype to determine which selinfo.
+ */
+static struct tty *
+tty_from_waitq(struct waitq *wq, int seltype)
+{
+ struct selinfo *si;
+ struct tty *tp = NULL;
+
+ /*
+ * The waitq is part of the selinfo structure managed by the driver. For
+ * certain drivers, we want to hook the knote into the selinfo
+ * structure's si_note field so selwakeup can call KNOTE.
+ *
+ * While 'wq' is not really a queue element, this macro only uses the
+ * pointer to calculate the offset into a structure given an element
+ * name.
+ */
+ si = qe_element(wq, struct selinfo, si_waitq);
+
+ /*
+ * For TTY drivers, the selinfo structure is somewhere in the struct
+ * tty. There are two different selinfo structures, and the one used
+ * corresponds to the type of filter requested.
+ *
+ * While 'si' is not really a queue element, this macro only uses the
+ * pointer to calculate the offset into a structure given an element
+ * name.
+ */
+ switch (seltype) {
+ case FREAD:
+ tp = qe_element(si, struct tty, t_rsel);
+ break;
+ case FWRITE:
+ tp = qe_element(si, struct tty, t_wsel);
+ break;
+ }
+
+ return tp;
+}
+
+static struct tty *
+tty_from_knote(struct knote *kn)
+{
+ return (struct tty *)kn->kn_hook;
+}
+
+/*
+ * Set the knote's struct tty to the kn_hook field.
+ *
+ * The idea is to fake a call to select with our own waitq set. If the driver
+ * calls selrecord, we'll get a link to their waitq and access to the tty
+ * structure.
+ *
+ * Returns -1 on failure, with the error set in the knote, or selres on success.
+ */
+static int
+tty_set_knote_hook(struct knote *kn)
+{
+ uthread_t uth;
+ vfs_context_t ctx;
+ vnode_t vp;
+ kern_return_t kr;
+ struct waitq *wq = NULL;
+ struct waitq_set *old_wqs;
+ struct waitq_set tmp_wqs;
+ uint64_t rsvd, rsvd_arg;
+ uint64_t *rlptr = NULL;
+ int selres = -1;
+ struct tty *tp;
+
+ uth = get_bsdthread_info(current_thread());
+
+ ctx = vfs_context_current();
+ vp = (vnode_t)kn->kn_fp->fp_glob->fg_data;
+
+ /*
+ * Reserve a link element to avoid potential allocation under
+ * a spinlock.
+ */
+ rsvd = rsvd_arg = waitq_link_reserve(NULL);
+ rlptr = (void *)&rsvd_arg;
+
+ /*
+ * Trick selrecord into hooking a known waitq set into the device's selinfo
+ * waitq. Once the link is in place, we can get back into the selinfo from
+ * the waitq and subsequently the tty (see tty_from_waitq).
+ *
+ * We can't use a real waitq set (such as the kqueue's) because wakeups
+ * might happen before we can unlink it.
+ */
+ kr = waitq_set_init(&tmp_wqs, SYNC_POLICY_FIFO | SYNC_POLICY_PREPOST, NULL,
+ NULL);
+ assert(kr == KERN_SUCCESS);
+
+ /*
+ * Lazy allocate the waitqset to avoid potential allocation under
+ * a spinlock;
+ */
+ waitq_set_lazy_init_link(&tmp_wqs);
+
+ old_wqs = uth->uu_wqset;
+ uth->uu_wqset = &tmp_wqs;
+ /*
+ * FMARK forces selects to always call selrecord, even if data is
+ * available. See ttselect, ptsselect, ptcselect.
+ *
+ * selres also contains the data currently available in the tty.
+ */
+ selres = VNOP_SELECT(vp, knote_get_seltype(kn) | FMARK, 0, rlptr, ctx);
+ uth->uu_wqset = old_wqs;
+
+ /*
+ * Make sure to cleanup the reserved link - this guards against
+ * drivers that may not actually call selrecord().
+ */
+ waitq_link_release(rsvd);
+ if (rsvd == rsvd_arg) {
+ /*
+ * The driver didn't call selrecord -- there's no tty hooked up so we
+ * can't attach.
+ */
+ knote_set_error(kn, ENOTTY);
+ selres = -1;
+ goto out;
+ }
+
+ /* rlptr may not point to a properly aligned pointer */
+ memcpy(&wq, rlptr, sizeof(void *));
+
+ tp = tty_from_waitq(wq, knote_get_seltype(kn));
+ assert(tp != NULL);
+
+ /*
+ * Take a reference and stash the tty in the knote.
+ */
+ tty_lock(tp);
+ ttyhold(tp);
+ kn->kn_hook = tp;
+ tty_unlock(tp);
+
+out:
+ /*
+ * Cleaning up the wqset will unlink its waitq and clean up any preposts
+ * that occurred as a result of data coming in while the tty was attached.
+ */
+ waitq_set_deinit(&tmp_wqs);
+
+ return selres;
+}
+
+static int
+filt_ttyattach(struct knote *kn, __unused struct kevent_qos_s *kev)
+{
+ int selres = 0;
+ struct tty *tp;
+
+ /*
+ * This function should be called from filt_specattach (spec_vnops.c),
+ * so most of the knote data structure should already be initialized.
+ */
+
+ /* don't support offsets in ttys or drivers that don't use struct tty */
+ if (kn->kn_vnode_use_ofst || !kn->kn_vnode_kqok) {
+ knote_set_error(kn, ENOTSUP);
+ return 0;
+ }
+
+ /*
+ * Connect the struct tty to the knote through the selinfo structure
+ * referenced by the waitq within the selinfo.
+ */
+ selres = tty_set_knote_hook(kn);
+ if (selres < 0) {
+ return 0;
+ }
+
+ /*
+ * Attach the knote to selinfo's klist.
+ */
+ tp = tty_from_knote(kn);
+ tty_lock(tp);
+
+ switch (kn->kn_filter) {
+ case EVFILT_READ:
+ KNOTE_ATTACH(&tp->t_rsel.si_note, kn);
+ break;
+ case EVFILT_WRITE:
+ KNOTE_ATTACH(&tp->t_wsel.si_note, kn);
+ break;
+ default:
+ panic("invalid knote %p attach, filter: %d", kn, kn->kn_filter);
+ }
+
+ tty_unlock(tp);
+
+ return selres;
+}
+
+static void
+filt_ttydetach(struct knote *kn)
+{
+ struct tty *tp = tty_from_knote(kn);
+
+ tty_lock(tp);
+
+ switch (kn->kn_filter) {
+ case EVFILT_READ:
+ KNOTE_DETACH(&tp->t_rsel.si_note, kn);
+ break;
+ case EVFILT_WRITE:
+ KNOTE_DETACH(&tp->t_wsel.si_note, kn);
+ break;
+ default:
+ panic("invalid knote %p detach, filter: %d", kn, kn->kn_filter);
+ break;
+ }
+
+ tty_unlock(tp);
+ ttyfree(tp);
+}
+
+static int
+filt_ttyevent(struct knote *kn, long hint)
+{
+ struct tty *tp = tty_from_knote(kn);
+ int ret;
+
+ TTY_LOCK_OWNED(tp);
+
+ if (hint & NOTE_REVOKE) {
+ kn->kn_flags |= EV_EOF | EV_ONESHOT;
+ ret = 1;
+ } else {
+ ret = filt_tty_common(kn, NULL, tp);
+ }
+
+ return ret;
+}
+
+static int
+filt_ttytouch(struct knote *kn, struct kevent_qos_s *kev)
+{
+ struct tty *tp = tty_from_knote(kn);
+ int res = 0;
+
+ tty_lock(tp);
+
+ kn->kn_sdata = kev->data;
+ kn->kn_sfflags = kev->fflags;
+
+ if (kn->kn_vnode_kqok) {
+ res = filt_tty_common(kn, NULL, tp);
+ }
+
+ tty_unlock(tp);
+
+ return res;
+}
+
+static int
+filt_ttyprocess(struct knote *kn, struct kevent_qos_s *kev)
+{
+ struct tty *tp = tty_from_knote(kn);
+ int res;
+
+ tty_lock(tp);
+
+ res = filt_tty_common(kn, kev, tp);
+
+ tty_unlock(tp);