diff --git a/sys/dev/sound/pcm/channel.h b/sys/dev/sound/pcm/channel.h --- a/sys/dev/sound/pcm/channel.h +++ b/sys/dev/sound/pcm/channel.h @@ -261,6 +261,7 @@ 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/channel.c b/sys/dev/sound/pcm/channel.c --- a/sys/dev/sound/pcm/channel.c +++ b/sys/dev/sound/pcm/channel.c @@ -271,7 +271,7 @@ * @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 @@ 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 @@ } 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 @@ 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/dsp.c b/sys/dev/sound/pcm/dsp.c --- a/sys/dev/sound/pcm/dsp.c +++ b/sys/dev/sound/pcm/dsp.c @@ -81,6 +81,7 @@ 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 @@ .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 @@ 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