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 @@ -313,8 +313,10 @@ bs = c->bufsoft; if (CHN_EMPTY(c, children.busy)) { - if (SEL_WAITING(sndbuf_getsel(bs)) && chn_polltrigger(c)) + if (SEL_WAITING(sndbuf_getsel(bs)) && chn_polltrigger(c)) { selwakeuppri(sndbuf_getsel(bs), PRIBIO); + KNOTE_LOCKED(&bs->sel.si_note, 0); + } CHN_BROADCAST(&c->intr_cv); } else { CHN_FOREACH(ch, c, children.busy) { 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", @@ -235,6 +237,7 @@ chn_vpc_reset(*ch, SND_VOL_C_PCM, 0); CHN_UNLOCK(*ch); + knlist_init_mtx(&(*ch)->bufsoft->sel.si_note, (*ch)->lock); return (0); } @@ -264,10 +267,16 @@ rdch = priv->rdch; wrch = priv->wrch; - if (rdch != NULL) + if (rdch != NULL) { + knlist_clear(&rdch->bufsoft->sel.si_note, 0); + knlist_destroy(&rdch->bufsoft->sel.si_note); CHN_REMOVE(d, rdch, channels.pcm.opened); - if (wrch != NULL) + } + if (wrch != NULL) { + knlist_clear(&wrch->bufsoft->sel.si_note, 0); + knlist_destroy(&wrch->bufsoft->sel.si_note); CHN_REMOVE(d, wrch, channels.pcm.opened); + } if (rdch != NULL || wrch != NULL) { PCM_UNLOCK(d); @@ -2962,6 +2971,138 @@ return (ret); } +static void +dsp_kqdetach(struct knote *kn) +{ + struct dsp_cdevpriv *priv = kn->kn_hook; + struct snddev_info *d; + struct pcm_channel *ch; + struct snd_dbuf *bs; + + d = priv->sc; + if (!DSP_REGISTERED(d)) + return; + PCM_GIANT_ENTER(d); + dsp_lock_chans(priv, FREAD | FWRITE); + ch = priv->rdch; + if (ch != NULL && !(ch->flags & CHN_F_DEAD)) { + bs = ch->bufsoft; + knlist_remove(&bs->sel.si_note, kn, 1); + } + ch = priv->wrch; + if (ch != NULL && !(ch->flags & CHN_F_DEAD)) { + bs = ch->bufsoft; + knlist_remove(&bs->sel.si_note, kn, 1); + } + dsp_unlock_chans(priv, FREAD | FWRITE); + PCM_GIANT_LEAVE(d); +} + +static int +dsp_read_filter(struct knote *kn, long hint) +{ + struct dsp_cdevpriv *priv = kn->kn_hook; + struct snddev_info *d; + struct pcm_channel *ch; + + d = priv->sc; + if (!DSP_REGISTERED(d)) { + kn->kn_data = EBADF; + kn->kn_flags |= EV_ERROR; + return (1); + } + PCM_GIANT_ENTER(d); + dsp_lock_chans(priv, FREAD); + ch = priv->rdch; + if (ch != NULL && !(ch->flags & CHN_F_DEAD)) + kn->kn_data = sndbuf_getready(ch->bufsoft); + else + kn->kn_data = 0; + dsp_unlock_chans(priv, FREAD); + PCM_GIANT_LEAVE(d); + return (kn->kn_data > 0); +} + +static int +dsp_write_filter(struct knote *kn, long hint) +{ + struct dsp_cdevpriv *priv = kn->kn_hook; + struct snddev_info *d; + struct pcm_channel *ch; + + d = priv->sc; + if (!DSP_REGISTERED(d)) { + kn->kn_data = EBADF; + kn->kn_flags |= EV_ERROR; + return (1); + } + PCM_GIANT_ENTER(d); + dsp_lock_chans(priv, FWRITE); + ch = priv->wrch; + if (ch != NULL && !(ch->flags & CHN_F_DEAD)) + kn->kn_data = sndbuf_getfree(ch->bufsoft); + else + kn->kn_data = 0; + dsp_unlock_chans(priv, FWRITE); + PCM_GIANT_LEAVE(d); + return (kn->kn_data > 0); +} + +static const struct filterops dsp_read_filtops = { + .f_isfd = 1, + .f_detach = dsp_kqdetach, + .f_event = dsp_read_filter, +}; + +static const struct filterops dsp_write_filtops = { + .f_isfd = 1, + .f_detach = dsp_kqdetach, + .f_event = dsp_write_filter, +}; + +static int +dsp_kqfilter(struct cdev *dev, struct knote *kn) +{ + int err = 0; + struct dsp_cdevpriv *priv; + struct snddev_info *d; + + if ((err = devfs_get_cdevpriv((void **)&priv)) != 0) + return (err); + d = priv->sc; + if (!DSP_REGISTERED(d)) + return (EBADF); + PCM_GIANT_ENTER(d); + kn->kn_hook = priv; + switch (kn->kn_filter) { + case EVFILT_READ: + dsp_lock_chans(priv, FREAD); + if (priv->rdch != NULL) { + kn->kn_fop = &dsp_read_filtops; + knlist_add(&priv->rdch->bufsoft->sel.si_note, kn, 1); + } else + err = EINVAL; + dsp_unlock_chans(priv, FREAD); + break; + case EVFILT_WRITE: + dsp_lock_chans(priv, FWRITE); + if (priv->wrch != NULL) { + kn->kn_fop = &dsp_write_filtops; + knlist_add(&priv->wrch->bufsoft->sel.si_note, kn, 1); + } else + err = EINVAL; + dsp_unlock_chans(priv, FWRITE); + break; + default: + kn->kn_hook = NULL; + err = EINVAL; + break; + } + PCM_GIANT_LEAVE(d); + return (err); +} + + #ifdef OSSV4_EXPERIMENT /** * @brief Retrieve an audio device's label