+
+ 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_internal_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_lock_from_knote(kn);
+ if (!tp) {
+ knote_set_error(kn, ENOENT);
+ return 0;
+ }
+
+ switch (knote_get_seltype(kn)) {
+ case FREAD:
+ KNOTE_ATTACH(&tp->t_rsel.si_note, kn);
+ break;
+ case FWRITE:
+ KNOTE_ATTACH(&tp->t_wsel.si_note, kn);
+ break;
+ }
+
+ tty_unlock(tp);
+
+ return selres;
+}
+
+static void
+filt_ttydetach(struct knote *kn)