+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;