git: 4181bf9d1816 - main - sound: Add kqueue support

From: Christos Margiolis <christos_at_FreeBSD.org>
Date: Tue, 11 Nov 2025 12:07:56 UTC
The branch main has been updated by christos:

URL: https://cgit.FreeBSD.org/src/commit/?id=4181bf9d18166c4061bfa137b8a46972a5a55f67

commit 4181bf9d18166c4061bfa137b8a46972a5a55f67
Author:     Christos Margiolis <christos@FreeBSD.org>
AuthorDate: 2025-11-11 12:04:45 +0000
Commit:     Christos Margiolis <christos@FreeBSD.org>
CommitDate: 2025-11-11 12:04:45 +0000

    sound: Add kqueue support
    
    Co-authored by: meka@tilda.center
    Sponsored by:   The FreeBSD Foundation
    MFC after:      2 weeks
    Reviewed by:    markj
    Differential Revision:  https://reviews.freebsd.org/D53029
---
 sys/dev/sound/pcm/channel.c |  9 +++--
 sys/dev/sound/pcm/channel.h |  1 +
 sys/dev/sound/pcm/dsp.c     | 82 +++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 90 insertions(+), 2 deletions(-)

diff --git a/sys/dev/sound/pcm/channel.c b/sys/dev/sound/pcm/channel.c
index 4d13f20a5262..062eeb251a5b 100644
--- a/sys/dev/sound/pcm/channel.c
+++ b/sys/dev/sound/pcm/channel.c
@@ -271,7 +271,7 @@ chn_lockdestroy(struct pcm_channel *c)
  * @retval 1 = ready for I/O
  * @retval 0 = not ready for I/O
  */
-static int
+int
 chn_polltrigger(struct pcm_channel *c)
 {
 	struct snd_dbuf *bs = c->bufsoft;
@@ -313,6 +313,7 @@ chn_wakeup(struct pcm_channel *c)
 	bs = c->bufsoft;
 
 	if (CHN_EMPTY(c, children.busy)) {
+		KNOTE_LOCKED(&bs->sel.si_note, 0);
 		if (SEL_WAITING(sndbuf_getsel(bs)) && chn_polltrigger(c))
 			selwakeuppri(sndbuf_getsel(bs), PRIBIO);
 		CHN_BROADCAST(&c->intr_cv);
@@ -1277,6 +1278,7 @@ chn_init(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t cls,
 	}
 	c->bufhard = b;
 	c->bufsoft = bs;
+	knlist_init_mtx(&bs->sel.si_note, c->lock);
 
 	c->devinfo = CHANNEL_INIT(c->methods, devinfo, b, c, direction);
 	if (c->devinfo == NULL) {
@@ -1373,8 +1375,11 @@ chn_kill(struct pcm_channel *c)
 	feeder_remove(c);
 	if (c->devinfo && CHANNEL_FREE(c->methods, c->devinfo))
 		sndbuf_free(b);
-	if (bs)
+	if (bs) {
+		knlist_clear(&bs->sel.si_note, 0);
+		knlist_destroy(&bs->sel.si_note);
 		sndbuf_destroy(bs);
+	}
 	if (b)
 		sndbuf_destroy(b);
 	CHN_LOCK(c);
diff --git a/sys/dev/sound/pcm/channel.h b/sys/dev/sound/pcm/channel.h
index 9ad21d219001..15180bc8f0b6 100644
--- a/sys/dev/sound/pcm/channel.h
+++ b/sys/dev/sound/pcm/channel.h
@@ -261,6 +261,7 @@ int chn_read(struct pcm_channel *c, struct uio *buf);
 u_int32_t chn_start(struct pcm_channel *c, int force);
 int chn_sync(struct pcm_channel *c, int threshold);
 int chn_flush(struct pcm_channel *c);
+int chn_polltrigger(struct pcm_channel *c);
 int chn_poll(struct pcm_channel *c, int ev, struct thread *td);
 
 char *chn_mkname(char *buf, size_t len, struct pcm_channel *c);
diff --git a/sys/dev/sound/pcm/dsp.c b/sys/dev/sound/pcm/dsp.c
index 27d5b740b90b..1b205d7229fc 100644
--- a/sys/dev/sound/pcm/dsp.c
+++ b/sys/dev/sound/pcm/dsp.c
@@ -81,6 +81,7 @@ static d_ioctl_t dsp_ioctl;
 static d_poll_t dsp_poll;
 static d_mmap_t dsp_mmap;
 static d_mmap_single_t dsp_mmap_single;
+static d_kqfilter_t dsp_kqfilter;
 
 struct cdevsw dsp_cdevsw = {
 	.d_version	= D_VERSION,
@@ -89,6 +90,7 @@ struct cdevsw dsp_cdevsw = {
 	.d_write	= dsp_write,
 	.d_ioctl	= dsp_ioctl,
 	.d_poll		= dsp_poll,
+	.d_kqfilter	= dsp_kqfilter,
 	.d_mmap		= dsp_mmap,
 	.d_mmap_single	= dsp_mmap_single,
 	.d_name		= "dsp",
@@ -2962,6 +2964,86 @@ dsp_oss_getchannelmask(struct pcm_channel *wrch, struct pcm_channel *rdch,
 	return (ret);
 }
 
+static void
+dsp_kqdetach(struct knote *kn)
+{
+	struct pcm_channel *ch = kn->kn_hook;
+
+	if (ch == NULL)
+		return;
+	CHN_LOCK(ch);
+	knlist_remove(&ch->bufsoft->sel.si_note, kn, 1);
+	CHN_UNLOCK(ch);
+}
+
+static int
+dsp_kqevent(struct knote *kn, long hint)
+{
+	struct pcm_channel *ch = kn->kn_hook;
+
+	CHN_LOCKASSERT(ch);
+	if (ch->flags & CHN_F_DEAD) {
+		kn->kn_flags |= EV_EOF;
+		return (1);
+	}
+	kn->kn_data = 0;
+	if (chn_polltrigger(ch)) {
+		if (kn->kn_filter == EVFILT_READ)
+			kn->kn_data = sndbuf_getready(ch->bufsoft);
+		else
+			kn->kn_data = sndbuf_getfree(ch->bufsoft);
+	}
+
+	return (kn->kn_data > 0);
+}
+
+static const struct filterops dsp_filtops = {
+	.f_isfd = 1,
+	.f_detach = dsp_kqdetach,
+	.f_event = dsp_kqevent,
+};
+
+static int
+dsp_kqfilter(struct cdev *dev, struct knote *kn)
+{
+	struct dsp_cdevpriv *priv;
+	struct snddev_info *d;
+	struct pcm_channel *ch;
+	int err = 0;
+
+	if ((err = devfs_get_cdevpriv((void **)&priv)) != 0)
+		return (err);
+
+	d = priv->sc;
+	if (!DSP_REGISTERED(d))
+		return (EBADF);
+	PCM_GIANT_ENTER(d);
+	switch (kn->kn_filter) {
+	case EVFILT_READ:
+		ch = priv->rdch;
+		break;
+	case EVFILT_WRITE:
+		ch = priv->wrch;
+		break;
+	default:
+		kn->kn_hook = NULL;
+		err = EINVAL;
+		ch = NULL;
+		break;
+	}
+	if (ch != NULL) {
+		kn->kn_fop = &dsp_filtops;
+		CHN_LOCK(ch);
+		knlist_add(&ch->bufsoft->sel.si_note, kn, 1);
+		CHN_UNLOCK(ch);
+		kn->kn_hook = ch;
+	} else
+		err = EINVAL;
+	PCM_GIANT_LEAVE(d);
+
+	return (err);
+}
+
 #ifdef OSSV4_EXPERIMENT
 /**
  * @brief Retrieve an audio device's label