+ assert((tp->t_wsel.si_flags & SI_KNPOSTING) == 0);
+ KNOTE(&tp->t_wsel.si_note, NOTE_REVOKE);
+
+ tty_unlock(tp);
+}
+
+/*
+ * kevent filter routines for the master side of a pty, a ptmx.
+ *
+ * Stuff the ptmx_ioctl structure into the hook for ptmx knotes. Use the
+ * embedded tty's lock for synchronization.
+ */
+
+int ptmx_kqfilter(dev_t dev, struct knote *kn);
+static void ptmx_kqops_detach(struct knote *);
+static int ptmx_kqops_event(struct knote *, long);
+static int ptmx_kqops_touch(struct knote *kn, struct kevent_qos_s *kev);
+static int ptmx_kqops_process(struct knote *kn, struct kevent_qos_s *kev);
+static int ptmx_kqops_common(struct knote *kn, struct kevent_qos_s *kev,
+ struct ptmx_ioctl *pti, struct tty *tp);
+
+SECURITY_READ_ONLY_EARLY(struct filterops) ptmx_kqops = {
+ .f_isfd = 1,
+ /* attach is handled by ptmx_kqfilter -- the dev node must be passed in */
+ .f_detach = ptmx_kqops_detach,
+ .f_event = ptmx_kqops_event,
+ .f_touch = ptmx_kqops_touch,
+ .f_process = ptmx_kqops_process,
+};
+
+static struct ptmx_ioctl *
+ptmx_knote_ioctl(struct knote *kn)
+{
+ return (struct ptmx_ioctl *)kn->kn_hook;
+}
+
+static struct tty *
+ptmx_knote_tty(struct knote *kn)
+{
+ return ptmx_knote_ioctl(kn)->pt_tty;
+}
+
+int
+ptmx_kqfilter(dev_t dev, struct knote *kn)
+{
+ struct tty *tp = NULL;
+ struct ptmx_ioctl *pti = NULL;
+ int ret;
+
+ /* make sure we're talking about the right device type */
+ if (cdevsw[major(dev)].d_open != ptcopen) {
+ knote_set_error(kn, ENODEV);
+ return 0;
+ }
+
+ if ((pti = ptmx_get_ioctl(minor(dev), 0)) == NULL) {
+ knote_set_error(kn, ENXIO);
+ return 0;
+ }
+
+ tp = pti->pt_tty;
+ tty_lock(tp);
+
+ kn->kn_filtid = EVFILTID_PTMX;
+ /* the tty will be freed when detaching the knote */
+ ttyhold(tp);
+ kn->kn_hook = pti;
+
+ /*
+ * Attach to the ptmx's selinfo structures. This is the major difference
+ * to the ptsd filtops, which use the selinfo structures in the tty
+ * structure.
+ */
+ switch (kn->kn_filter) {
+ case EVFILT_READ:
+ KNOTE_ATTACH(&pti->pt_selr.si_note, kn);
+ break;
+ case EVFILT_WRITE:
+ KNOTE_ATTACH(&pti->pt_selw.si_note, kn);
+ break;
+ default:
+ panic("ptmx kevent: unexpected filter: %d, kn = %p, tty = %p",
+ kn->kn_filter, kn, tp);
+ break;
+ }
+
+ /* capture current event state */
+ ret = ptmx_kqops_common(kn, NULL, pti, tp);
+
+ tty_unlock(tp);
+
+ return ret;
+}
+
+static void
+ptmx_kqops_detach(struct knote *kn)
+{
+ struct ptmx_ioctl *pti = kn->kn_hook;
+ struct tty *tp = pti->pt_tty;
+
+ tty_lock(tp);
+
+ switch (kn->kn_filter) {
+ case EVFILT_READ:
+ KNOTE_DETACH(&pti->pt_selr.si_note, kn);
+ break;
+ case EVFILT_WRITE:
+ KNOTE_DETACH(&pti->pt_selw.si_note, kn);
+ break;
+ default:
+ panic("invalid knote %p detach, filter: %d", kn, kn->kn_filter);
+ break;
+ }