diff --git a/share/examples/sound/oss/audio.c b/share/examples/sound/oss/audio.c --- a/share/examples/sound/oss/audio.c +++ b/share/examples/sound/oss/audio.c @@ -32,15 +32,21 @@ #include #include -#include #include +#include #include #include #include #include +#include + +#define EXAMPLE_LOOP 0 +#define EXAMPLE_SELECT 1 +#define EXAMPLE_POLL 2 +#define EXAMPLE_KQUEUE 3 #ifndef SAMPLE_SIZE -#define SAMPLE_SIZE 16 +#define SAMPLE_SIZE 32 #endif /* Format can be unsigned, in which case replace S with U */ @@ -248,11 +254,33 @@ config->chsamples = config->sample_count / config->channels; } +int +process_audio(config_t *config, int8_t *ibuf, int8_t *obuf, sample_t *channels, int bytes) +{ + int ret; + + ret = read(config->fd, ibuf, bytes); + if (ret < bytes) { + fprintf(stderr, "Requested %d bytes, but read %d!\n", bytes, ret); + return (1); + } + oss_split(config, (sample_t *)ibuf, channels); + /* All processing will happen here */ + oss_merge(config, channels, (sample_t *)obuf); + ret = write(config->fd, obuf, bytes); + if (ret < bytes) { + fprintf(stderr, "Requested %d bytes, but wrote %d!\n", bytes, ret); + return (1); + } + return (0); +} + int main(int argc, char *argv[]) { - int ret, bytes; + int fd, ret, bytes, example; int8_t *ibuf, *obuf; + sample_t *channels; config_t config = { .device = "/dev/dsp", .channels = -1, @@ -264,16 +292,30 @@ .mmap = 0, }; + if (argc != 2) + errx(1, "Usage: %s ", argv[0]); + + if (strcmp(argv[1], "loop") == 0) + example = EXAMPLE_LOOP; + else if (strcmp(argv[1], "select") == 0) + example = EXAMPLE_SELECT; + else if (strcmp(argv[1], "poll") == 0) + example = EXAMPLE_POLL; + else if (strcmp(argv[1], "kqueue") == 0) + example = EXAMPLE_KQUEUE; + else + errx(1, "Unknown example: %s", argv[1]); + /* Initialize device */ oss_init(&config); + bytes = config.buffer_info.bytes; /* * Allocate input and output buffers so that their size match frag_size */ - bytes = config.buffer_info.bytes; ibuf = malloc(bytes); obuf = malloc(bytes); - sample_t *channels = malloc(bytes); + channels = malloc(bytes); printf("bytes: %d, fragments: %d, fragsize: %d, fragstotal: %d, " "samples: %d\n", @@ -281,24 +323,60 @@ config.buffer_info.fragsize, config.buffer_info.fragstotal, config.sample_count); - /* Minimal engine: read input and copy it to the output */ - for (;;) { - ret = read(config.fd, ibuf, bytes); - if (ret < bytes) { - fprintf(stderr, "Requested %d bytes, but read %d!\n", - bytes, ret); - break; + if (example == EXAMPLE_LOOP) { + for (;;) { + printf("Loop processing\n"); + if (process_audio(&config, ibuf, obuf, channels, bytes) != 0) + break; } - oss_split(&config, (sample_t *)ibuf, channels); - /* All processing will happen here */ - oss_merge(&config, channels, (sample_t *)obuf); - ret = write(config.fd, obuf, bytes); - if (ret < bytes) { - fprintf(stderr, "Requested %d bytes, but wrote %d!\n", - bytes, ret); - break; + } else if (example == EXAMPLE_SELECT) { + fd_set fds; + + for (;;) { + printf("Select processing\n"); + FD_ZERO(&fds); + FD_SET(config.fd, &fds); + ret = select(config.fd + 1, &fds, NULL, NULL, NULL); + if (FD_ISSET(config.fd, &fds)) + if (process_audio(&config, ibuf, obuf, channels, bytes) != 0) + break; } - } + } else if (example == EXAMPLE_POLL) { + struct pollfd pfds[1]; + + for (;;) { + printf("Poll processing\n"); + pfds[0].fd = config.fd; + pfds[0].events = POLLIN; + ret = poll(pfds, sizeof(pfds) / sizeof(struct pollfd), -1); + if (pfds[0].revents != 0) + if (process_audio(&config, ibuf, obuf, channels, bytes) != 0) + break; + } + } else if (example == EXAMPLE_KQUEUE) { + int kq; + struct kevent event = {}; + + kq = kqueue(); + if (kq == -1) + errx(1, "Failed to allocate kqueue"); + EV_SET(&event, fd, EVFILT_WRITE, EV_ADD | EV_CLEAR, NOTE_WRITE, 0, 0); + ret = kevent(kq, &event, 1, 0, 0, 0); + if (ret == -1) + errx(1, "Failed to register kevent"); + for (;;) { + printf("Kqueue processing\n"); + ret = kevent(kq, 0, 0, &event, 1, 0); + if (ret == -1 || event.data == 0) + break; + if (event.flags & EV_ERROR) + errx(1, "Event error: %s", strerror(event.data)); + if (process_audio(&config, ibuf, obuf, channels, bytes) != 0) + break; + } + close(kq); + } else + errx(1, "Unknown example id: %d", example); /* Cleanup */ free(channels); 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,140 @@ return (ret); } +static void +dsp_kqdetach(struct knote *kn) +{ + struct dsp_cdevpriv *priv; + struct snddev_info *d; + struct pcm_channel *ch = kn->kn_hook; + struct snd_dbuf *bs; + + if (devfs_get_cdevpriv((void **)&priv) != 0) + return; + d = priv->sc; + if (!DSP_REGISTERED(d)) + return; + PCM_GIANT_ENTER(d); + dsp_lock_chans(priv, FREAD | FWRITE); + 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; + struct snddev_info *d; + struct pcm_channel *ch = kn->kn_hook; + int err; + + if ((err = devfs_get_cdevpriv((void **)&priv)) != 0) + return (err); + 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); + if (ch != NULL && !(ch->flags & CHN_F_DEAD)) + kn->kn_data = sndbuf_getallocsize(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; + struct snddev_info *d; + struct pcm_channel *ch = kn->kn_hook; + int err; + + if ((err = devfs_get_cdevpriv((void **)&priv)) != 0) + return (err); + 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); + if (ch != NULL && !(ch->flags & CHN_F_DEAD)) + kn->kn_data = sndbuf_getallocsize(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; + 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); + switch (kn->kn_filter) { + case EVFILT_READ: + dsp_lock_chans(priv, FREAD); + if (priv->rdch != NULL) { + kn->kn_hook = priv->rdch; + kn->kn_fop = &dsp_read_filtops; + knlist_add(&priv->rdch->bufsoft->sel.si_note, kn, 1); + err = 0; + } else + err = EINVAL; + dsp_unlock_chans(priv, FREAD); + break; + case EVFILT_WRITE: + dsp_lock_chans(priv, FWRITE); + if (priv->rdch != NULL) { + kn->kn_hook = priv->wrch; + kn->kn_fop = &dsp_write_filtops; + knlist_add(&priv->wrch->bufsoft->sel.si_note, kn, 1); + err = 0; + } else + err = EINVAL; + dsp_unlock_chans(priv, FWRITE); + break; + default: + err = EINVAL; + break; + } + PCM_GIANT_LEAVE(d); + return(err); +} + + #ifdef OSSV4_EXPERIMENT /** * @brief Retrieve an audio device's label