+
+ uth = get_bsdthread_info(current_thread());
+
+ ctx = vfs_context_current();
+ vp = (vnode_t)kn->kn_fp->f_fglob->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)