aboutsummaryrefslogtreecommitdiff
path: root/sys/dev/sound
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/sound')
-rw-r--r--sys/dev/sound/chip.h47
-rw-r--r--sys/dev/sound/driver.c9
-rw-r--r--sys/dev/sound/dummy.c385
-rw-r--r--sys/dev/sound/fdt/audio_soc.c12
-rw-r--r--sys/dev/sound/macio/aoa.c6
-rw-r--r--sys/dev/sound/macio/i2s.c6
-rw-r--r--sys/dev/sound/macio/onyx.c1
-rw-r--r--sys/dev/sound/midi/midi.c137
-rw-r--r--sys/dev/sound/midi/midi.h12
-rw-r--r--sys/dev/sound/midi/mpu401.c2
-rw-r--r--sys/dev/sound/midi/sequencer.c27
-rw-r--r--sys/dev/sound/pci/als4000.c11
-rw-r--r--sys/dev/sound/pci/atiixp.c10
-rw-r--r--sys/dev/sound/pci/cmi.c6
-rw-r--r--sys/dev/sound/pci/cs4281.c6
-rw-r--r--sys/dev/sound/pci/csa.c98
-rw-r--r--sys/dev/sound/pci/csamidi.c1
-rw-r--r--sys/dev/sound/pci/csapcm.c9
-rw-r--r--sys/dev/sound/pci/csareg.h2
-rw-r--r--sys/dev/sound/pci/emu10k1.c5
-rw-r--r--sys/dev/sound/pci/emu10kx-midi.c1
-rw-r--r--sys/dev/sound/pci/emu10kx-pcm.c9
-rw-r--r--sys/dev/sound/pci/emu10kx.c185
-rw-r--r--sys/dev/sound/pci/envy24.c7
-rw-r--r--sys/dev/sound/pci/envy24ht.c8
-rw-r--r--sys/dev/sound/pci/es137x.c6
-rw-r--r--sys/dev/sound/pci/fm801.c15
-rw-r--r--sys/dev/sound/pci/hda/hdaa.c52
-rw-r--r--sys/dev/sound/pci/hda/hdaa_patches.c62
-rw-r--r--sys/dev/sound/pci/hda/hdac.c57
-rw-r--r--sys/dev/sound/pci/hda/hdac.h23
-rw-r--r--sys/dev/sound/pci/hda/hdacc.c10
-rw-r--r--sys/dev/sound/pci/hda/pin_patch_realtek.h15
-rw-r--r--sys/dev/sound/pci/hdsp-pcm.c1136
-rw-r--r--sys/dev/sound/pci/hdsp.c1022
-rw-r--r--sys/dev/sound/pci/hdsp.h266
-rw-r--r--sys/dev/sound/pci/hdspe-pcm.c83
-rw-r--r--sys/dev/sound/pci/hdspe.c239
-rw-r--r--sys/dev/sound/pci/hdspe.h78
-rw-r--r--sys/dev/sound/pci/ich.c9
-rw-r--r--sys/dev/sound/pci/maestro3.c15
-rw-r--r--sys/dev/sound/pci/neomagic.c5
-rw-r--r--sys/dev/sound/pci/solo.c7
-rw-r--r--sys/dev/sound/pci/spicds.c2
-rw-r--r--sys/dev/sound/pci/t4dwave.c6
-rw-r--r--sys/dev/sound/pci/via8233.c6
-rw-r--r--sys/dev/sound/pci/via82c686.c5
-rw-r--r--sys/dev/sound/pci/vibes.c11
-rw-r--r--sys/dev/sound/pcm/ac97.c119
-rw-r--r--sys/dev/sound/pcm/ac97_patch.c117
-rw-r--r--sys/dev/sound/pcm/ac97_patch.h35
-rw-r--r--sys/dev/sound/pcm/buffer.c31
-rw-r--r--sys/dev/sound/pcm/buffer.h2
-rw-r--r--sys/dev/sound/pcm/channel.c471
-rw-r--r--sys/dev/sound/pcm/channel.h71
-rw-r--r--sys/dev/sound/pcm/dsp.c1009
-rw-r--r--sys/dev/sound/pcm/dsp.h10
-rw-r--r--sys/dev/sound/pcm/feeder.c139
-rw-r--r--sys/dev/sound/pcm/feeder.h32
-rw-r--r--sys/dev/sound/pcm/feeder_chain.c30
-rw-r--r--sys/dev/sound/pcm/feeder_eq.c238
-rw-r--r--sys/dev/sound/pcm/feeder_format.c130
-rw-r--r--sys/dev/sound/pcm/feeder_matrix.c229
-rw-r--r--sys/dev/sound/pcm/feeder_mixer.c176
-rw-r--r--sys/dev/sound/pcm/feeder_rate.c53
-rw-r--r--sys/dev/sound/pcm/feeder_volume.c17
-rw-r--r--sys/dev/sound/pcm/g711.h44
-rw-r--r--sys/dev/sound/pcm/intpcm.h135
-rw-r--r--sys/dev/sound/pcm/matrix.h640
-rw-r--r--sys/dev/sound/pcm/matrix_map.h672
-rw-r--r--sys/dev/sound/pcm/mixer.c237
-rw-r--r--sys/dev/sound/pcm/mixer.h2
-rw-r--r--sys/dev/sound/pcm/pcm.h664
-rw-r--r--sys/dev/sound/pcm/sndstat.c356
-rw-r--r--sys/dev/sound/pcm/sound.c879
-rw-r--r--sys/dev/sound/pcm/sound.h323
-rw-r--r--sys/dev/sound/pcm/vchan.c595
-rw-r--r--sys/dev/sound/pcm/vchan.h8
-rw-r--r--sys/dev/sound/usb/uaudio.c81
-rw-r--r--sys/dev/sound/usb/uaudio.h4
-rw-r--r--sys/dev/sound/usb/uaudio_pcm.c1
-rw-r--r--sys/dev/sound/version.h42
82 files changed, 6790 insertions, 4934 deletions
diff --git a/sys/dev/sound/chip.h b/sys/dev/sound/chip.h
deleted file mode 100644
index bb40d2809a00..000000000000
--- a/sys/dev/sound/chip.h
+++ /dev/null
@@ -1,47 +0,0 @@
-/*-
- * SPDX-License-Identifier: BSD-2-Clause
- *
- * Copyright (c) 1999 Seigo Tanimura
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-/*
- * These are the function codes assigned to the children of
- * sound cards.
- */
-enum {
- SCF_PCM,
- SCF_MIDI,
- SCF_SYNTH,
-};
-
-/*
- * This is the device information struct, used by
- * a bridge device to pass the device function code
- * to the children.
- */
-struct sndcard_func {
- int func; /* The function code. */
- void *varinfo; /* Bridge-specific information. */
-};
diff --git a/sys/dev/sound/driver.c b/sys/dev/sound/driver.c
index 927941ab3d01..c9219261e08f 100644
--- a/sys/dev/sound/driver.c
+++ b/sys/dev/sound/driver.c
@@ -55,18 +55,27 @@ static moduledata_t snd_mod = {
DECLARE_MODULE(snd_driver, snd_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
MODULE_VERSION(snd_driver, 1);
+#if defined(__powerpc__)
+MODULE_DEPEND(snd_driver, snd_ai2s, 1, 1, 1);
+#endif
MODULE_DEPEND(snd_driver, snd_als4000, 1, 1, 1);
MODULE_DEPEND(snd_driver, snd_atiixp, 1, 1, 1);
+#if defined(__i386__) || defined(__amd64__)
MODULE_DEPEND(snd_driver, snd_cmi, 1, 1, 1);
+#endif
MODULE_DEPEND(snd_driver, snd_cs4281, 1, 1, 1);
MODULE_DEPEND(snd_driver, snd_csa, 1, 1, 1);
MODULE_DEPEND(snd_driver, snd_csapcm, 1, 1, 1);
+#if defined(__powerpc__)
+MODULE_DEPEND(snd_driver, snd_davbus, 1, 1, 1);
+#endif
MODULE_DEPEND(snd_driver, snd_emu10kx, 1, 1, 1);
MODULE_DEPEND(snd_driver, snd_envy24, 1, 1, 1);
MODULE_DEPEND(snd_driver, snd_envy24ht, 1, 1, 1);
MODULE_DEPEND(snd_driver, snd_es137x, 1, 1, 1);
MODULE_DEPEND(snd_driver, snd_fm801, 1, 1, 1);
MODULE_DEPEND(snd_driver, snd_hda, 1, 1, 1);
+MODULE_DEPEND(snd_driver, snd_hdsp, 1, 1, 1);
MODULE_DEPEND(snd_driver, snd_hdspe, 1, 1, 1);
MODULE_DEPEND(snd_driver, snd_ich, 1, 1, 1);
MODULE_DEPEND(snd_driver, snd_maestro3, 1, 1, 1);
diff --git a/sys/dev/sound/dummy.c b/sys/dev/sound/dummy.c
new file mode 100644
index 000000000000..4df5b112d3f4
--- /dev/null
+++ b/sys/dev/sound/dummy.c
@@ -0,0 +1,385 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2024-2025 The FreeBSD Foundation
+ *
+ * This software was developed by Christos Margiolis <christos@FreeBSD.org>
+ * under sponsorship from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+
+#ifdef HAVE_KERNEL_OPTION_HEADERS
+#include "opt_snd.h"
+#endif
+
+#include <dev/sound/pcm/sound.h>
+#include <mixer_if.h>
+
+#define DUMMY_NPCHAN 1
+#define DUMMY_NRCHAN 1
+#define DUMMY_NCHAN (DUMMY_NPCHAN + DUMMY_NRCHAN)
+
+struct dummy_chan {
+ struct dummy_softc *sc;
+ struct pcm_channel *chan;
+ struct snd_dbuf *buf;
+ struct pcmchan_caps *caps;
+ uint32_t ptr;
+ int dir;
+ int run;
+};
+
+struct dummy_softc {
+ struct snddev_info info;
+ device_t dev;
+ uint32_t cap_fmts[4];
+ struct pcmchan_caps caps;
+ int chnum;
+ struct dummy_chan chans[DUMMY_NCHAN];
+ struct callout callout;
+ struct mtx *lock;
+ bool stopped;
+};
+
+static bool
+dummy_active(struct dummy_softc *sc)
+{
+ struct dummy_chan *ch;
+ int i;
+
+ snd_mtxassert(sc->lock);
+
+ for (i = 0; i < sc->chnum; i++) {
+ ch = &sc->chans[i];
+ if (ch->run)
+ return (true);
+ }
+
+ /* No channel is running at the moment. */
+ return (false);
+}
+
+static void
+dummy_chan_io(void *arg)
+{
+ struct dummy_softc *sc = arg;
+ struct dummy_chan *ch;
+ int i = 0;
+
+ if (sc->stopped)
+ return;
+
+ /* Do not reschedule if no channel is running. */
+ if (!dummy_active(sc))
+ return;
+
+ for (i = 0; i < sc->chnum; i++) {
+ ch = &sc->chans[i];
+ if (!ch->run)
+ continue;
+ if (ch->dir == PCMDIR_PLAY)
+ ch->ptr += sndbuf_getblksz(ch->buf);
+ else
+ sndbuf_fillsilence(ch->buf);
+ snd_mtxunlock(sc->lock);
+ chn_intr(ch->chan);
+ snd_mtxlock(sc->lock);
+ }
+ if (!sc->stopped)
+ callout_schedule(&sc->callout, 1);
+}
+
+static int
+dummy_chan_free(kobj_t obj, void *data)
+{
+ struct dummy_chan *ch =data;
+ uint8_t *buf;
+
+ buf = sndbuf_getbuf(ch->buf);
+ if (buf != NULL)
+ free(buf, M_DEVBUF);
+
+ return (0);
+}
+
+static void *
+dummy_chan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b,
+ struct pcm_channel *c, int dir)
+{
+ struct dummy_softc *sc;
+ struct dummy_chan *ch;
+ uint8_t *buf;
+ size_t bufsz;
+
+ sc = devinfo;
+
+ snd_mtxlock(sc->lock);
+
+ ch = &sc->chans[sc->chnum++];
+ ch->sc = sc;
+ ch->dir = dir;
+ ch->chan = c;
+ ch->buf = b;
+ ch->caps = &sc->caps;
+
+ snd_mtxunlock(sc->lock);
+
+ bufsz = pcm_getbuffersize(sc->dev, 2048, 2048, 65536);
+ buf = malloc(bufsz, M_DEVBUF, M_WAITOK | M_ZERO);
+ if (sndbuf_setup(ch->buf, buf, bufsz) != 0) {
+ dummy_chan_free(obj, ch);
+ return (NULL);
+ }
+
+ return (ch);
+}
+
+static int
+dummy_chan_setformat(kobj_t obj, void *data, uint32_t format)
+{
+ struct dummy_chan *ch = data;
+ int i;
+
+ for (i = 0; ch->caps->fmtlist[i]; i++)
+ if (format == ch->caps->fmtlist[i])
+ return (0);
+
+ return (EINVAL);
+}
+
+static uint32_t
+dummy_chan_setspeed(kobj_t obj, void *data, uint32_t speed)
+{
+ struct dummy_chan *ch = data;
+
+ RANGE(speed, ch->caps->minspeed, ch->caps->maxspeed);
+
+ return (speed);
+}
+
+static uint32_t
+dummy_chan_setblocksize(kobj_t obj, void *data, uint32_t blocksize)
+{
+ struct dummy_chan *ch = data;
+
+ return (sndbuf_getblksz(ch->buf));
+}
+
+static int
+dummy_chan_trigger(kobj_t obj, void *data, int go)
+{
+ struct dummy_chan *ch = data;
+ struct dummy_softc *sc = ch->sc;
+
+ snd_mtxlock(sc->lock);
+
+ if (sc->stopped) {
+ snd_mtxunlock(sc->lock);
+ return (0);
+ }
+
+ switch (go) {
+ case PCMTRIG_START:
+ ch->ptr = 0;
+ ch->run = 1;
+ callout_reset(&sc->callout, 1, dummy_chan_io, sc);
+ break;
+ case PCMTRIG_STOP:
+ case PCMTRIG_ABORT:
+ ch->run = 0;
+ /* If all channels are stopped, stop the callout as well. */
+ if (!dummy_active(sc))
+ callout_stop(&sc->callout);
+ default:
+ break;
+ }
+
+ snd_mtxunlock(sc->lock);
+
+ return (0);
+}
+
+static uint32_t
+dummy_chan_getptr(kobj_t obj, void *data)
+{
+ struct dummy_chan *ch = data;
+
+ return (ch->run ? ch->ptr : 0);
+}
+
+static struct pcmchan_caps *
+dummy_chan_getcaps(kobj_t obj, void *data)
+{
+ struct dummy_chan *ch = data;
+
+ return (ch->caps);
+}
+
+static kobj_method_t dummy_chan_methods[] = {
+ KOBJMETHOD(channel_init, dummy_chan_init),
+ KOBJMETHOD(channel_free, dummy_chan_free),
+ KOBJMETHOD(channel_setformat, dummy_chan_setformat),
+ KOBJMETHOD(channel_setspeed, dummy_chan_setspeed),
+ KOBJMETHOD(channel_setblocksize,dummy_chan_setblocksize),
+ KOBJMETHOD(channel_trigger, dummy_chan_trigger),
+ KOBJMETHOD(channel_getptr, dummy_chan_getptr),
+ KOBJMETHOD(channel_getcaps, dummy_chan_getcaps),
+ KOBJMETHOD_END
+};
+
+CHANNEL_DECLARE(dummy_chan);
+
+static int
+dummy_mixer_init(struct snd_mixer *m)
+{
+ struct dummy_softc *sc;
+
+ sc = mix_getdevinfo(m);
+ if (sc == NULL)
+ return (-1);
+
+ pcm_setflags(sc->dev, pcm_getflags(sc->dev) | SD_F_SOFTPCMVOL);
+ mix_setdevs(m, SOUND_MASK_PCM | SOUND_MASK_VOLUME | SOUND_MASK_RECLEV);
+ mix_setrecdevs(m, SOUND_MASK_RECLEV);
+
+ return (0);
+}
+
+static int
+dummy_mixer_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right)
+{
+ return (0);
+}
+
+static uint32_t
+dummy_mixer_setrecsrc(struct snd_mixer *m, uint32_t src)
+{
+ return (src == SOUND_MASK_RECLEV ? src : 0);
+}
+
+static kobj_method_t dummy_mixer_methods[] = {
+ KOBJMETHOD(mixer_init, dummy_mixer_init),
+ KOBJMETHOD(mixer_set, dummy_mixer_set),
+ KOBJMETHOD(mixer_setrecsrc, dummy_mixer_setrecsrc),
+ KOBJMETHOD_END
+};
+
+MIXER_DECLARE(dummy_mixer);
+
+static void
+dummy_identify(driver_t *driver, device_t parent)
+{
+ if (device_find_child(parent, driver->name, DEVICE_UNIT_ANY) != NULL)
+ return;
+ if (BUS_ADD_CHILD(parent, 0, driver->name, DEVICE_UNIT_ANY) == NULL)
+ device_printf(parent, "add child failed\n");
+}
+
+static int
+dummy_probe(device_t dev)
+{
+ device_set_desc(dev, "Dummy Audio Device");
+
+ return (0);
+}
+
+static int
+dummy_attach(device_t dev)
+{
+ struct dummy_softc *sc;
+ char status[SND_STATUSLEN];
+ int i = 0;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+ sc->lock = snd_mtxcreate(device_get_nameunit(dev), "snd_dummy softc");
+ callout_init_mtx(&sc->callout, sc->lock, 0);
+
+ sc->cap_fmts[0] = SND_FORMAT(AFMT_S32_LE, 2, 0);
+ sc->cap_fmts[1] = SND_FORMAT(AFMT_S24_LE, 2, 0);
+ sc->cap_fmts[2] = SND_FORMAT(AFMT_S16_LE, 2, 0);
+ sc->cap_fmts[3] = 0;
+ sc->caps = (struct pcmchan_caps){
+ 8000, /* minspeed */
+ 96000, /* maxspeed */
+ sc->cap_fmts, /* fmtlist */
+ 0, /* caps */
+ };
+
+ pcm_setflags(dev, pcm_getflags(dev) | SD_F_MPSAFE);
+ pcm_init(dev, sc);
+ for (i = 0; i < DUMMY_NPCHAN; i++)
+ pcm_addchan(dev, PCMDIR_PLAY, &dummy_chan_class, sc);
+ for (i = 0; i < DUMMY_NRCHAN; i++)
+ pcm_addchan(dev, PCMDIR_REC, &dummy_chan_class, sc);
+
+ snprintf(status, SND_STATUSLEN, "on %s",
+ device_get_nameunit(device_get_parent(dev)));
+ if (pcm_register(dev, status))
+ return (ENXIO);
+ mixer_init(dev, &dummy_mixer_class, sc);
+
+ return (0);
+}
+
+static int
+dummy_detach(device_t dev)
+{
+ struct dummy_softc *sc = device_get_softc(dev);
+ int err;
+
+ snd_mtxlock(sc->lock);
+ sc->stopped = true;
+ snd_mtxunlock(sc->lock);
+ callout_drain(&sc->callout);
+ err = pcm_unregister(dev);
+ snd_mtxfree(sc->lock);
+
+ return (err);
+}
+
+static device_method_t dummy_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_identify, dummy_identify),
+ DEVMETHOD(device_probe, dummy_probe),
+ DEVMETHOD(device_attach, dummy_attach),
+ DEVMETHOD(device_detach, dummy_detach),
+ DEVMETHOD_END
+};
+
+static driver_t dummy_driver = {
+ "pcm",
+ dummy_methods,
+ sizeof(struct dummy_softc),
+};
+
+DRIVER_MODULE(snd_dummy, nexus, dummy_driver, 0, 0);
+MODULE_DEPEND(snd_dummy, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER);
+MODULE_VERSION(snd_dummy, 1);
diff --git a/sys/dev/sound/fdt/audio_soc.c b/sys/dev/sound/fdt/audio_soc.c
index 3e937720bb5b..c2bdea399364 100644
--- a/sys/dev/sound/fdt/audio_soc.c
+++ b/sys/dev/sound/fdt/audio_soc.c
@@ -367,7 +367,7 @@ audio_soc_init(void *arg)
auxdev = OF_device_from_xref(aux_devs[i]);
if (auxdev == NULL)
device_printf(sc->dev, "warning: no driver attached to aux node\n");
- aux_node = (struct audio_soc_aux_node *)malloc(sizeof(*aux_node), M_DEVBUF, M_NOWAIT);
+ aux_node = malloc(sizeof(*aux_node), M_DEVBUF, M_NOWAIT);
if (aux_node == NULL) {
device_printf(sc->dev, "failed to allocate aux node struct\n");
return;
@@ -397,10 +397,7 @@ audio_soc_init(void *arg)
}
}
- if (pcm_register(sc->dev, sc, 1, 1)) {
- device_printf(sc->dev, "failed to register PCM\n");
- return;
- }
+ pcm_init(sc->dev, sc);
sc->play_channel.sc = sc;
sc->rec_channel.sc = sc;
@@ -408,7 +405,10 @@ audio_soc_init(void *arg)
pcm_addchan(sc->dev, PCMDIR_PLAY, &audio_soc_chan_class, &sc->play_channel);
pcm_addchan(sc->dev, PCMDIR_REC, &audio_soc_chan_class, &sc->rec_channel);
- pcm_setstatus(sc->dev, "at simplebus");
+ if (pcm_register(sc->dev, "at simplebus")) {
+ device_printf(sc->dev, "failed to register PCM\n");
+ return;
+ }
AUDIO_DAI_SETUP_INTR(sc->cpu_dev, audio_soc_intr, sc);
AUDIO_DAI_SETUP_MIXER(sc->codec_dev, sc->dev);
diff --git a/sys/dev/sound/macio/aoa.c b/sys/dev/sound/macio/aoa.c
index 27626b3d570a..9861bbd92a0c 100644
--- a/sys/dev/sound/macio/aoa.c
+++ b/sys/dev/sound/macio/aoa.c
@@ -372,8 +372,7 @@ aoa_attach(void *xsc)
sc = xsc;
self = sc->sc_dev;
- if (pcm_register(self, sc, 1, 0))
- return (ENXIO);
+ pcm_init(self, sc);
err = pcm_getbuffersize(self, AOA_BUFFER_SIZE, AOA_BUFFER_SIZE,
AOA_BUFFER_SIZE);
@@ -382,7 +381,6 @@ aoa_attach(void *xsc)
pcm_addchan(self, PCMDIR_PLAY, &aoa_chan_class, sc);
snprintf(status, sizeof(status), "at %s", ofw_bus_get_name(self));
- pcm_setstatus(self, status);
- return (0);
+ return (pcm_register(self, status));
}
diff --git a/sys/dev/sound/macio/i2s.c b/sys/dev/sound/macio/i2s.c
index 5f8cb3aa15f7..647d66c27bba 100644
--- a/sys/dev/sound/macio/i2s.c
+++ b/sys/dev/sound/macio/i2s.c
@@ -241,10 +241,8 @@ i2s_attach(device_t self)
* Register a hook for delayed attach in order to allow
* the I2C controller to attach.
*/
- if ((i2s_delayed_attach = malloc(sizeof(struct intr_config_hook),
- M_TEMP, M_WAITOK | M_ZERO)) == NULL)
- return (ENOMEM);
-
+ i2s_delayed_attach = malloc(sizeof(struct intr_config_hook),
+ M_TEMP, M_WAITOK | M_ZERO);
i2s_delayed_attach->ich_func = i2s_postattach;
i2s_delayed_attach->ich_arg = sc;
diff --git a/sys/dev/sound/macio/onyx.c b/sys/dev/sound/macio/onyx.c
index 00c7b826f142..d13f3da92db6 100644
--- a/sys/dev/sound/macio/onyx.c
+++ b/sys/dev/sound/macio/onyx.c
@@ -197,7 +197,6 @@ onyx_probe(device_t dev)
if (strcmp(name, "codec") == 0) {
if (iicbus_get_addr(dev) != PCM3052_IICADDR)
return (ENXIO);
- } else if (strcmp(name, "codec") == 0) {
compat = ofw_bus_get_compat(dev);
if (compat == NULL || strcmp(compat, "pcm3052") != 0)
return (ENXIO);
diff --git a/sys/dev/sound/midi/midi.c b/sys/dev/sound/midi/midi.c
index 81c20580f7b8..fbfb69de2913 100644
--- a/sys/dev/sound/midi/midi.c
+++ b/sys/dev/sound/midi/midi.c
@@ -73,10 +73,6 @@ MALLOC_DEFINE(M_MIDI, "midi buffers", "Midi data allocation area");
#define KOBJMETHOD_END { NULL, NULL }
#endif
-#define PCMMKMINOR(u, d, c) ((((c) & 0xff) << 16) | (((u) & 0x0f) << 4) | ((d) & 0x0f))
-#define MIDIMKMINOR(u, d, c) PCMMKMINOR(u, d, c)
-
-#define MIDI_DEV_RAW 2
#define MIDI_DEV_MIDICTL 12
enum midi_states {
@@ -181,7 +177,8 @@ TAILQ_HEAD(, snd_midi) midi_devs;
* /dev/midistat variables and declarations, protected by midistat_lock
*/
-static struct sx midistat_lock;
+struct sx mstat_lock;
+
static int midistat_isopen = 0;
static struct sbuf midistat_sbuf;
static struct cdev *midistat_dev;
@@ -261,6 +258,24 @@ SYSCTL_INT(_hw_midi_stat, OID_AUTO, verbose, CTLFLAG_RW,
* CODE START
*/
+void
+midistat_lock(void)
+{
+ sx_xlock(&mstat_lock);
+}
+
+void
+midistat_unlock(void)
+{
+ sx_xunlock(&mstat_lock);
+}
+
+void
+midistat_lockassert(void)
+{
+ sx_assert(&mstat_lock, SA_XLOCKED);
+}
+
/*
* Register a new rmidi device. cls midi_if interface unit == 0 means
* auto-assign new unit number unit != 0 already assigned a unit number, eg.
@@ -280,10 +295,10 @@ midi_init(kobj_class_t cls, int unit, int channel, void *cookie)
struct snd_midi *m;
int i;
int inqsize, outqsize;
- MIDI_TYPE *buf;
+ uint8_t *buf;
MIDI_DEBUG(1, printf("midiinit: unit %d/%d.\n", unit, channel));
- sx_xlock(&midistat_lock);
+ midistat_lock();
/*
* Protect against call with existing unit/channel or auto-allocate a
* new unit number.
@@ -329,14 +344,14 @@ midi_init(kobj_class_t cls, int unit, int channel, void *cookie)
mtx_lock(&m->qlock);
if (inqsize)
- buf = malloc(sizeof(MIDI_TYPE) * inqsize, M_MIDI, M_NOWAIT);
+ buf = malloc(sizeof(uint8_t) * inqsize, M_MIDI, M_NOWAIT);
else
buf = NULL;
MIDIQ_INIT(m->inq, buf, inqsize);
if (outqsize)
- buf = malloc(sizeof(MIDI_TYPE) * outqsize, M_MIDI, M_NOWAIT);
+ buf = malloc(sizeof(uint8_t) * outqsize, M_MIDI, M_NOWAIT);
else
buf = NULL;
m->hiwat = outqsize / 2;
@@ -361,11 +376,10 @@ midi_init(kobj_class_t cls, int unit, int channel, void *cookie)
TAILQ_INSERT_TAIL(&midi_devs, m, link);
- sx_xunlock(&midistat_lock);
+ midistat_unlock();
- m->dev = make_dev(&midi_cdevsw,
- MIDIMKMINOR(unit, MIDI_DEV_RAW, channel),
- UID_ROOT, GID_WHEEL, 0666, "midi%d.%d", unit, channel);
+ m->dev = make_dev(&midi_cdevsw, unit, UID_ROOT, GID_WHEEL, 0666,
+ "midi%d.%d", unit, channel);
m->dev->si_drv1 = m;
return m;
@@ -382,7 +396,7 @@ err1:
free(m->synth, M_MIDI);
free(m, M_MIDI);
err0:
- sx_xunlock(&midistat_lock);
+ midistat_unlock();
MIDI_DEBUG(1, printf("midi_init ended in error\n"));
return NULL;
}
@@ -400,7 +414,7 @@ midi_uninit(struct snd_midi *m)
int err;
err = EBUSY;
- sx_xlock(&midistat_lock);
+ midistat_lock();
mtx_lock(&m->lock);
if (m->busy) {
if (!(m->rchan || m->wchan))
@@ -422,7 +436,7 @@ midi_uninit(struct snd_midi *m)
err:
mtx_unlock(&m->lock);
exit:
- sx_xunlock(&midistat_lock);
+ midistat_unlock();
return err;
}
@@ -447,12 +461,12 @@ static int midi_lengths[] = {2, 2, 2, 2, 1, 1, 2, 0};
#define MIDI_SYSEX_END 0xF7
int
-midi_in(struct snd_midi *m, MIDI_TYPE *buf, int size)
+midi_in(struct snd_midi *m, uint8_t *buf, int size)
{
/* int i, sig, enq; */
int used;
- /* MIDI_TYPE data; */
+ /* uint8_t data; */
MIDI_DEBUG(5, printf("midi_in: m=%p size=%d\n", m, size));
/*
@@ -577,7 +591,7 @@ midi_in(struct snd_midi *m, MIDI_TYPE *buf, int size)
* midi_out: The only clearer of the M_TXEN flag.
*/
int
-midi_out(struct snd_midi *m, MIDI_TYPE *buf, int size)
+midi_out(struct snd_midi *m, uint8_t *buf, int size)
{
int used;
@@ -930,21 +944,17 @@ midistat_open(struct cdev *i_dev, int flags, int mode, struct thread *td)
MIDI_DEBUG(1, printf("midistat_open\n"));
- sx_xlock(&midistat_lock);
+ midistat_lock();
if (midistat_isopen) {
- sx_xunlock(&midistat_lock);
+ midistat_unlock();
return EBUSY;
}
midistat_isopen = 1;
- if (sbuf_new(&midistat_sbuf, NULL, 4096, SBUF_AUTOEXTEND) == NULL) {
- error = ENXIO;
- goto out;
- }
+ sbuf_new(&midistat_sbuf, NULL, 4096, SBUF_AUTOEXTEND);
error = (midistat_prepare(&midistat_sbuf) > 0) ? 0 : ENOMEM;
-out:
if (error)
midistat_isopen = 0;
- sx_xunlock(&midistat_lock);
+ midistat_unlock();
return error;
}
@@ -952,14 +962,14 @@ static int
midistat_close(struct cdev *i_dev, int flags, int mode, struct thread *td)
{
MIDI_DEBUG(1, printf("midistat_close\n"));
- sx_xlock(&midistat_lock);
+ midistat_lock();
if (!midistat_isopen) {
- sx_xunlock(&midistat_lock);
+ midistat_unlock();
return EBADF;
}
sbuf_delete(&midistat_sbuf);
midistat_isopen = 0;
- sx_xunlock(&midistat_lock);
+ midistat_unlock();
return 0;
}
@@ -970,13 +980,13 @@ midistat_read(struct cdev *i_dev, struct uio *uio, int flag)
int err;
MIDI_DEBUG(4, printf("midistat_read\n"));
- sx_xlock(&midistat_lock);
+ midistat_lock();
if (!midistat_isopen) {
- sx_xunlock(&midistat_lock);
+ midistat_unlock();
return EBADF;
}
if (uio->uio_offset < 0 || uio->uio_offset > sbuf_len(&midistat_sbuf)) {
- sx_xunlock(&midistat_lock);
+ midistat_unlock();
return EINVAL;
}
err = 0;
@@ -985,7 +995,7 @@ midistat_read(struct cdev *i_dev, struct uio *uio, int flag)
err = uiomove(sbuf_data(&midistat_sbuf) + uio->uio_offset, l,
uio);
}
- sx_xunlock(&midistat_lock);
+ midistat_unlock();
return err;
}
@@ -998,7 +1008,7 @@ midistat_prepare(struct sbuf *s)
{
struct snd_midi *m;
- sx_assert(&midistat_lock, SA_XLOCKED);
+ midistat_lockassert();
sbuf_printf(s, "FreeBSD Midi Driver (midi2)\n");
if (TAILQ_EMPTY(&midi_devs)) {
@@ -1357,7 +1367,7 @@ midisynth_bender(void *n, uint8_t chn, uint16_t val)
static int
midi_destroy(struct snd_midi *m, int midiuninit)
{
- sx_assert(&midistat_lock, SA_XLOCKED);
+ midistat_lockassert();
mtx_assert(&m->lock, MA_OWNED);
MIDI_DEBUG(3, printf("midi_destroy\n"));
@@ -1383,12 +1393,11 @@ midi_destroy(struct snd_midi *m, int midiuninit)
static int
midi_load(void)
{
- sx_init(&midistat_lock, "midistat lock");
+ sx_init(&mstat_lock, "midistat lock");
TAILQ_INIT(&midi_devs);
- midistat_dev = make_dev(&midistat_cdevsw,
- MIDIMKMINOR(0, MIDI_DEV_MIDICTL, 0),
- UID_ROOT, GID_WHEEL, 0666, "midistat");
+ midistat_dev = make_dev(&midistat_cdevsw, MIDI_DEV_MIDICTL, UID_ROOT,
+ GID_WHEEL, 0666, "midistat");
return 0;
}
@@ -1401,7 +1410,7 @@ midi_unload(void)
MIDI_DEBUG(1, printf("midi_unload()\n"));
retval = EBUSY;
- sx_xlock(&midistat_lock);
+ midistat_lock();
if (midistat_isopen)
goto exit0;
@@ -1414,19 +1423,19 @@ midi_unload(void)
if (retval)
goto exit1;
}
- sx_xunlock(&midistat_lock);
+ midistat_unlock();
destroy_dev(midistat_dev);
/*
* Made it here then unload is complete
*/
- sx_destroy(&midistat_lock);
+ sx_destroy(&mstat_lock);
return 0;
exit1:
mtx_unlock(&m->lock);
exit0:
- sx_xunlock(&midistat_lock);
+ midistat_unlock();
if (retval)
MIDI_DEBUG(2, printf("midi_unload: failed\n"));
return retval;
@@ -1470,16 +1479,28 @@ midimapper_addseq(void *arg1, int *unit, void **cookie)
}
int
-midimapper_open(void *arg1, void **cookie)
+midimapper_open_locked(void *arg1, void **cookie)
{
int retval = 0;
struct snd_midi *m;
- sx_xlock(&midistat_lock);
+ midistat_lockassert();
TAILQ_FOREACH(m, &midi_devs, link) {
retval++;
}
- sx_xunlock(&midistat_lock);
+
+ return retval;
+}
+
+int
+midimapper_open(void *arg1, void **cookie)
+{
+ int retval;
+
+ midistat_lock();
+ retval = midimapper_open_locked(arg1, cookie);
+ midistat_unlock();
+
return retval;
}
@@ -1490,22 +1511,32 @@ midimapper_close(void *arg1, void *cookie)
}
kobj_t
-midimapper_fetch_synth(void *arg, void *cookie, int unit)
+midimapper_fetch_synth_locked(void *arg, void *cookie, int unit)
{
struct snd_midi *m;
int retval = 0;
- sx_xlock(&midistat_lock);
+ midistat_lockassert();
TAILQ_FOREACH(m, &midi_devs, link) {
- if (unit == retval) {
- sx_xunlock(&midistat_lock);
+ if (unit == retval)
return (kobj_t)m->synth;
- }
retval++;
}
- sx_xunlock(&midistat_lock);
+
return NULL;
}
+kobj_t
+midimapper_fetch_synth(void *arg, void *cookie, int unit)
+{
+ kobj_t synth;
+
+ midistat_lock();
+ synth = midimapper_fetch_synth_locked(arg, cookie, unit);
+ midistat_unlock();
+
+ return synth;
+}
+
DEV_MODULE(midi, midi_modevent, NULL);
MODULE_VERSION(midi, 1);
diff --git a/sys/dev/sound/midi/midi.h b/sys/dev/sound/midi/midi.h
index 567279d1e654..2254fab690e9 100644
--- a/sys/dev/sound/midi/midi.h
+++ b/sys/dev/sound/midi/midi.h
@@ -39,19 +39,23 @@ MALLOC_DECLARE(M_MIDI);
#define M_RXEN 0x04
#define M_TXEN 0x08
-#define MIDI_TYPE unsigned char
-
struct snd_midi;
+void midistat_lock(void);
+void midistat_unlock(void);
+void midistat_lockassert(void);
+
struct snd_midi *
midi_init(kobj_class_t _mpu_cls, int _unit, int _channel, void *cookie);
int midi_uninit(struct snd_midi *_m);
-int midi_out(struct snd_midi *_m, MIDI_TYPE *_buf, int _size);
-int midi_in(struct snd_midi *_m, MIDI_TYPE *_buf, int _size);
+int midi_out(struct snd_midi *_m, uint8_t *_buf, int _size);
+int midi_in(struct snd_midi *_m, uint8_t *_buf, int _size);
kobj_t midimapper_addseq(void *arg1, int *unit, void **cookie);
+int midimapper_open_locked(void *arg1, void **cookie);
int midimapper_open(void *arg1, void **cookie);
int midimapper_close(void *arg1, void *cookie);
+kobj_t midimapper_fetch_synth_locked(void *arg, void *cookie, int unit);
kobj_t midimapper_fetch_synth(void *arg, void *cookie, int unit);
#endif
diff --git a/sys/dev/sound/midi/mpu401.c b/sys/dev/sound/midi/mpu401.c
index a344801d1982..2be285bc0040 100644
--- a/sys/dev/sound/midi/mpu401.c
+++ b/sys/dev/sound/midi/mpu401.c
@@ -118,7 +118,7 @@ static int
mpu401_intr(struct mpu401 *m)
{
#define MPU_INTR_BUF 16
- MIDI_TYPE b[MPU_INTR_BUF];
+ uint8_t b[MPU_INTR_BUF];
int i;
int s;
diff --git a/sys/dev/sound/midi/sequencer.c b/sys/dev/sound/midi/sequencer.c
index 817540f1545a..03b71688175c 100644
--- a/sys/dev/sound/midi/sequencer.c
+++ b/sys/dev/sound/midi/sequencer.c
@@ -64,6 +64,7 @@
#include <sys/kthread.h>
#include <sys/unistd.h>
#include <sys/selinfo.h>
+#include <sys/sx.h>
#ifdef HAVE_KERNEL_OPTION_HEADERS
#include "opt_snd.h"
@@ -90,10 +91,6 @@
#define LOOKUP_OPEN (1)
#define LOOKUP_CLOSE (2)
-#define PCMMKMINOR(u, d, c) \
- ((((c) & 0xff) << 16) | (((u) & 0x0f) << 4) | ((d) & 0x0f))
-#define MIDIMKMINOR(u, d, c) PCMMKMINOR(u, d, c)
-#define MIDIUNIT(y) ((dev2unit(y) >> 4) & 0x0f)
#define MIDIDEV(y) (dev2unit(y) & 0x0f)
/* These are the entries to the sequencer driver. */
@@ -522,6 +519,9 @@ seq_addunit(void)
int ret;
u_char *buf;
+ gone_in(15, "Warning! MIDI sequencer to be removed soon: no longer "
+ "needed or used\n");
+
/* Allocate the softc. */
ret = ENOMEM;
scp = malloc(sizeof(*scp), M_DEVBUF, M_NOWAIT | M_ZERO);
@@ -566,12 +566,10 @@ seq_addunit(void)
if (scp->mapper == NULL)
goto err;
- scp->seqdev = make_dev(&seq_cdevsw,
- MIDIMKMINOR(scp->unit, SND_DEV_SEQ, 0), UID_ROOT,
- GID_WHEEL, 0666, "sequencer%d", scp->unit);
+ scp->seqdev = make_dev(&seq_cdevsw, SND_DEV_SEQ, UID_ROOT, GID_WHEEL,
+ 0666, "sequencer%d", scp->unit);
- scp->musicdev = make_dev(&seq_cdevsw,
- MIDIMKMINOR(scp->unit, SND_DEV_MUSIC, 0), UID_ROOT,
+ scp->musicdev = make_dev(&seq_cdevsw, SND_DEV_MUSIC, UID_ROOT,
GID_WHEEL, 0666, "music%d", scp->unit);
if (scp->seqdev == NULL || scp->musicdev == NULL)
@@ -741,6 +739,9 @@ mseq_open(struct cdev *i_dev, int flags, int mode, struct thread *td)
struct seq_softc *scp = i_dev->si_drv1;
int i;
+ gone_in(15, "Warning! MIDI sequencer to be removed soon: no longer "
+ "needed or used\n");
+
if (scp == NULL)
return ENXIO;
@@ -751,9 +752,11 @@ mseq_open(struct cdev *i_dev, int flags, int mode, struct thread *td)
* Mark this device busy.
*/
+ midistat_lock();
mtx_lock(&scp->seq_lock);
if (scp->busy) {
mtx_unlock(&scp->seq_lock);
+ midistat_unlock();
SEQ_DEBUG(2, printf("seq_open: unit %d is busy.\n", scp->unit));
return EBUSY;
}
@@ -768,14 +771,15 @@ mseq_open(struct cdev *i_dev, int flags, int mode, struct thread *td)
* Enumerate the available midi devices
*/
scp->midi_number = 0;
- scp->maxunits = midimapper_open(scp->mapper, &scp->mapper_cookie);
+ scp->maxunits = midimapper_open_locked(scp->mapper, &scp->mapper_cookie);
if (scp->maxunits == 0)
SEQ_DEBUG(2, printf("seq_open: no midi devices\n"));
for (i = 0; i < scp->maxunits; i++) {
scp->midis[scp->midi_number] =
- midimapper_fetch_synth(scp->mapper, scp->mapper_cookie, i);
+ midimapper_fetch_synth_locked(scp->mapper,
+ scp->mapper_cookie, i);
if (scp->midis[scp->midi_number]) {
if (SYNTH_OPEN(scp->midis[scp->midi_number], scp,
scp->fflags) != 0)
@@ -787,6 +791,7 @@ mseq_open(struct cdev *i_dev, int flags, int mode, struct thread *td)
}
}
}
+ midistat_unlock();
timer_setvals(scp, 60, 100);
diff --git a/sys/dev/sound/pci/als4000.c b/sys/dev/sound/pci/als4000.c
index b1376d2b6e5a..9d86713b379e 100644
--- a/sys/dev/sound/pci/als4000.c
+++ b/sys/dev/sound/pci/als4000.c
@@ -839,10 +839,7 @@ als_pci_attach(device_t dev)
goto bad_attach;
}
- if (pcm_register(dev, sc, 1, 1)) {
- device_printf(dev, "failed to register pcm entries\n");
- goto bad_attach;
- }
+ pcm_init(dev, sc);
pcm_addchan(dev, PCMDIR_PLAY, &alspchan_class, sc);
pcm_addchan(dev, PCMDIR_REC, &alsrchan_class, sc);
@@ -850,7 +847,11 @@ als_pci_attach(device_t dev)
snprintf(status, SND_STATUSLEN, "port 0x%jx irq %jd on %s",
rman_get_start(sc->reg), rman_get_start(sc->irq),
device_get_nameunit(device_get_parent(dev)));
- pcm_setstatus(dev, status);
+ if (pcm_register(dev, status)) {
+ device_printf(dev, "failed to register pcm entries\n");
+ goto bad_attach;
+ }
+
return 0;
bad_attach:
diff --git a/sys/dev/sound/pci/atiixp.c b/sys/dev/sound/pci/atiixp.c
index dcbf041f9605..90e5742e6523 100644
--- a/sys/dev/sound/pci/atiixp.c
+++ b/sys/dev/sound/pci/atiixp.c
@@ -1084,8 +1084,7 @@ atiixp_chip_post_init(void *arg)
mixer_init(sc->dev, ac97_getmixerclass(), sc->codec);
- if (pcm_register(sc->dev, sc, ATI_IXP_NPCHAN, ATI_IXP_NRCHAN))
- goto postinitbad;
+ pcm_init(sc->dev, sc);
for (i = 0; i < ATI_IXP_NPCHAN; i++)
pcm_addchan(sc->dev, PCMDIR_PLAY, &atiixp_chan_class, sc);
@@ -1101,7 +1100,8 @@ atiixp_chip_post_init(void *arg)
rman_get_start(sc->reg), rman_get_start(sc->irq),
device_get_nameunit(device_get_parent(sc->dev)));
- pcm_setstatus(sc->dev, status);
+ if (pcm_register(sc->dev, status))
+ goto postinitbad;
atiixp_lock(sc);
if (sc->polling == 0)
@@ -1168,12 +1168,12 @@ atiixp_release_resource(struct atiixp_info *sc)
static int
atiixp_pci_probe(device_t dev)
{
- int i;
+ size_t i;
uint16_t devid, vendor;
vendor = pci_get_vendor(dev);
devid = pci_get_device(dev);
- for (i = 0; i < sizeof(atiixp_hw) / sizeof(atiixp_hw[0]); i++) {
+ for (i = 0; i < nitems(atiixp_hw); i++) {
if (vendor == atiixp_hw[i].vendor &&
devid == atiixp_hw[i].devid) {
device_set_desc(dev, atiixp_hw[i].desc);
diff --git a/sys/dev/sound/pci/cmi.c b/sys/dev/sound/pci/cmi.c
index 9a92066c51a4..22f1e76a4d1f 100644
--- a/sys/dev/sound/pci/cmi.c
+++ b/sys/dev/sound/pci/cmi.c
@@ -982,8 +982,7 @@ cmi_attach(device_t dev)
if (mixer_init(dev, &cmi_mixer_class, sc))
goto bad;
- if (pcm_register(dev, sc, 1, 1))
- goto bad;
+ pcm_init(dev, sc);
cmi_initsys(sc);
@@ -993,7 +992,8 @@ cmi_attach(device_t dev)
snprintf(status, SND_STATUSLEN, "port 0x%jx irq %jd on %s",
rman_get_start(sc->reg), rman_get_start(sc->irq),
device_get_nameunit(device_get_parent(dev)));
- pcm_setstatus(dev, status);
+ if (pcm_register(dev, status))
+ goto bad;
DEB(printf("cmi_attach: succeeded\n"));
return 0;
diff --git a/sys/dev/sound/pci/cs4281.c b/sys/dev/sound/pci/cs4281.c
index 972b83efff8f..7a25f7f4c08d 100644
--- a/sys/dev/sound/pci/cs4281.c
+++ b/sys/dev/sound/pci/cs4281.c
@@ -839,8 +839,7 @@ cs4281_pci_attach(device_t dev)
mixer_init(dev, ac97_getmixerclass(), codec);
- if (pcm_register(dev, sc, 1, 1))
- goto bad;
+ pcm_init(dev, sc);
pcm_addchan(dev, PCMDIR_PLAY, &cs4281chan_class, sc);
pcm_addchan(dev, PCMDIR_REC, &cs4281chan_class, sc);
@@ -849,7 +848,8 @@ cs4281_pci_attach(device_t dev)
(sc->regtype == SYS_RES_IOPORT)? "port" : "mem",
rman_get_start(sc->reg), rman_get_start(sc->irq),
device_get_nameunit(device_get_parent(dev)));
- pcm_setstatus(dev, status);
+ if (pcm_register(dev, status))
+ goto bad;
return 0;
diff --git a/sys/dev/sound/pci/csa.c b/sys/dev/sound/pci/csa.c
index 7191f0cc4bf9..4bd8ff029f43 100644
--- a/sys/dev/sound/pci/csa.c
+++ b/sys/dev/sound/pci/csa.c
@@ -45,7 +45,6 @@
#endif
#include <dev/sound/pcm/sound.h>
-#include <dev/sound/chip.h>
#include <dev/sound/pci/csareg.h>
#include <dev/sound/pci/csavar.h>
@@ -109,46 +108,25 @@ static int
clkrun_hack(int run)
{
#ifdef __i386__
- devclass_t pci_devclass;
- device_t *pci_devices, *pci_children, *busp, *childp;
- int pci_count = 0, pci_childcount = 0;
- int i, j, port;
+ device_t child;
+ int port;
u_int16_t control;
bus_space_tag_t btag;
- if ((pci_devclass = devclass_find("pci")) == NULL) {
- return ENXIO;
- }
+ child = pci_find_device(0x8086, 0x7113);
+ if (child == NULL)
+ return (ENXIO);
- devclass_get_devices(pci_devclass, &pci_devices, &pci_count);
-
- for (i = 0, busp = pci_devices; i < pci_count; i++, busp++) {
- pci_childcount = 0;
- if (device_get_children(*busp, &pci_children, &pci_childcount))
- continue;
- for (j = 0, childp = pci_children; j < pci_childcount; j++, childp++) {
- if (pci_get_vendor(*childp) == 0x8086 && pci_get_device(*childp) == 0x7113) {
- port = (pci_read_config(*childp, 0x41, 1) << 8) + 0x10;
- /* XXX */
- btag = X86_BUS_SPACE_IO;
-
- control = bus_space_read_2(btag, 0x0, port);
- control &= ~0x2000;
- control |= run? 0 : 0x2000;
- bus_space_write_2(btag, 0x0, port, control);
- free(pci_devices, M_TEMP);
- free(pci_children, M_TEMP);
- return 0;
- }
- }
- free(pci_children, M_TEMP);
- }
+ port = (pci_read_config(child, 0x41, 1) << 8) + 0x10;
+ /* XXX */
+ btag = X86_BUS_SPACE_IO;
- free(pci_devices, M_TEMP);
- return ENXIO;
-#else
- return 0;
+ control = bus_space_read_2(btag, 0x0, port);
+ control &= ~0x2000;
+ control |= run? 0 : 0x2000;
+ bus_space_write_2(btag, 0x0, port, control);
#endif
+ return (0);
}
static struct csa_card cards_4610[] = {
@@ -295,28 +273,20 @@ csa_attach(device_t dev)
/* Attach the children. */
/* PCM Audio */
- func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT | M_ZERO);
- if (func == NULL) {
- error = ENOMEM;
- goto err_teardown;
- }
+ func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_WAITOK | M_ZERO);
func->varinfo = &scp->binfo;
func->func = SCF_PCM;
- scp->pcm = device_add_child(dev, "pcm", -1);
+ scp->pcm = device_add_child(dev, "pcm", DEVICE_UNIT_ANY);
device_set_ivars(scp->pcm, func);
/* Midi Interface */
- func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT | M_ZERO);
- if (func == NULL) {
- error = ENOMEM;
- goto err_teardown;
- }
+ func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_WAITOK | M_ZERO);
func->varinfo = &scp->binfo;
func->func = SCF_MIDI;
- scp->midi = device_add_child(dev, "midi", -1);
+ scp->midi = device_add_child(dev, "midi", DEVICE_UNIT_ANY);
device_set_ivars(scp->midi, func);
- bus_generic_attach(dev);
+ bus_attach_children(dev);
return (0);
@@ -331,43 +301,32 @@ err_io:
return (error);
}
+static void
+csa_child_deleted(device_t dev, device_t child)
+{
+ free(device_get_ivars(child), M_DEVBUF);
+}
+
static int
csa_detach(device_t dev)
{
csa_res *resp;
sc_p scp;
- struct sndcard_func *func;
int err;
scp = device_get_softc(dev);
resp = &scp->res;
- if (scp->midi != NULL) {
- func = device_get_ivars(scp->midi);
- err = device_delete_child(dev, scp->midi);
- if (err != 0)
- return err;
- if (func != NULL)
- free(func, M_DEVBUF);
- scp->midi = NULL;
- }
-
- if (scp->pcm != NULL) {
- func = device_get_ivars(scp->pcm);
- err = device_delete_child(dev, scp->pcm);
- if (err != 0)
- return err;
- if (func != NULL)
- free(func, M_DEVBUF);
- scp->pcm = NULL;
- }
+ err = bus_generic_detach(dev);
+ if (err != 0)
+ return err;
bus_teardown_intr(dev, resp->irq, scp->ih);
bus_release_resource(dev, SYS_RES_IRQ, resp->irq_rid, resp->irq);
bus_release_resource(dev, SYS_RES_MEMORY, resp->mem_rid, resp->mem);
bus_release_resource(dev, SYS_RES_MEMORY, resp->io_rid, resp->io);
- return bus_generic_detach(dev);
+ return (0);
}
static int
@@ -1082,6 +1041,7 @@ static device_method_t csa_methods[] = {
DEVMETHOD(device_resume, csa_resume),
/* Bus interface */
+ DEVMETHOD(bus_child_deleted, csa_child_deleted),
DEVMETHOD(bus_alloc_resource, csa_alloc_resource),
DEVMETHOD(bus_release_resource, csa_release_resource),
DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
diff --git a/sys/dev/sound/pci/csamidi.c b/sys/dev/sound/pci/csamidi.c
index df1699092990..29d5548b0954 100644
--- a/sys/dev/sound/pci/csamidi.c
+++ b/sys/dev/sound/pci/csamidi.c
@@ -43,7 +43,6 @@
#include <dev/pci/pcireg.h>
#include <dev/pci/pcivar.h>
-#include <dev/sound/chip.h>
#include <dev/sound/pcm/sound.h>
#include <dev/sound/midi/midi.h>
diff --git a/sys/dev/sound/pci/csapcm.c b/sys/dev/sound/pci/csapcm.c
index 71b9a0253cdb..a966a2e66402 100644
--- a/sys/dev/sound/pci/csapcm.c
+++ b/sys/dev/sound/pci/csapcm.c
@@ -36,7 +36,6 @@
#include <dev/sound/pcm/sound.h>
#include <dev/sound/pcm/ac97.h>
-#include <dev/sound/chip.h>
#include <dev/sound/pci/csareg.h>
#include <dev/sound/pci/csavar.h>
@@ -833,14 +832,14 @@ pcmcsa_attach(device_t dev)
csa_writemem(resp, BA1_CIE, (csa_readmem(resp, BA1_CIE) & ~0x0000003f) | 0x00000001);
csa_active(csa, -1);
- if (pcm_register(dev, csa, 1, 1)) {
+ pcm_init(dev, csa);
+ pcm_addchan(dev, PCMDIR_REC, &csachan_class, csa);
+ pcm_addchan(dev, PCMDIR_PLAY, &csachan_class, csa);
+ if (pcm_register(dev, status)) {
ac97_destroy(codec);
csa_releaseres(csa, dev);
return (ENXIO);
}
- pcm_addchan(dev, PCMDIR_REC, &csachan_class, csa);
- pcm_addchan(dev, PCMDIR_PLAY, &csachan_class, csa);
- pcm_setstatus(dev, status);
return (0);
}
diff --git a/sys/dev/sound/pci/csareg.h b/sys/dev/sound/pci/csareg.h
index a36e36177f5c..3341390f618b 100644
--- a/sys/dev/sound/pci/csareg.h
+++ b/sys/dev/sound/pci/csareg.h
@@ -34,7 +34,7 @@
#define _CSA_REG_H
/*
- * The following constats are orginally in the sample by Crystal Semiconductor.
+ * The following constats are originally in the sample by Crystal Semiconductor.
* Copyright (c) 1996-1998 Crystal Semiconductor Corp.
*/
diff --git a/sys/dev/sound/pci/emu10k1.c b/sys/dev/sound/pci/emu10k1.c
index 0813f89c87b8..e4b2c22f4f07 100644
--- a/sys/dev/sound/pci/emu10k1.c
+++ b/sys/dev/sound/pci/emu10k1.c
@@ -2130,13 +2130,14 @@ emu_pci_attach(device_t dev)
rman_get_start(sc->reg), rman_get_start(sc->irq),
device_get_nameunit(device_get_parent(dev)));
- if (pcm_register(dev, sc, sc->nchans, gotmic ? 3 : 2)) goto bad;
+ pcm_init(dev, sc);
for (i = 0; i < sc->nchans; i++)
pcm_addchan(dev, PCMDIR_PLAY, &emupchan_class, sc);
for (i = 0; i < (gotmic ? 3 : 2); i++)
pcm_addchan(dev, PCMDIR_REC, &emurchan_class, sc);
- pcm_setstatus(dev, status);
+ if (pcm_register(dev, status))
+ goto bad;
return 0;
diff --git a/sys/dev/sound/pci/emu10kx-midi.c b/sys/dev/sound/pci/emu10kx-midi.c
index 4ed8e6c1dd9c..2a98562f8f39 100644
--- a/sys/dev/sound/pci/emu10kx-midi.c
+++ b/sys/dev/sound/pci/emu10kx-midi.c
@@ -43,7 +43,6 @@
#include "opt_snd.h"
#endif
-#include <dev/sound/chip.h>
#include <dev/sound/pcm/sound.h>
#include <dev/sound/midi/midi.h>
diff --git a/sys/dev/sound/pci/emu10kx-pcm.c b/sys/dev/sound/pci/emu10kx-pcm.c
index 825a39fc4e63..c280b64892f6 100644
--- a/sys/dev/sound/pci/emu10kx-pcm.c
+++ b/sys/dev/sound/pci/emu10kx-pcm.c
@@ -43,7 +43,6 @@
#include "opt_snd.h"
#endif
-#include <dev/sound/chip.h>
#include <dev/sound/pcm/sound.h>
#include <dev/sound/pcm/ac97.h>
@@ -1460,10 +1459,7 @@ emu_pcm_attach(device_t dev)
pcm_setflags(dev, pcm_getflags(dev) | SD_F_MPSAFE);
/* XXX we should better get number of available channels from parent */
- if (pcm_register(dev, sc, (route == RT_FRONT) ? MAX_CHANNELS : 1, (route == RT_FRONT) ? 1 : 0)) {
- device_printf(dev, "can't register PCM channels!\n");
- goto bad;
- }
+ pcm_init(dev, sc);
sc->pnum = 0;
if (route != RT_MCHRECORD)
pcm_addchan(dev, PCMDIR_PLAY, &emupchan_class, sc);
@@ -1477,7 +1473,8 @@ emu_pcm_attach(device_t dev)
snprintf(status, SND_STATUSLEN, "on %s",
device_get_nameunit(device_get_parent(dev)));
- pcm_setstatus(dev, status);
+ if (pcm_register(dev, status))
+ goto bad;
return (0);
diff --git a/sys/dev/sound/pci/emu10kx.c b/sys/dev/sound/pci/emu10kx.c
index d17f5fb16d34..9cd7dbca1cb2 100644
--- a/sys/dev/sound/pci/emu10kx.c
+++ b/sys/dev/sound/pci/emu10kx.c
@@ -49,7 +49,6 @@
#include "opt_snd.h"
#endif
-#include <dev/sound/chip.h>
#include <dev/sound/pcm/sound.h>
#include <dev/sound/pcm/ac97.h>
@@ -3212,120 +3211,72 @@ emu_pci_attach(device_t dev)
sc->pcm[i] = NULL;
/* FRONT */
- func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT | M_ZERO);
- if (func == NULL) {
- error = ENOMEM;
- goto bad;
- }
- pcminfo = malloc(sizeof(struct emu_pcminfo), M_DEVBUF, M_NOWAIT | M_ZERO);
- if (pcminfo == NULL) {
- error = ENOMEM;
- goto bad;
- }
+ func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_WAITOK | M_ZERO);
+ pcminfo = malloc(sizeof(struct emu_pcminfo), M_DEVBUF, M_WAITOK | M_ZERO);
pcminfo->card = sc;
pcminfo->route = RT_FRONT;
func->func = SCF_PCM;
func->varinfo = pcminfo;
- sc->pcm[RT_FRONT] = device_add_child(dev, "pcm", -1);
+ sc->pcm[RT_FRONT] = device_add_child(dev, "pcm", DEVICE_UNIT_ANY);
device_set_ivars(sc->pcm[RT_FRONT], func);
if (!(sc->mch_disabled)) {
/* REAR */
- func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT | M_ZERO);
- if (func == NULL) {
- error = ENOMEM;
- goto bad;
- }
- pcminfo = malloc(sizeof(struct emu_pcminfo), M_DEVBUF, M_NOWAIT | M_ZERO);
- if (pcminfo == NULL) {
- error = ENOMEM;
- goto bad;
- }
+ func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_WAITOK | M_ZERO);
+ pcminfo = malloc(sizeof(struct emu_pcminfo), M_DEVBUF, M_WAITOK | M_ZERO);
pcminfo->card = sc;
pcminfo->route = RT_REAR;
func->func = SCF_PCM;
func->varinfo = pcminfo;
- sc->pcm[RT_REAR] = device_add_child(dev, "pcm", -1);
+ sc->pcm[RT_REAR] = device_add_child(dev, "pcm", DEVICE_UNIT_ANY);
device_set_ivars(sc->pcm[RT_REAR], func);
if (sc->has_51) {
/* CENTER */
- func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT | M_ZERO);
- if (func == NULL) {
- error = ENOMEM;
- goto bad;
- }
- pcminfo = malloc(sizeof(struct emu_pcminfo), M_DEVBUF, M_NOWAIT | M_ZERO);
- if (pcminfo == NULL) {
- error = ENOMEM;
- goto bad;
- }
+ func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_WAITOK | M_ZERO);
+ pcminfo = malloc(sizeof(struct emu_pcminfo), M_DEVBUF, M_WAITOK | M_ZERO);
pcminfo->card = sc;
pcminfo->route = RT_CENTER;
func->func = SCF_PCM;
func->varinfo = pcminfo;
- sc->pcm[RT_CENTER] = device_add_child(dev, "pcm", -1);
+ sc->pcm[RT_CENTER] = device_add_child(dev, "pcm", DEVICE_UNIT_ANY);
device_set_ivars(sc->pcm[RT_CENTER], func);
/* SUB */
- func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT | M_ZERO);
- if (func == NULL) {
- error = ENOMEM;
- goto bad;
- }
- pcminfo = malloc(sizeof(struct emu_pcminfo), M_DEVBUF, M_NOWAIT | M_ZERO);
- if (pcminfo == NULL) {
- error = ENOMEM;
- goto bad;
- }
+ func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_WAITOK | M_ZERO);
+ pcminfo = malloc(sizeof(struct emu_pcminfo), M_DEVBUF, M_WAITOK | M_ZERO);
pcminfo->card = sc;
pcminfo->route = RT_SUB;
func->func = SCF_PCM;
func->varinfo = pcminfo;
- sc->pcm[RT_SUB] = device_add_child(dev, "pcm", -1);
+ sc->pcm[RT_SUB] = device_add_child(dev, "pcm", DEVICE_UNIT_ANY);
device_set_ivars(sc->pcm[RT_SUB], func);
}
if (sc->has_71) {
/* SIDE */
- func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT | M_ZERO);
- if (func == NULL) {
- error = ENOMEM;
- goto bad;
- }
- pcminfo = malloc(sizeof(struct emu_pcminfo), M_DEVBUF, M_NOWAIT | M_ZERO);
- if (pcminfo == NULL) {
- error = ENOMEM;
- goto bad;
- }
+ func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_WAITOK | M_ZERO);
+ pcminfo = malloc(sizeof(struct emu_pcminfo), M_DEVBUF, M_WAITOK | M_ZERO);
pcminfo->card = sc;
pcminfo->route = RT_SIDE;
func->func = SCF_PCM;
func->varinfo = pcminfo;
- sc->pcm[RT_SIDE] = device_add_child(dev, "pcm", -1);
+ sc->pcm[RT_SIDE] = device_add_child(dev, "pcm", DEVICE_UNIT_ANY);
device_set_ivars(sc->pcm[RT_SIDE], func);
}
} /* mch_disabled */
if (sc->mch_rec) {
- func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT | M_ZERO);
- if (func == NULL) {
- error = ENOMEM;
- goto bad;
- }
- pcminfo = malloc(sizeof(struct emu_pcminfo), M_DEVBUF, M_NOWAIT | M_ZERO);
- if (pcminfo == NULL) {
- error = ENOMEM;
- goto bad;
- }
+ func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_WAITOK | M_ZERO);
+ pcminfo = malloc(sizeof(struct emu_pcminfo), M_DEVBUF, M_WAITOK | M_ZERO);
pcminfo->card = sc;
pcminfo->route = RT_MCHRECORD;
func->func = SCF_PCM;
func->varinfo = pcminfo;
- sc->pcm[RT_MCHRECORD] = device_add_child(dev, "pcm", -1);
+ sc->pcm[RT_MCHRECORD] = device_add_child(dev, "pcm", DEVICE_UNIT_ANY);
device_set_ivars(sc->pcm[RT_MCHRECORD], func);
} /*mch_rec */
@@ -3336,16 +3287,8 @@ emu_pci_attach(device_t dev)
#if 0
/* Midi Interface 1: Live!, Audigy, Audigy 2 */
if ((sc->is_emu10k1) || (sc->is_emu10k2) || (sc->is_ca0102)) {
- func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT | M_ZERO);
- if (func == NULL) {
- error = ENOMEM;
- goto bad;
- }
- midiinfo = malloc(sizeof(struct emu_midiinfo), M_DEVBUF, M_NOWAIT | M_ZERO);
- if (midiinfo == NULL) {
- error = ENOMEM;
- goto bad;
- }
+ func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_WAITOK | M_ZERO);
+ midiinfo = malloc(sizeof(struct emu_midiinfo), M_DEVBUF, M_WAITOK | M_ZERO);
midiinfo->card = sc;
if (sc->is_emu10k2 || (sc->is_ca0102)) {
midiinfo->port = EMU_A_MUDATA1;
@@ -3357,21 +3300,13 @@ emu_pci_attach(device_t dev)
}
func->func = SCF_MIDI;
func->varinfo = midiinfo;
- sc->midi[0] = device_add_child(dev, "midi", -1);
+ sc->midi[0] = device_add_child(dev, "midi", DEVICE_UNIT_ANY);
device_set_ivars(sc->midi[0], func);
}
/* Midi Interface 2: Audigy, Audigy 2 (on AudigyDrive) */
if (sc->is_emu10k2 || (sc->is_ca0102)) {
- func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT | M_ZERO);
- if (func == NULL) {
- error = ENOMEM;
- goto bad;
- }
- midiinfo = malloc(sizeof(struct emu_midiinfo), M_DEVBUF, M_NOWAIT | M_ZERO);
- if (midiinfo == NULL) {
- error = ENOMEM;
- goto bad;
- }
+ func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_WAITOK | M_ZERO);
+ midiinfo = malloc(sizeof(struct emu_midiinfo), M_DEVBUF, M_WAITOK | M_ZERO);
midiinfo->card = sc;
midiinfo->port = EMU_A_MUDATA2;
@@ -3379,11 +3314,12 @@ emu_pci_attach(device_t dev)
func->func = SCF_MIDI;
func->varinfo = midiinfo;
- sc->midi[1] = device_add_child(dev, "midi", -1);
+ sc->midi[1] = device_add_child(dev, "midi", DEVICE_UNIT_ANY);
device_set_ivars(sc->midi[1], func);
}
#endif
- return (bus_generic_attach(dev));
+ bus_attach_children(dev);
+ return (0);
bad:
/* XXX can we just call emu_pci_detach here? */
@@ -3402,65 +3338,29 @@ bad:
return (error);
}
+static void
+emu_pci_child_deleted(device_t dev, device_t child)
+{
+ struct sndcard_func *func;
+
+ func = device_get_ivars(child);
+ if (func != NULL) {
+ free(func->varinfo, M_DEVBUF);
+ free(func, M_DEVBUF);
+ }
+}
+
static int
emu_pci_detach(device_t dev)
{
struct emu_sc_info *sc;
- struct sndcard_func *func;
- int devcount, i;
- device_t *childlist;
int r = 0;
sc = device_get_softc(dev);
- for (i = 0; i < RT_COUNT; i++) {
- if (sc->pcm[i] != NULL) {
- func = device_get_ivars(sc->pcm[i]);
- if (func != NULL && func->func == SCF_PCM) {
- device_set_ivars(sc->pcm[i], NULL);
- free(func->varinfo, M_DEVBUF);
- free(func, M_DEVBUF);
- }
- r = device_delete_child(dev, sc->pcm[i]);
- if (r) return (r);
- }
- }
-
- if (sc->midi[0] != NULL) {
- func = device_get_ivars(sc->midi[0]);
- if (func != NULL && func->func == SCF_MIDI) {
- device_set_ivars(sc->midi[0], NULL);
- free(func->varinfo, M_DEVBUF);
- free(func, M_DEVBUF);
- }
- r = device_delete_child(dev, sc->midi[0]);
- if (r) return (r);
- }
-
- if (sc->midi[1] != NULL) {
- func = device_get_ivars(sc->midi[1]);
- if (func != NULL && func->func == SCF_MIDI) {
- device_set_ivars(sc->midi[1], NULL);
- free(func->varinfo, M_DEVBUF);
- free(func, M_DEVBUF);
- }
- r = device_delete_child(dev, sc->midi[1]);
- if (r) return (r);
- }
-
- if (device_get_children(dev, &childlist, &devcount) == 0)
- for (i = 0; i < devcount - 1; i++) {
- device_printf(dev, "removing stale child %d (unit %d)\n", i, device_get_unit(childlist[i]));
- func = device_get_ivars(childlist[i]);
- if (func != NULL && (func->func == SCF_MIDI || func->func == SCF_PCM)) {
- device_set_ivars(childlist[i], NULL);
- free(func->varinfo, M_DEVBUF);
- free(func, M_DEVBUF);
- }
- device_delete_child(dev, childlist[i]);
- }
- if (childlist != NULL)
- free(childlist, M_TEMP);
+ r = bus_generic_detach(dev);
+ if (r != 0)
+ return (r);
r = emu10kx_dev_uninit(sc);
if (r)
@@ -3480,7 +3380,7 @@ emu_pci_detach(device_t dev)
mtx_destroy(&sc->rw);
mtx_destroy(&sc->lock);
- return (bus_generic_detach(dev));
+ return (0);
}
/* add suspend, resume */
static device_method_t emu_methods[] = {
@@ -3489,6 +3389,7 @@ static device_method_t emu_methods[] = {
DEVMETHOD(device_attach, emu_pci_attach),
DEVMETHOD(device_detach, emu_pci_detach),
/* Bus methods */
+ DEVMETHOD(bus_child_deleted, emu_pci_child_deleted),
DEVMETHOD(bus_read_ivar, emu_read_ivar),
DEVMETHOD(bus_write_ivar, emu_write_ivar),
diff --git a/sys/dev/sound/pci/envy24.c b/sys/dev/sound/pci/envy24.c
index f7cc7ff5724d..51842bfdb480 100644
--- a/sys/dev/sound/pci/envy24.c
+++ b/sys/dev/sound/pci/envy24.c
@@ -2575,9 +2575,7 @@ envy24_pci_attach(device_t dev)
mixer_init(dev, &envy24mixer_class, sc);
/* set channel information */
- err = pcm_register(dev, sc, 5, 2 + sc->adcn);
- if (err)
- goto bad;
+ pcm_init(dev, sc);
sc->chnum = 0;
for (i = 0; i < 5; i++) {
pcm_addchan(dev, PCMDIR_PLAY, &envy24chan_class, sc);
@@ -2601,7 +2599,8 @@ envy24_pci_attach(device_t dev)
rman_get_end(sc->mt) - rman_get_start(sc->mt) + 1,
rman_get_start(sc->irq),
device_get_nameunit(device_get_parent(dev)));
- pcm_setstatus(dev, status);
+ if (pcm_register(dev, status))
+ goto bad;
return 0;
diff --git a/sys/dev/sound/pci/envy24ht.c b/sys/dev/sound/pci/envy24ht.c
index 65c67b84ce53..b8202a9fa7cd 100644
--- a/sys/dev/sound/pci/envy24ht.c
+++ b/sys/dev/sound/pci/envy24ht.c
@@ -2480,10 +2480,7 @@ envy24ht_pci_attach(device_t dev)
mixer_init(dev, &envy24htmixer_class, sc);
/* set channel information */
- /* err = pcm_register(dev, sc, 5, 2 + sc->adcn); */
- err = pcm_register(dev, sc, 1, 2 + sc->adcn);
- if (err)
- goto bad;
+ pcm_init(dev, sc);
sc->chnum = 0;
/* for (i = 0; i < 5; i++) { */
pcm_addchan(dev, PCMDIR_PLAY, &envy24htchan_class, sc);
@@ -2503,7 +2500,8 @@ envy24ht_pci_attach(device_t dev)
rman_get_end(sc->mt) - rman_get_start(sc->mt) + 1,
rman_get_start(sc->irq),
device_get_nameunit(device_get_parent(dev)));
- pcm_setstatus(dev, status);
+ if (pcm_register(dev, status))
+ goto bad;
return 0;
diff --git a/sys/dev/sound/pci/es137x.c b/sys/dev/sound/pci/es137x.c
index 8f832d899dd3..3c1bea09b5d1 100644
--- a/sys/dev/sound/pci/es137x.c
+++ b/sys/dev/sound/pci/es137x.c
@@ -1861,13 +1861,13 @@ es_pci_attach(device_t dev)
rman_get_start(es->reg), rman_get_start(es->irq),
device_get_nameunit(device_get_parent(dev)));
- if (pcm_register(dev, es, numplay, 1))
- goto bad;
+ pcm_init(dev, es);
for (i = 0; i < numplay; i++)
pcm_addchan(dev, PCMDIR_PLAY, ct, es);
pcm_addchan(dev, PCMDIR_REC, ct, es);
es_init_sysctls(dev);
- pcm_setstatus(dev, status);
+ if (pcm_register(dev, status))
+ goto bad;
es->escfg = ES_SET_GP(es->escfg, 0);
if (numplay == 1)
device_printf(dev, "<Playback: DAC%d / Record: ADC>\n",
diff --git a/sys/dev/sound/pci/fm801.c b/sys/dev/sound/pci/fm801.c
index cbc74249c04d..3537c7807ded 100644
--- a/sys/dev/sound/pci/fm801.c
+++ b/sys/dev/sound/pci/fm801.c
@@ -642,13 +642,14 @@ fm801_pci_attach(device_t dev)
device_get_nameunit(device_get_parent(dev)));
#define FM801_MAXPLAYCH 1
- if (pcm_register(dev, fm801, FM801_MAXPLAYCH, 1)) goto oops;
+ pcm_init(dev, fm801);
pcm_addchan(dev, PCMDIR_PLAY, &fm801ch_class, fm801);
pcm_addchan(dev, PCMDIR_REC, &fm801ch_class, fm801);
- pcm_setstatus(dev, status);
+ if (pcm_register(dev, status))
+ goto oops;
- fm801->radio = device_add_child(dev, "radio", -1);
- bus_generic_attach(dev);
+ fm801->radio = device_add_child(dev, "radio", DEVICE_UNIT_ANY);
+ bus_attach_children(dev);
return 0;
@@ -675,12 +676,6 @@ fm801_pci_detach(device_t dev)
r = bus_generic_detach(dev);
if (r)
return r;
- if (fm801->radio != NULL) {
- r = device_delete_child(dev, fm801->radio);
- if (r)
- return r;
- fm801->radio = NULL;
- }
r = pcm_unregister(dev);
if (r)
diff --git a/sys/dev/sound/pci/hda/hdaa.c b/sys/dev/sound/pci/hda/hdaa.c
index dcd10cb36510..1e486b01b168 100644
--- a/sys/dev/sound/pci/hda/hdaa.c
+++ b/sys/dev/sound/pci/hda/hdaa.c
@@ -267,7 +267,8 @@ hdaa_channels_handler(struct hdaa_audio_as *as)
struct hdaa_chan *ch = &devinfo->chans[as->chans[0]];
struct hdaa_widget *w;
uint8_t *eld;
- int i, total, sub, assume, channels;
+ int total, sub, assume, channels;
+ size_t i;
uint16_t cpins, upins, tpins;
cpins = upins = 0;
@@ -347,7 +348,7 @@ hdaa_channels_handler(struct hdaa_audio_as *as)
printf("\n");
);
/* Look for maximal fitting matrix. */
- for (i = 0; i < sizeof(matrixes) / sizeof(struct matrix); i++) {
+ for (i = 0; i < nitems(matrixes); i++) {
if (as->pinset != 0 && matrixes[i].analog == 0)
continue;
if ((matrixes[i].m.mask & ~channels) == 0) {
@@ -1252,7 +1253,8 @@ hdaa_sysctl_config(SYSCTL_HANDLER_ARGS)
static void
hdaa_config_fetch(const char *str, uint32_t *on, uint32_t *off)
{
- int i = 0, j, k, len, inv;
+ size_t k;
+ int i = 0, j, len, inv;
for (;;) {
while (str[i] != '\0' &&
@@ -1292,7 +1294,8 @@ static int
hdaa_sysctl_quirks(SYSCTL_HANDLER_ARGS)
{
char buf[256];
- int error, n = 0, i;
+ int error, n = 0;
+ size_t i;
uint32_t quirks, quirks_off;
quirks = *(uint32_t *)oidp->oid_arg1;
@@ -2909,7 +2912,7 @@ hdaa_dump_gpo(struct hdaa_devinfo *devinfo)
data = hda_command(dev,
HDA_CMD_GET_GPO_DATA(0, devinfo->nid));
for (i = 0; i < HDA_PARAM_GPIO_COUNT_NUM_GPO(devinfo->gpio_cap); i++) {
- device_printf(dev, " GPO%d: state=%d", i,
+ device_printf(dev, " GPO%d: state=%d\n", i,
(data >> i) & 1);
}
}
@@ -3030,8 +3033,7 @@ hdaa_audio_ctl_parse(struct hdaa_devinfo *devinfo)
if (max < 1)
return;
- ctls = (struct hdaa_audio_ctl *)malloc(
- sizeof(*ctls) * max, M_HDAA, M_ZERO | M_NOWAIT);
+ ctls = malloc(sizeof(*ctls) * max, M_HDAA, M_ZERO | M_NOWAIT);
if (ctls == NULL) {
/* Blekh! */
@@ -3183,8 +3185,7 @@ hdaa_audio_as_parse(struct hdaa_devinfo *devinfo)
if (max < 1)
return;
- as = (struct hdaa_audio_as *)malloc(
- sizeof(*as) * max, M_HDAA, M_ZERO | M_NOWAIT);
+ as = malloc(sizeof(*as) * max, M_HDAA, M_ZERO | M_NOWAIT);
if (as == NULL) {
/* Blekh! */
@@ -3217,7 +3218,7 @@ hdaa_audio_as_parse(struct hdaa_devinfo *devinfo)
continue;
}
KASSERT(cnt < max,
- ("%s: Associations owerflow (%d of %d)",
+ ("%s: Associations overflow (%d of %d)",
__func__, cnt, max));
type = w->wclass.pin.config &
HDA_CONFIG_DEFAULTCONF_DEVICE_MASK;
@@ -4074,8 +4075,7 @@ hdaa_audio_bind_as(struct hdaa_devinfo *devinfo)
cnt += as[j].num_chans;
}
if (devinfo->num_chans == 0) {
- devinfo->chans = (struct hdaa_chan *)malloc(
- sizeof(struct hdaa_chan) * cnt,
+ devinfo->chans = malloc(sizeof(struct hdaa_chan) * cnt,
M_HDAA, M_ZERO | M_NOWAIT);
if (devinfo->chans == NULL) {
device_printf(devinfo->dev,
@@ -5487,10 +5487,8 @@ hdaa_prepare_pcms(struct hdaa_devinfo *devinfo)
}
devinfo->num_devs =
max(ardev, apdev) + max(drdev, dpdev);
- devinfo->devs =
- (struct hdaa_pcm_devinfo *)malloc(
- devinfo->num_devs * sizeof(struct hdaa_pcm_devinfo),
- M_HDAA, M_ZERO | M_NOWAIT);
+ devinfo->devs = malloc(devinfo->num_devs *
+ sizeof(struct hdaa_pcm_devinfo), M_HDAA, M_ZERO | M_NOWAIT);
if (devinfo->devs == NULL) {
device_printf(devinfo->dev,
"Unable to allocate memory for devices\n");
@@ -5539,7 +5537,7 @@ hdaa_create_pcms(struct hdaa_devinfo *devinfo)
for (i = 0; i < devinfo->num_devs; i++) {
struct hdaa_pcm_devinfo *pdevinfo = &devinfo->devs[i];
- pdevinfo->dev = device_add_child(devinfo->dev, "pcm", -1);
+ pdevinfo->dev = device_add_child(devinfo->dev, "pcm", DEVICE_UNIT_ANY);
device_set_ivars(pdevinfo->dev, (void *)pdevinfo);
}
}
@@ -6202,7 +6200,9 @@ hdaa_configure(device_t dev)
HDA_BOOTHVERBOSE(
device_printf(dev, "Creating PCM devices...\n");
);
+ hdaa_unlock(devinfo);
hdaa_create_pcms(devinfo);
+ hdaa_lock(devinfo);
HDA_BOOTVERBOSE(
if (devinfo->quirks != 0) {
@@ -6470,7 +6470,7 @@ hdaa_sysctl_reconfig(SYSCTL_HANDLER_ARGS)
hdaa_unconfigure(dev);
hdaa_configure(dev);
hdaa_unlock(devinfo);
- bus_generic_attach(dev);
+ bus_attach_children(dev);
HDA_BOOTHVERBOSE(
device_printf(dev, "Reconfiguration done\n");
);
@@ -6622,9 +6622,8 @@ hdaa_attach(device_t dev)
);
if (devinfo->nodecnt > 0)
- devinfo->widget = (struct hdaa_widget *)malloc(
- sizeof(*(devinfo->widget)) * devinfo->nodecnt, M_HDAA,
- M_WAITOK | M_ZERO);
+ devinfo->widget = malloc(sizeof(*(devinfo->widget)) *
+ devinfo->nodecnt, M_HDAA, M_WAITOK | M_ZERO);
else
devinfo->widget = NULL;
@@ -6677,7 +6676,7 @@ hdaa_attach(device_t dev)
SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
"init_clear", CTLFLAG_RW,
&devinfo->init_clear, 1,"Clear initial pin widget configuration");
- bus_generic_attach(dev);
+ bus_attach_children(dev);
return (0);
}
@@ -6687,7 +6686,7 @@ hdaa_detach(device_t dev)
struct hdaa_devinfo *devinfo = device_get_softc(dev);
int error;
- if ((error = device_delete_children(dev)) != 0)
+ if ((error = bus_generic_detach(dev)) != 0)
return (error);
hdaa_lock(devinfo);
@@ -7056,9 +7055,7 @@ hdaa_pcm_attach(device_t dev)
HDA_BOOTHVERBOSE(
device_printf(dev, "Registering PCM channels...\n");
);
- if (pcm_register(dev, pdevinfo, (pdevinfo->playas >= 0)?1:0,
- (pdevinfo->recas >= 0)?1:0) != 0)
- device_printf(dev, "Can't register PCM\n");
+ pcm_init(dev, pdevinfo);
pdevinfo->registered++;
@@ -7111,9 +7108,8 @@ hdaa_pcm_attach(device_t dev)
snprintf(status, SND_STATUSLEN, "on %s",
device_get_nameunit(device_get_parent(dev)));
- pcm_setstatus(dev, status);
- return (0);
+ return (pcm_register(dev, status));
}
static int
diff --git a/sys/dev/sound/pci/hda/hdaa_patches.c b/sys/dev/sound/pci/hda/hdaa_patches.c
index 3a7dfc63637a..8967cb49125c 100644
--- a/sys/dev/sound/pci/hda/hdaa_patches.c
+++ b/sys/dev/sound/pci/hda/hdaa_patches.c
@@ -113,6 +113,12 @@ static const struct {
{ APPLE_MACBOOKAIR31, HDA_CODEC_CS4206, HDA_MATCH_ALL,
0, 0,
HDAA_GPIO_SET(1) | HDAA_GPIO_SET(3) },
+ { HDA_MATCH_ALL, HDA_CODEC_CS4208, APPLE_MACBOOKAIR61,
+ 0, 0,
+ HDAA_GPIO_SET(0) },
+ { HDA_MATCH_ALL, HDA_CODEC_CS4208, APPLE_MACBOOKAIR62,
+ 0, 0,
+ HDAA_GPIO_SET(0) },
{ APPLE_MACBOOKPRO55, HDA_CODEC_CS4206, HDA_MATCH_ALL,
0, 0,
HDAA_GPIO_SET(1) | HDAA_GPIO_SET(3) },
@@ -300,6 +306,22 @@ hdac_pin_patch(struct hdaa_widget *w)
patch_str = "as=2";
break;
}
+ } else if (id == HDA_CODEC_CX20590 &&
+ subid == LENOVO_T420S_SUBVENDOR) {
+ switch (nid) {
+ case 25:
+ patch_str = "as=1 seq=15";
+ break;
+ case 27:
+ patch_str = "as=2 seq=15";
+ break;
+ case 31:
+ patch_str = "as=1 seq=0";
+ break;
+ case 35:
+ patch_str = "as=2 seq=0";
+ break;
+ }
} else if (id == HDA_CODEC_ALC235 && subid == ASUS_GL553VE_SUBVENDOR) {
switch (nid) {
case 33:
@@ -318,7 +340,8 @@ hdac_pin_patch(struct hdaa_widget *w)
}
} else if (id == HDA_CODEC_ALC257 &&
(subid == LENOVO_L5AMD_SUBVENDOR ||
- subid == LENOVO_L5INTEL_SUBVENDOR)) {
+ subid == LENOVO_L5INTEL_SUBVENDOR ||
+ subid == LENOVO_IDEAPAD3_SUBVENDOR)) {
switch (nid) {
case 20:
patch_str = "as=1 seq=0";
@@ -329,7 +352,8 @@ hdac_pin_patch(struct hdaa_widget *w)
}
} else if (id == HDA_CODEC_IDT92HD95B &&
(subid == FRAMEWORK_LAPTOP_0001_SUBVENDOR ||
- subid == FRAMEWORK_LAPTOP_0002_SUBVENDOR)) {
+ subid == FRAMEWORK_LAPTOP_0002_SUBVENDOR ||
+ subid == FRAMEWORK_LAPTOP_0003_SUBVENDOR)) {
switch (nid) {
case 10:
patch_str = "as=1 seq=15 color=Black loc=Left";
@@ -338,8 +362,29 @@ hdac_pin_patch(struct hdaa_widget *w)
patch_str = "as=3 seq=15 color=Black loc=Left";
break;
}
+ } else if (id == HDA_CODEC_ALC295 &&
+ subid == FRAMEWORK_LAPTOP_0005_SUBVENDOR) {
+ switch (nid) {
+ case 20:
+ /*
+ * This pin is a duplicate of pin 23 (both as=1 seq=0),
+ * which ends up in the driver disabling the
+ * association altogether. Since sound quality from pin
+ * 23 seems to be better, configure this one as a back
+ * speaker.
+ */
+ patch_str = "as=1 seq=2";
+ break;
+ }
+ } else if (id == HDA_CODEC_ALC295 &&
+ subid == FRAMEWORK_LAPTOP_0006_SUBVENDOR) {
+ switch (nid) {
+ case 33:
+ patch_str = "as=1 seq=15 color=Black loc=Left";
+ break;
+ }
} else if (id == HDA_CODEC_ALC230 &&
- subid == LENOVO_I330_SUBVENDOR) {
+ subid == LENOVO_IDEAPAD330_SUBVENDOR) {
switch (nid) {
case 20:
patch_str = "as=1 seq=0 device=Speaker";
@@ -358,6 +403,17 @@ hdac_pin_patch(struct hdaa_widget *w)
patch_str = "as=4 seq=15";
break;
}
+ } else if (id == HDA_CODEC_ALC294 &&
+ subid == ASUS_UX331_SUBVENDOR) {
+ switch (nid) {
+ case 25:
+ /* XXX You are not expected to understand this. */
+ config = 0x01a1103c;
+ break;
+ case 33:
+ patch_str = "as=1 seq=15";
+ break;
+ }
} else {
/*
* loop over hdaa_model_pin_patch
diff --git a/sys/dev/sound/pci/hda/hdac.c b/sys/dev/sound/pci/hda/hdac.c
index f3dff2052b51..900578b73de4 100644
--- a/sys/dev/sound/pci/hda/hdac.c
+++ b/sys/dev/sound/pci/hda/hdac.c
@@ -106,9 +106,8 @@ static const struct {
{ HDA_INTEL_CMLKS, "Intel Comet Lake-S", 0, 0 },
{ HDA_INTEL_CNLK, "Intel Cannon Lake", 0, 0 },
{ HDA_INTEL_ICLK, "Intel Ice Lake", 0, 0 },
- { HDA_INTEL_CMLKLP, "Intel Comet Lake-LP", 0, 0 },
- { HDA_INTEL_CMLKH, "Intel Comet Lake-H", 0, 0 },
{ HDA_INTEL_TGLK, "Intel Tiger Lake", 0, 0 },
+ { HDA_INTEL_TGLKH, "Intel Tiger Lake-H", 0, 0 },
{ HDA_INTEL_GMLK, "Intel Gemini Lake", 0, 0 },
{ HDA_INTEL_ALLK, "Intel Alder Lake", 0, 0 },
{ HDA_INTEL_ALLKM, "Intel Alder Lake-M", 0, 0 },
@@ -118,6 +117,7 @@ static const struct {
{ HDA_INTEL_ALLKPS, "Intel Alder Lake-PS", 0, 0 },
{ HDA_INTEL_RPTLK1, "Intel Raptor Lake-P", 0, 0 },
{ HDA_INTEL_RPTLK2, "Intel Raptor Lake-P", 0, 0 },
+ { HDA_INTEL_RPTLK3, "Intel Raptor Lake-S", 0, 0 },
{ HDA_INTEL_MTL, "Intel Meteor Lake-P", 0, 0 },
{ HDA_INTEL_ARLS, "Intel Arrow Lake-S", 0, 0 },
{ HDA_INTEL_ARL, "Intel Arrow Lake", 0, 0 },
@@ -193,6 +193,7 @@ static const struct {
{ HDA_ATI_RV940, "ATI RV940", 0, 0 },
{ HDA_ATI_RV970, "ATI RV970", 0, 0 },
{ HDA_ATI_R1000, "ATI R1000", 0, 0 },
+ { HDA_ATI_OLAND, "ATI Oland", 0, 0 },
{ HDA_ATI_KABINI, "ATI Kabini", 0, 0 },
{ HDA_ATI_TRINITY, "ATI Trinity", 0, 0 },
{ HDA_AMD_X370, "AMD X370", 0, 0 },
@@ -1277,6 +1278,7 @@ hdac_attach(device_t dev)
goto hdac_attach_fail;
/* Get Capabilities */
+ hdac_reset(sc, 1);
result = hdac_get_capabilities(sc);
if (result != 0)
goto hdac_attach_fail;
@@ -1615,7 +1617,7 @@ hdac_attach2(void *arg)
HDA_PARAM_REVISION_ID_REVISION_ID(revisionid);
sc->codecs[i].stepping_id =
HDA_PARAM_REVISION_ID_STEPPING_ID(revisionid);
- child = device_add_child(sc->dev, "hdacc", -1);
+ child = device_add_child(sc->dev, "hdacc", DEVICE_UNIT_ANY);
if (child == NULL) {
device_printf(sc->dev,
"Failed to add CODEC device\n");
@@ -1625,7 +1627,7 @@ hdac_attach2(void *arg)
sc->codecs[i].dev = child;
}
}
- bus_generic_attach(sc->dev);
+ bus_attach_children(sc->dev);
SYSCTL_ADD_PROC(device_get_sysctl_ctx(sc->dev),
SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev)), OID_AUTO,
@@ -1638,6 +1640,35 @@ hdac_attach2(void *arg)
}
/****************************************************************************
+ * int hdac_shutdown(device_t)
+ *
+ * Power down HDA bus and codecs.
+ ****************************************************************************/
+static int
+hdac_shutdown(device_t dev)
+{
+ struct hdac_softc *sc = device_get_softc(dev);
+
+ HDA_BOOTHVERBOSE(
+ device_printf(dev, "Shutdown...\n");
+ );
+ callout_drain(&sc->poll_callout);
+ taskqueue_drain(taskqueue_thread, &sc->unsolq_task);
+ bus_generic_shutdown(dev);
+
+ hdac_lock(sc);
+ HDA_BOOTHVERBOSE(
+ device_printf(dev, "Reset controller...\n");
+ );
+ hdac_reset(sc, false);
+ hdac_unlock(sc);
+ HDA_BOOTHVERBOSE(
+ device_printf(dev, "Shutdown done\n");
+ );
+ return (0);
+}
+
+/****************************************************************************
* int hdac_suspend(device_t)
*
* Suspend and power down HDA bus and codecs.
@@ -1740,24 +1771,17 @@ static int
hdac_detach(device_t dev)
{
struct hdac_softc *sc = device_get_softc(dev);
- device_t *devlist;
- int cad, i, devcount, error;
+ int i, error;
- if ((error = device_get_children(dev, &devlist, &devcount)) != 0)
+ error = bus_generic_detach(dev);
+ if (error != 0)
return (error);
- for (i = 0; i < devcount; i++) {
- cad = (intptr_t)device_get_ivars(devlist[i]);
- if ((error = device_delete_child(dev, devlist[i])) != 0) {
- free(devlist, M_TEMP);
- return (error);
- }
- sc->codecs[cad].dev = NULL;
- }
- free(devlist, M_TEMP);
hdac_lock(sc);
+ callout_stop(&sc->poll_callout);
hdac_reset(sc, false);
hdac_unlock(sc);
+ callout_drain(&sc->poll_callout);
taskqueue_drain(taskqueue_thread, &sc->unsolq_task);
hdac_irq_free(sc);
@@ -2154,6 +2178,7 @@ static device_method_t hdac_methods[] = {
DEVMETHOD(device_probe, hdac_probe),
DEVMETHOD(device_attach, hdac_attach),
DEVMETHOD(device_detach, hdac_detach),
+ DEVMETHOD(device_shutdown, hdac_shutdown),
DEVMETHOD(device_suspend, hdac_suspend),
DEVMETHOD(device_resume, hdac_resume),
/* Bus interface */
diff --git a/sys/dev/sound/pci/hda/hdac.h b/sys/dev/sound/pci/hda/hdac.h
index 4dd589ed2a09..223434a214b1 100644
--- a/sys/dev/sound/pci/hda/hdac.h
+++ b/sys/dev/sound/pci/hda/hdac.h
@@ -77,6 +77,7 @@
#define HDA_INTEL_ALLKPS HDA_MODEL_CONSTRUCT(INTEL, 0x51c9)
#define HDA_INTEL_RPTLK1 HDA_MODEL_CONSTRUCT(INTEL, 0x51ca)
#define HDA_INTEL_RPTLK2 HDA_MODEL_CONSTRUCT(INTEL, 0x51cb)
+#define HDA_INTEL_RPTLK3 HDA_MODEL_CONSTRUCT(INTEL, 0x7a50)
#define HDA_INTEL_SCH HDA_MODEL_CONSTRUCT(INTEL, 0x811b)
#define HDA_INTEL_LPT1 HDA_MODEL_CONSTRUCT(INTEL, 0x8c20)
#define HDA_INTEL_LPT2 HDA_MODEL_CONSTRUCT(INTEL, 0x8c21)
@@ -95,9 +96,8 @@
#define HDA_INTEL_CMLKS HDA_MODEL_CONSTRUCT(INTEL, 0xa3f0)
#define HDA_INTEL_CNLK HDA_MODEL_CONSTRUCT(INTEL, 0x9dc8)
#define HDA_INTEL_ICLK HDA_MODEL_CONSTRUCT(INTEL, 0x34c8)
-#define HDA_INTEL_CMLKLP HDA_MODEL_CONSTRUCT(INTEL, 0x02c8)
-#define HDA_INTEL_CMLKH HDA_MODEL_CONSTRUCT(INTEL, 0x06c8)
#define HDA_INTEL_TGLK HDA_MODEL_CONSTRUCT(INTEL, 0xa0c8)
+#define HDA_INTEL_TGLKH HDA_MODEL_CONSTRUCT(INTEL, 0x43c8)
#define HDA_INTEL_MTL HDA_MODEL_CONSTRUCT(INTEL, 0x7e28)
#define HDA_INTEL_ARLS HDA_MODEL_CONSTRUCT(INTEL, 0x7f50)
#define HDA_INTEL_ARL HDA_MODEL_CONSTRUCT(INTEL, 0x7728)
@@ -178,6 +178,7 @@
#define HDA_ATI_RV930 HDA_MODEL_CONSTRUCT(ATI, 0xaa90)
#define HDA_ATI_RV910 HDA_MODEL_CONSTRUCT(ATI, 0xaa98)
#define HDA_ATI_R1000 HDA_MODEL_CONSTRUCT(ATI, 0xaaa0)
+#define HDA_ATI_OLAND HDA_MODEL_CONSTRUCT(ATI, 0xaab0)
#define HDA_ATI_KABINI HDA_MODEL_CONSTRUCT(ATI, 0x9840)
#define HDA_ATI_TRINITY HDA_MODEL_CONSTRUCT(ATI, 0x9902)
#define HDA_ATI_ALL HDA_MODEL_CONSTRUCT(ATI, 0xffff)
@@ -332,6 +333,7 @@
#define ASUS_G2K_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x1339)
#define ASUS_Z550SA_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x13b0)
#define ASUS_A7T_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x13c2)
+#define ASUS_UX331_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x14de)
#define ASUS_UX31A_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x1517)
#define ASUS_GL553VE_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x15e0)
#define ASUS_Z71V_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x1964)
@@ -371,6 +373,7 @@
#define LENOVO_X300_SUBVENDOR HDA_MODEL_CONSTRUCT(LENOVO, 0x20ac)
#define LENOVO_T400_SUBVENDOR HDA_MODEL_CONSTRUCT(LENOVO, 0x20f2)
#define LENOVO_T420_SUBVENDOR HDA_MODEL_CONSTRUCT(LENOVO, 0x21ce)
+#define LENOVO_T420S_SUBVENDOR HDA_MODEL_CONSTRUCT(LENOVO, 0x21d2)
#define LENOVO_T430_SUBVENDOR HDA_MODEL_CONSTRUCT(LENOVO, 0x21f3)
#define LENOVO_T430S_SUBVENDOR HDA_MODEL_CONSTRUCT(LENOVO, 0x21fb)
#define LENOVO_T520_SUBVENDOR HDA_MODEL_CONSTRUCT(LENOVO, 0x21cf)
@@ -378,11 +381,13 @@
#define LENOVO_X230_SUBVENDOR HDA_MODEL_CONSTRUCT(LENOVO, 0x21fa)
#define LENOVO_X230T_SUBVENDOR HDA_MODEL_CONSTRUCT(LENOVO, 0x2203)
#define LENOVO_T431S_SUBVENDOR HDA_MODEL_CONSTRUCT(LENOVO, 0x2208)
+#define LENOVO_X1CRBNG11_SUBVENDOR HDA_MODEL_CONSTRUCT(LENOVO, 0x2315)
#define LENOVO_G580_SUBVENDOR HDA_MODEL_CONSTRUCT(LENOVO, 0x3977)
#define LENOVO_L5AMD_SUBVENDOR HDA_MODEL_CONSTRUCT(LENOVO, 0x381b)
#define LENOVO_L5INTEL_SUBVENDOR HDA_MODEL_CONSTRUCT(LENOVO, 0x380f)
#define LENOVO_3000_SUBVENDOR HDA_MODEL_CONSTRUCT(LENOVO, 0x384e)
-#define LENOVO_I330_SUBVENDOR HDA_MODEL_CONSTRUCT(LENOVO, 0x3808)
+#define LENOVO_IDEAPAD330_SUBVENDOR HDA_MODEL_CONSTRUCT(LENOVO, 0x3808)
+#define LENOVO_IDEAPAD3_SUBVENDOR HDA_MODEL_CONSTRUCT(LENOVO, 0x3881)
#define LENOVO_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(LENOVO, 0xffff)
/* Samsung */
@@ -422,6 +427,8 @@
*/
#define APPLE_INTEL_MAC 0x76808384
#define APPLE_MACBOOKAIR31 0x0d9410de
+#define APPLE_MACBOOKAIR61 0x106b7100
+#define APPLE_MACBOOKAIR62 0x106b7200
#define APPLE_MACBOOKPRO55 0xcb7910de
#define APPLE_MACBOOKPRO71 0xcb8910de
@@ -525,6 +532,9 @@
#define FRAMEWORK_VENDORID 0xf111
#define FRAMEWORK_LAPTOP_0001_SUBVENDOR HDA_MODEL_CONSTRUCT(FRAMEWORK, 0x0001)
#define FRAMEWORK_LAPTOP_0002_SUBVENDOR HDA_MODEL_CONSTRUCT(FRAMEWORK, 0x0002)
+#define FRAMEWORK_LAPTOP_0003_SUBVENDOR HDA_MODEL_CONSTRUCT(FRAMEWORK, 0x0003)
+#define FRAMEWORK_LAPTOP_0005_SUBVENDOR HDA_MODEL_CONSTRUCT(FRAMEWORK, 0x0005)
+#define FRAMEWORK_LAPTOP_0006_SUBVENDOR HDA_MODEL_CONSTRUCT(FRAMEWORK, 0x0006)
/* All codecs you can eat... */
#define HDA_CODEC_CONSTRUCT(vendor, id) \
@@ -534,6 +544,7 @@
#define CIRRUSLOGIC_VENDORID 0x1013
#define HDA_CODEC_CS4206 HDA_CODEC_CONSTRUCT(CIRRUSLOGIC, 0x4206)
#define HDA_CODEC_CS4207 HDA_CODEC_CONSTRUCT(CIRRUSLOGIC, 0x4207)
+#define HDA_CODEC_CS4208 HDA_CODEC_CONSTRUCT(CIRRUSLOGIC, 0x4208)
#define HDA_CODEC_CS4210 HDA_CODEC_CONSTRUCT(CIRRUSLOGIC, 0x4210)
#define HDA_CODEC_CSXXXX HDA_CODEC_CONSTRUCT(CIRRUSLOGIC, 0xffff)
@@ -571,6 +582,7 @@
#define HDA_CODEC_ALC284 HDA_CODEC_CONSTRUCT(REALTEK, 0x0284)
#define HDA_CODEC_ALC285 HDA_CODEC_CONSTRUCT(REALTEK, 0x0285)
#define HDA_CODEC_ALC286 HDA_CODEC_CONSTRUCT(REALTEK, 0x0286)
+#define HDA_CODEC_ALC287 HDA_CODEC_CONSTRUCT(REALTEK, 0x0287)
#define HDA_CODEC_ALC288 HDA_CODEC_CONSTRUCT(REALTEK, 0x0288)
#define HDA_CODEC_ALC289 HDA_CODEC_CONSTRUCT(REALTEK, 0x0289)
#define HDA_CODEC_ALC290 HDA_CODEC_CONSTRUCT(REALTEK, 0x0290)
@@ -580,8 +592,6 @@
#define HDA_CODEC_ALC295 HDA_CODEC_CONSTRUCT(REALTEK, 0x0295)
#define HDA_CODEC_ALC298 HDA_CODEC_CONSTRUCT(REALTEK, 0x0298)
#define HDA_CODEC_ALC299 HDA_CODEC_CONSTRUCT(REALTEK, 0x0299)
-#define HDA_CODEC_ALC292 HDA_CODEC_CONSTRUCT(REALTEK, 0x0292)
-#define HDA_CODEC_ALC295 HDA_CODEC_CONSTRUCT(REALTEK, 0x0295)
#define HDA_CODEC_ALC300 HDA_CODEC_CONSTRUCT(REALTEK, 0x0300)
#define HDA_CODEC_ALC623 HDA_CODEC_CONSTRUCT(REALTEK, 0x0623)
#define HDA_CODEC_ALC660 HDA_CODEC_CONSTRUCT(REALTEK, 0x0660)
@@ -605,6 +615,7 @@
#define HDA_CODEC_ALC889 HDA_CODEC_CONSTRUCT(REALTEK, 0x0889)
#define HDA_CODEC_ALC892 HDA_CODEC_CONSTRUCT(REALTEK, 0x0892)
#define HDA_CODEC_ALC897 HDA_CODEC_CONSTRUCT(REALTEK, 0x0897)
+#define HDA_CODEC_ALC898 HDA_CODEC_CONSTRUCT(REALTEK, 0x0898)
#define HDA_CODEC_ALC899 HDA_CODEC_CONSTRUCT(REALTEK, 0x0899)
#define HDA_CODEC_ALC1150 HDA_CODEC_CONSTRUCT(REALTEK, 0x0900)
#define HDA_CODEC_ALCS1200A HDA_CODEC_CONSTRUCT(REALTEK, 0x0b00)
@@ -881,6 +892,7 @@
#define HDA_CODEC_NVIDIATEGRA124 HDA_CODEC_CONSTRUCT(NVIDIA, 0x0028)
#define HDA_CODEC_NVIDIATEGRA210 HDA_CODEC_CONSTRUCT(NVIDIA, 0x0029)
#define HDA_CODEC_NVIDIAMCP67 HDA_CODEC_CONSTRUCT(NVIDIA, 0x0067)
+#define HDA_CODEC_NVIDIAGM204 HDA_CODEC_CONSTRUCT(NVIDIA, 0x0071)
#define HDA_CODEC_NVIDIAMCP73 HDA_CODEC_CONSTRUCT(NVIDIA, 0x8001)
#define HDA_CODEC_NVIDIAXXXX HDA_CODEC_CONSTRUCT(NVIDIA, 0xffff)
@@ -906,6 +918,7 @@
#define HDA_CODEC_INTELGMLK1 HDA_CODEC_CONSTRUCT(INTEL, 0x280d)
#define HDA_CODEC_INTELICLK HDA_CODEC_CONSTRUCT(INTEL, 0x280f)
#define HDA_CODEC_INTELTGLK HDA_CODEC_CONSTRUCT(INTEL, 0x2812)
+#define HDA_CODEC_INTELTGLKH HDA_CODEC_CONSTRUCT(INTEL, 0x2814)
#define HDA_CODEC_INTELALLK HDA_CODEC_CONSTRUCT(INTEL, 0x2815)
#define HDA_CODEC_INTELJLK HDA_CODEC_CONSTRUCT(INTEL, 0x281a)
#define HDA_CODEC_INTELELLK HDA_CODEC_CONSTRUCT(INTEL, 0x281b)
diff --git a/sys/dev/sound/pci/hda/hdacc.c b/sys/dev/sound/pci/hda/hdacc.c
index 009c9098ac3b..4198982c9c2a 100644
--- a/sys/dev/sound/pci/hda/hdacc.c
+++ b/sys/dev/sound/pci/hda/hdacc.c
@@ -74,6 +74,7 @@ static const struct {
} hdacc_codecs[] = {
{ HDA_CODEC_CS4206, 0, "Cirrus Logic CS4206" },
{ HDA_CODEC_CS4207, 0, "Cirrus Logic CS4207" },
+ { HDA_CODEC_CS4208, 0, "Cirrus Logic CS4208" },
{ HDA_CODEC_CS4210, 0, "Cirrus Logic CS4210" },
{ HDA_CODEC_ALC215, 0, "Realtek ALC215" },
{ HDA_CODEC_ALC221, 0, "Realtek ALC221" },
@@ -147,6 +148,7 @@ static const struct {
{ HDA_CODEC_ALC889, 0, "Realtek ALC889" },
{ HDA_CODEC_ALC892, 0, "Realtek ALC892" },
{ HDA_CODEC_ALC897, 0, "Realtek ALC897" },
+ { HDA_CODEC_ALC898, 0, "Realtek ALC898" },
{ HDA_CODEC_ALC899, 0, "Realtek ALC899" },
{ HDA_CODEC_ALC1150, 0, "Realtek ALC1150" },
{ HDA_CODEC_ALCS1200A, 0, "Realtek ALCS1200A" },
@@ -358,6 +360,7 @@ static const struct {
{ HDA_CODEC_NVIDIAMCP78_3, 0, "NVIDIA MCP78" },
{ HDA_CODEC_NVIDIAMCP78_4, 0, "NVIDIA MCP78" },
{ HDA_CODEC_NVIDIAMCP7A, 0, "NVIDIA MCP7A" },
+ { HDA_CODEC_NVIDIAGM204, 0, "NVIDIA GM204" },
{ HDA_CODEC_NVIDIAGT220, 0, "NVIDIA GT220" },
{ HDA_CODEC_NVIDIAGT21X, 0, "NVIDIA GT21x" },
{ HDA_CODEC_NVIDIAMCP89, 0, "NVIDIA MCP89" },
@@ -393,6 +396,7 @@ static const struct {
{ HDA_CODEC_INTELGMLK1, 0, "Intel Gemini Lake" },
{ HDA_CODEC_INTELICLK, 0, "Intel Ice Lake" },
{ HDA_CODEC_INTELTGLK, 0, "Intel Tiger Lake" },
+ { HDA_CODEC_INTELTGLKH, 0, "Intel Tiger Lake-H" },
{ HDA_CODEC_INTELALLK, 0, "Intel Alder Lake" },
{ HDA_CODEC_SII1390, 0, "Silicon Image SiI1390" },
{ HDA_CODEC_SII1392, 0, "Silicon Image SiI1392" },
@@ -520,7 +524,7 @@ hdacc_attach(device_t dev)
codec->fgs[n].subsystem_id = hda_command(dev,
HDA_CMD_GET_SUBSYSTEM_ID(0, i));
hdacc_unlock(codec);
- codec->fgs[n].dev = child = device_add_child(dev, NULL, -1);
+ codec->fgs[n].dev = child = device_add_child(dev, NULL, DEVICE_UNIT_ANY);
if (child == NULL) {
device_printf(dev, "Failed to add function device\n");
continue;
@@ -528,7 +532,7 @@ hdacc_attach(device_t dev)
device_set_ivars(child, &codec->fgs[n]);
}
- bus_generic_attach(dev);
+ bus_attach_children(dev);
return (0);
}
@@ -539,7 +543,7 @@ hdacc_detach(device_t dev)
struct hdacc_softc *codec = device_get_softc(dev);
int error;
- if ((error = device_delete_children(dev)) != 0)
+ if ((error = bus_generic_detach(dev)) != 0)
return (error);
free(codec->fgs, M_HDACC);
return (0);
diff --git a/sys/dev/sound/pci/hda/pin_patch_realtek.h b/sys/dev/sound/pci/hda/pin_patch_realtek.h
index 49afdedea8a1..abb03c92571b 100644
--- a/sys/dev/sound/pci/hda/pin_patch_realtek.h
+++ b/sys/dev/sound/pci/hda/pin_patch_realtek.h
@@ -582,6 +582,21 @@ static struct hdaa_model_pin_patch_t realtek_model_pin_patches[] = {
}
}, { }
}
+ }, { /**** CODEC: HDA_CODEC_ALC287 ****/
+ .id = HDA_CODEC_ALC287,
+ .patches = (struct model_pin_patch_t[]){
+ {
+ .models = (struct pin_machine_model_t[]){
+ PIN_SUBVENDOR(LENOVO_X1CRBNG11_SUBVENDOR),
+ { }
+ },
+ .pin_patches = (struct pin_patch_t[]){
+ PIN_PATCH_NOT_APPLICABLE(20),
+ PIN_PATCH_STRING(33, "as=1 seq=15 device=Headphones loc=Right"),
+ { }
+ }
+ }, { }
+ }
}, { /**** CODEC: HDA_CODEC_ALC288 ****/
.id = HDA_CODEC_ALC288,
.patches = (struct model_pin_patch_t[]){
diff --git a/sys/dev/sound/pci/hdsp-pcm.c b/sys/dev/sound/pci/hdsp-pcm.c
new file mode 100644
index 000000000000..5ac571e64fde
--- /dev/null
+++ b/sys/dev/sound/pci/hdsp-pcm.c
@@ -0,0 +1,1136 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2012-2021 Ruslan Bukin <br@bsdpad.com>
+ * Copyright (c) 2023-2024 Florian Walpen <dev@submerge.ch>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * RME HDSP driver for FreeBSD (pcm-part).
+ * Supported cards: HDSP 9632, HDSP 9652.
+ */
+
+#include <sys/libkern.h>
+
+#include <dev/sound/pcm/sound.h>
+#include <dev/sound/pci/hdsp.h>
+
+#include <dev/pci/pcireg.h>
+#include <dev/pci/pcivar.h>
+
+#include <mixer_if.h>
+
+#define HDSP_MATRIX_MAX 8
+
+struct hdsp_latency {
+ uint32_t n;
+ uint32_t period;
+ float ms;
+};
+
+static struct hdsp_latency latency_map[] = {
+ { 7, 32, 0.7 },
+ { 0, 64, 1.5 },
+ { 1, 128, 3 },
+ { 2, 256, 6 },
+ { 3, 512, 12 },
+ { 4, 1024, 23 },
+ { 5, 2048, 46 },
+ { 6, 4096, 93 },
+
+ { 0, 0, 0 },
+};
+
+struct hdsp_rate {
+ uint32_t speed;
+ uint32_t reg;
+};
+
+static struct hdsp_rate rate_map[] = {
+ { 32000, (HDSP_FREQ_32000) },
+ { 44100, (HDSP_FREQ_44100) },
+ { 48000, (HDSP_FREQ_48000) },
+ { 64000, (HDSP_FREQ_32000 | HDSP_FREQ_DOUBLE) },
+ { 88200, (HDSP_FREQ_44100 | HDSP_FREQ_DOUBLE) },
+ { 96000, (HDSP_FREQ_48000 | HDSP_FREQ_DOUBLE) },
+ { 128000, (HDSP_FREQ_32000 | HDSP_FREQ_QUAD) },
+ { 176400, (HDSP_FREQ_44100 | HDSP_FREQ_QUAD) },
+ { 192000, (HDSP_FREQ_48000 | HDSP_FREQ_QUAD) },
+
+ { 0, 0 },
+};
+
+static uint32_t
+hdsp_adat_slot_map(uint32_t speed)
+{
+ /* ADAT slot bitmap depends on sample rate. */
+ if (speed <= 48000)
+ return (0x000000ff); /* 8 channels single speed. */
+ else if (speed <= 96000)
+ return (0x000000aa); /* 4 channels (1,3,5,7) double speed. */
+ else
+ return (0x00000000); /* ADAT disabled at quad speed. */
+}
+
+static uint32_t
+hdsp_port_slot_map(uint32_t ports, uint32_t speed)
+{
+ uint32_t slot_map = 0;
+
+ if (ports & HDSP_CHAN_9632_ALL) {
+ /* Map HDSP 9632 ports to slot bitmap. */
+ if (ports & HDSP_CHAN_9632_ADAT)
+ slot_map |= (hdsp_adat_slot_map(speed) << 0);
+ if (ports & HDSP_CHAN_9632_SPDIF)
+ slot_map |= (0x03 << 8); /* 2 channels SPDIF. */
+ if (ports & HDSP_CHAN_9632_LINE)
+ slot_map |= (0x03 << 10); /* 2 channels line. */
+ if (ports & HDSP_CHAN_9632_EXT)
+ slot_map |= (0x0f << 12); /* 4 channels extension. */
+ } else if ((ports & HDSP_CHAN_9652_ALL) && (speed <= 96000)) {
+ /* Map HDSP 9652 ports to slot bitmap, no quad speed. */
+ if (ports & HDSP_CHAN_9652_ADAT1)
+ slot_map |= (hdsp_adat_slot_map(speed) << 0);
+ if (ports & HDSP_CHAN_9652_ADAT2)
+ slot_map |= (hdsp_adat_slot_map(speed) << 8);
+ if (ports & HDSP_CHAN_9652_ADAT3)
+ slot_map |= (hdsp_adat_slot_map(speed) << 16);
+ if (ports & HDSP_CHAN_9652_SPDIF)
+ slot_map |= (0x03 << 24); /* 2 channels SPDIF. */
+ }
+
+ return (slot_map);
+}
+
+static uint32_t
+hdsp_slot_first(uint32_t slots)
+{
+ return (slots & (~(slots - 1))); /* Extract first bit set. */
+}
+
+static uint32_t
+hdsp_slot_first_row(uint32_t slots)
+{
+ uint32_t ends;
+
+ /* Ends of slot rows are followed by a slot which is not in the set. */
+ ends = slots & (~(slots >> 1));
+ /* First row of contiguous slots ends in the first row end. */
+ return (slots & (ends ^ (ends - 1)));
+}
+
+static uint32_t
+hdsp_slot_first_n(uint32_t slots, unsigned int n)
+{
+ /* Clear all but the first n slots. */
+ for (uint32_t slot = 1; slot != 0; slot <<= 1) {
+ if ((slots & slot) && n > 0)
+ --n;
+ else
+ slots &= ~slot;
+ }
+ return (slots);
+}
+
+static unsigned int
+hdsp_slot_count(uint32_t slots)
+{
+ return (bitcount32(slots));
+}
+
+static unsigned int
+hdsp_slot_offset(uint32_t slots)
+{
+ return (hdsp_slot_count(hdsp_slot_first(slots) - 1));
+}
+
+static unsigned int
+hdsp_slot_channel_offset(uint32_t subset, uint32_t slots)
+{
+ uint32_t preceding;
+
+ /* Make sure we have a subset of slots. */
+ subset &= slots;
+ /* Include all slots preceding the first one of the subset. */
+ preceding = slots & (hdsp_slot_first(subset) - 1);
+
+ return (hdsp_slot_count(preceding));
+}
+
+static uint32_t
+hdsp_port_first(uint32_t ports)
+{
+ return (ports & (~(ports - 1))); /* Extract first bit set. */
+}
+
+static unsigned int
+hdsp_port_slot_count(uint32_t ports, uint32_t speed)
+{
+ return (hdsp_slot_count(hdsp_port_slot_map(ports, speed)));
+}
+
+static unsigned int
+hdsp_port_slot_count_max(uint32_t ports)
+{
+ return (hdsp_slot_count(hdsp_port_slot_map(ports, 48000)));
+}
+
+static uint32_t
+hdsp_channel_play_ports(struct hdsp_channel *hc)
+{
+ return (hc->ports & (HDSP_CHAN_9632_ALL | HDSP_CHAN_9652_ALL));
+}
+
+static uint32_t
+hdsp_channel_rec_ports(struct hdsp_channel *hc)
+{
+ return (hc->ports & (HDSP_CHAN_9632_ALL | HDSP_CHAN_9652_ALL));
+}
+
+static int
+hdsp_hw_mixer(struct sc_chinfo *ch, unsigned int dst,
+ unsigned int src, unsigned short data)
+{
+ struct sc_pcminfo *scp;
+ struct sc_info *sc;
+ uint32_t value;
+ int offset;
+
+ scp = ch->parent;
+ sc = scp->sc;
+
+ offset = 0;
+ value = (HDSP_MIN_GAIN << 16) | (uint16_t) data;
+
+ if (ch->dir != PCMDIR_PLAY)
+ return (0);
+
+ switch (sc->type) {
+ case HDSP_9632:
+ /* Mixer is 2 rows of sources (inputs, playback) per output. */
+ offset = dst * (2 * HDSP_MIX_SLOTS_9632);
+ /* Source index in the second row (playback). */
+ offset += HDSP_MIX_SLOTS_9632 + src;
+ break;
+ case HDSP_9652:
+ /* Mixer is 2 rows of sources (inputs, playback) per output. */
+ offset = dst * (2 * HDSP_MIX_SLOTS_9652);
+ /* Source index in the second row (playback). */
+ offset += HDSP_MIX_SLOTS_9652 + src;
+ break;
+ default:
+ return (0);
+ }
+
+ /*
+ * We have to write mixer matrix values in pairs, with the second
+ * (odd) value in the upper 16 bits of the 32 bit value.
+ * Make value offset even and shift value accordingly.
+ * Assume the paired value to be silenced, since we only set gain
+ * on the diagonal where src and dst are the same.
+ */
+ if (offset % 2) {
+ offset -= 1;
+ value = (value << 16) | HDSP_MIN_GAIN;
+ }
+
+ hdsp_write_4(sc, HDSP_MIXER_BASE + offset * sizeof(uint16_t), value);
+
+ return (0);
+};
+
+static int
+hdspchan_setgain(struct sc_chinfo *ch)
+{
+ uint32_t port, ports;
+ uint32_t slot, slots;
+ unsigned int offset;
+ unsigned short volume;
+
+ /* Iterate through all physical ports of the channel. */
+ ports = ch->ports;
+ port = hdsp_port_first(ports);
+ while (port != 0) {
+ /*
+ * Get slot map from physical port.
+ * Unlike DMA buffers, the hardware mixer's channel mapping
+ * does not change with double or quad speed sample rates.
+ */
+ slots = hdsp_port_slot_map(port, 48000);
+ slot = hdsp_slot_first(slots);
+
+ /* Treat first slot as left channel. */
+ volume = ch->lvol * HDSP_MAX_GAIN / 100;
+ while (slot != 0) {
+ offset = hdsp_slot_offset(slot);
+ hdsp_hw_mixer(ch, offset, offset, volume);
+
+ slots &= ~slot;
+ slot = hdsp_slot_first(slots);
+
+ /* Subsequent slots all get the right channel volume. */
+ volume = ch->rvol * HDSP_MAX_GAIN / 100;
+ }
+
+ ports &= ~port;
+ port = hdsp_port_first(ports);
+ }
+
+ return (0);
+}
+
+static int
+hdspmixer_init(struct snd_mixer *m)
+{
+ struct sc_pcminfo *scp;
+ struct sc_info *sc;
+ int mask;
+
+ scp = mix_getdevinfo(m);
+ sc = scp->sc;
+ if (sc == NULL)
+ return (-1);
+
+ mask = SOUND_MASK_PCM;
+
+ if (hdsp_channel_play_ports(scp->hc))
+ mask |= SOUND_MASK_VOLUME;
+
+ if (hdsp_channel_rec_ports(scp->hc))
+ mask |= SOUND_MASK_RECLEV;
+
+ snd_mtxlock(sc->lock);
+ pcm_setflags(scp->dev, pcm_getflags(scp->dev) | SD_F_SOFTPCMVOL);
+ mix_setdevs(m, mask);
+ snd_mtxunlock(sc->lock);
+
+ return (0);
+}
+
+static int
+hdspmixer_set(struct snd_mixer *m, unsigned dev,
+ unsigned left, unsigned right)
+{
+ struct sc_pcminfo *scp;
+ struct sc_chinfo *ch;
+ int i;
+
+ scp = mix_getdevinfo(m);
+
+#if 0
+ device_printf(scp->dev, "hdspmixer_set() %d %d\n",
+ left, right);
+#endif
+
+ for (i = 0; i < scp->chnum; i++) {
+ ch = &scp->chan[i];
+ if ((dev == SOUND_MIXER_VOLUME && ch->dir == PCMDIR_PLAY) ||
+ (dev == SOUND_MIXER_RECLEV && ch->dir == PCMDIR_REC)) {
+ ch->lvol = left;
+ ch->rvol = right;
+ if (ch->run)
+ hdspchan_setgain(ch);
+ }
+ }
+
+ return (0);
+}
+
+static kobj_method_t hdspmixer_methods[] = {
+ KOBJMETHOD(mixer_init, hdspmixer_init),
+ KOBJMETHOD(mixer_set, hdspmixer_set),
+ KOBJMETHOD_END
+};
+MIXER_DECLARE(hdspmixer);
+
+static void
+hdspchan_enable(struct sc_chinfo *ch, int value)
+{
+ struct sc_pcminfo *scp;
+ struct sc_info *sc;
+ uint32_t slot, slots;
+ unsigned int offset;
+ int reg;
+
+ scp = ch->parent;
+ sc = scp->sc;
+
+ if (ch->dir == PCMDIR_PLAY)
+ reg = HDSP_OUT_ENABLE_BASE;
+ else
+ reg = HDSP_IN_ENABLE_BASE;
+
+ ch->run = value;
+
+ /* Iterate through all slots of the channel's physical ports. */
+ slots = hdsp_port_slot_map(ch->ports, sc->speed);
+ slot = hdsp_slot_first(slots);
+ while (slot != 0) {
+ /* Set register to enable or disable slot. */
+ offset = hdsp_slot_offset(slot);
+ hdsp_write_1(sc, reg + (4 * offset), value);
+
+ slots &= ~slot;
+ slot = hdsp_slot_first(slots);
+ }
+}
+
+static int
+hdsp_running(struct sc_info *sc)
+{
+ struct sc_pcminfo *scp;
+ struct sc_chinfo *ch;
+ device_t *devlist;
+ int devcount;
+ int i, j;
+ int running;
+
+ running = 0;
+
+ devlist = NULL;
+ devcount = 0;
+
+ if (device_get_children(sc->dev, &devlist, &devcount) != 0)
+ running = 1; /* On error, avoid channel config changes. */
+
+ for (i = 0; running == 0 && i < devcount; i++) {
+ scp = device_get_ivars(devlist[i]);
+ for (j = 0; j < scp->chnum; j++) {
+ ch = &scp->chan[j];
+ if (ch->run) {
+ running = 1;
+ break;
+ }
+ }
+ }
+
+#if 0
+ if (running == 1)
+ device_printf(sc->dev, "hdsp is running\n");
+#endif
+
+ free(devlist, M_TEMP);
+
+ return (running);
+}
+
+static void
+hdsp_start_audio(struct sc_info *sc)
+{
+
+ sc->ctrl_register |= (HDSP_AUDIO_INT_ENABLE | HDSP_ENABLE);
+ hdsp_write_4(sc, HDSP_CONTROL_REG, sc->ctrl_register);
+}
+
+static void
+hdsp_stop_audio(struct sc_info *sc)
+{
+
+ if (hdsp_running(sc) == 1)
+ return;
+
+ sc->ctrl_register &= ~(HDSP_AUDIO_INT_ENABLE | HDSP_ENABLE);
+ hdsp_write_4(sc, HDSP_CONTROL_REG, sc->ctrl_register);
+}
+
+static void
+buffer_mux_write(uint32_t *dma, uint32_t *pcm, unsigned int pos,
+ unsigned int pos_end, unsigned int width, unsigned int channels)
+{
+ unsigned int slot;
+
+ for (; pos < pos_end; ++pos) {
+ for (slot = 0; slot < width; slot++) {
+ dma[slot * HDSP_CHANBUF_SAMPLES + pos] =
+ pcm[pos * channels + slot];
+ }
+ }
+}
+
+static void
+buffer_mux_port(uint32_t *dma, uint32_t *pcm, uint32_t subset, uint32_t slots,
+ unsigned int pos, unsigned int samples, unsigned int channels)
+{
+ unsigned int slot_offset, width;
+ unsigned int chan_pos;
+
+ /* Translate DMA slot offset to DMA buffer offset. */
+ slot_offset = hdsp_slot_offset(subset);
+ dma += slot_offset * HDSP_CHANBUF_SAMPLES;
+
+ /* Channel position of the slot subset. */
+ chan_pos = hdsp_slot_channel_offset(subset, slots);
+ pcm += chan_pos;
+
+ /* Only copy channels supported by both hardware and pcm format. */
+ width = hdsp_slot_count(subset);
+
+ /* Let the compiler inline and loop unroll common cases. */
+ if (width == 1)
+ buffer_mux_write(dma, pcm, pos, pos + samples, 1, channels);
+ else if (width == 2)
+ buffer_mux_write(dma, pcm, pos, pos + samples, 2, channels);
+ else if (width == 4)
+ buffer_mux_write(dma, pcm, pos, pos + samples, 4, channels);
+ else if (width == 8)
+ buffer_mux_write(dma, pcm, pos, pos + samples, 8, channels);
+ else
+ buffer_mux_write(dma, pcm, pos, pos + samples, width, channels);
+}
+
+static void
+buffer_demux_read(uint32_t *dma, uint32_t *pcm, unsigned int pos,
+ unsigned int pos_end, unsigned int width, unsigned int channels)
+{
+ unsigned int slot;
+
+ for (; pos < pos_end; ++pos) {
+ for (slot = 0; slot < width; slot++) {
+ pcm[pos * channels + slot] =
+ dma[slot * HDSP_CHANBUF_SAMPLES + pos];
+ }
+ }
+}
+
+static void
+buffer_demux_port(uint32_t *dma, uint32_t *pcm, uint32_t subset, uint32_t slots,
+ unsigned int pos, unsigned int samples, unsigned int channels)
+{
+ unsigned int slot_offset, width;
+ unsigned int chan_pos;
+
+ /* Translate DMA slot offset to DMA buffer offset. */
+ slot_offset = hdsp_slot_offset(subset);
+ dma += slot_offset * HDSP_CHANBUF_SAMPLES;
+
+ /* Channel position of the slot subset. */
+ chan_pos = hdsp_slot_channel_offset(subset, slots);
+ pcm += chan_pos;
+
+ /* Only copy channels supported by both hardware and pcm format. */
+ width = hdsp_slot_count(subset);
+
+ /* Let the compiler inline and loop unroll common cases. */
+ if (width == 1)
+ buffer_demux_read(dma, pcm, pos, pos + samples, 1, channels);
+ else if (width == 2)
+ buffer_demux_read(dma, pcm, pos, pos + samples, 2, channels);
+ else if (width == 4)
+ buffer_demux_read(dma, pcm, pos, pos + samples, 4, channels);
+ else if (width == 8)
+ buffer_demux_read(dma, pcm, pos, pos + samples, 8, channels);
+ else
+ buffer_demux_read(dma, pcm, pos, pos + samples, width, channels);
+}
+
+
+/* Copy data between DMA and PCM buffers. */
+static void
+buffer_copy(struct sc_chinfo *ch)
+{
+ struct sc_pcminfo *scp;
+ struct sc_info *sc;
+ uint32_t row, slots;
+ uint32_t dma_pos;
+ unsigned int pos, length, remainder, offset, buffer_size;
+ unsigned int channels;
+
+ scp = ch->parent;
+ sc = scp->sc;
+
+ channels = AFMT_CHANNEL(ch->format); /* Number of PCM channels. */
+
+ /* HDSP cards read / write a double buffer, twice the latency period. */
+ buffer_size = 2 * sc->period * sizeof(uint32_t);
+
+ /* Derive buffer position and length to be copied. */
+ if (ch->dir == PCMDIR_PLAY) {
+ /* Buffer position scaled down to a single channel. */
+ pos = sndbuf_getreadyptr(ch->buffer) / channels;
+ length = sndbuf_getready(ch->buffer) / channels;
+ /* Copy no more than 2 periods in advance. */
+ if (length > buffer_size)
+ length = buffer_size;
+ /* Skip what was already copied last time. */
+ offset = (ch->position + buffer_size) - pos;
+ offset %= buffer_size;
+ if (offset <= length) {
+ pos = (pos + offset) % buffer_size;
+ length -= offset;
+ }
+ } else {
+ /* Buffer position scaled down to a single channel. */
+ pos = sndbuf_getfreeptr(ch->buffer) / channels;
+ /* Get DMA buffer write position. */
+ dma_pos = hdsp_read_2(sc, HDSP_STATUS_REG);
+ dma_pos &= HDSP_BUF_POSITION_MASK;
+ dma_pos %= buffer_size;
+ /* Copy what is newly available. */
+ length = (dma_pos + buffer_size) - pos;
+ length %= buffer_size;
+ }
+
+ /* Position and length in samples (4 bytes). */
+ pos /= 4;
+ length /= 4;
+ buffer_size /= sizeof(uint32_t);
+
+ /* Split copy length to wrap around at buffer end. */
+ remainder = 0;
+ if (pos + length > buffer_size)
+ remainder = (pos + length) - buffer_size;
+
+ /* Iterate through rows of contiguous slots. */
+ slots = hdsp_port_slot_map(ch->ports, sc->speed);
+ slots = hdsp_slot_first_n(slots, channels);
+ row = hdsp_slot_first_row(slots);
+
+ while (row != 0) {
+ if (ch->dir == PCMDIR_PLAY) {
+ buffer_mux_port(sc->pbuf, ch->data, row, slots, pos,
+ length - remainder, channels);
+ buffer_mux_port(sc->pbuf, ch->data, row, slots, 0,
+ remainder, channels);
+ } else {
+ buffer_demux_port(sc->rbuf, ch->data, row, slots, pos,
+ length - remainder, channels);
+ buffer_demux_port(sc->rbuf, ch->data, row, slots, 0,
+ remainder, channels);
+ }
+
+ slots &= ~row;
+ row = hdsp_slot_first_row(slots);
+ }
+
+ ch->position = ((pos + length) * 4) % buffer_size;
+}
+
+static int
+clean(struct sc_chinfo *ch)
+{
+ struct sc_pcminfo *scp;
+ struct sc_info *sc;
+ uint32_t *buf;
+ uint32_t slot, slots;
+ unsigned int offset;
+
+ scp = ch->parent;
+ sc = scp->sc;
+ buf = sc->rbuf;
+
+ if (ch->dir == PCMDIR_PLAY)
+ buf = sc->pbuf;
+
+ /* Iterate through all of the channel's slots. */
+ slots = hdsp_port_slot_map(ch->ports, sc->speed);
+ slot = hdsp_slot_first(slots);
+ while (slot != 0) {
+ /* Clear the slot's buffer. */
+ offset = hdsp_slot_offset(slot);
+ bzero(buf + offset * HDSP_CHANBUF_SAMPLES, HDSP_CHANBUF_SIZE);
+
+ slots &= ~slot;
+ slot = hdsp_slot_first(slots);
+ }
+
+ ch->position = 0;
+
+ return (0);
+}
+
+/* Channel interface. */
+static int
+hdspchan_free(kobj_t obj, void *data)
+{
+ struct sc_pcminfo *scp;
+ struct sc_chinfo *ch;
+ struct sc_info *sc;
+
+ ch = data;
+ scp = ch->parent;
+ sc = scp->sc;
+
+#if 0
+ device_printf(scp->dev, "hdspchan_free()\n");
+#endif
+
+ snd_mtxlock(sc->lock);
+ if (ch->data != NULL) {
+ free(ch->data, M_HDSP);
+ ch->data = NULL;
+ }
+ if (ch->caps != NULL) {
+ free(ch->caps, M_HDSP);
+ ch->caps = NULL;
+ }
+ snd_mtxunlock(sc->lock);
+
+ return (0);
+}
+
+static void *
+hdspchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b,
+ struct pcm_channel *c, int dir)
+{
+ struct sc_pcminfo *scp;
+ struct sc_chinfo *ch;
+ struct sc_info *sc;
+ int num;
+
+ scp = devinfo;
+ sc = scp->sc;
+
+ snd_mtxlock(sc->lock);
+ num = scp->chnum;
+
+ ch = &scp->chan[num];
+
+ if (dir == PCMDIR_PLAY)
+ ch->ports = hdsp_channel_play_ports(scp->hc);
+ else
+ ch->ports = hdsp_channel_rec_ports(scp->hc);
+
+ ch->run = 0;
+ ch->lvol = 0;
+ ch->rvol = 0;
+
+ /* Support all possible ADAT widths as channel formats. */
+ ch->cap_fmts[0] =
+ SND_FORMAT(AFMT_S32_LE, hdsp_port_slot_count(ch->ports, 48000), 0);
+ ch->cap_fmts[1] =
+ SND_FORMAT(AFMT_S32_LE, hdsp_port_slot_count(ch->ports, 96000), 0);
+ ch->cap_fmts[2] =
+ SND_FORMAT(AFMT_S32_LE, hdsp_port_slot_count(ch->ports, 192000), 0);
+ ch->cap_fmts[3] = 0;
+
+ ch->caps = malloc(sizeof(struct pcmchan_caps), M_HDSP, M_NOWAIT);
+ *(ch->caps) = (struct pcmchan_caps) {32000, 192000, ch->cap_fmts, 0};
+
+ /* HDSP 9652 does not support quad speed sample rates. */
+ if (sc->type == HDSP_9652) {
+ ch->cap_fmts[2] = SND_FORMAT(AFMT_S32_LE, 2, 0);
+ ch->caps->maxspeed = 96000;
+ }
+
+ /* Allocate maximum buffer size. */
+ ch->size = HDSP_CHANBUF_SIZE * hdsp_port_slot_count_max(ch->ports);
+ ch->data = malloc(ch->size, M_HDSP, M_NOWAIT);
+ ch->position = 0;
+
+ ch->buffer = b;
+ ch->channel = c;
+ ch->parent = scp;
+
+ ch->dir = dir;
+
+ snd_mtxunlock(sc->lock);
+
+ if (sndbuf_setup(ch->buffer, ch->data, ch->size) != 0) {
+ device_printf(scp->dev, "Can't setup sndbuf.\n");
+ hdspchan_free(obj, ch);
+ return (NULL);
+ }
+
+ return (ch);
+}
+
+static int
+hdspchan_trigger(kobj_t obj, void *data, int go)
+{
+ struct sc_pcminfo *scp;
+ struct sc_chinfo *ch;
+ struct sc_info *sc;
+
+ ch = data;
+ scp = ch->parent;
+ sc = scp->sc;
+
+ snd_mtxlock(sc->lock);
+ switch (go) {
+ case PCMTRIG_START:
+#if 0
+ device_printf(scp->dev, "hdspchan_trigger(): start\n");
+#endif
+ hdspchan_enable(ch, 1);
+ hdspchan_setgain(ch);
+ hdsp_start_audio(sc);
+ break;
+
+ case PCMTRIG_STOP:
+ case PCMTRIG_ABORT:
+#if 0
+ device_printf(scp->dev, "hdspchan_trigger(): stop or abort\n");
+#endif
+ clean(ch);
+ hdspchan_enable(ch, 0);
+ hdsp_stop_audio(sc);
+ break;
+
+ case PCMTRIG_EMLDMAWR:
+ case PCMTRIG_EMLDMARD:
+ if(ch->run)
+ buffer_copy(ch);
+ break;
+ }
+
+ snd_mtxunlock(sc->lock);
+
+ return (0);
+}
+
+static uint32_t
+hdspchan_getptr(kobj_t obj, void *data)
+{
+ struct sc_pcminfo *scp;
+ struct sc_chinfo *ch;
+ struct sc_info *sc;
+ uint32_t ret, pos;
+
+ ch = data;
+ scp = ch->parent;
+ sc = scp->sc;
+
+ snd_mtxlock(sc->lock);
+ ret = hdsp_read_2(sc, HDSP_STATUS_REG);
+ snd_mtxunlock(sc->lock);
+
+ pos = ret & HDSP_BUF_POSITION_MASK;
+ pos %= (2 * sc->period * sizeof(uint32_t)); /* Double buffer. */
+ pos *= AFMT_CHANNEL(ch->format); /* Hardbuf with multiple channels. */
+
+ return (pos);
+}
+
+static int
+hdspchan_setformat(kobj_t obj, void *data, uint32_t format)
+{
+ struct sc_chinfo *ch;
+
+ ch = data;
+
+#if 0
+ struct sc_pcminfo *scp = ch->parent;
+ device_printf(scp->dev, "hdspchan_setformat(%d)\n", format);
+#endif
+
+ ch->format = format;
+
+ return (0);
+}
+
+static uint32_t
+hdspchan_setspeed(kobj_t obj, void *data, uint32_t speed)
+{
+ struct sc_pcminfo *scp;
+ struct hdsp_rate *hr;
+ struct sc_chinfo *ch;
+ struct sc_info *sc;
+ int threshold;
+ int i;
+
+ ch = data;
+ scp = ch->parent;
+ sc = scp->sc;
+ hr = NULL;
+
+#if 0
+ device_printf(scp->dev, "hdspchan_setspeed(%d)\n", speed);
+#endif
+
+ if (hdsp_running(sc) == 1)
+ goto end;
+
+ /* HDSP 9652 only supports sample rates up to 96kHz. */
+ if (sc->type == HDSP_9652 && speed > 96000)
+ speed = 96000;
+
+ if (sc->force_speed > 0)
+ speed = sc->force_speed;
+
+ /* First look for equal frequency. */
+ for (i = 0; rate_map[i].speed != 0; i++) {
+ if (rate_map[i].speed == speed)
+ hr = &rate_map[i];
+ }
+
+ /* If no match, just find nearest. */
+ if (hr == NULL) {
+ for (i = 0; rate_map[i].speed != 0; i++) {
+ hr = &rate_map[i];
+ threshold = hr->speed + ((rate_map[i + 1].speed != 0) ?
+ ((rate_map[i + 1].speed - hr->speed) >> 1) : 0);
+ if (speed < threshold)
+ break;
+ }
+ }
+
+ /* Write frequency on the device. */
+ sc->ctrl_register &= ~HDSP_FREQ_MASK;
+ sc->ctrl_register |= hr->reg;
+ hdsp_write_4(sc, HDSP_CONTROL_REG, sc->ctrl_register);
+
+ if (sc->type == HDSP_9632) {
+ /* Set DDS value. */
+ hdsp_write_4(sc, HDSP_FREQ_REG, hdsp_freq_reg_value(hr->speed));
+ }
+
+ sc->speed = hr->speed;
+end:
+
+ return (sc->speed);
+}
+
+static uint32_t
+hdspchan_setblocksize(kobj_t obj, void *data, uint32_t blocksize)
+{
+ struct hdsp_latency *hl;
+ struct sc_pcminfo *scp;
+ struct sc_chinfo *ch;
+ struct sc_info *sc;
+ int threshold;
+ int i;
+
+ ch = data;
+ scp = ch->parent;
+ sc = scp->sc;
+ hl = NULL;
+
+#if 0
+ device_printf(scp->dev, "hdspchan_setblocksize(%d)\n", blocksize);
+#endif
+
+ if (hdsp_running(sc) == 1)
+ goto end;
+
+ if (blocksize > HDSP_LAT_BYTES_MAX)
+ blocksize = HDSP_LAT_BYTES_MAX;
+ else if (blocksize < HDSP_LAT_BYTES_MIN)
+ blocksize = HDSP_LAT_BYTES_MIN;
+
+ blocksize /= 4 /* samples */;
+
+ if (sc->force_period > 0)
+ blocksize = sc->force_period;
+
+ /* First look for equal latency. */
+ for (i = 0; latency_map[i].period != 0; i++) {
+ if (latency_map[i].period == blocksize)
+ hl = &latency_map[i];
+ }
+
+ /* If no match, just find nearest. */
+ if (hl == NULL) {
+ for (i = 0; latency_map[i].period != 0; i++) {
+ hl = &latency_map[i];
+ threshold = hl->period + ((latency_map[i + 1].period != 0) ?
+ ((latency_map[i + 1].period - hl->period) >> 1) : 0);
+ if (blocksize < threshold)
+ break;
+ }
+ }
+
+ snd_mtxlock(sc->lock);
+ sc->ctrl_register &= ~HDSP_LAT_MASK;
+ sc->ctrl_register |= hdsp_encode_latency(hl->n);
+ hdsp_write_4(sc, HDSP_CONTROL_REG, sc->ctrl_register);
+ sc->period = hl->period;
+ snd_mtxunlock(sc->lock);
+
+#if 0
+ device_printf(scp->dev, "New period=%d\n", sc->period);
+#endif
+
+ sndbuf_resize(ch->buffer, 2,
+ (sc->period * AFMT_CHANNEL(ch->format) * sizeof(uint32_t)));
+
+ /* Reset pointer, rewrite frequency (same register) for 9632. */
+ hdsp_write_4(sc, HDSP_RESET_POINTER, 0);
+ if (sc->type == HDSP_9632)
+ hdsp_write_4(sc, HDSP_FREQ_REG, hdsp_freq_reg_value(sc->speed));
+end:
+
+ return (sndbuf_getblksz(ch->buffer));
+}
+
+static uint32_t hdsp_bkp_fmt[] = {
+ SND_FORMAT(AFMT_S32_LE, 2, 0),
+ 0
+};
+
+/* Capabilities fallback, no quad speed for HDSP 9652 compatibility. */
+static struct pcmchan_caps hdsp_bkp_caps = {32000, 96000, hdsp_bkp_fmt, 0};
+
+static struct pcmchan_caps *
+hdspchan_getcaps(kobj_t obj, void *data)
+{
+ struct sc_chinfo *ch;
+
+ ch = data;
+
+#if 0
+ device_printf(ch->parent->dev, "hdspchan_getcaps()\n");
+#endif
+
+ if (ch->caps != NULL)
+ return (ch->caps);
+
+ return (&hdsp_bkp_caps);
+}
+
+static kobj_method_t hdspchan_methods[] = {
+ KOBJMETHOD(channel_init, hdspchan_init),
+ KOBJMETHOD(channel_free, hdspchan_free),
+ KOBJMETHOD(channel_setformat, hdspchan_setformat),
+ KOBJMETHOD(channel_setspeed, hdspchan_setspeed),
+ KOBJMETHOD(channel_setblocksize, hdspchan_setblocksize),
+ KOBJMETHOD(channel_trigger, hdspchan_trigger),
+ KOBJMETHOD(channel_getptr, hdspchan_getptr),
+ KOBJMETHOD(channel_getcaps, hdspchan_getcaps),
+ KOBJMETHOD_END
+};
+CHANNEL_DECLARE(hdspchan);
+
+static int
+hdsp_pcm_probe(device_t dev)
+{
+
+#if 0
+ device_printf(dev,"hdsp_pcm_probe()\n");
+#endif
+
+ return (0);
+}
+
+static uint32_t
+hdsp_pcm_intr(struct sc_pcminfo *scp)
+{
+ struct sc_chinfo *ch;
+ struct sc_info *sc;
+ int i;
+
+ sc = scp->sc;
+
+ for (i = 0; i < scp->chnum; i++) {
+ ch = &scp->chan[i];
+ snd_mtxunlock(sc->lock);
+ chn_intr(ch->channel);
+ snd_mtxlock(sc->lock);
+ }
+
+ return (0);
+}
+
+static int
+hdsp_pcm_attach(device_t dev)
+{
+ char status[SND_STATUSLEN];
+ struct sc_pcminfo *scp;
+ const char *buf;
+ uint32_t pcm_flags;
+ int err;
+ int play, rec;
+
+ scp = device_get_ivars(dev);
+ scp->ih = &hdsp_pcm_intr;
+
+ if (scp->hc->ports & HDSP_CHAN_9632_ALL)
+ buf = "9632";
+ else if (scp->hc->ports & HDSP_CHAN_9652_ALL)
+ buf = "9652";
+ else
+ buf = "?";
+ device_set_descf(dev, "HDSP %s [%s]", buf, scp->hc->descr);
+
+ /*
+ * We don't register interrupt handler with snd_setup_intr
+ * in pcm device. Mark pcm device as MPSAFE manually.
+ */
+ pcm_flags = pcm_getflags(dev) | SD_F_MPSAFE;
+ if (hdsp_port_slot_count_max(scp->hc->ports) > HDSP_MATRIX_MAX)
+ /* Disable vchan conversion, too many channels. */
+ pcm_flags |= SD_F_BITPERFECT;
+ pcm_setflags(dev, pcm_flags);
+
+ pcm_init(dev, scp);
+
+ play = (hdsp_channel_play_ports(scp->hc)) ? 1 : 0;
+ rec = (hdsp_channel_rec_ports(scp->hc)) ? 1 : 0;
+
+ scp->chnum = 0;
+ if (play) {
+ pcm_addchan(dev, PCMDIR_PLAY, &hdspchan_class, scp);
+ scp->chnum++;
+ }
+
+ if (rec) {
+ pcm_addchan(dev, PCMDIR_REC, &hdspchan_class, scp);
+ scp->chnum++;
+ }
+
+ snprintf(status, SND_STATUSLEN, "port 0x%jx irq %jd on %s",
+ rman_get_start(scp->sc->cs),
+ rman_get_start(scp->sc->irq),
+ device_get_nameunit(device_get_parent(dev)));
+ err = pcm_register(dev, status);
+ if (err) {
+ device_printf(dev, "Can't register pcm.\n");
+ return (ENXIO);
+ }
+
+ mixer_init(dev, &hdspmixer_class, scp);
+
+ return (0);
+}
+
+static int
+hdsp_pcm_detach(device_t dev)
+{
+ int err;
+
+ err = pcm_unregister(dev);
+ if (err) {
+ device_printf(dev, "Can't unregister device.\n");
+ return (err);
+ }
+
+ return (0);
+}
+
+static device_method_t hdsp_pcm_methods[] = {
+ DEVMETHOD(device_probe, hdsp_pcm_probe),
+ DEVMETHOD(device_attach, hdsp_pcm_attach),
+ DEVMETHOD(device_detach, hdsp_pcm_detach),
+ { 0, 0 }
+};
+
+static driver_t hdsp_pcm_driver = {
+ "pcm",
+ hdsp_pcm_methods,
+ PCM_SOFTC_SIZE,
+};
+
+DRIVER_MODULE(snd_hdsp_pcm, hdsp, hdsp_pcm_driver, 0, 0);
+MODULE_DEPEND(snd_hdsp, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER);
+MODULE_VERSION(snd_hdsp, 1);
diff --git a/sys/dev/sound/pci/hdsp.c b/sys/dev/sound/pci/hdsp.c
new file mode 100644
index 000000000000..4ba23d22ebce
--- /dev/null
+++ b/sys/dev/sound/pci/hdsp.c
@@ -0,0 +1,1022 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2012-2016 Ruslan Bukin <br@bsdpad.com>
+ * Copyright (c) 2023-2024 Florian Walpen <dev@submerge.ch>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * RME HDSP driver for FreeBSD.
+ * Supported cards: HDSP 9632, HDSP 9652.
+ */
+
+#include <sys/types.h>
+#include <sys/sysctl.h>
+
+#include <dev/sound/pcm/sound.h>
+#include <dev/sound/pci/hdsp.h>
+
+#include <dev/pci/pcireg.h>
+#include <dev/pci/pcivar.h>
+
+#include <mixer_if.h>
+
+static bool hdsp_unified_pcm = false;
+
+static SYSCTL_NODE(_hw, OID_AUTO, hdsp, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
+ "PCI HDSP");
+
+SYSCTL_BOOL(_hw_hdsp, OID_AUTO, unified_pcm, CTLFLAG_RWTUN,
+ &hdsp_unified_pcm, 0, "Combine physical ports in one unified pcm device");
+
+static struct hdsp_clock_source hdsp_clock_source_table_9632[] = {
+ { "internal", HDSP_CLOCK_INTERNAL },
+ { "adat", HDSP_CLOCK_ADAT1 },
+ { "spdif", HDSP_CLOCK_SPDIF },
+ { "word", HDSP_CLOCK_WORD },
+ { NULL, HDSP_CLOCK_INTERNAL }
+};
+
+static struct hdsp_clock_source hdsp_clock_source_table_9652[] = {
+ { "internal", HDSP_CLOCK_INTERNAL },
+ { "adat1", HDSP_CLOCK_ADAT1 },
+ { "adat2", HDSP_CLOCK_ADAT2 },
+ { "adat3", HDSP_CLOCK_ADAT3 },
+ { "spdif", HDSP_CLOCK_SPDIF },
+ { "word", HDSP_CLOCK_WORD },
+ { "adat_sync", HDSP_CLOCK_ADAT_SYNC },
+ { NULL, HDSP_CLOCK_INTERNAL }
+};
+
+static struct hdsp_channel chan_map_9632[] = {
+ { HDSP_CHAN_9632_ADAT, "adat" },
+ { HDSP_CHAN_9632_SPDIF, "s/pdif" },
+ { HDSP_CHAN_9632_LINE, "line" },
+ { HDSP_CHAN_9632_EXT, "ext" },
+ { 0, NULL },
+};
+
+static struct hdsp_channel chan_map_9632_uni[] = {
+ { HDSP_CHAN_9632_ALL, "all" },
+ { 0, NULL },
+};
+
+static struct hdsp_channel chan_map_9652[] = {
+ { HDSP_CHAN_9652_ADAT1, "adat1" },
+ { HDSP_CHAN_9652_ADAT2, "adat2" },
+ { HDSP_CHAN_9652_ADAT3, "adat3" },
+ { HDSP_CHAN_9652_SPDIF, "s/pdif" },
+ { 0, NULL },
+};
+
+static struct hdsp_channel chan_map_9652_uni[] = {
+ { HDSP_CHAN_9652_ALL, "all" },
+ { 0, NULL },
+};
+
+static void
+hdsp_intr(void *p)
+{
+ struct sc_pcminfo *scp;
+ struct sc_info *sc;
+ device_t *devlist;
+ int devcount;
+ int status;
+ int err;
+ int i;
+
+ sc = (struct sc_info *)p;
+
+ snd_mtxlock(sc->lock);
+
+ status = hdsp_read_1(sc, HDSP_STATUS_REG);
+ if (status & HDSP_AUDIO_IRQ_PENDING) {
+ if ((err = device_get_children(sc->dev, &devlist, &devcount)) != 0)
+ return;
+
+ for (i = 0; i < devcount; i++) {
+ scp = device_get_ivars(devlist[i]);
+ if (scp->ih != NULL)
+ scp->ih(scp);
+ }
+
+ hdsp_write_1(sc, HDSP_INTERRUPT_ACK, 0);
+ free(devlist, M_TEMP);
+ }
+
+ snd_mtxunlock(sc->lock);
+}
+
+static void
+hdsp_dmapsetmap(void *arg, bus_dma_segment_t *segs, int nseg, int error)
+{
+#if 0
+ device_printf(sc->dev, "hdsp_dmapsetmap()\n");
+#endif
+}
+
+static int
+hdsp_alloc_resources(struct sc_info *sc)
+{
+
+ /* Allocate resource. */
+ sc->csid = PCIR_BAR(0);
+ sc->cs = bus_alloc_resource_any(sc->dev, SYS_RES_MEMORY,
+ &sc->csid, RF_ACTIVE);
+
+ if (!sc->cs) {
+ device_printf(sc->dev, "Unable to map SYS_RES_MEMORY.\n");
+ return (ENXIO);
+ }
+
+ sc->cst = rman_get_bustag(sc->cs);
+ sc->csh = rman_get_bushandle(sc->cs);
+
+ /* Allocate interrupt resource. */
+ sc->irqid = 0;
+ sc->irq = bus_alloc_resource_any(sc->dev, SYS_RES_IRQ, &sc->irqid,
+ RF_ACTIVE | RF_SHAREABLE);
+
+ if (!sc->irq ||
+ bus_setup_intr(sc->dev, sc->irq, INTR_MPSAFE | INTR_TYPE_AV,
+ NULL, hdsp_intr, sc, &sc->ih)) {
+ device_printf(sc->dev, "Unable to alloc interrupt resource.\n");
+ return (ENXIO);
+ }
+
+ /* Allocate DMA resources. */
+ if (bus_dma_tag_create(/*parent*/bus_get_dma_tag(sc->dev),
+ /*alignment*/4,
+ /*boundary*/0,
+ /*lowaddr*/BUS_SPACE_MAXADDR_32BIT,
+ /*highaddr*/BUS_SPACE_MAXADDR,
+ /*filter*/NULL,
+ /*filterarg*/NULL,
+ /*maxsize*/2 * HDSP_DMASEGSIZE,
+ /*nsegments*/2,
+ /*maxsegsz*/HDSP_DMASEGSIZE,
+ /*flags*/0,
+ /*lockfunc*/NULL,
+ /*lockarg*/NULL,
+ /*dmatag*/&sc->dmat) != 0) {
+ device_printf(sc->dev, "Unable to create dma tag.\n");
+ return (ENXIO);
+ }
+
+ sc->bufsize = HDSP_DMASEGSIZE;
+
+ /* pbuf (play buffer). */
+ if (bus_dmamem_alloc(sc->dmat, (void **)&sc->pbuf, BUS_DMA_WAITOK,
+ &sc->pmap)) {
+ device_printf(sc->dev, "Can't alloc pbuf.\n");
+ return (ENXIO);
+ }
+
+ if (bus_dmamap_load(sc->dmat, sc->pmap, sc->pbuf, sc->bufsize,
+ hdsp_dmapsetmap, sc, BUS_DMA_NOWAIT)) {
+ device_printf(sc->dev, "Can't load pbuf.\n");
+ return (ENXIO);
+ }
+
+ /* rbuf (rec buffer). */
+ if (bus_dmamem_alloc(sc->dmat, (void **)&sc->rbuf, BUS_DMA_WAITOK,
+ &sc->rmap)) {
+ device_printf(sc->dev, "Can't alloc rbuf.\n");
+ return (ENXIO);
+ }
+
+ if (bus_dmamap_load(sc->dmat, sc->rmap, sc->rbuf, sc->bufsize,
+ hdsp_dmapsetmap, sc, BUS_DMA_NOWAIT)) {
+ device_printf(sc->dev, "Can't load rbuf.\n");
+ return (ENXIO);
+ }
+
+ bzero(sc->pbuf, sc->bufsize);
+ bzero(sc->rbuf, sc->bufsize);
+
+ return (0);
+}
+
+static void
+hdsp_map_dmabuf(struct sc_info *sc)
+{
+ uint32_t paddr, raddr;
+
+ paddr = vtophys(sc->pbuf);
+ raddr = vtophys(sc->rbuf);
+
+ hdsp_write_4(sc, HDSP_PAGE_ADDR_BUF_OUT, paddr);
+ hdsp_write_4(sc, HDSP_PAGE_ADDR_BUF_IN, raddr);
+}
+
+static const char *
+hdsp_control_input_level(uint32_t control)
+{
+ switch (control & HDSP_INPUT_LEVEL_MASK) {
+ case HDSP_INPUT_LEVEL_LOWGAIN:
+ return ("LowGain");
+ case HDSP_INPUT_LEVEL_PLUS4DBU:
+ return ("+4dBu");
+ case HDSP_INPUT_LEVEL_MINUS10DBV:
+ return ("-10dBV");
+ default:
+ return (NULL);
+ }
+}
+
+static int
+hdsp_sysctl_input_level(SYSCTL_HANDLER_ARGS)
+{
+ struct sc_info *sc;
+ const char *label;
+ char buf[16] = "invalid";
+ int error;
+ uint32_t control;
+
+ sc = oidp->oid_arg1;
+
+ /* Only available on HDSP 9632. */
+ if (sc->type != HDSP_9632)
+ return (ENXIO);
+
+ /* Extract current input level from control register. */
+ control = sc->ctrl_register & HDSP_INPUT_LEVEL_MASK;
+ label = hdsp_control_input_level(control);
+ if (label != NULL)
+ strlcpy(buf, label, sizeof(buf));
+
+ /* Process sysctl string request. */
+ error = sysctl_handle_string(oidp, buf, sizeof(buf), req);
+ if (error != 0 || req->newptr == NULL)
+ return (error);
+
+ /* Find input level matching the sysctl string. */
+ label = hdsp_control_input_level(HDSP_INPUT_LEVEL_LOWGAIN);
+ if (strncasecmp(buf, label, sizeof(buf)) == 0)
+ control = HDSP_INPUT_LEVEL_LOWGAIN;
+ label = hdsp_control_input_level(HDSP_INPUT_LEVEL_PLUS4DBU);
+ if (strncasecmp(buf, label, sizeof(buf)) == 0)
+ control = HDSP_INPUT_LEVEL_PLUS4DBU;
+ label = hdsp_control_input_level(HDSP_INPUT_LEVEL_MINUS10DBV);
+ if (strncasecmp(buf, label, sizeof(buf)) == 0)
+ control = HDSP_INPUT_LEVEL_MINUS10DBV;
+
+ /* Set input level in control register. */
+ control &= HDSP_INPUT_LEVEL_MASK;
+ if (control != (sc->ctrl_register & HDSP_INPUT_LEVEL_MASK)) {
+ snd_mtxlock(sc->lock);
+ sc->ctrl_register &= ~HDSP_INPUT_LEVEL_MASK;
+ sc->ctrl_register |= control;
+ hdsp_write_4(sc, HDSP_CONTROL_REG, sc->ctrl_register);
+ snd_mtxunlock(sc->lock);
+ }
+ return (0);
+}
+
+static const char *
+hdsp_control_output_level(uint32_t control)
+{
+ switch (control & HDSP_OUTPUT_LEVEL_MASK) {
+ case HDSP_OUTPUT_LEVEL_MINUS10DBV:
+ return ("-10dBV");
+ case HDSP_OUTPUT_LEVEL_PLUS4DBU:
+ return ("+4dBu");
+ case HDSP_OUTPUT_LEVEL_HIGHGAIN:
+ return ("HighGain");
+ default:
+ return (NULL);
+ }
+}
+
+static int
+hdsp_sysctl_output_level(SYSCTL_HANDLER_ARGS)
+{
+ struct sc_info *sc;
+ const char *label;
+ char buf[16] = "invalid";
+ int error;
+ uint32_t control;
+
+ sc = oidp->oid_arg1;
+
+ /* Only available on HDSP 9632. */
+ if (sc->type != HDSP_9632)
+ return (ENXIO);
+
+ /* Extract current output level from control register. */
+ control = sc->ctrl_register & HDSP_OUTPUT_LEVEL_MASK;
+ label = hdsp_control_output_level(control);
+ if (label != NULL)
+ strlcpy(buf, label, sizeof(buf));
+
+ /* Process sysctl string request. */
+ error = sysctl_handle_string(oidp, buf, sizeof(buf), req);
+ if (error != 0 || req->newptr == NULL)
+ return (error);
+
+ /* Find output level matching the sysctl string. */
+ label = hdsp_control_output_level(HDSP_OUTPUT_LEVEL_MINUS10DBV);
+ if (strncasecmp(buf, label, sizeof(buf)) == 0)
+ control = HDSP_OUTPUT_LEVEL_MINUS10DBV;
+ label = hdsp_control_output_level(HDSP_OUTPUT_LEVEL_PLUS4DBU);
+ if (strncasecmp(buf, label, sizeof(buf)) == 0)
+ control = HDSP_OUTPUT_LEVEL_PLUS4DBU;
+ label = hdsp_control_output_level(HDSP_OUTPUT_LEVEL_HIGHGAIN);
+ if (strncasecmp(buf, label, sizeof(buf)) == 0)
+ control = HDSP_OUTPUT_LEVEL_HIGHGAIN;
+
+ /* Set output level in control register. */
+ control &= HDSP_OUTPUT_LEVEL_MASK;
+ if (control != (sc->ctrl_register & HDSP_OUTPUT_LEVEL_MASK)) {
+ snd_mtxlock(sc->lock);
+ sc->ctrl_register &= ~HDSP_OUTPUT_LEVEL_MASK;
+ sc->ctrl_register |= control;
+ hdsp_write_4(sc, HDSP_CONTROL_REG, sc->ctrl_register);
+ snd_mtxunlock(sc->lock);
+ }
+ return (0);
+}
+
+static const char *
+hdsp_control_phones_level(uint32_t control)
+{
+ switch (control & HDSP_PHONES_LEVEL_MASK) {
+ case HDSP_PHONES_LEVEL_MINUS12DB:
+ return ("-12dB");
+ case HDSP_PHONES_LEVEL_MINUS6DB:
+ return ("-6dB");
+ case HDSP_PHONES_LEVEL_0DB:
+ return ("0dB");
+ default:
+ return (NULL);
+ }
+}
+
+static int
+hdsp_sysctl_phones_level(SYSCTL_HANDLER_ARGS)
+{
+ struct sc_info *sc;
+ const char *label;
+ char buf[16] = "invalid";
+ int error;
+ uint32_t control;
+
+ sc = oidp->oid_arg1;
+
+ /* Only available on HDSP 9632. */
+ if (sc->type != HDSP_9632)
+ return (ENXIO);
+
+ /* Extract current phones level from control register. */
+ control = sc->ctrl_register & HDSP_PHONES_LEVEL_MASK;
+ label = hdsp_control_phones_level(control);
+ if (label != NULL)
+ strlcpy(buf, label, sizeof(buf));
+
+ /* Process sysctl string request. */
+ error = sysctl_handle_string(oidp, buf, sizeof(buf), req);
+ if (error != 0 || req->newptr == NULL)
+ return (error);
+
+ /* Find phones level matching the sysctl string. */
+ label = hdsp_control_phones_level(HDSP_PHONES_LEVEL_MINUS12DB);
+ if (strncasecmp(buf, label, sizeof(buf)) == 0)
+ control = HDSP_PHONES_LEVEL_MINUS12DB;
+ label = hdsp_control_phones_level(HDSP_PHONES_LEVEL_MINUS6DB);
+ if (strncasecmp(buf, label, sizeof(buf)) == 0)
+ control = HDSP_PHONES_LEVEL_MINUS6DB;
+ label = hdsp_control_phones_level(HDSP_PHONES_LEVEL_0DB);
+ if (strncasecmp(buf, label, sizeof(buf)) == 0)
+ control = HDSP_PHONES_LEVEL_0DB;
+
+ /* Set phones level in control register. */
+ control &= HDSP_PHONES_LEVEL_MASK;
+ if (control != (sc->ctrl_register & HDSP_PHONES_LEVEL_MASK)) {
+ snd_mtxlock(sc->lock);
+ sc->ctrl_register &= ~HDSP_PHONES_LEVEL_MASK;
+ sc->ctrl_register |= control;
+ hdsp_write_4(sc, HDSP_CONTROL_REG, sc->ctrl_register);
+ snd_mtxunlock(sc->lock);
+ }
+ return (0);
+}
+
+static int
+hdsp_sysctl_sample_rate(SYSCTL_HANDLER_ARGS)
+{
+ struct sc_info *sc = oidp->oid_arg1;
+ int error;
+ unsigned int speed, multiplier;
+
+ speed = sc->force_speed;
+
+ /* Process sysctl (unsigned) integer request. */
+ error = sysctl_handle_int(oidp, &speed, 0, req);
+ if (error != 0 || req->newptr == NULL)
+ return (error);
+
+ /* Speed from 32000 to 192000, 0 falls back to pcm speed setting. */
+ sc->force_speed = 0;
+ if (speed > 0) {
+ multiplier = 1;
+ if ((speed > (96000 + 128000) / 2) && sc->type == HDSP_9632)
+ multiplier = 4;
+ else if (speed > (48000 + 64000) / 2)
+ multiplier = 2;
+
+ if (speed < ((32000 + 44100) / 2) * multiplier)
+ sc->force_speed = 32000 * multiplier;
+ else if (speed < ((44100 + 48000) / 2) * multiplier)
+ sc->force_speed = 44100 * multiplier;
+ else
+ sc->force_speed = 48000 * multiplier;
+ }
+
+ return (0);
+}
+
+
+static int
+hdsp_sysctl_period(SYSCTL_HANDLER_ARGS)
+{
+ struct sc_info *sc = oidp->oid_arg1;
+ int error;
+ unsigned int period;
+
+ period = sc->force_period;
+
+ /* Process sysctl (unsigned) integer request. */
+ error = sysctl_handle_int(oidp, &period, 0, req);
+ if (error != 0 || req->newptr == NULL)
+ return (error);
+
+ /* Period is from 2^5 to 2^14, 0 falls back to pcm latency settings. */
+ sc->force_period = 0;
+ if (period > 0) {
+ sc->force_period = 32;
+ while (sc->force_period < period && sc->force_period < 4096)
+ sc->force_period <<= 1;
+ }
+
+ return (0);
+}
+
+static uint32_t
+hdsp_control_clock_preference(enum hdsp_clock_type type)
+{
+ switch (type) {
+ case HDSP_CLOCK_INTERNAL:
+ return (HDSP_CONTROL_MASTER);
+ case HDSP_CLOCK_ADAT1:
+ return (HDSP_CONTROL_CLOCK(0));
+ case HDSP_CLOCK_ADAT2:
+ return (HDSP_CONTROL_CLOCK(1));
+ case HDSP_CLOCK_ADAT3:
+ return (HDSP_CONTROL_CLOCK(2));
+ case HDSP_CLOCK_SPDIF:
+ return (HDSP_CONTROL_CLOCK(3));
+ case HDSP_CLOCK_WORD:
+ return (HDSP_CONTROL_CLOCK(4));
+ case HDSP_CLOCK_ADAT_SYNC:
+ return (HDSP_CONTROL_CLOCK(5));
+ default:
+ return (HDSP_CONTROL_MASTER);
+ }
+}
+
+static int
+hdsp_sysctl_clock_preference(SYSCTL_HANDLER_ARGS)
+{
+ struct sc_info *sc;
+ struct hdsp_clock_source *clock_table, *clock;
+ char buf[16] = "invalid";
+ int error;
+ uint32_t control;
+
+ sc = oidp->oid_arg1;
+
+ /* Select sync ports table for device type. */
+ if (sc->type == HDSP_9632)
+ clock_table = hdsp_clock_source_table_9632;
+ else if (sc->type == HDSP_9652)
+ clock_table = hdsp_clock_source_table_9652;
+ else
+ return (ENXIO);
+
+ /* Extract preferred clock source from control register. */
+ control = sc->ctrl_register & HDSP_CONTROL_CLOCK_MASK;
+ for (clock = clock_table; clock->name != NULL; ++clock) {
+ if (hdsp_control_clock_preference(clock->type) == control)
+ break;
+ }
+ if (clock->name != NULL)
+ strlcpy(buf, clock->name, sizeof(buf));
+
+ /* Process sysctl string request. */
+ error = sysctl_handle_string(oidp, buf, sizeof(buf), req);
+ if (error != 0 || req->newptr == NULL)
+ return (error);
+
+ /* Find clock source matching the sysctl string. */
+ for (clock = clock_table; clock->name != NULL; ++clock) {
+ if (strncasecmp(buf, clock->name, sizeof(buf)) == 0)
+ break;
+ }
+
+ /* Set preferred clock source in control register. */
+ if (clock->name != NULL) {
+ control = hdsp_control_clock_preference(clock->type);
+ control &= HDSP_CONTROL_CLOCK_MASK;
+ snd_mtxlock(sc->lock);
+ sc->ctrl_register &= ~HDSP_CONTROL_CLOCK_MASK;
+ sc->ctrl_register |= control;
+ hdsp_write_4(sc, HDSP_CONTROL_REG, sc->ctrl_register);
+ snd_mtxunlock(sc->lock);
+ }
+ return (0);
+}
+
+static uint32_t
+hdsp_status2_clock_source(enum hdsp_clock_type type)
+{
+ switch (type) {
+ case HDSP_CLOCK_INTERNAL:
+ return (0);
+ case HDSP_CLOCK_ADAT1:
+ return (HDSP_STATUS2_CLOCK(0));
+ case HDSP_CLOCK_ADAT2:
+ return (HDSP_STATUS2_CLOCK(1));
+ case HDSP_CLOCK_ADAT3:
+ return (HDSP_STATUS2_CLOCK(2));
+ case HDSP_CLOCK_SPDIF:
+ return (HDSP_STATUS2_CLOCK(3));
+ case HDSP_CLOCK_WORD:
+ return (HDSP_STATUS2_CLOCK(4));
+ case HDSP_CLOCK_ADAT_SYNC:
+ return (HDSP_STATUS2_CLOCK(5));
+ default:
+ return (0);
+ }
+}
+
+static int
+hdsp_sysctl_clock_source(SYSCTL_HANDLER_ARGS)
+{
+ struct sc_info *sc;
+ struct hdsp_clock_source *clock_table, *clock;
+ char buf[16] = "invalid";
+ uint32_t status2;
+
+ sc = oidp->oid_arg1;
+
+ /* Select sync ports table for device type. */
+ if (sc->type == HDSP_9632)
+ clock_table = hdsp_clock_source_table_9632;
+ else if (sc->type == HDSP_9652)
+ clock_table = hdsp_clock_source_table_9652;
+ else
+ return (ENXIO);
+
+ /* Read current (autosync) clock source from status2 register. */
+ snd_mtxlock(sc->lock);
+ status2 = hdsp_read_4(sc, HDSP_STATUS2_REG);
+ status2 &= HDSP_STATUS2_CLOCK_MASK;
+ snd_mtxunlock(sc->lock);
+
+ /* Translate status2 register value to clock source. */
+ for (clock = clock_table; clock->name != NULL; ++clock) {
+ /* In clock master mode, override with internal clock source. */
+ if (sc->ctrl_register & HDSP_CONTROL_MASTER) {
+ if (clock->type == HDSP_CLOCK_INTERNAL)
+ break;
+ } else if (hdsp_status2_clock_source(clock->type) == status2)
+ break;
+ }
+
+ /* Process sysctl string request. */
+ if (clock->name != NULL)
+ strlcpy(buf, clock->name, sizeof(buf));
+ return (sysctl_handle_string(oidp, buf, sizeof(buf), req));
+}
+
+static int
+hdsp_sysctl_clock_list(SYSCTL_HANDLER_ARGS)
+{
+ struct sc_info *sc;
+ struct hdsp_clock_source *clock_table, *clock;
+ char buf[256];
+ int n;
+
+ sc = oidp->oid_arg1;
+ n = 0;
+
+ /* Select clock source table for device type. */
+ if (sc->type == HDSP_9632)
+ clock_table = hdsp_clock_source_table_9632;
+ else if (sc->type == HDSP_9652)
+ clock_table = hdsp_clock_source_table_9652;
+ else
+ return (ENXIO);
+
+ /* List available clock sources. */
+ buf[0] = 0;
+ for (clock = clock_table; clock->name != NULL; ++clock) {
+ if (n > 0)
+ n += strlcpy(buf + n, ",", sizeof(buf) - n);
+ n += strlcpy(buf + n, clock->name, sizeof(buf) - n);
+ }
+ return (sysctl_handle_string(oidp, buf, sizeof(buf), req));
+}
+
+static bool
+hdsp_clock_source_locked(enum hdsp_clock_type type, uint32_t status,
+ uint32_t status2)
+{
+ switch (type) {
+ case HDSP_CLOCK_INTERNAL:
+ return (true);
+ case HDSP_CLOCK_ADAT1:
+ return ((status >> 3) & 0x01);
+ case HDSP_CLOCK_ADAT2:
+ return ((status >> 2) & 0x01);
+ case HDSP_CLOCK_ADAT3:
+ return ((status >> 1) & 0x01);
+ case HDSP_CLOCK_SPDIF:
+ return (!((status >> 25) & 0x01));
+ case HDSP_CLOCK_WORD:
+ return ((status2 >> 3) & 0x01);
+ case HDSP_CLOCK_ADAT_SYNC:
+ return ((status >> 5) & 0x01);
+ default:
+ return (false);
+ }
+}
+
+static bool
+hdsp_clock_source_synced(enum hdsp_clock_type type, uint32_t status,
+ uint32_t status2)
+{
+ switch (type) {
+ case HDSP_CLOCK_INTERNAL:
+ return (true);
+ case HDSP_CLOCK_ADAT1:
+ return ((status >> 18) & 0x01);
+ case HDSP_CLOCK_ADAT2:
+ return ((status >> 17) & 0x01);
+ case HDSP_CLOCK_ADAT3:
+ return ((status >> 16) & 0x01);
+ case HDSP_CLOCK_SPDIF:
+ return (((status >> 4) & 0x01) && !((status >> 25) & 0x01));
+ case HDSP_CLOCK_WORD:
+ return ((status2 >> 4) & 0x01);
+ case HDSP_CLOCK_ADAT_SYNC:
+ return ((status >> 27) & 0x01);
+ default:
+ return (false);
+ }
+}
+
+static int
+hdsp_sysctl_sync_status(SYSCTL_HANDLER_ARGS)
+{
+ struct sc_info *sc;
+ struct hdsp_clock_source *clock_table, *clock;
+ char buf[256];
+ char *state;
+ int n;
+ uint32_t status, status2;
+
+ sc = oidp->oid_arg1;
+ n = 0;
+
+ /* Select sync ports table for device type. */
+ if (sc->type == HDSP_9632)
+ clock_table = hdsp_clock_source_table_9632;
+ else if (sc->type == HDSP_9652)
+ clock_table = hdsp_clock_source_table_9652;
+ else
+ return (ENXIO);
+
+ /* Read current lock and sync bits from status registers. */
+ snd_mtxlock(sc->lock);
+ status = hdsp_read_4(sc, HDSP_STATUS_REG);
+ status2 = hdsp_read_4(sc, HDSP_STATUS2_REG);
+ snd_mtxunlock(sc->lock);
+
+ /* List clock sources with lock and sync state. */
+ for (clock = clock_table; clock->name != NULL; ++clock) {
+ if (clock->type == HDSP_CLOCK_INTERNAL)
+ continue;
+ if (n > 0)
+ n += strlcpy(buf + n, ",", sizeof(buf) - n);
+ state = "none";
+ if (hdsp_clock_source_locked(clock->type, status, status2)) {
+ if (hdsp_clock_source_synced(clock->type, status,
+ status2))
+ state = "sync";
+ else
+ state = "lock";
+ }
+ n += snprintf(buf + n, sizeof(buf) - n, "%s(%s)",
+ clock->name, state);
+ }
+ return (sysctl_handle_string(oidp, buf, sizeof(buf), req));
+}
+
+static int
+hdsp_probe(device_t dev)
+{
+ uint32_t rev;
+
+ if (pci_get_vendor(dev) == PCI_VENDOR_XILINX &&
+ pci_get_device(dev) == PCI_DEVICE_XILINX_HDSP) {
+ rev = pci_get_revid(dev);
+ switch (rev) {
+ case PCI_REVISION_9632:
+ device_set_desc(dev, "RME HDSP 9632");
+ return (0);
+ case PCI_REVISION_9652:
+ device_set_desc(dev, "RME HDSP 9652");
+ return (0);
+ }
+ }
+
+ return (ENXIO);
+}
+
+static int
+hdsp_init(struct sc_info *sc)
+{
+ unsigned mixer_controls;
+
+ /* Set latency. */
+ sc->period = 256;
+ /*
+ * The pcm channel latency settings propagate unreliable blocksizes,
+ * different for recording and playback, and skewed due to rounding
+ * and total buffer size limits.
+ * Force period to a consistent default until these issues are fixed.
+ */
+ sc->force_period = 256;
+ sc->ctrl_register = hdsp_encode_latency(2);
+
+ /* Set rate. */
+ sc->speed = HDSP_SPEED_DEFAULT;
+ sc->force_speed = 0;
+ sc->ctrl_register &= ~HDSP_FREQ_MASK;
+ sc->ctrl_register |= HDSP_FREQ_MASK_DEFAULT;
+
+ /* Set internal clock source (master). */
+ sc->ctrl_register &= ~HDSP_CONTROL_CLOCK_MASK;
+ sc->ctrl_register |= HDSP_CONTROL_MASTER;
+
+ /* SPDIF from coax in, line out. */
+ sc->ctrl_register &= ~HDSP_CONTROL_SPDIF_COAX;
+ sc->ctrl_register |= HDSP_CONTROL_SPDIF_COAX;
+ sc->ctrl_register &= ~HDSP_CONTROL_LINE_OUT;
+ sc->ctrl_register |= HDSP_CONTROL_LINE_OUT;
+
+ /* Default gain levels. */
+ sc->ctrl_register &= ~HDSP_INPUT_LEVEL_MASK;
+ sc->ctrl_register |= HDSP_INPUT_LEVEL_LOWGAIN;
+ sc->ctrl_register &= ~HDSP_OUTPUT_LEVEL_MASK;
+ sc->ctrl_register |= HDSP_OUTPUT_LEVEL_MINUS10DBV;
+ sc->ctrl_register &= ~HDSP_PHONES_LEVEL_MASK;
+ sc->ctrl_register |= HDSP_PHONES_LEVEL_MINUS12DB;
+
+ hdsp_write_4(sc, HDSP_CONTROL_REG, sc->ctrl_register);
+
+ if (sc->type == HDSP_9652)
+ hdsp_write_4(sc, HDSP_CONTROL2_REG, HDSP_CONTROL2_9652_MIXER);
+ else
+ hdsp_write_4(sc, HDSP_CONTROL2_REG, 0);
+
+ switch (sc->type) {
+ case HDSP_9632:
+ /* Mixer matrix is 2 source rows (input, playback) per output. */
+ mixer_controls = 2 * HDSP_MIX_SLOTS_9632 * HDSP_MIX_SLOTS_9632;
+ break;
+ case HDSP_9652:
+ /* Mixer matrix is 2 source rows (input, playback) per output. */
+ mixer_controls = 2 * HDSP_MIX_SLOTS_9652 * HDSP_MIX_SLOTS_9652;
+ break;
+ default:
+ return (ENXIO);
+ }
+
+ /* Initialize mixer matrix by silencing all controls. */
+ for (unsigned offset = 0; offset < mixer_controls * 2; offset += 4) {
+ /* Only accepts 4 byte values, pairs of 16 bit volume controls. */
+ hdsp_write_4(sc, HDSP_MIXER_BASE + offset,
+ (HDSP_MIN_GAIN << 16) | HDSP_MIN_GAIN);
+ }
+
+ /* Reset pointer, rewrite frequency (same register) for 9632. */
+ hdsp_write_4(sc, HDSP_RESET_POINTER, 0);
+ if (sc->type == HDSP_9632) {
+ /* Set DDS value. */
+ hdsp_write_4(sc, HDSP_FREQ_REG, hdsp_freq_reg_value(sc->speed));
+ }
+
+ return (0);
+}
+
+static int
+hdsp_attach(device_t dev)
+{
+ struct hdsp_channel *chan_map;
+ struct sc_pcminfo *scp;
+ struct sc_info *sc;
+ uint32_t rev;
+ int i, err;
+
+#if 0
+ device_printf(dev, "hdsp_attach()\n");
+#endif
+
+ sc = device_get_softc(dev);
+ sc->lock = snd_mtxcreate(device_get_nameunit(dev),
+ "snd_hdsp softc");
+ sc->dev = dev;
+
+ pci_enable_busmaster(dev);
+ rev = pci_get_revid(dev);
+ switch (rev) {
+ case PCI_REVISION_9632:
+ sc->type = HDSP_9632;
+ chan_map = hdsp_unified_pcm ? chan_map_9632_uni : chan_map_9632;
+ break;
+ case PCI_REVISION_9652:
+ sc->type = HDSP_9652;
+ chan_map = hdsp_unified_pcm ? chan_map_9652_uni : chan_map_9652;
+ break;
+ default:
+ return (ENXIO);
+ }
+
+ /* Allocate resources. */
+ err = hdsp_alloc_resources(sc);
+ if (err) {
+ device_printf(dev, "Unable to allocate system resources.\n");
+ return (ENXIO);
+ }
+
+ if (hdsp_init(sc) != 0)
+ return (ENXIO);
+
+ for (i = 0; i < HDSP_MAX_CHANS && chan_map[i].descr != NULL; i++) {
+ scp = malloc(sizeof(struct sc_pcminfo), M_DEVBUF, M_WAITOK | M_ZERO);
+ scp->hc = &chan_map[i];
+ scp->sc = sc;
+ scp->dev = device_add_child(dev, "pcm", DEVICE_UNIT_ANY);
+ device_set_ivars(scp->dev, scp);
+ }
+
+ hdsp_map_dmabuf(sc);
+
+ SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
+ SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
+ "sync_status", CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE,
+ sc, 0, hdsp_sysctl_sync_status, "A",
+ "List clock source signal lock and sync status");
+
+ SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
+ SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
+ "clock_source", CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE,
+ sc, 0, hdsp_sysctl_clock_source, "A",
+ "Currently effective clock source");
+
+ SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
+ SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
+ "clock_preference", CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_MPSAFE,
+ sc, 0, hdsp_sysctl_clock_preference, "A",
+ "Set 'internal' (master) or preferred autosync clock source");
+
+ SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
+ SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
+ "clock_list", CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE,
+ sc, 0, hdsp_sysctl_clock_list, "A",
+ "List of supported clock sources");
+
+ SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
+ SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
+ "period", CTLTYPE_UINT | CTLFLAG_RW | CTLFLAG_MPSAFE,
+ sc, 0, hdsp_sysctl_period, "A",
+ "Force period of samples per interrupt (32, 64, ... 4096)");
+
+ SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
+ SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
+ "sample_rate", CTLTYPE_UINT | CTLFLAG_RW | CTLFLAG_MPSAFE,
+ sc, 0, hdsp_sysctl_sample_rate, "A",
+ "Force sample rate (32000, 44100, 48000, ... 192000)");
+
+ if (sc->type == HDSP_9632) {
+ SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
+ SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
+ "phones_level", CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_MPSAFE,
+ sc, 0, hdsp_sysctl_phones_level, "A",
+ "Phones output level ('0dB', '-6dB', '-12dB')");
+
+ SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
+ SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
+ "output_level", CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_MPSAFE,
+ sc, 0, hdsp_sysctl_output_level, "A",
+ "Analog output level ('HighGain', '+4dBU', '-10dBV')");
+
+ SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
+ SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
+ "input_level", CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_MPSAFE,
+ sc, 0, hdsp_sysctl_input_level, "A",
+ "Analog input level ('LowGain', '+4dBU', '-10dBV')");
+ }
+
+ bus_attach_children(dev);
+ return (0);
+}
+
+static void
+hdsp_child_deleted(device_t dev, device_t child)
+{
+ free(device_get_ivars(child), M_DEVBUF);
+}
+
+static void
+hdsp_dmafree(struct sc_info *sc)
+{
+
+ bus_dmamap_unload(sc->dmat, sc->rmap);
+ bus_dmamap_unload(sc->dmat, sc->pmap);
+ bus_dmamem_free(sc->dmat, sc->rbuf, sc->rmap);
+ bus_dmamem_free(sc->dmat, sc->pbuf, sc->pmap);
+ sc->rbuf = sc->pbuf = NULL;
+}
+
+static int
+hdsp_detach(device_t dev)
+{
+ struct sc_info *sc;
+ int err;
+
+ sc = device_get_softc(dev);
+ if (sc == NULL) {
+ device_printf(dev,"Can't detach: softc is null.\n");
+ return (0);
+ }
+
+ err = bus_generic_detach(dev);
+ if (err)
+ return (err);
+
+ hdsp_dmafree(sc);
+
+ if (sc->ih)
+ bus_teardown_intr(dev, sc->irq, sc->ih);
+ if (sc->dmat)
+ bus_dma_tag_destroy(sc->dmat);
+ if (sc->irq)
+ bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq);
+ if (sc->cs)
+ bus_release_resource(dev, SYS_RES_MEMORY, PCIR_BAR(0), sc->cs);
+ if (sc->lock)
+ snd_mtxfree(sc->lock);
+
+ return (0);
+}
+
+static device_method_t hdsp_methods[] = {
+ DEVMETHOD(device_probe, hdsp_probe),
+ DEVMETHOD(device_attach, hdsp_attach),
+ DEVMETHOD(device_detach, hdsp_detach),
+ DEVMETHOD(bus_child_deleted, hdsp_child_deleted),
+ { 0, 0 }
+};
+
+static driver_t hdsp_driver = {
+ "hdsp",
+ hdsp_methods,
+ PCM_SOFTC_SIZE,
+};
+
+DRIVER_MODULE(snd_hdsp, pci, hdsp_driver, 0, 0);
diff --git a/sys/dev/sound/pci/hdsp.h b/sys/dev/sound/pci/hdsp.h
new file mode 100644
index 000000000000..8ac438cd79f9
--- /dev/null
+++ b/sys/dev/sound/pci/hdsp.h
@@ -0,0 +1,266 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2012-2016 Ruslan Bukin <br@bsdpad.com>
+ * Copyright (c) 2023-2024 Florian Walpen <dev@submerge.ch>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#define PCI_VENDOR_XILINX 0x10ee
+#define PCI_DEVICE_XILINX_HDSP 0x3fc5 /* HDSP 9652 */
+#define PCI_REVISION_9632 0x9b
+#define PCI_REVISION_9652 0x6c
+
+#define HDSP_9632 0
+#define HDSP_9652 1
+
+/* Hardware mixer */
+#define HDSP_OUT_ENABLE_BASE 128
+#define HDSP_IN_ENABLE_BASE 384
+#define HDSP_MIXER_BASE 4096
+#define HDSP_MAX_GAIN 32768
+#define HDSP_MIN_GAIN 0
+#define HDSP_MIX_SLOTS_9632 16
+#define HDSP_MIX_SLOTS_9652 26
+#define HDSP_CONTROL2_9652_MIXER (1 << 11)
+
+/* Buffer */
+#define HDSP_PAGE_ADDR_BUF_OUT 32
+#define HDSP_PAGE_ADDR_BUF_IN 36
+#define HDSP_BUF_POSITION_MASK 0x000FFC0
+
+/* Frequency */
+#define HDSP_FREQ_0 (1 << 6)
+#define HDSP_FREQ_1 (1 << 7)
+#define HDSP_FREQ_DOUBLE (1 << 8)
+#define HDSP_FREQ_QUAD (1 << 31)
+
+#define HDSP_FREQ_32000 HDSP_FREQ_0
+#define HDSP_FREQ_44100 HDSP_FREQ_1
+#define HDSP_FREQ_48000 (HDSP_FREQ_0 | HDSP_FREQ_1)
+#define HDSP_FREQ_MASK (HDSP_FREQ_0 | HDSP_FREQ_1 | \
+ HDSP_FREQ_DOUBLE | HDSP_FREQ_QUAD)
+#define HDSP_FREQ_MASK_DEFAULT HDSP_FREQ_48000
+#define HDSP_FREQ_REG 0
+#define HDSP_FREQ_9632 104857600000000ULL
+#define hdsp_freq_multiplier(s) (((s) > 96000) ? 4 : \
+ (((s) > 48000) ? 2 : 1))
+#define hdsp_freq_single(s) ((s) / hdsp_freq_multiplier(s))
+#define hdsp_freq_reg_value(s) (HDSP_FREQ_9632 / hdsp_freq_single(s))
+
+#define HDSP_SPEED_DEFAULT 48000
+
+/* Latency */
+#define HDSP_LAT_0 (1 << 1)
+#define HDSP_LAT_1 (1 << 2)
+#define HDSP_LAT_2 (1 << 3)
+#define HDSP_LAT_MASK (HDSP_LAT_0 | HDSP_LAT_1 | HDSP_LAT_2)
+#define HDSP_LAT_BYTES_MAX (4096 * 4)
+#define HDSP_LAT_BYTES_MIN (32 * 4)
+#define hdsp_encode_latency(x) (((x)<<1) & HDSP_LAT_MASK)
+
+/* Register addresses */
+#define HDSP_RESET_POINTER 0
+#define HDSP_CONTROL_REG 64
+#define HDSP_CONTROL2_REG 256
+#define HDSP_STATUS_REG 0
+#define HDSP_STATUS2_REG 192
+
+/* Control register flags */
+#define HDSP_ENABLE (1 << 0)
+#define HDSP_CONTROL_SPDIF_COAX (1 << 14)
+#define HDSP_CONTROL_LINE_OUT (1 << 24)
+#define HDSP_CONTROL_INPUT_GAIN0 (1 << 25)
+#define HDSP_CONTROL_INPUT_GAIN1 (1 << 26)
+#define HDSP_CONTROL_OUTPUT_GAIN0 (1 << 27)
+#define HDSP_CONTROL_OUTPUT_GAIN1 (1 << 28)
+#define HDSP_CONTROL_PHONES_GAIN0 (1 << 29)
+#define HDSP_CONTROL_PHONES_GAIN1 (1 << 30)
+
+/* Analog input gain level */
+#define HDSP_INPUT_LEVEL_MASK (HDSP_CONTROL_INPUT_GAIN0 | \
+ HDSP_CONTROL_INPUT_GAIN1)
+#define HDSP_INPUT_LEVEL_LOWGAIN 0
+#define HDSP_INPUT_LEVEL_PLUS4DBU (HDSP_CONTROL_INPUT_GAIN0)
+#define HDSP_INPUT_LEVEL_MINUS10DBV (HDSP_CONTROL_INPUT_GAIN0 | \
+ HDSP_CONTROL_INPUT_GAIN1)
+
+/* Analog output gain level */
+#define HDSP_OUTPUT_LEVEL_MASK (HDSP_CONTROL_OUTPUT_GAIN0 | \
+ HDSP_CONTROL_OUTPUT_GAIN1)
+#define HDSP_OUTPUT_LEVEL_MINUS10DBV 0
+#define HDSP_OUTPUT_LEVEL_PLUS4DBU (HDSP_CONTROL_OUTPUT_GAIN0)
+#define HDSP_OUTPUT_LEVEL_HIGHGAIN (HDSP_CONTROL_OUTPUT_GAIN0 | \
+ HDSP_CONTROL_OUTPUT_GAIN1)
+
+/* Phones output gain level */
+#define HDSP_PHONES_LEVEL_MASK (HDSP_CONTROL_PHONES_GAIN0 | \
+ HDSP_CONTROL_PHONES_GAIN1)
+#define HDSP_PHONES_LEVEL_MINUS12DB 0
+#define HDSP_PHONES_LEVEL_MINUS6DB (HDSP_CONTROL_PHONES_GAIN0)
+#define HDSP_PHONES_LEVEL_0DB (HDSP_CONTROL_PHONES_GAIN0 | \
+ HDSP_CONTROL_PHONES_GAIN1)
+
+/* Interrupts */
+#define HDSP_AUDIO_IRQ_PENDING (1 << 0)
+#define HDSP_AUDIO_INT_ENABLE (1 << 5)
+#define HDSP_INTERRUPT_ACK 96
+
+/* Channels */
+#define HDSP_MAX_SLOTS 64 /* Mono channels */
+#define HDSP_MAX_CHANS (HDSP_MAX_SLOTS / 2) /* Stereo pairs */
+
+#define HDSP_CHANBUF_SAMPLES (16 * 1024)
+#define HDSP_CHANBUF_SIZE (4 * HDSP_CHANBUF_SAMPLES)
+#define HDSP_DMASEGSIZE (HDSP_CHANBUF_SIZE * HDSP_MAX_SLOTS)
+
+#define HDSP_CHAN_9632_ADAT (1 << 0)
+#define HDSP_CHAN_9632_SPDIF (1 << 1)
+#define HDSP_CHAN_9632_LINE (1 << 2)
+#define HDSP_CHAN_9632_EXT (1 << 3) /* Extension boards */
+#define HDSP_CHAN_9632_ALL (HDSP_CHAN_9632_ADAT | \
+ HDSP_CHAN_9632_SPDIF | \
+ HDSP_CHAN_9632_LINE | \
+ HDSP_CHAN_9632_EXT)
+
+#define HDSP_CHAN_9652_ADAT1 (1 << 5)
+#define HDSP_CHAN_9652_ADAT2 (1 << 6)
+#define HDSP_CHAN_9652_ADAT3 (1 << 7)
+#define HDSP_CHAN_9652_ADAT_ALL (HDSP_CHAN_9652_ADAT1 | \
+ HDSP_CHAN_9652_ADAT2 | \
+ HDSP_CHAN_9652_ADAT3)
+#define HDSP_CHAN_9652_SPDIF (1 << 8)
+#define HDSP_CHAN_9652_ALL (HDSP_CHAN_9652_ADAT_ALL | \
+ HDSP_CHAN_9652_SPDIF)
+
+struct hdsp_channel {
+ uint32_t ports;
+ char *descr;
+};
+
+enum hdsp_clock_type {
+ HDSP_CLOCK_INTERNAL,
+ HDSP_CLOCK_ADAT1,
+ HDSP_CLOCK_ADAT2,
+ HDSP_CLOCK_ADAT3,
+ HDSP_CLOCK_SPDIF,
+ HDSP_CLOCK_WORD,
+ HDSP_CLOCK_ADAT_SYNC
+};
+
+/* Preferred clock source. */
+#define HDSP_CONTROL_MASTER (1 << 4)
+#define HDSP_CONTROL_CLOCK_MASK (HDSP_CONTROL_MASTER | (1 << 13) | \
+ (1 << 16) | (1 << 17))
+#define HDSP_CONTROL_CLOCK(n) (((n & 0x04) << 11) | ((n & 0x03) << 16))
+
+/* Autosync selected clock source. */
+#define HDSP_STATUS2_CLOCK(n) ((n & 0x07) << 8)
+#define HDSP_STATUS2_CLOCK_MASK HDSP_STATUS2_CLOCK(0x07);
+
+struct hdsp_clock_source {
+ char *name;
+ enum hdsp_clock_type type;
+};
+
+static MALLOC_DEFINE(M_HDSP, "hdsp", "hdsp audio");
+
+/* Channel registers */
+struct sc_chinfo {
+ struct snd_dbuf *buffer;
+ struct pcm_channel *channel;
+ struct sc_pcminfo *parent;
+
+ /* Channel information */
+ struct pcmchan_caps *caps;
+ uint32_t cap_fmts[4];
+ uint32_t dir;
+ uint32_t format;
+ uint32_t ports;
+ uint32_t lvol;
+ uint32_t rvol;
+
+ /* Buffer */
+ uint32_t *data;
+ uint32_t size;
+ uint32_t position;
+
+ /* Flags */
+ uint32_t run;
+};
+
+/* PCM device private data */
+struct sc_pcminfo {
+ device_t dev;
+ uint32_t (*ih) (struct sc_pcminfo *scp);
+ uint32_t chnum;
+ struct sc_chinfo chan[HDSP_MAX_CHANS];
+ struct sc_info *sc;
+ struct hdsp_channel *hc;
+};
+
+/* HDSP device private data */
+struct sc_info {
+ device_t dev;
+ struct mtx *lock;
+
+ uint32_t ctrl_register;
+ uint32_t type;
+
+ /* Control/Status register */
+ struct resource *cs;
+ int csid;
+ bus_space_tag_t cst;
+ bus_space_handle_t csh;
+
+ struct resource *irq;
+ int irqid;
+ void *ih;
+ bus_dma_tag_t dmat;
+
+ /* Play/Record DMA buffers */
+ uint32_t *pbuf;
+ uint32_t *rbuf;
+ uint32_t bufsize;
+ bus_dmamap_t pmap;
+ bus_dmamap_t rmap;
+ uint32_t period;
+ uint32_t speed;
+ uint32_t force_period;
+ uint32_t force_speed;
+};
+
+#define hdsp_read_1(sc, regno) \
+ bus_space_read_1((sc)->cst, (sc)->csh, (regno))
+#define hdsp_read_2(sc, regno) \
+ bus_space_read_2((sc)->cst, (sc)->csh, (regno))
+#define hdsp_read_4(sc, regno) \
+ bus_space_read_4((sc)->cst, (sc)->csh, (regno))
+
+#define hdsp_write_1(sc, regno, data) \
+ bus_space_write_1((sc)->cst, (sc)->csh, (regno), (data))
+#define hdsp_write_2(sc, regno, data) \
+ bus_space_write_2((sc)->cst, (sc)->csh, (regno), (data))
+#define hdsp_write_4(sc, regno, data) \
+ bus_space_write_4((sc)->cst, (sc)->csh, (regno), (data))
diff --git a/sys/dev/sound/pci/hdspe-pcm.c b/sys/dev/sound/pci/hdspe-pcm.c
index 0e78be113a66..09bbbe22dacf 100644
--- a/sys/dev/sound/pci/hdspe-pcm.c
+++ b/sys/dev/sound/pci/hdspe-pcm.c
@@ -34,7 +34,6 @@
#include <dev/sound/pcm/sound.h>
#include <dev/sound/pci/hdspe.h>
-#include <dev/sound/chip.h>
#include <dev/pci/pcireg.h>
#include <dev/pci/pcivar.h>
@@ -115,10 +114,8 @@ hdspe_port_first_row(uint32_t ports)
uint32_t ends;
/* Restrict ports to one set with contiguous slots. */
- if (ports & HDSPE_CHAN_AIO_LINE)
- ports = HDSPE_CHAN_AIO_LINE; /* Gap in the AIO slots here. */
- else if (ports & HDSPE_CHAN_AIO_ALL)
- ports &= HDSPE_CHAN_AIO_ALL; /* Rest of the AIO slots. */
+ if (ports & HDSPE_CHAN_AIO_ALL)
+ ports &= HDSPE_CHAN_AIO_ALL; /* All AIO slots. */
else if (ports & HDSPE_CHAN_RAY_ALL)
ports &= HDSPE_CHAN_RAY_ALL; /* All RayDAT slots. */
@@ -137,6 +134,8 @@ hdspe_channel_count(uint32_t ports, uint32_t adat_width)
/* AIO ports. */
if (ports & HDSPE_CHAN_AIO_LINE)
count += 2;
+ if (ports & HDSPE_CHAN_AIO_EXT)
+ count += 4;
if (ports & HDSPE_CHAN_AIO_PHONE)
count += 2;
if (ports & HDSPE_CHAN_AIO_AES)
@@ -190,6 +189,8 @@ hdspe_port_slot_offset(uint32_t port, unsigned int adat_width)
/* AIO ports */
case HDSPE_CHAN_AIO_LINE:
return (0);
+ case HDSPE_CHAN_AIO_EXT:
+ return (2);
case HDSPE_CHAN_AIO_PHONE:
return (6);
case HDSPE_CHAN_AIO_AES:
@@ -651,6 +652,35 @@ clean(struct sc_chinfo *ch)
}
/* Channel interface. */
+static int
+hdspechan_free(kobj_t obj, void *data)
+{
+ struct sc_pcminfo *scp;
+ struct sc_chinfo *ch;
+ struct sc_info *sc;
+
+ ch = data;
+ scp = ch->parent;
+ sc = scp->sc;
+
+#if 0
+ device_printf(scp->dev, "hdspechan_free()\n");
+#endif
+
+ snd_mtxlock(sc->lock);
+ if (ch->data != NULL) {
+ free(ch->data, M_HDSPE);
+ ch->data = NULL;
+ }
+ if (ch->caps != NULL) {
+ free(ch->caps, M_HDSPE);
+ ch->caps = NULL;
+ }
+ snd_mtxunlock(sc->lock);
+
+ return (0);
+}
+
static void *
hdspechan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b,
struct pcm_channel *c, int dir)
@@ -703,6 +733,7 @@ hdspechan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b,
if (sndbuf_setup(ch->buffer, ch->data, ch->size) != 0) {
device_printf(scp->dev, "Can't setup sndbuf.\n");
+ hdspechan_free(obj, ch);
return (NULL);
}
@@ -776,35 +807,6 @@ hdspechan_getptr(kobj_t obj, void *data)
}
static int
-hdspechan_free(kobj_t obj, void *data)
-{
- struct sc_pcminfo *scp;
- struct sc_chinfo *ch;
- struct sc_info *sc;
-
- ch = data;
- scp = ch->parent;
- sc = scp->sc;
-
-#if 0
- device_printf(scp->dev, "hdspechan_free()\n");
-#endif
-
- snd_mtxlock(sc->lock);
- if (ch->data != NULL) {
- free(ch->data, M_HDSPE);
- ch->data = NULL;
- }
- if (ch->caps != NULL) {
- free(ch->caps, M_HDSPE);
- ch->caps = NULL;
- }
- snd_mtxunlock(sc->lock);
-
- return (0);
-}
-
-static int
hdspechan_setformat(kobj_t obj, void *data, uint32_t format)
{
struct sc_chinfo *ch;
@@ -1062,13 +1064,10 @@ hdspe_pcm_attach(device_t dev)
pcm_flags |= SD_F_BITPERFECT;
pcm_setflags(dev, pcm_flags);
+ pcm_init(dev, scp);
+
play = (hdspe_channel_play_ports(scp->hc)) ? 1 : 0;
rec = (hdspe_channel_rec_ports(scp->hc)) ? 1 : 0;
- err = pcm_register(dev, scp, play, rec);
- if (err) {
- device_printf(dev, "Can't register pcm.\n");
- return (ENXIO);
- }
scp->chnum = 0;
if (play) {
@@ -1085,7 +1084,11 @@ hdspe_pcm_attach(device_t dev)
rman_get_start(scp->sc->cs),
rman_get_start(scp->sc->irq),
device_get_nameunit(device_get_parent(dev)));
- pcm_setstatus(dev, status);
+ err = pcm_register(dev, status);
+ if (err) {
+ device_printf(dev, "Can't register pcm.\n");
+ return (ENXIO);
+ }
mixer_init(dev, &hdspemixer_class, scp);
diff --git a/sys/dev/sound/pci/hdspe.c b/sys/dev/sound/pci/hdspe.c
index f9c36df359e3..c292b2ddef56 100644
--- a/sys/dev/sound/pci/hdspe.c
+++ b/sys/dev/sound/pci/hdspe.c
@@ -37,7 +37,6 @@
#include <dev/sound/pcm/sound.h>
#include <dev/sound/pci/hdspe.h>
-#include <dev/sound/chip.h>
#include <dev/pci/pcireg.h>
#include <dev/pci/pcivar.h>
@@ -79,6 +78,7 @@ static struct hdspe_clock_source hdspe_clock_source_table_aio[] = {
static struct hdspe_channel chan_map_aio[] = {
{ HDSPE_CHAN_AIO_LINE, "line" },
+ { HDSPE_CHAN_AIO_EXT, "ext" },
{ HDSPE_CHAN_AIO_PHONE, "phone" },
{ HDSPE_CHAN_AIO_AES, "aes" },
{ HDSPE_CHAN_AIO_SPDIF, "s/pdif" },
@@ -246,6 +246,198 @@ hdspe_map_dmabuf(struct sc_info *sc)
}
}
+static const char *
+hdspe_settings_input_level(uint32_t settings)
+{
+ switch (settings & HDSPE_INPUT_LEVEL_MASK) {
+ case HDSPE_INPUT_LEVEL_LOWGAIN:
+ return ("LowGain");
+ case HDSPE_INPUT_LEVEL_PLUS4DBU:
+ return ("+4dBu");
+ case HDSPE_INPUT_LEVEL_MINUS10DBV:
+ return ("-10dBV");
+ default:
+ return (NULL);
+ }
+}
+
+static int
+hdspe_sysctl_input_level(SYSCTL_HANDLER_ARGS)
+{
+ struct sc_info *sc;
+ const char *label;
+ char buf[16] = "invalid";
+ int error;
+ uint32_t settings;
+
+ sc = oidp->oid_arg1;
+
+ /* Only available on HDSPE AIO. */
+ if (sc->type != HDSPE_AIO)
+ return (ENXIO);
+
+ /* Extract current input level from settings register. */
+ settings = sc->settings_register & HDSPE_INPUT_LEVEL_MASK;
+ label = hdspe_settings_input_level(settings);
+ if (label != NULL)
+ strlcpy(buf, label, sizeof(buf));
+
+ /* Process sysctl string request. */
+ error = sysctl_handle_string(oidp, buf, sizeof(buf), req);
+ if (error != 0 || req->newptr == NULL)
+ return (error);
+
+ /* Find input level matching the sysctl string. */
+ label = hdspe_settings_input_level(HDSPE_INPUT_LEVEL_LOWGAIN);
+ if (strncasecmp(buf, label, sizeof(buf)) == 0)
+ settings = HDSPE_INPUT_LEVEL_LOWGAIN;
+ label = hdspe_settings_input_level(HDSPE_INPUT_LEVEL_PLUS4DBU);
+ if (strncasecmp(buf, label, sizeof(buf)) == 0)
+ settings = HDSPE_INPUT_LEVEL_PLUS4DBU;
+ label = hdspe_settings_input_level(HDSPE_INPUT_LEVEL_MINUS10DBV);
+ if (strncasecmp(buf, label, sizeof(buf)) == 0)
+ settings = HDSPE_INPUT_LEVEL_MINUS10DBV;
+
+ /* Set input level in settings register. */
+ settings &= HDSPE_INPUT_LEVEL_MASK;
+ if (settings != (sc->settings_register & HDSPE_INPUT_LEVEL_MASK)) {
+ snd_mtxlock(sc->lock);
+ sc->settings_register &= ~HDSPE_INPUT_LEVEL_MASK;
+ sc->settings_register |= settings;
+ hdspe_write_4(sc, HDSPE_SETTINGS_REG, sc->settings_register);
+ snd_mtxunlock(sc->lock);
+ }
+ return (0);
+}
+
+static const char *
+hdspe_settings_output_level(uint32_t settings)
+{
+ switch (settings & HDSPE_OUTPUT_LEVEL_MASK) {
+ case HDSPE_OUTPUT_LEVEL_HIGHGAIN:
+ return ("HighGain");
+ case HDSPE_OUTPUT_LEVEL_PLUS4DBU:
+ return ("+4dBu");
+ case HDSPE_OUTPUT_LEVEL_MINUS10DBV:
+ return ("-10dBV");
+ default:
+ return (NULL);
+ }
+}
+
+static int
+hdspe_sysctl_output_level(SYSCTL_HANDLER_ARGS)
+{
+ struct sc_info *sc;
+ const char *label;
+ char buf[16] = "invalid";
+ int error;
+ uint32_t settings;
+
+ sc = oidp->oid_arg1;
+
+ /* Only available on HDSPE AIO. */
+ if (sc->type != HDSPE_AIO)
+ return (ENXIO);
+
+ /* Extract current output level from settings register. */
+ settings = sc->settings_register & HDSPE_OUTPUT_LEVEL_MASK;
+ label = hdspe_settings_output_level(settings);
+ if (label != NULL)
+ strlcpy(buf, label, sizeof(buf));
+
+ /* Process sysctl string request. */
+ error = sysctl_handle_string(oidp, buf, sizeof(buf), req);
+ if (error != 0 || req->newptr == NULL)
+ return (error);
+
+ /* Find output level matching the sysctl string. */
+ label = hdspe_settings_output_level(HDSPE_OUTPUT_LEVEL_HIGHGAIN);
+ if (strncasecmp(buf, label, sizeof(buf)) == 0)
+ settings = HDSPE_OUTPUT_LEVEL_HIGHGAIN;
+ label = hdspe_settings_output_level(HDSPE_OUTPUT_LEVEL_PLUS4DBU);
+ if (strncasecmp(buf, label, sizeof(buf)) == 0)
+ settings = HDSPE_OUTPUT_LEVEL_PLUS4DBU;
+ label = hdspe_settings_output_level(HDSPE_OUTPUT_LEVEL_MINUS10DBV);
+ if (strncasecmp(buf, label, sizeof(buf)) == 0)
+ settings = HDSPE_OUTPUT_LEVEL_MINUS10DBV;
+
+ /* Set output level in settings register. */
+ settings &= HDSPE_OUTPUT_LEVEL_MASK;
+ if (settings != (sc->settings_register & HDSPE_OUTPUT_LEVEL_MASK)) {
+ snd_mtxlock(sc->lock);
+ sc->settings_register &= ~HDSPE_OUTPUT_LEVEL_MASK;
+ sc->settings_register |= settings;
+ hdspe_write_4(sc, HDSPE_SETTINGS_REG, sc->settings_register);
+ snd_mtxunlock(sc->lock);
+ }
+ return (0);
+}
+
+static const char *
+hdspe_settings_phones_level(uint32_t settings)
+{
+ switch (settings & HDSPE_PHONES_LEVEL_MASK) {
+ case HDSPE_PHONES_LEVEL_HIGHGAIN:
+ return ("HighGain");
+ case HDSPE_PHONES_LEVEL_PLUS4DBU:
+ return ("+4dBu");
+ case HDSPE_PHONES_LEVEL_MINUS10DBV:
+ return ("-10dBV");
+ default:
+ return (NULL);
+ }
+}
+
+static int
+hdspe_sysctl_phones_level(SYSCTL_HANDLER_ARGS)
+{
+ struct sc_info *sc;
+ const char *label;
+ char buf[16] = "invalid";
+ int error;
+ uint32_t settings;
+
+ sc = oidp->oid_arg1;
+
+ /* Only available on HDSPE AIO. */
+ if (sc->type != HDSPE_AIO)
+ return (ENXIO);
+
+ /* Extract current phones level from settings register. */
+ settings = sc->settings_register & HDSPE_PHONES_LEVEL_MASK;
+ label = hdspe_settings_phones_level(settings);
+ if (label != NULL)
+ strlcpy(buf, label, sizeof(buf));
+
+ /* Process sysctl string request. */
+ error = sysctl_handle_string(oidp, buf, sizeof(buf), req);
+ if (error != 0 || req->newptr == NULL)
+ return (error);
+
+ /* Find phones level matching the sysctl string. */
+ label = hdspe_settings_phones_level(HDSPE_PHONES_LEVEL_HIGHGAIN);
+ if (strncasecmp(buf, label, sizeof(buf)) == 0)
+ settings = HDSPE_PHONES_LEVEL_HIGHGAIN;
+ label = hdspe_settings_phones_level(HDSPE_PHONES_LEVEL_PLUS4DBU);
+ if (strncasecmp(buf, label, sizeof(buf)) == 0)
+ settings = HDSPE_PHONES_LEVEL_PLUS4DBU;
+ label = hdspe_settings_phones_level(HDSPE_PHONES_LEVEL_MINUS10DBV);
+ if (strncasecmp(buf, label, sizeof(buf)) == 0)
+ settings = HDSPE_PHONES_LEVEL_MINUS10DBV;
+
+ /* Set phones level in settings register. */
+ settings &= HDSPE_PHONES_LEVEL_MASK;
+ if (settings != (sc->settings_register & HDSPE_PHONES_LEVEL_MASK)) {
+ snd_mtxlock(sc->lock);
+ sc->settings_register &= ~HDSPE_PHONES_LEVEL_MASK;
+ sc->settings_register |= settings;
+ hdspe_write_4(sc, HDSPE_SETTINGS_REG, sc->settings_register);
+ snd_mtxunlock(sc->lock);
+ }
+ return (0);
+}
+
static int
hdspe_sysctl_sample_rate(SYSCTL_HANDLER_ARGS)
{
@@ -529,6 +721,15 @@ hdspe_init(struct sc_info *sc)
/* Other settings. */
sc->settings_register = 0;
+
+ /* Default gain levels. */
+ sc->settings_register &= ~HDSPE_INPUT_LEVEL_MASK;
+ sc->settings_register |= HDSPE_INPUT_LEVEL_LOWGAIN;
+ sc->settings_register &= ~HDSPE_OUTPUT_LEVEL_MASK;
+ sc->settings_register |= HDSPE_OUTPUT_LEVEL_MINUS10DBV;
+ sc->settings_register &= ~HDSPE_PHONES_LEVEL_MASK;
+ sc->settings_register |= HDSPE_PHONES_LEVEL_MINUS10DBV;
+
hdspe_write_4(sc, HDSPE_SETTINGS_REG, sc->settings_register);
return (0);
@@ -578,10 +779,10 @@ hdspe_attach(device_t dev)
return (ENXIO);
for (i = 0; i < HDSPE_MAX_CHANS && chan_map[i].descr != NULL; i++) {
- scp = malloc(sizeof(struct sc_pcminfo), M_DEVBUF, M_NOWAIT | M_ZERO);
+ scp = malloc(sizeof(struct sc_pcminfo), M_DEVBUF, M_WAITOK | M_ZERO);
scp->hc = &chan_map[i];
scp->sc = sc;
- scp->dev = device_add_child(dev, "pcm", -1);
+ scp->dev = device_add_child(dev, "pcm", DEVICE_UNIT_ANY);
device_set_ivars(scp->dev, scp);
}
@@ -623,7 +824,34 @@ hdspe_attach(device_t dev)
sc, 0, hdspe_sysctl_sample_rate, "A",
"Force sample rate (32000, 44100, 48000, ... 192000)");
- return (bus_generic_attach(dev));
+ if (sc->type == HDSPE_AIO) {
+ SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
+ SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
+ "phones_level", CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_MPSAFE,
+ sc, 0, hdspe_sysctl_phones_level, "A",
+ "Phones output level ('HighGain', '+4dBU', '-10dBV')");
+
+ SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
+ SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
+ "output_level", CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_MPSAFE,
+ sc, 0, hdspe_sysctl_output_level, "A",
+ "Analog output level ('HighGain', '+4dBU', '-10dBV')");
+
+ SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
+ SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
+ "input_level", CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_MPSAFE,
+ sc, 0, hdspe_sysctl_input_level, "A",
+ "Analog input level ('LowGain', '+4dBU', '-10dBV')");
+ }
+
+ bus_attach_children(dev);
+ return (0);
+}
+
+static void
+hdspe_child_deleted(device_t dev, device_t child)
+{
+ free(device_get_ivars(child), M_DEVBUF);
}
static void
@@ -649,7 +877,7 @@ hdspe_detach(device_t dev)
return (0);
}
- err = device_delete_children(dev);
+ err = bus_generic_detach(dev);
if (err)
return (err);
@@ -673,6 +901,7 @@ static device_method_t hdspe_methods[] = {
DEVMETHOD(device_probe, hdspe_probe),
DEVMETHOD(device_attach, hdspe_attach),
DEVMETHOD(device_detach, hdspe_detach),
+ DEVMETHOD(bus_child_deleted, hdspe_child_deleted),
{ 0, 0 }
};
diff --git a/sys/dev/sound/pci/hdspe.h b/sys/dev/sound/pci/hdspe.h
index daffeb4ddebc..bced78758068 100644
--- a/sys/dev/sound/pci/hdspe.h
+++ b/sys/dev/sound/pci/hdspe.h
@@ -74,35 +74,43 @@
#define HDSPE_LAT_BYTES_MIN (32 * 4)
#define hdspe_encode_latency(x) (((x)<<1) & HDSPE_LAT_MASK)
-/* Gain */
-#define HDSP_ADGain0 (1 << 25)
-#define HDSP_ADGain1 (1 << 26)
-#define HDSP_DAGain0 (1 << 27)
-#define HDSP_DAGain1 (1 << 28)
-#define HDSP_PhoneGain0 (1 << 29)
-#define HDSP_PhoneGain1 (1 << 30)
-
-#define HDSP_ADGainMask (HDSP_ADGain0 | HDSP_ADGain1)
-#define HDSP_ADGainMinus10dBV (HDSP_ADGainMask)
-#define HDSP_ADGainPlus4dBu (HDSP_ADGain0)
-#define HDSP_ADGainLowGain 0
-
-#define HDSP_DAGainMask (HDSP_DAGain0 | HDSP_DAGain1)
-#define HDSP_DAGainHighGain (HDSP_DAGainMask)
-#define HDSP_DAGainPlus4dBu (HDSP_DAGain0)
-#define HDSP_DAGainMinus10dBV 0
-
-#define HDSP_PhoneGainMask (HDSP_PhoneGain0|HDSP_PhoneGain1)
-#define HDSP_PhoneGain0dB HDSP_PhoneGainMask
-#define HDSP_PhoneGainMinus6dB (HDSP_PhoneGain0)
-#define HDSP_PhoneGainMinus12dB 0
-
-/* Settings */
+/* Register addresses */
#define HDSPE_SETTINGS_REG 0
#define HDSPE_CONTROL_REG 64
#define HDSPE_STATUS_REG 0
#define HDSPE_STATUS1_REG 64
#define HDSPE_STATUS2_REG 192
+
+/* Settings register flags */
+#define HDSPE_SETTINGS_INPUT_GAIN0 (1 << 20)
+#define HDSPE_SETTINGS_INPUT_GAIN1 (1 << 21)
+#define HDSPE_SETTINGS_OUTPUT_GAIN0 (1 << 22)
+#define HDSPE_SETTINGS_OUTPUT_GAIN1 (1 << 23)
+#define HDSPE_SETTINGS_PHONES_GAIN0 (1 << 24)
+#define HDSPE_SETTINGS_PHONES_GAIN1 (1 << 25)
+
+/* Analog input gain level */
+#define HDSPE_INPUT_LEVEL_MASK (HDSPE_SETTINGS_INPUT_GAIN0 | \
+ HDSPE_SETTINGS_INPUT_GAIN1)
+#define HDSPE_INPUT_LEVEL_LOWGAIN 0
+#define HDSPE_INPUT_LEVEL_PLUS4DBU (HDSPE_SETTINGS_INPUT_GAIN0)
+#define HDSPE_INPUT_LEVEL_MINUS10DBV (HDSPE_SETTINGS_INPUT_GAIN1)
+
+/* Analog output gain level */
+#define HDSPE_OUTPUT_LEVEL_MASK (HDSPE_SETTINGS_OUTPUT_GAIN0 | \
+ HDSPE_SETTINGS_OUTPUT_GAIN1)
+#define HDSPE_OUTPUT_LEVEL_HIGHGAIN 0
+#define HDSPE_OUTPUT_LEVEL_PLUS4DBU (HDSPE_SETTINGS_OUTPUT_GAIN0)
+#define HDSPE_OUTPUT_LEVEL_MINUS10DBV (HDSPE_SETTINGS_OUTPUT_GAIN1)
+
+/* Phones output gain level */
+#define HDSPE_PHONES_LEVEL_MASK (HDSPE_SETTINGS_PHONES_GAIN0 | \
+ HDSPE_SETTINGS_PHONES_GAIN1)
+#define HDSPE_PHONES_LEVEL_HIGHGAIN 0
+#define HDSPE_PHONES_LEVEL_PLUS4DBU (HDSPE_SETTINGS_PHONES_GAIN0)
+#define HDSPE_PHONES_LEVEL_MINUS10DBV (HDSPE_SETTINGS_PHONES_GAIN1)
+
+/* Control register flags */
#define HDSPE_ENABLE (1 << 0)
/* Interrupts */
@@ -119,23 +127,25 @@
#define HDSPE_DMASEGSIZE (HDSPE_CHANBUF_SIZE * HDSPE_MAX_SLOTS)
#define HDSPE_CHAN_AIO_LINE (1 << 0)
-#define HDSPE_CHAN_AIO_PHONE (1 << 1)
-#define HDSPE_CHAN_AIO_AES (1 << 2)
-#define HDSPE_CHAN_AIO_SPDIF (1 << 3)
-#define HDSPE_CHAN_AIO_ADAT (1 << 4)
+#define HDSPE_CHAN_AIO_EXT (1 << 1)
+#define HDSPE_CHAN_AIO_PHONE (1 << 2)
+#define HDSPE_CHAN_AIO_AES (1 << 3)
+#define HDSPE_CHAN_AIO_SPDIF (1 << 4)
+#define HDSPE_CHAN_AIO_ADAT (1 << 5)
#define HDSPE_CHAN_AIO_ALL_REC (HDSPE_CHAN_AIO_LINE | \
+ HDSPE_CHAN_AIO_EXT | \
HDSPE_CHAN_AIO_AES | \
HDSPE_CHAN_AIO_SPDIF | \
HDSPE_CHAN_AIO_ADAT)
#define HDSPE_CHAN_AIO_ALL (HDSPE_CHAN_AIO_ALL_REC | \
HDSPE_CHAN_AIO_PHONE) \
-#define HDSPE_CHAN_RAY_AES (1 << 5)
-#define HDSPE_CHAN_RAY_SPDIF (1 << 6)
-#define HDSPE_CHAN_RAY_ADAT1 (1 << 7)
-#define HDSPE_CHAN_RAY_ADAT2 (1 << 8)
-#define HDSPE_CHAN_RAY_ADAT3 (1 << 9)
-#define HDSPE_CHAN_RAY_ADAT4 (1 << 10)
+#define HDSPE_CHAN_RAY_AES (1 << 6)
+#define HDSPE_CHAN_RAY_SPDIF (1 << 7)
+#define HDSPE_CHAN_RAY_ADAT1 (1 << 8)
+#define HDSPE_CHAN_RAY_ADAT2 (1 << 9)
+#define HDSPE_CHAN_RAY_ADAT3 (1 << 10)
+#define HDSPE_CHAN_RAY_ADAT4 (1 << 11)
#define HDSPE_CHAN_RAY_ALL (HDSPE_CHAN_RAY_AES | \
HDSPE_CHAN_RAY_SPDIF | \
HDSPE_CHAN_RAY_ADAT1 | \
diff --git a/sys/dev/sound/pci/ich.c b/sys/dev/sound/pci/ich.c
index fbde0accfd28..500d6d95daac 100644
--- a/sys/dev/sound/pci/ich.c
+++ b/sys/dev/sound/pci/ich.c
@@ -695,7 +695,7 @@ ich_setstatus(struct sc_info *sc)
device_printf(sc->dev,
"PCI Master abort workaround enabled\n");
- pcm_setstatus(sc->dev, status);
+ pcm_register(sc->dev, status);
}
/* -------------------------------------------------------------------- */
@@ -860,12 +860,12 @@ ich_init(struct sc_info *sc)
static int
ich_pci_probe(device_t dev)
{
- int i;
+ size_t i;
uint16_t devid, vendor;
vendor = pci_get_vendor(dev);
devid = pci_get_device(dev);
- for (i = 0; i < sizeof(ich_devs)/sizeof(ich_devs[0]); i++) {
+ for (i = 0; i < nitems(ich_devs); i++) {
if (vendor == ich_devs[i].vendor &&
devid == ich_devs[i].devid) {
device_set_desc(dev, ich_devs[i].name);
@@ -1066,8 +1066,7 @@ ich_pci_attach(device_t dev)
ich_setmap, sc, 0))
goto bad;
- if (pcm_register(dev, sc, 1, (sc->hasmic) ? 2 : 1))
- goto bad;
+ pcm_init(dev, sc);
pcm_addchan(dev, PCMDIR_PLAY, &ichchan_class, sc); /* play */
pcm_addchan(dev, PCMDIR_REC, &ichchan_class, sc); /* record */
diff --git a/sys/dev/sound/pci/maestro3.c b/sys/dev/sound/pci/maestro3.c
index 6dd54a66f683..2d102fcd6dbe 100644
--- a/sys/dev/sound/pci/maestro3.c
+++ b/sys/dev/sound/pci/maestro3.c
@@ -488,7 +488,7 @@ m3_pchan_init(kobj_t kobj, void *devinfo, struct snd_dbuf *b, struct pcm_channel
DMAC_BLOCKF_SELECTOR);
/* set an armload of static initializers */
- for(i = 0 ; i < (sizeof(pv) / sizeof(pv[0])) ; i++) {
+ for(i = 0 ; i < nitems(pv); i++) {
m3_wr_assp_data(sc, ch->dac_data + pv[i].addr, pv[i].val);
}
@@ -862,7 +862,7 @@ m3_rchan_init(kobj_t kobj, void *devinfo, struct snd_dbuf *b, struct pcm_channel
DMAC_PAGE3_SELECTOR + DMAC_BLOCKF_SELECTOR);
/* set an armload of static initializers */
- for(i = 0 ; i < (sizeof(rv) / sizeof(rv[0])) ; i++) {
+ for(i = 0 ; i < nitems(rv); i++) {
m3_wr_assp_data(sc, ch->adc_data + rv[i].addr, rv[i].val);
}
@@ -1423,10 +1423,7 @@ m3_pci_attach(device_t dev)
m3_enable_ints(sc);
- if (pcm_register(dev, sc, dacn, adcn)) {
- device_printf(dev, "pcm_register error\n");
- goto bad;
- }
+ pcm_init(dev, sc);
for (i=0 ; i<dacn ; i++) {
if (pcm_addchan(dev, PCMDIR_PLAY, &m3_pch_class, sc)) {
device_printf(dev, "pcm_addchan (play) error\n");
@@ -1443,8 +1440,8 @@ m3_pci_attach(device_t dev)
(sc->regtype == SYS_RES_IOPORT)? "port" : "mem",
rman_get_start(sc->reg), rman_get_start(sc->irq),
device_get_nameunit(device_get_parent(dev)));
- if (pcm_setstatus(dev, status)) {
- device_printf(dev, "attach: pcm_setstatus error\n");
+ if (pcm_register(dev, status)) {
+ device_printf(dev, "pcm_register error\n");
goto bad;
}
@@ -1453,7 +1450,7 @@ m3_pci_attach(device_t dev)
/* Create the buffer for saving the card state during suspend */
len = sizeof(u_int16_t) * (REV_B_CODE_MEMORY_LENGTH +
REV_B_DATA_MEMORY_LENGTH);
- sc->savemem = (u_int16_t*)malloc(len, M_DEVBUF, M_WAITOK | M_ZERO);
+ sc->savemem = malloc(len, M_DEVBUF, M_WAITOK | M_ZERO);
return 0;
diff --git a/sys/dev/sound/pci/neomagic.c b/sys/dev/sound/pci/neomagic.c
index 25273633ff18..d7824c990a52 100644
--- a/sys/dev/sound/pci/neomagic.c
+++ b/sys/dev/sound/pci/neomagic.c
@@ -707,10 +707,11 @@ nm_pci_attach(device_t dev)
rman_get_start(sc->irq),
device_get_nameunit(device_get_parent(dev)));
- if (pcm_register(dev, sc, 1, 1)) goto bad;
+ pcm_init(dev, sc);
pcm_addchan(dev, PCMDIR_REC, &nmchan_class, sc);
pcm_addchan(dev, PCMDIR_PLAY, &nmchan_class, sc);
- pcm_setstatus(dev, status);
+ if (pcm_register(dev, status))
+ goto bad;
return 0;
diff --git a/sys/dev/sound/pci/solo.c b/sys/dev/sound/pci/solo.c
index bee79e723696..90dd2e26ad41 100644
--- a/sys/dev/sound/pci/solo.c
+++ b/sys/dev/sound/pci/solo.c
@@ -35,7 +35,6 @@
#include <dev/pci/pcivar.h>
#include <dev/sound/isa/sb.h>
-#include <dev/sound/chip.h>
#include "mixer_if.h"
@@ -1027,11 +1026,11 @@ ess_attach(device_t dev)
rman_get_start(sc->irq),
device_get_nameunit(device_get_parent(dev)));
- if (pcm_register(dev, sc, 1, 1))
- goto no;
+ pcm_init(dev, sc);
pcm_addchan(dev, PCMDIR_REC, &esschan_class, sc);
pcm_addchan(dev, PCMDIR_PLAY, &esschan_class, sc);
- pcm_setstatus(dev, status);
+ if (pcm_register(dev, status))
+ goto no;
return 0;
diff --git a/sys/dev/sound/pci/spicds.c b/sys/dev/sound/pci/spicds.c
index 3b67101b8df0..da0e8d9da6d5 100644
--- a/sys/dev/sound/pci/spicds.c
+++ b/sys/dev/sound/pci/spicds.c
@@ -144,7 +144,7 @@ spicds_create(device_t dev, void *devinfo, int num, spicds_ctrl ctrl)
#if(0)
device_printf(dev, "spicds_create(dev, devinfo, %d, ctrl)\n", num);
#endif
- codec = (struct spicds_info *)malloc(sizeof *codec, M_SPICDS, M_NOWAIT);
+ codec = malloc(sizeof(*codec), M_SPICDS, M_NOWAIT);
if (codec == NULL)
return NULL;
diff --git a/sys/dev/sound/pci/t4dwave.c b/sys/dev/sound/pci/t4dwave.c
index 653e610febbe..07b9e1004573 100644
--- a/sys/dev/sound/pci/t4dwave.c
+++ b/sys/dev/sound/pci/t4dwave.c
@@ -921,12 +921,12 @@ tr_pci_attach(device_t dev)
rman_get_start(tr->reg), rman_get_start(tr->irq),
device_get_nameunit(device_get_parent(dev)));
- if (pcm_register(dev, tr, dacn, 1))
- goto bad;
+ pcm_init(dev, tr);
pcm_addchan(dev, PCMDIR_REC, &trrchan_class, tr);
for (i = 0; i < dacn; i++)
pcm_addchan(dev, PCMDIR_PLAY, &trpchan_class, tr);
- pcm_setstatus(dev, status);
+ if (pcm_register(dev, status))
+ goto bad;
return 0;
diff --git a/sys/dev/sound/pci/via8233.c b/sys/dev/sound/pci/via8233.c
index 9f3b312e4365..243353805b94 100644
--- a/sys/dev/sound/pci/via8233.c
+++ b/sys/dev/sound/pci/via8233.c
@@ -1352,8 +1352,7 @@ via_attach(device_t dev)
device_get_nameunit(device_get_parent(dev)));
/* Register */
- if (pcm_register(dev, via, via_dxs_chnum + via_sgd_chnum, NWRCHANS))
- goto bad;
+ pcm_init(dev, via);
for (i = 0; i < via_dxs_chnum; i++)
pcm_addchan(dev, PCMDIR_PLAY, &via8233dxs_class, via);
for (i = 0; i < via_sgd_chnum; i++)
@@ -1366,7 +1365,8 @@ via_attach(device_t dev)
(via_dxs_chnum > 0) ? "En" : "Dis", (via->dxs_src) ? "(SRC)" : "",
via_dxs_chnum, via_sgd_chnum, NWRCHANS);
- pcm_setstatus(dev, status);
+ if (pcm_register(dev, status))
+ goto bad;
return (0);
bad:
diff --git a/sys/dev/sound/pci/via82c686.c b/sys/dev/sound/pci/via82c686.c
index 44f846b684d7..40f3521a57a2 100644
--- a/sys/dev/sound/pci/via82c686.c
+++ b/sys/dev/sound/pci/via82c686.c
@@ -585,10 +585,11 @@ via_attach(device_t dev)
device_get_nameunit(device_get_parent(dev)));
/* Register */
- if (pcm_register(dev, via, 1, 1)) goto bad;
+ pcm_init(dev, via);
pcm_addchan(dev, PCMDIR_PLAY, &viachan_class, via);
pcm_addchan(dev, PCMDIR_REC, &viachan_class, via);
- pcm_setstatus(dev, status);
+ if (pcm_register(dev, status))
+ goto bad;
return 0;
bad:
if (via->codec) ac97_destroy(via->codec);
diff --git a/sys/dev/sound/pci/vibes.c b/sys/dev/sound/pci/vibes.c
index e587f0113b5d..7e908f188614 100644
--- a/sys/dev/sound/pci/vibes.c
+++ b/sys/dev/sound/pci/vibes.c
@@ -866,18 +866,17 @@ sv_attach(device_t dev) {
if (bootverbose)
printf("Sonicvibes: revision %d.\n", sc->rev);
- if (pcm_register(dev, sc, 1, 1)) {
- device_printf(dev, "sv_attach: pcm_register fail\n");
- goto fail;
- }
-
+ pcm_init(dev, sc);
pcm_addchan(dev, PCMDIR_PLAY, &svpchan_class, sc);
pcm_addchan(dev, PCMDIR_REC, &svrchan_class, sc);
snprintf(status, SND_STATUSLEN, "port 0x%jx irq %jd on %s",
rman_get_start(sc->enh_reg), rman_get_start(sc->irq),
device_get_nameunit(device_get_parent(dev)));
- pcm_setstatus(dev, status);
+ if (pcm_register(dev, status)) {
+ device_printf(dev, "sv_attach: pcm_register fail\n");
+ goto fail;
+ }
DEB(printf("sv_attach: succeeded\n"));
diff --git a/sys/dev/sound/pcm/ac97.c b/sys/dev/sound/pcm/ac97.c
index d04ec2d8271c..f5ca06cd3942 100644
--- a/sys/dev/sound/pcm/ac97.c
+++ b/sys/dev/sound/pcm/ac97.c
@@ -32,7 +32,6 @@
#include <dev/sound/pcm/sound.h>
#include <dev/sound/pcm/ac97.h>
-#include <dev/sound/pcm/ac97_patch.h>
#include <dev/pci/pcivar.h>
@@ -40,6 +39,8 @@
static MALLOC_DEFINE(M_AC97, "ac97", "ac97 codec");
+typedef void (*ac97_patch)(struct ac97_info *);
+
struct ac97mixtable_entry {
int reg; /* register index */
/* reg < 0 if inverted polarity */
@@ -133,6 +134,12 @@ static const struct ac97_vendorid ac97vendorid[] = {
{ 0x00000000, NULL }
};
+static void ad1886_patch(struct ac97_info *);
+static void ad198x_patch(struct ac97_info *);
+static void ad1981b_patch(struct ac97_info *);
+static void cmi9739_patch(struct ac97_info *);
+static void alc655_patch(struct ac97_info *);
+
static struct ac97_codecid ac97codecid[] = {
{ 0x41445303, 0x00, 0, "AD1819", 0 },
{ 0x41445340, 0x00, 0, "AD1881", 0 },
@@ -315,12 +322,6 @@ ac97_rdcd(struct ac97_info *codec, int reg)
i[1] = AC97_READ(codec->methods, codec->devinfo, reg);
while (i[0] != i[1] && j)
i[j-- & 1] = AC97_READ(codec->methods, codec->devinfo, reg);
-#if 0
- if (j < 100) {
- device_printf(codec->dev, "%s(): Inconsistent register value at"
- " 0x%08x (retry: %d)\n", __func__, reg, 100 - j);
- }
-#endif
return i[!(j & 1)];
}
return AC97_READ(codec->methods, codec->devinfo, reg);
@@ -505,9 +506,6 @@ ac97_setmixer(struct ac97_info *codec, unsigned channel, unsigned left, unsigned
snd_mtxunlock(codec->lock);
return left | (right << 8);
} else {
-#if 0
- printf("ac97_setmixer: reg=%d, bits=%d, enable=%d\n", e->reg, e->bits, e->enable);
-#endif
return -1;
}
}
@@ -730,10 +728,6 @@ ac97_initmixer(struct ac97_info *codec)
for (j = 0; k >> j; j++)
;
if (j != 0) {
-#if 0
- device_printf(codec->dev, "%2d: [ac97_rdcd() = %d] [Testbit = %d] %d -> %d\n",
- i, k, bit, codec->mix[i].bits, j);
-#endif
codec->mix[i].enable = 1;
codec->mix[i].bits = j;
} else if (reg == AC97_MIX_BEEP) {
@@ -749,9 +743,6 @@ ac97_initmixer(struct ac97_info *codec)
codec->mix[i].enable = 0;
ac97_wrcd(codec, reg, old);
}
-#if 0
- printf("mixch %d, en=%d, b=%d\n", i, codec->mix[i].enable, codec->mix[i].bits);
-#endif
}
device_printf(codec->dev, "<%s>\n",
@@ -872,6 +863,93 @@ ac97_getflags(struct ac97_info *codec)
return codec->flags;
}
+static void
+ad1886_patch(struct ac97_info *codec)
+{
+#define AC97_AD_JACK_SPDIF 0x72
+ /*
+ * Presario700 workaround
+ * for Jack Sense/SPDIF Register misetting causing
+ * no audible output
+ * by Santiago Nullo 04/05/2002
+ */
+ ac97_wrcd(codec, AC97_AD_JACK_SPDIF, 0x0010);
+}
+
+static void
+ad198x_patch(struct ac97_info *codec)
+{
+ switch (ac97_getsubvendor(codec)) {
+ case 0x11931043: /* Not for ASUS A9T (probably else too). */
+ break;
+ default:
+ ac97_wrcd(codec, 0x76, ac97_rdcd(codec, 0x76) | 0x0420);
+ break;
+ }
+}
+
+static void
+ad1981b_patch(struct ac97_info *codec)
+{
+ /*
+ * Enable headphone jack sensing.
+ */
+ switch (ac97_getsubvendor(codec)) {
+ case 0x02d91014: /* IBM Thinkcentre */
+ case 0x099c103c: /* HP nx6110 */
+ ac97_wrcd(codec, AC97_AD_JACK_SPDIF,
+ ac97_rdcd(codec, AC97_AD_JACK_SPDIF) | 0x0800);
+ break;
+ default:
+ break;
+ }
+}
+
+static void
+cmi9739_patch(struct ac97_info *codec)
+{
+ /*
+ * Few laptops need extra register initialization
+ * to power up the internal speakers.
+ */
+ switch (ac97_getsubvendor(codec)) {
+ case 0x18431043: /* ASUS W1000N */
+ ac97_wrcd(codec, AC97_REG_POWER, 0x000f);
+ ac97_wrcd(codec, AC97_MIXEXT_CLFE, 0x0000);
+ ac97_wrcd(codec, 0x64, 0x7110);
+ break;
+ default:
+ break;
+ }
+}
+
+static void
+alc655_patch(struct ac97_info *codec)
+{
+ /*
+ * MSI (Micro-Star International) specific EAPD quirk.
+ */
+ switch (ac97_getsubvendor(codec)) {
+ case 0x00611462: /* MSI S250 */
+ case 0x01311462: /* MSI S270 */
+ case 0x01611462: /* LG K1 Express */
+ case 0x03511462: /* MSI L725 */
+ ac97_wrcd(codec, 0x7a, ac97_rdcd(codec, 0x7a) & 0xfffd);
+ break;
+ case 0x10ca1734:
+ /*
+ * Amilo Pro V2055 with ALC655 has phone out by default
+ * disabled (surround on), leaving us only with internal
+ * speakers. This should really go to mixer. We write the
+ * Data Flow Control reg.
+ */
+ ac97_wrcd(codec, 0x6a, ac97_rdcd(codec, 0x6a) | 0x0001);
+ break;
+ default:
+ break;
+ }
+}
+
/* -------------------------------------------------------------------- */
static int
@@ -1003,13 +1081,6 @@ ac97mix_init(struct snd_mixer *m)
if (pcm_getflags(codec->dev) & SD_F_SOFTPCMVOL)
ac97_wrcd(codec, AC97_MIX_PCM, 0);
-#if 0
- /* XXX For the sake of debugging purposes */
- mix_setparentchild(m, SOUND_MIXER_VOLUME,
- SOUND_MASK_PCM | SOUND_MASK_CD);
- mix_setrealdev(m, SOUND_MIXER_VOLUME, SOUND_MIXER_NONE);
- ac97_wrcd(codec, AC97_MIX_MASTER, 0);
-#endif
mask = 0;
for (i = 0; i < AC97_MIXER_SIZE; i++)
diff --git a/sys/dev/sound/pcm/ac97_patch.c b/sys/dev/sound/pcm/ac97_patch.c
deleted file mode 100644
index 671b6598f51a..000000000000
--- a/sys/dev/sound/pcm/ac97_patch.c
+++ /dev/null
@@ -1,117 +0,0 @@
-/*-
- * SPDX-License-Identifier: BSD-2-Clause
- *
- * Copyright (c) 2002 Orion Hodson
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#ifdef HAVE_KERNEL_OPTION_HEADERS
-#include "opt_snd.h"
-#endif
-
-#include <dev/sound/pcm/sound.h>
-#include <dev/sound/pcm/ac97.h>
-#include <dev/sound/pcm/ac97_patch.h>
-
-void ad1886_patch(struct ac97_info* codec)
-{
-#define AC97_AD_JACK_SPDIF 0x72
- /*
- * Presario700 workaround
- * for Jack Sense/SPDIF Register misetting causing
- * no audible output
- * by Santiago Nullo 04/05/2002
- */
- ac97_wrcd(codec, AC97_AD_JACK_SPDIF, 0x0010);
-}
-
-void ad198x_patch(struct ac97_info* codec)
-{
- switch (ac97_getsubvendor(codec)) {
- case 0x11931043: /* Not for ASUS A9T (probably else too). */
- break;
- default:
- ac97_wrcd(codec, 0x76, ac97_rdcd(codec, 0x76) | 0x0420);
- break;
- }
-}
-
-void ad1981b_patch(struct ac97_info* codec)
-{
- /*
- * Enable headphone jack sensing.
- */
- switch (ac97_getsubvendor(codec)) {
- case 0x02d91014: /* IBM Thinkcentre */
- case 0x099c103c: /* HP nx6110 */
- ac97_wrcd(codec, AC97_AD_JACK_SPDIF,
- ac97_rdcd(codec, AC97_AD_JACK_SPDIF) | 0x0800);
- break;
- default:
- break;
- }
-}
-
-void cmi9739_patch(struct ac97_info* codec)
-{
- /*
- * Few laptops need extra register initialization
- * to power up the internal speakers.
- */
- switch (ac97_getsubvendor(codec)) {
- case 0x18431043: /* ASUS W1000N */
- ac97_wrcd(codec, AC97_REG_POWER, 0x000f);
- ac97_wrcd(codec, AC97_MIXEXT_CLFE, 0x0000);
- ac97_wrcd(codec, 0x64, 0x7110);
- break;
- default:
- break;
- }
-}
-
-void alc655_patch(struct ac97_info* codec)
-{
- /*
- * MSI (Micro-Star International) specific EAPD quirk.
- */
- switch (ac97_getsubvendor(codec)) {
- case 0x00611462: /* MSI S250 */
- case 0x01311462: /* MSI S270 */
- case 0x01611462: /* LG K1 Express */
- case 0x03511462: /* MSI L725 */
- ac97_wrcd(codec, 0x7a, ac97_rdcd(codec, 0x7a) & 0xfffd);
- break;
- case 0x10ca1734:
- /*
- * Amilo Pro V2055 with ALC655 has phone out by default
- * disabled (surround on), leaving us only with internal
- * speakers. This should really go to mixer. We write the
- * Data Flow Control reg.
- */
- ac97_wrcd(codec, 0x6a, ac97_rdcd(codec, 0x6a) | 0x0001);
- break;
- default:
- break;
- }
-}
diff --git a/sys/dev/sound/pcm/ac97_patch.h b/sys/dev/sound/pcm/ac97_patch.h
deleted file mode 100644
index 997b10dbd02c..000000000000
--- a/sys/dev/sound/pcm/ac97_patch.h
+++ /dev/null
@@ -1,35 +0,0 @@
-/*-
- * SPDX-License-Identifier: BSD-2-Clause
- *
- * Copyright (c) 2003 Orion Hodson
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-typedef void (*ac97_patch)(struct ac97_info*);
-
-void ad1886_patch(struct ac97_info*);
-void ad198x_patch(struct ac97_info*);
-void ad1981b_patch(struct ac97_info*);
-void cmi9739_patch(struct ac97_info*);
-void alc655_patch(struct ac97_info*);
diff --git a/sys/dev/sound/pcm/buffer.c b/sys/dev/sound/pcm/buffer.c
index afb4b95e357a..de535ec2dcba 100644
--- a/sys/dev/sound/pcm/buffer.c
+++ b/sys/dev/sound/pcm/buffer.c
@@ -356,15 +356,6 @@ sndbuf_setfmt(struct snd_dbuf *b, u_int32_t fmt)
b->fmt = fmt;
b->bps = AFMT_BPS(b->fmt);
b->align = AFMT_ALIGN(b->fmt);
-#if 0
- b->bps = AFMT_CHANNEL(b->fmt);
- if (b->fmt & AFMT_16BIT)
- b->bps <<= 1;
- else if (b->fmt & AFMT_24BIT)
- b->bps *= 3;
- else if (b->fmt & AFMT_32BIT)
- b->bps <<= 2;
-#endif
return 0;
}
@@ -470,39 +461,30 @@ sndbuf_getsel(struct snd_dbuf *b)
unsigned int
sndbuf_getxrun(struct snd_dbuf *b)
{
- SNDBUF_LOCKASSERT(b);
-
return b->xrun;
}
void
sndbuf_setxrun(struct snd_dbuf *b, unsigned int xrun)
{
- SNDBUF_LOCKASSERT(b);
-
b->xrun = xrun;
}
unsigned int
sndbuf_gethwptr(struct snd_dbuf *b)
{
- SNDBUF_LOCKASSERT(b);
-
return b->hp;
}
void
sndbuf_sethwptr(struct snd_dbuf *b, unsigned int ptr)
{
- SNDBUF_LOCKASSERT(b);
-
b->hp = ptr;
}
unsigned int
sndbuf_getready(struct snd_dbuf *b)
{
- SNDBUF_LOCKASSERT(b);
KASSERT((b->rl >= 0) && (b->rl <= b->bufsize), ("%s: b->rl invalid %d", __func__, b->rl));
return b->rl;
@@ -511,7 +493,6 @@ sndbuf_getready(struct snd_dbuf *b)
unsigned int
sndbuf_getreadyptr(struct snd_dbuf *b)
{
- SNDBUF_LOCKASSERT(b);
KASSERT((b->rp >= 0) && (b->rp <= b->bufsize), ("%s: b->rp invalid %d", __func__, b->rp));
return b->rp;
@@ -520,7 +501,6 @@ sndbuf_getreadyptr(struct snd_dbuf *b)
unsigned int
sndbuf_getfree(struct snd_dbuf *b)
{
- SNDBUF_LOCKASSERT(b);
KASSERT((b->rl >= 0) && (b->rl <= b->bufsize), ("%s: b->rl invalid %d", __func__, b->rl));
return b->bufsize - b->rl;
@@ -529,7 +509,6 @@ sndbuf_getfree(struct snd_dbuf *b)
unsigned int
sndbuf_getfreeptr(struct snd_dbuf *b)
{
- SNDBUF_LOCKASSERT(b);
KASSERT((b->rp >= 0) && (b->rp <= b->bufsize), ("%s: b->rp invalid %d", __func__, b->rp));
KASSERT((b->rl >= 0) && (b->rl <= b->bufsize), ("%s: b->rl invalid %d", __func__, b->rl));
@@ -539,40 +518,30 @@ sndbuf_getfreeptr(struct snd_dbuf *b)
u_int64_t
sndbuf_getblocks(struct snd_dbuf *b)
{
- SNDBUF_LOCKASSERT(b);
-
return b->total / b->blksz;
}
u_int64_t
sndbuf_getprevblocks(struct snd_dbuf *b)
{
- SNDBUF_LOCKASSERT(b);
-
return b->prev_total / b->blksz;
}
u_int64_t
sndbuf_gettotal(struct snd_dbuf *b)
{
- SNDBUF_LOCKASSERT(b);
-
return b->total;
}
u_int64_t
sndbuf_getprevtotal(struct snd_dbuf *b)
{
- SNDBUF_LOCKASSERT(b);
-
return b->prev_total;
}
void
sndbuf_updateprevtotal(struct snd_dbuf *b)
{
- SNDBUF_LOCKASSERT(b);
-
b->prev_total = b->total;
}
diff --git a/sys/dev/sound/pcm/buffer.h b/sys/dev/sound/pcm/buffer.h
index 2c5d6e7c214b..ddf4083ec19f 100644
--- a/sys/dev/sound/pcm/buffer.h
+++ b/sys/dev/sound/pcm/buffer.h
@@ -26,8 +26,6 @@
* SUCH DAMAGE.
*/
-#define SNDBUF_LOCKASSERT(b)
-
#define SNDBUF_F_MANAGED 0x00000008
#define SNDBUF_NAMELEN 48
diff --git a/sys/dev/sound/pcm/channel.c b/sys/dev/sound/pcm/channel.c
index 859476f212ae..4d13f20a5262 100644
--- a/sys/dev/sound/pcm/channel.c
+++ b/sys/dev/sound/pcm/channel.c
@@ -6,6 +6,10 @@
* Copyright (c) 1999 Cameron Grant <cg@FreeBSD.org>
* Portions Copyright (c) Luigi Rizzo <luigi@FreeBSD.org> - 1997-99
* All rights reserved.
+ * Copyright (c) 2024-2025 The FreeBSD Foundation
+ *
+ * Portions of this software were developed by Christos Margiolis
+ * <christos@FreeBSD.org> under sponsorship from the FreeBSD Foundation.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -128,6 +132,7 @@ chn_vpc_proc(int reset, int db)
struct pcm_channel *c;
int i;
+ bus_topo_lock();
for (i = 0; pcm_devclass != NULL &&
i < devclass_get_maxunit(pcm_devclass); i++) {
d = devclass_get_softc(pcm_devclass, i);
@@ -146,6 +151,7 @@ chn_vpc_proc(int reset, int db)
PCM_RELEASE(d);
PCM_UNLOCK(d);
}
+ bus_topo_unlock();
}
static int
@@ -166,7 +172,7 @@ sysctl_hw_snd_vpc_0db(SYSCTL_HANDLER_ARGS)
return (0);
}
SYSCTL_PROC(_hw_snd, OID_AUTO, vpc_0db,
- CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_NEEDGIANT, 0, sizeof(int),
+ CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, 0, sizeof(int),
sysctl_hw_snd_vpc_0db, "I",
"0db relative level");
@@ -186,7 +192,7 @@ sysctl_hw_snd_vpc_reset(SYSCTL_HANDLER_ARGS)
return (0);
}
SYSCTL_PROC(_hw_snd, OID_AUTO, vpc_reset,
- CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, 0, sizeof(int),
+ CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, 0, sizeof(int),
sysctl_hw_snd_vpc_reset, "I",
"reset volume on all channels");
@@ -309,14 +315,7 @@ chn_wakeup(struct pcm_channel *c)
if (CHN_EMPTY(c, children.busy)) {
if (SEL_WAITING(sndbuf_getsel(bs)) && chn_polltrigger(c))
selwakeuppri(sndbuf_getsel(bs), PRIBIO);
- if (c->flags & CHN_F_SLEEPING) {
- /*
- * Ok, I can just panic it right here since it is
- * quite obvious that we never allow multiple waiters
- * from userland. I'm too generous...
- */
- CHN_BROADCAST(&c->intr_cv);
- }
+ CHN_BROADCAST(&c->intr_cv);
} else {
CHN_FOREACH(ch, c, children.busy) {
CHN_LOCK(ch);
@@ -332,15 +331,13 @@ chn_sleep(struct pcm_channel *c, int timeout)
int ret;
CHN_LOCKASSERT(c);
- KASSERT((c->flags & CHN_F_SLEEPING) == 0,
- ("%s(): entered with CHN_F_SLEEPING", __func__));
if (c->flags & CHN_F_DEAD)
return (EINVAL);
- c->flags |= CHN_F_SLEEPING;
+ c->sleeping++;
ret = cv_timedwait_sig(&c->intr_cv, c->lock, timeout);
- c->flags &= ~CHN_F_SLEEPING;
+ c->sleeping--;
return ((c->flags & CHN_F_DEAD) ? EINVAL : ret);
}
@@ -416,23 +413,6 @@ chn_wrfeed(struct pcm_channel *c)
chn_wakeup(c);
}
-#if 0
-static void
-chn_wrupdate(struct pcm_channel *c)
-{
-
- CHN_LOCKASSERT(c);
- KASSERT(c->direction == PCMDIR_PLAY, ("%s(): bad channel", __func__));
-
- if ((c->flags & (CHN_F_MMAP | CHN_F_VIRTUAL)) || CHN_STOPPED(c))
- return;
- chn_dmaupdate(c);
- chn_wrfeed(c);
- /* tell the driver we've updated the primary buffer */
- chn_trigger(c, PCMTRIG_EMLDMAWR);
-}
-#endif
-
static void
chn_wrintr(struct pcm_channel *c)
{
@@ -546,22 +526,6 @@ chn_rdfeed(struct pcm_channel *c)
chn_wakeup(c);
}
-#if 0
-static void
-chn_rdupdate(struct pcm_channel *c)
-{
-
- CHN_LOCKASSERT(c);
- KASSERT(c->direction == PCMDIR_REC, ("chn_rdupdate on bad channel"));
-
- if ((c->flags & (CHN_F_MMAP | CHN_F_VIRTUAL)) || CHN_STOPPED(c))
- return;
- chn_trigger(c, PCMTRIG_EMLDMARD);
- chn_dmaupdate(c);
- chn_rdfeed(c);
-}
-#endif
-
/* read interrupt routine. Must be called with interrupts blocked. */
static void
chn_rdintr(struct pcm_channel *c)
@@ -999,29 +963,38 @@ static const struct {
{ "mulaw", NULL, NULL, AFMT_MU_LAW },
{ "u8", "8", NULL, AFMT_U8 },
{ "s8", NULL, NULL, AFMT_S8 },
+ { "ac3", NULL, NULL, AFMT_AC3 },
#if BYTE_ORDER == LITTLE_ENDIAN
{ "s16le", "s16", "16", AFMT_S16_LE },
{ "s16be", NULL, NULL, AFMT_S16_BE },
-#else
- { "s16le", NULL, NULL, AFMT_S16_LE },
- { "s16be", "s16", "16", AFMT_S16_BE },
-#endif
- { "u16le", NULL, NULL, AFMT_U16_LE },
- { "u16be", NULL, NULL, AFMT_U16_BE },
- { "s24le", NULL, NULL, AFMT_S24_LE },
+ { "s24le", "s24", "24", AFMT_S24_LE },
{ "s24be", NULL, NULL, AFMT_S24_BE },
- { "u24le", NULL, NULL, AFMT_U24_LE },
- { "u24be", NULL, NULL, AFMT_U24_BE },
-#if BYTE_ORDER == LITTLE_ENDIAN
{ "s32le", "s32", "32", AFMT_S32_LE },
{ "s32be", NULL, NULL, AFMT_S32_BE },
+ { "f32le", "f32", NULL, AFMT_F32_LE },
+ { "f32be", NULL, NULL, AFMT_F32_BE },
+ { "u16le", "u16", NULL, AFMT_U16_LE },
+ { "u16be", NULL, NULL, AFMT_U16_BE },
+ { "u24le", "u24", NULL, AFMT_U24_LE },
+ { "u24be", NULL, NULL, AFMT_U24_BE },
+ { "u32le", "u32", NULL, AFMT_U32_LE },
+ { "u32be", NULL, NULL, AFMT_U32_BE },
#else
+ { "s16le", NULL, NULL, AFMT_S16_LE },
+ { "s16be", "s16", "16", AFMT_S16_BE },
+ { "s24le", NULL, NULL, AFMT_S24_LE },
+ { "s24be", "s24", "24", AFMT_S24_BE },
{ "s32le", NULL, NULL, AFMT_S32_LE },
{ "s32be", "s32", "32", AFMT_S32_BE },
-#endif
+ { "f32le", NULL, NULL, AFMT_F32_LE },
+ { "f32be", "f32", NULL, AFMT_F32_BE },
+ { "u16le", NULL, NULL, AFMT_U16_LE },
+ { "u16be", "u16", NULL, AFMT_U16_BE },
+ { "u24le", NULL, NULL, AFMT_U24_LE },
+ { "u24be", "u24", NULL, AFMT_U24_BE },
{ "u32le", NULL, NULL, AFMT_U32_LE },
- { "u32be", NULL, NULL, AFMT_U32_BE },
- { "ac3", NULL, NULL, AFMT_AC3 },
+ { "u32be", "u32", NULL, AFMT_U32_BE },
+#endif
{ NULL, NULL, NULL, 0 }
};
@@ -1157,89 +1130,171 @@ chn_reset(struct pcm_channel *c, uint32_t fmt, uint32_t spd)
return r;
}
-int
-chn_init(struct pcm_channel *c, void *devinfo, int dir, int direction)
+static struct unrhdr *
+chn_getunr(struct snddev_info *d, int type)
+{
+ switch (type) {
+ case PCMDIR_PLAY:
+ return (d->p_unr);
+ case PCMDIR_PLAY_VIRTUAL:
+ return (d->vp_unr);
+ case PCMDIR_REC:
+ return (d->r_unr);
+ case PCMDIR_REC_VIRTUAL:
+ return (d->vr_unr);
+ default:
+ __assert_unreachable();
+ }
+
+}
+
+char *
+chn_mkname(char *buf, size_t len, struct pcm_channel *c)
+{
+ const char *str;
+
+ KASSERT(buf != NULL && len != 0,
+ ("%s(): bogus buf=%p len=%zu", __func__, buf, len));
+
+ switch (c->type) {
+ case PCMDIR_PLAY:
+ str = "play";
+ break;
+ case PCMDIR_PLAY_VIRTUAL:
+ str = "virtual_play";
+ break;
+ case PCMDIR_REC:
+ str = "record";
+ break;
+ case PCMDIR_REC_VIRTUAL:
+ str = "virtual_record";
+ break;
+ default:
+ __assert_unreachable();
+ }
+
+ snprintf(buf, len, "dsp%d.%s.%d",
+ device_get_unit(c->dev), str, c->unit);
+
+ return (buf);
+}
+
+struct pcm_channel *
+chn_init(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t cls,
+ int dir, void *devinfo)
{
+ struct pcm_channel *c;
struct feeder_class *fc;
struct snd_dbuf *b, *bs;
- int i, ret;
+ char buf[CHN_NAMELEN];
+ int err, i, direction, *vchanrate, *vchanformat;
- chn_lockinit(c, dir);
+ PCM_BUSYASSERT(d);
+ PCM_LOCKASSERT(d);
+
+ switch (dir) {
+ case PCMDIR_PLAY:
+ d->playcount++;
+ /* FALLTHROUGH */
+ case PCMDIR_PLAY_VIRTUAL:
+ if (dir == PCMDIR_PLAY_VIRTUAL)
+ d->pvchancount++;
+ direction = PCMDIR_PLAY;
+ vchanrate = &d->pvchanrate;
+ vchanformat = &d->pvchanformat;
+ break;
+ case PCMDIR_REC:
+ d->reccount++;
+ /* FALLTHROUGH */
+ case PCMDIR_REC_VIRTUAL:
+ if (dir == PCMDIR_REC_VIRTUAL)
+ d->rvchancount++;
+ direction = PCMDIR_REC;
+ vchanrate = &d->rvchanrate;
+ vchanformat = &d->rvchanformat;
+ break;
+ default:
+ device_printf(d->dev,
+ "%s(): invalid channel direction: %d\n",
+ __func__, dir);
+ return (NULL);
+ }
+ PCM_UNLOCK(d);
b = NULL;
bs = NULL;
+
+ c = malloc(sizeof(*c), M_DEVBUF, M_WAITOK | M_ZERO);
+ c->methods = kobj_create(cls, M_DEVBUF, M_WAITOK | M_ZERO);
+ chn_lockinit(c, dir);
CHN_INIT(c, children);
CHN_INIT(c, children.busy);
- c->devinfo = NULL;
- c->feeder = NULL;
+ c->direction = direction;
+ c->type = dir;
+ c->unit = alloc_unr(chn_getunr(d, c->type));
+ c->format = SND_FORMAT(AFMT_S16_LE, 2, 0);
+ c->speed = 48000;
+ c->pid = -1;
c->latency = -1;
c->timeout = 1;
-
- ret = ENOMEM;
- b = sndbuf_create(c->dev, c->name, "primary", c);
- if (b == NULL)
- goto out;
- bs = sndbuf_create(c->dev, c->name, "secondary", c);
- if (bs == NULL)
- goto out;
-
- CHN_LOCK(c);
-
- ret = EINVAL;
- fc = feeder_getclass(NULL);
- if (fc == NULL)
- goto out;
- if (chn_addfeeder(c, fc, NULL))
- goto out;
-
- /*
- * XXX - sndbuf_setup() & sndbuf_resize() expect to be called
- * with the channel unlocked because they are also called
- * from driver methods that don't know about locking
- */
- CHN_UNLOCK(c);
- sndbuf_setup(bs, NULL, 0);
- CHN_LOCK(c);
- c->bufhard = b;
- c->bufsoft = bs;
- c->flags = 0;
- c->feederflags = 0;
- c->sm = NULL;
- c->format = SND_FORMAT(AFMT_U8, 1, 0);
- c->speed = DSP_DEFAULT_SPEED;
+ strlcpy(c->comm, CHN_COMM_UNUSED, sizeof(c->comm));
+ c->parentsnddev = d;
+ c->parentchannel = parent;
+ c->dev = d->dev;
+ c->trigger = PCMTRIG_STOP;
+ strlcpy(c->name, chn_mkname(buf, sizeof(buf), c), sizeof(c->name));
c->matrix = *feeder_matrix_id_map(SND_CHN_MATRIX_1_0);
c->matrix.id = SND_CHN_MATRIX_PCMCHANNEL;
- for (i = 0; i < SND_CHN_T_MAX; i++) {
+ for (i = 0; i < SND_CHN_T_MAX; i++)
c->volume[SND_VOL_C_MASTER][i] = SND_VOL_0DB_MASTER;
- }
c->volume[SND_VOL_C_MASTER][SND_CHN_T_VOL_0DB] = SND_VOL_0DB_MASTER;
c->volume[SND_VOL_C_PCM][SND_CHN_T_VOL_0DB] = chn_vol_0db_pcm;
- memset(c->muted, 0, sizeof(c->muted));
-
+ CHN_LOCK(c);
chn_vpc_reset(c, SND_VOL_C_PCM, 1);
+ CHN_UNLOCK(c);
- ret = ENODEV;
- CHN_UNLOCK(c); /* XXX - Unlock for CHANNEL_INIT() malloc() call */
- c->devinfo = CHANNEL_INIT(c->methods, devinfo, b, c, direction);
- CHN_LOCK(c);
- if (c->devinfo == NULL)
- goto out;
+ fc = feeder_getclass(NULL);
+ if (fc == NULL) {
+ device_printf(d->dev, "%s(): failed to get feeder class\n",
+ __func__);
+ goto fail;
+ }
+ if (feeder_add(c, fc, NULL)) {
+ device_printf(d->dev, "%s(): failed to add feeder\n", __func__);
+ goto fail;
+ }
- ret = ENOMEM;
- if ((sndbuf_getsize(b) == 0) && ((c->flags & CHN_F_VIRTUAL) == 0))
- goto out;
+ b = sndbuf_create(c->dev, c->name, "primary", c);
+ bs = sndbuf_create(c->dev, c->name, "secondary", c);
+ if (b == NULL || bs == NULL) {
+ device_printf(d->dev, "%s(): failed to create %s buffer\n",
+ __func__, b == NULL ? "hardware" : "software");
+ goto fail;
+ }
+ c->bufhard = b;
+ c->bufsoft = bs;
- ret = 0;
- c->direction = direction;
+ c->devinfo = CHANNEL_INIT(c->methods, devinfo, b, c, direction);
+ if (c->devinfo == NULL) {
+ device_printf(d->dev, "%s(): CHANNEL_INIT() failed\n", __func__);
+ goto fail;
+ }
+
+ if ((sndbuf_getsize(b) == 0) && ((c->flags & CHN_F_VIRTUAL) == 0)) {
+ device_printf(d->dev, "%s(): hardware buffer's size is 0\n",
+ __func__);
+ goto fail;
+ }
sndbuf_setfmt(b, c->format);
sndbuf_setspd(b, c->speed);
sndbuf_setfmt(bs, c->format);
sndbuf_setspd(bs, c->speed);
+ sndbuf_setup(bs, NULL, 0);
/**
* @todo Should this be moved somewhere else? The primary buffer
@@ -1248,53 +1303,80 @@ chn_init(struct pcm_channel *c, void *devinfo, int dir, int direction)
*/
if (c->direction == PCMDIR_PLAY) {
bs->sl = sndbuf_getmaxsize(bs);
- bs->shadbuf = malloc(bs->sl, M_DEVBUF, M_NOWAIT);
- if (bs->shadbuf == NULL) {
- ret = ENOMEM;
- goto out;
- }
+ bs->shadbuf = malloc(bs->sl, M_DEVBUF, M_WAITOK);
}
-out:
- CHN_UNLOCK(c);
- if (ret) {
- if (c->devinfo) {
- if (CHANNEL_FREE(c->methods, c->devinfo))
- sndbuf_free(b);
- }
- if (bs)
- sndbuf_destroy(bs);
- if (b)
- sndbuf_destroy(b);
+ if ((c->flags & CHN_F_VIRTUAL) == 0) {
CHN_LOCK(c);
- c->flags |= CHN_F_DEAD;
- chn_lockdestroy(c);
+ err = chn_reset(c, c->format, c->speed);
+ CHN_UNLOCK(c);
+ if (err != 0)
+ goto fail;
+ }
- return ret;
+ PCM_LOCK(d);
+ CHN_INSERT_SORT_ASCEND(d, c, channels.pcm);
+ if ((c->flags & CHN_F_VIRTUAL) == 0) {
+ CHN_INSERT_SORT_ASCEND(d, c, channels.pcm.primary);
+ /* Initialize the *vchanrate/vchanformat parameters. */
+ *vchanrate = sndbuf_getspd(c->bufsoft);
+ *vchanformat = sndbuf_getfmt(c->bufsoft);
}
- return 0;
+ return (c);
+
+fail:
+ chn_kill(c);
+ PCM_LOCK(d);
+
+ return (NULL);
}
void
chn_kill(struct pcm_channel *c)
{
+ struct snddev_info *d = c->parentsnddev;
struct snd_dbuf *b = c->bufhard;
struct snd_dbuf *bs = c->bufsoft;
PCM_BUSYASSERT(c->parentsnddev);
+ PCM_LOCK(d);
+ CHN_REMOVE(d, c, channels.pcm);
+ if ((c->flags & CHN_F_VIRTUAL) == 0)
+ CHN_REMOVE(d, c, channels.pcm.primary);
+
+ switch (c->type) {
+ case PCMDIR_PLAY:
+ d->playcount--;
+ break;
+ case PCMDIR_PLAY_VIRTUAL:
+ d->pvchancount--;
+ break;
+ case PCMDIR_REC:
+ d->reccount--;
+ break;
+ case PCMDIR_REC_VIRTUAL:
+ d->rvchancount--;
+ break;
+ default:
+ __assert_unreachable();
+ }
+ PCM_UNLOCK(d);
+
if (CHN_STARTED(c)) {
CHN_LOCK(c);
chn_trigger(c, PCMTRIG_ABORT);
CHN_UNLOCK(c);
}
- while (chn_removefeeder(c) == 0)
- ;
- if (CHANNEL_FREE(c->methods, c->devinfo))
+ free_unr(chn_getunr(d, c->type), c->unit);
+ feeder_remove(c);
+ if (c->devinfo && CHANNEL_FREE(c->methods, c->devinfo))
sndbuf_free(b);
- sndbuf_destroy(bs);
- sndbuf_destroy(b);
+ if (bs)
+ sndbuf_destroy(bs);
+ if (b)
+ sndbuf_destroy(b);
CHN_LOCK(c);
c->flags |= CHN_F_DEAD;
chn_lockdestroy(c);
@@ -1327,19 +1409,6 @@ chn_release(struct pcm_channel *c)
}
int
-chn_ref(struct pcm_channel *c, int ref)
-{
- PCM_BUSYASSERT(c->parentsnddev);
- CHN_LOCKASSERT(c);
- KASSERT((c->refcount + ref) >= 0,
- ("%s(): new refcount will be negative", __func__));
-
- c->refcount += ref;
-
- return (c->refcount);
-}
-
-int
chn_setvolume_multi(struct pcm_channel *c, int vc, int left, int right,
int center)
{
@@ -1895,12 +1964,6 @@ chn_resizebuf(struct pcm_channel *c, int latency,
hblksz -= hblksz % sndbuf_getalign(b);
-#if 0
- hblksz = sndbuf_getmaxsize(b) >> 1;
- hblksz -= hblksz % sndbuf_getalign(b);
- hblkcnt = 2;
-#endif
-
CHN_UNLOCK(c);
if (chn_usefrags == 0 ||
CHANNEL_SETFRAGMENTS(c->methods, c->devinfo,
@@ -1931,14 +1994,6 @@ chn_resizebuf(struct pcm_channel *c, int latency,
if (limit > CHN_2NDBUFMAXSIZE)
limit = CHN_2NDBUFMAXSIZE;
-#if 0
- while (limit > 0 && (sblksz * sblkcnt) > limit) {
- if (sblkcnt < 4)
- break;
- sblkcnt >>= 1;
- }
-#endif
-
while ((sblksz * sblkcnt) < limit)
sblkcnt <<= 1;
@@ -2052,27 +2107,23 @@ chn_setparam(struct pcm_channel *c, uint32_t format, uint32_t speed)
int
chn_setspeed(struct pcm_channel *c, uint32_t speed)
{
- uint32_t oldformat, oldspeed, format;
+ uint32_t oldformat, oldspeed;
int ret;
-#if 0
- /* XXX force 48k */
- if (c->format & AFMT_PASSTHROUGH)
- speed = AFMT_PASSTHROUGH_RATE;
-#endif
-
oldformat = c->format;
oldspeed = c->speed;
- format = oldformat;
- ret = chn_setparam(c, format, speed);
+ if (c->speed == speed)
+ return (0);
+
+ ret = chn_setparam(c, c->format, speed);
if (ret != 0) {
if (snd_verbose > 3)
device_printf(c->dev,
"%s(): Setting speed %d failed, "
"falling back to %d\n",
__func__, speed, oldspeed);
- chn_setparam(c, c->format, oldspeed);
+ chn_setparam(c, oldformat, oldspeed);
}
return (ret);
@@ -2081,7 +2132,7 @@ chn_setspeed(struct pcm_channel *c, uint32_t speed)
int
chn_setformat(struct pcm_channel *c, uint32_t format)
{
- uint32_t oldformat, oldspeed, speed;
+ uint32_t oldformat, oldspeed;
int ret;
/* XXX force stereo */
@@ -2092,9 +2143,11 @@ chn_setformat(struct pcm_channel *c, uint32_t format)
oldformat = c->format;
oldspeed = c->speed;
- speed = oldspeed;
- ret = chn_setparam(c, format, speed);
+ if (c->format == format)
+ return (0);
+
+ ret = chn_setparam(c, format, c->speed);
if (ret != 0) {
if (snd_verbose > 3)
device_printf(c->dev,
@@ -2182,7 +2235,7 @@ chn_syncstate(struct pcm_channel *c)
else
bass = ((bass & 0x7f) + ((bass >> 8) & 0x7f)) >> 1;
- f = chn_findfeeder(c, FEEDER_EQ);
+ f = feeder_find(c, FEEDER_EQ);
if (f != NULL) {
if (FEEDER_SET(f, FEEDEQ_TREBLE, treble) != 0)
device_printf(c->dev,
@@ -2222,44 +2275,46 @@ chn_trigger(struct pcm_channel *c, int go)
if (go == c->trigger)
return (0);
+ if (snd_verbose > 3) {
+ device_printf(c->dev, "%s() %s: calling go=0x%08x , "
+ "prev=0x%08x\n", __func__, c->name, go, c->trigger);
+ }
+
+ c->trigger = go;
ret = CHANNEL_TRIGGER(c->methods, c->devinfo, go);
if (ret != 0)
return (ret);
+ CHN_UNLOCK(c);
+ PCM_LOCK(d);
+ CHN_LOCK(c);
+
+ /*
+ * Do nothing if another thread set a different trigger while we had
+ * dropped the mutex.
+ */
+ if (go != c->trigger) {
+ PCM_UNLOCK(d);
+ return (0);
+ }
+
+ /*
+ * Use the SAFE variants to prevent inserting/removing an already
+ * existing/missing element.
+ */
switch (go) {
case PCMTRIG_START:
- if (snd_verbose > 3)
- device_printf(c->dev,
- "%s() %s: calling go=0x%08x , "
- "prev=0x%08x\n", __func__, c->name, go,
- c->trigger);
- if (c->trigger != PCMTRIG_START) {
- c->trigger = go;
- CHN_UNLOCK(c);
- PCM_LOCK(d);
- CHN_INSERT_HEAD(d, c, channels.pcm.busy);
- PCM_UNLOCK(d);
- CHN_LOCK(c);
- chn_syncstate(c);
- }
+ CHN_INSERT_HEAD_SAFE(d, c, channels.pcm.busy);
+ PCM_UNLOCK(d);
+ chn_syncstate(c);
break;
case PCMTRIG_STOP:
case PCMTRIG_ABORT:
- if (snd_verbose > 3)
- device_printf(c->dev,
- "%s() %s: calling go=0x%08x , "
- "prev=0x%08x\n", __func__, c->name, go,
- c->trigger);
- if (c->trigger == PCMTRIG_START) {
- c->trigger = go;
- CHN_UNLOCK(c);
- PCM_LOCK(d);
- CHN_REMOVE(d, c, channels.pcm.busy);
- PCM_UNLOCK(d);
- CHN_LOCK(c);
- }
+ CHN_REMOVE(d, c, channels.pcm.busy);
+ PCM_UNLOCK(d);
break;
default:
+ PCM_UNLOCK(d);
break;
}
@@ -2324,7 +2379,7 @@ chn_notify(struct pcm_channel *c, u_int32_t flags)
CHN_LOCKASSERT(c);
if (CHN_EMPTY(c, children))
- return (ENODEV);
+ return (0);
err = 0;
diff --git a/sys/dev/sound/pcm/channel.h b/sys/dev/sound/pcm/channel.h
index 698a1186924f..fab182b22774 100644
--- a/sys/dev/sound/pcm/channel.h
+++ b/sys/dev/sound/pcm/channel.h
@@ -5,6 +5,10 @@
* Portions Copyright (c) Ryan Beasley <ryan.beasley@gmail.com> - GSoC 2006
* Copyright (c) 1999 Cameron Grant <cg@FreeBSD.org>
* All rights reserved.
+ * Copyright (c) 2024-2025 The FreeBSD Foundation
+ *
+ * Portions of this software were developed by Christos Margiolis
+ * <christos@FreeBSD.org> under sponsorship from the FreeBSD Foundation.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -84,7 +88,6 @@ struct pcm_channel {
kobj_t methods;
pid_t pid;
- int refcount;
struct pcm_feeder *feeder;
u_int32_t align;
@@ -118,6 +121,8 @@ struct pcm_channel {
* lock.
*/
unsigned int inprog;
+ /* Incrememnt/decrement around cv_timedwait_sig() in chn_sleep(). */
+ unsigned int sleeping;
/**
* Special channel operations should examine @c inprog after acquiring
* lock. If zero, operations may continue. Else, thread should
@@ -159,6 +164,9 @@ struct pcm_channel {
struct {
SLIST_ENTRY(pcm_channel) link;
} opened;
+ struct {
+ SLIST_ENTRY(pcm_channel) link;
+ } primary;
} pcm;
} channels;
@@ -167,8 +175,6 @@ struct pcm_channel {
int16_t volume[SND_VOL_C_MAX][SND_CHN_T_VOL_MAX];
int8_t muted[SND_VOL_C_MAX][SND_CHN_T_VOL_MAX];
-
- void *data1, *data2;
};
#define CHN_HEAD(x, y) &(x)->y.head
@@ -176,6 +182,7 @@ struct pcm_channel {
#define CHN_LINK(y) y.link
#define CHN_EMPTY(x, y) SLIST_EMPTY(CHN_HEAD(x, y))
#define CHN_FIRST(x, y) SLIST_FIRST(CHN_HEAD(x, y))
+#define CHN_NEXT(elm, list) SLIST_NEXT((elm), CHN_LINK(list))
#define CHN_FOREACH(x, y, z) \
SLIST_FOREACH(x, CHN_HEAD(y, z), CHN_LINK(z))
@@ -189,9 +196,6 @@ struct pcm_channel {
#define CHN_INSERT_AFTER(x, y, z) \
SLIST_INSERT_AFTER(x, y, CHN_LINK(z))
-#define CHN_REMOVE(x, y, z) \
- SLIST_REMOVE(CHN_HEAD(x, z), y, pcm_channel, CHN_LINK(z))
-
#define CHN_INSERT_HEAD_SAFE(x, y, z) do { \
struct pcm_channel *t = NULL; \
CHN_FOREACH(t, x, z) { \
@@ -212,20 +216,25 @@ struct pcm_channel {
CHN_INSERT_AFTER(x, y, z); \
} while (0)
-#define CHN_REMOVE_SAFE(x, y, z) do { \
- struct pcm_channel *t = NULL; \
- CHN_FOREACH(t, x, z) { \
- if (t == y) \
- break; \
- } \
- if (t == y) \
- CHN_REMOVE(x, y, z); \
+#define CHN_REMOVE(holder, elm, list) do { \
+ if (CHN_FIRST(holder, list) == (elm)) { \
+ SLIST_REMOVE_HEAD(CHN_HEAD(holder, list), CHN_LINK(list)); \
+ } else { \
+ struct pcm_channel *t = NULL; \
+ CHN_FOREACH(t, holder, list) { \
+ if (CHN_NEXT(t, list) == (elm)) { \
+ SLIST_REMOVE_AFTER(t, CHN_LINK(list)); \
+ break; \
+ } \
+ } \
+ } \
} while (0)
#define CHN_INSERT_SORT(w, x, y, z) do { \
struct pcm_channel *t, *a = NULL; \
CHN_FOREACH(t, x, z) { \
- if ((y)->type w t->type) \
+ if (((y)->type w t->type) || \
+ (((y)->type == t->type) && ((y)->unit w t->unit))) \
a = t; \
else \
break; \
@@ -236,7 +245,7 @@ struct pcm_channel {
CHN_INSERT_HEAD(x, y, z); \
} while (0)
-#define CHN_INSERT_SORT_ASCEND(x, y, z) CHN_INSERT_SORT(>=, x, y, z)
+#define CHN_INSERT_SORT_ASCEND(x, y, z) CHN_INSERT_SORT(>, x, y, z)
#define CHN_INSERT_SORT_DESCEND(x, y, z) CHN_INSERT_SORT(<, x, y, z)
#define CHN_BUF_PARENT(x, y) \
@@ -244,11 +253,6 @@ struct pcm_channel {
(x)->parentchannel->bufhard != NULL) ? \
(x)->parentchannel->bufhard : (y))
-#define CHN_BROADCAST(x) do { \
- if ((x)->cv_waiters != 0) \
- cv_broadcastpri(x, PRIBIO); \
-} while (0)
-
#include "channel_if.h"
int chn_reinit(struct pcm_channel *c);
@@ -259,11 +263,12 @@ int chn_sync(struct pcm_channel *c, int threshold);
int chn_flush(struct pcm_channel *c);
int chn_poll(struct pcm_channel *c, int ev, struct thread *td);
-int chn_init(struct pcm_channel *c, void *devinfo, int dir, int direction);
+char *chn_mkname(char *buf, size_t len, struct pcm_channel *c);
+struct pcm_channel *chn_init(struct snddev_info *d, struct pcm_channel *parent,
+ kobj_class_t cls, int dir, void *devinfo);
void chn_kill(struct pcm_channel *c);
void chn_shutdown(struct pcm_channel *c);
int chn_release(struct pcm_channel *c);
-int chn_ref(struct pcm_channel *c, int ref);
int chn_reset(struct pcm_channel *c, u_int32_t fmt, u_int32_t spd);
int chn_setvolume_multi(struct pcm_channel *c, int vc, int left, int right,
int center);
@@ -321,6 +326,8 @@ int chn_getpeaks(struct pcm_channel *c, int *lpeak, int *rpeak);
#define CHN_LOCKASSERT(c) mtx_assert((c)->lock, MA_OWNED)
#define CHN_UNLOCKASSERT(c) mtx_assert((c)->lock, MA_NOTOWNED)
+#define CHN_BROADCAST(x) cv_broadcastpri(x, PRIBIO)
+
int snd_fmtvalid(uint32_t fmt, uint32_t *fmtlist);
uint32_t snd_str2afmt(const char *);
@@ -333,10 +340,12 @@ extern int chn_latency_profile;
extern int report_soft_formats;
extern int report_soft_matrix;
-#define PCMDIR_PLAY 1
-#define PCMDIR_PLAY_VIRTUAL 2
-#define PCMDIR_REC -1
-#define PCMDIR_REC_VIRTUAL -2
+enum {
+ PCMDIR_PLAY = 1,
+ PCMDIR_PLAY_VIRTUAL,
+ PCMDIR_REC,
+ PCMDIR_REC_VIRTUAL,
+};
#define PCMTRIG_START 1
#define PCMTRIG_EMLDMAWR 2
@@ -353,7 +362,7 @@ extern int report_soft_matrix;
#define CHN_F_RUNNING 0x00000004 /* dma is running */
#define CHN_F_TRIGGERED 0x00000008
#define CHN_F_NOTRIGGER 0x00000010
-#define CHN_F_SLEEPING 0x00000020
+/* unused 0x00000020 */
#define CHN_F_NBIO 0x00000040 /* do non-blocking i/o */
#define CHN_F_MMAP 0x00000080 /* has been mmap()ed */
@@ -361,7 +370,7 @@ extern int report_soft_matrix;
#define CHN_F_BUSY 0x00000100 /* has been opened */
#define CHN_F_DIRTY 0x00000200 /* need re-config */
#define CHN_F_DEAD 0x00000400 /* too many errors, dead, mdk */
-#define CHN_F_SILENCE 0x00000800 /* silence, nil, null, yada */
+/* unused 0x00000800 */
#define CHN_F_HAS_SIZE 0x00001000 /* user set block size */
#define CHN_F_HAS_VCHAN 0x00002000 /* vchan master */
@@ -381,13 +390,13 @@ extern int report_soft_matrix;
"\003RUNNING" \
"\004TRIGGERED" \
"\005NOTRIGGER" \
- "\006SLEEPING" \
+ /* \006 */ \
"\007NBIO" \
"\010MMAP" \
"\011BUSY" \
"\012DIRTY" \
"\013DEAD" \
- "\014SILENCE" \
+ /* \014 */ \
"\015HAS_SIZE" \
"\016HAS_VCHAN" \
"\017VCHAN_PASSTHROUGH" \
diff --git a/sys/dev/sound/pcm/dsp.c b/sys/dev/sound/pcm/dsp.c
index 9c31fff7e8cd..aa6ad4a59778 100644
--- a/sys/dev/sound/pcm/dsp.c
+++ b/sys/dev/sound/pcm/dsp.c
@@ -5,7 +5,7 @@
* Portions Copyright (c) Ryan Beasley <ryan.beasley@gmail.com> - GSoC 2006
* Copyright (c) 1999 Cameron Grant <cg@FreeBSD.org>
* All rights reserved.
- * Copyright (c) 2024 The FreeBSD Foundation
+ * Copyright (c) 2024-2025 The FreeBSD Foundation
*
* Portions of this software were developed by Christos Margiolis
* <christos@FreeBSD.org> under sponsorship from the FreeBSD Foundation.
@@ -37,6 +37,7 @@
#endif
#include <dev/sound/pcm/sound.h>
+#include <dev/sound/pcm/vchan.h>
#include <sys/ctype.h>
#include <sys/lock.h>
#include <sys/rwlock.h>
@@ -51,8 +52,6 @@ struct dsp_cdevpriv {
struct snddev_info *sc;
struct pcm_channel *rdch;
struct pcm_channel *wrch;
- struct pcm_channel *volch;
- int simplex;
};
static int dsp_mmap_allow_prot_exec = 0;
@@ -67,6 +66,12 @@ SYSCTL_INT(_hw_snd, OID_AUTO, basename_clone, CTLFLAG_RWTUN,
#define DSP_REGISTERED(x) (PCM_REGISTERED(x) && (x)->dsp_dev != NULL)
+#define DSP_F_VALID(x) ((x) & (FREAD | FWRITE))
+#define DSP_F_DUPLEX(x) (((x) & (FREAD | FWRITE)) == (FREAD | FWRITE))
+#define DSP_F_SIMPLEX(x) (!DSP_F_DUPLEX(x))
+#define DSP_F_READ(x) ((x) & FREAD)
+#define DSP_F_WRITE(x) ((x) & FWRITE)
+
#define OLDPCM_IOCTL
static d_open_t dsp_open;
@@ -138,147 +143,116 @@ dsp_destroy_dev(device_t dev)
struct snddev_info *d;
d = device_get_softc(dev);
- destroy_dev_sched(d->dsp_dev);
+ destroy_dev(d->dsp_dev);
}
static void
-getchns(struct dsp_cdevpriv *priv, uint32_t prio)
+dsp_lock_chans(struct dsp_cdevpriv *priv, uint32_t prio)
{
- struct snddev_info *d;
- struct pcm_channel *ch;
- uint32_t flags;
-
- if (priv->simplex) {
- d = priv->sc;
- if (!PCM_REGISTERED(d))
- return;
- PCM_LOCK(d);
- PCM_WAIT(d);
- PCM_ACQUIRE(d);
- /*
- * Note: order is important -
- * pcm flags -> prio query flags -> wild guess
- */
- ch = NULL;
- flags = pcm_getflags(d->dev);
- if (flags & SD_F_PRIO_WR) {
- ch = priv->rdch;
- } else if (flags & SD_F_PRIO_RD) {
- ch = priv->wrch;
- } else if (prio & SD_F_PRIO_WR) {
- ch = priv->rdch;
- flags |= SD_F_PRIO_WR;
- } else if (prio & SD_F_PRIO_RD) {
- ch = priv->wrch;
- flags |= SD_F_PRIO_RD;
- } else if (priv->wrch != NULL) {
- ch = priv->rdch;
- flags |= SD_F_PRIO_WR;
- } else if (priv->rdch != NULL) {
- ch = priv->wrch;
- flags |= SD_F_PRIO_RD;
- }
- pcm_setflags(d->dev, flags);
- if (ch != NULL) {
- CHN_LOCK(ch);
- chn_ref(ch, -1);
- chn_release(ch);
- }
- PCM_RELEASE(d);
- PCM_UNLOCK(d);
- }
-
- if (priv->rdch != NULL && (prio & SD_F_PRIO_RD))
+ if (priv->rdch != NULL && DSP_F_READ(prio))
CHN_LOCK(priv->rdch);
- if (priv->wrch != NULL && (prio & SD_F_PRIO_WR))
+ if (priv->wrch != NULL && DSP_F_WRITE(prio))
CHN_LOCK(priv->wrch);
}
static void
-relchns(struct dsp_cdevpriv *priv, uint32_t prio)
+dsp_unlock_chans(struct dsp_cdevpriv *priv, uint32_t prio)
{
- if (priv->rdch != NULL && (prio & SD_F_PRIO_RD))
+ if (priv->rdch != NULL && DSP_F_READ(prio))
CHN_UNLOCK(priv->rdch);
- if (priv->wrch != NULL && (prio & SD_F_PRIO_WR))
+ if (priv->wrch != NULL && DSP_F_WRITE(prio))
CHN_UNLOCK(priv->wrch);
}
-/* duplex / simplex cdev type */
-enum {
- DSP_CDEV_TYPE_RDONLY, /* simplex read-only (record) */
- DSP_CDEV_TYPE_WRONLY, /* simplex write-only (play) */
- DSP_CDEV_TYPE_RDWR /* duplex read, write, or both */
-};
+static int
+dsp_chn_alloc(struct snddev_info *d, struct pcm_channel **ch, int direction,
+ int flags, struct thread *td)
+{
+ struct pcm_channel *c;
+ char *comm;
+ pid_t pid;
+ int err;
+ bool vdir_enabled;
-#define DSP_F_VALID(x) ((x) & (FREAD | FWRITE))
-#define DSP_F_DUPLEX(x) (((x) & (FREAD | FWRITE)) == (FREAD | FWRITE))
-#define DSP_F_SIMPLEX(x) (!DSP_F_DUPLEX(x))
-#define DSP_F_READ(x) ((x) & FREAD)
-#define DSP_F_WRITE(x) ((x) & FWRITE)
+ KASSERT(d != NULL && ch != NULL &&
+ (direction == PCMDIR_PLAY || direction == PCMDIR_REC),
+ ("%s(): invalid d=%p ch=%p direction=%d",
+ __func__, d, ch, direction));
+ PCM_BUSYASSERT(d);
-static const struct {
- int type;
- char *name;
- char *sep;
- char *alias;
- int use_sep;
- int hw;
- int max;
- int volctl;
- uint32_t fmt, spd;
- int query;
-} dsp_cdevs[] = {
- { SND_DEV_DSP, "dsp", ".", NULL, 0, 0, 0, 0,
- SND_FORMAT(AFMT_U8, 1, 0), DSP_DEFAULT_SPEED,
- DSP_CDEV_TYPE_RDWR },
- { SND_DEV_AUDIO, "audio", ".", NULL, 0, 0, 0, 0,
- SND_FORMAT(AFMT_MU_LAW, 1, 0), DSP_DEFAULT_SPEED,
- DSP_CDEV_TYPE_RDWR },
- { SND_DEV_DSP16, "dspW", ".", NULL, 0, 0, 0, 0,
- SND_FORMAT(AFMT_S16_LE, 1, 0), DSP_DEFAULT_SPEED,
- DSP_CDEV_TYPE_RDWR },
- { SND_DEV_DSPHW_PLAY, "dsp", ".p", NULL, 1, 1, SND_MAXHWCHAN, 1,
- SND_FORMAT(AFMT_S16_LE, 2, 0), 48000, DSP_CDEV_TYPE_WRONLY },
- { SND_DEV_DSPHW_VPLAY, "dsp", ".vp", NULL, 1, 1, SND_MAXVCHANS, 1,
- SND_FORMAT(AFMT_S16_LE, 2, 0), 48000, DSP_CDEV_TYPE_WRONLY },
- { SND_DEV_DSPHW_REC, "dsp", ".r", NULL, 1, 1, SND_MAXHWCHAN, 1,
- SND_FORMAT(AFMT_S16_LE, 2, 0), 48000, DSP_CDEV_TYPE_RDONLY },
- { SND_DEV_DSPHW_VREC, "dsp", ".vr", NULL, 1, 1, SND_MAXVCHANS, 1,
- SND_FORMAT(AFMT_S16_LE, 2, 0), 48000, DSP_CDEV_TYPE_RDONLY },
- { SND_DEV_DSPHW_CD, "dspcd", ".", NULL, 0, 0, 0, 0,
- SND_FORMAT(AFMT_S16_LE, 2, 0), 44100, DSP_CDEV_TYPE_RDWR },
- /* Low priority, OSSv4 aliases. */
- { SND_DEV_DSP, "dsp_ac3", ".", "dsp", 0, 0, 0, 0,
- SND_FORMAT(AFMT_U8, 1, 0), DSP_DEFAULT_SPEED,
- DSP_CDEV_TYPE_RDWR },
- { SND_DEV_DSP, "dsp_mmap", ".", "dsp", 0, 0, 0, 0,
- SND_FORMAT(AFMT_U8, 1, 0), DSP_DEFAULT_SPEED,
- DSP_CDEV_TYPE_RDWR },
- { SND_DEV_DSP, "dsp_multich", ".", "dsp", 0, 0, 0, 0,
- SND_FORMAT(AFMT_U8, 1, 0), DSP_DEFAULT_SPEED,
- DSP_CDEV_TYPE_RDWR },
- { SND_DEV_DSP, "dsp_spdifout", ".", "dsp", 0, 0, 0, 0,
- SND_FORMAT(AFMT_U8, 1, 0), DSP_DEFAULT_SPEED,
- DSP_CDEV_TYPE_RDWR },
- { SND_DEV_DSP, "dsp_spdifin", ".", "dsp", 0, 0, 0, 0,
- SND_FORMAT(AFMT_U8, 1, 0), DSP_DEFAULT_SPEED,
- DSP_CDEV_TYPE_RDWR },
-};
+ pid = td->td_proc->p_pid;
+ comm = td->td_proc->p_comm;
+
+ vdir_enabled = (direction == PCMDIR_PLAY && d->flags & SD_F_PVCHANS) ||
+ (direction == PCMDIR_REC && d->flags & SD_F_RVCHANS);
+
+ *ch = NULL;
+ CHN_FOREACH(c, d, channels.pcm.primary) {
+ CHN_LOCK(c);
+ if (c->direction != direction) {
+ CHN_UNLOCK(c);
+ continue;
+ }
+ /* Find an available primary channel to use. */
+ if ((c->flags & CHN_F_BUSY) == 0 ||
+ (vdir_enabled && (c->flags & CHN_F_HAS_VCHAN)))
+ break;
+ CHN_UNLOCK(c);
+ }
+ if (c == NULL)
+ return (EBUSY);
+
+ /*
+ * We can have the following cases:
+ * - vchans are enabled, add a new vchan to the primary channel.
+ * - vchans are disabled, use the primary channel directly.
+ */
+ if (vdir_enabled && ((c->flags & CHN_F_BUSY) == 0 ||
+ c->flags & CHN_F_HAS_VCHAN)) {
+ err = vchan_create(c, ch);
+ CHN_UNLOCK(c);
+ if (err != 0)
+ return (err);
+ CHN_LOCK(*ch);
+ } else if ((c->flags & CHN_F_BUSY) == 0) {
+ *ch = c;
+ } else {
+ CHN_UNLOCK(c);
+ return (ENODEV);
+ }
+
+ (*ch)->flags |= CHN_F_BUSY;
+ if (flags & O_NONBLOCK)
+ (*ch)->flags |= CHN_F_NBIO;
+ if (flags & O_EXCL)
+ (*ch)->flags |= CHN_F_EXCLUSIVE;
+ (*ch)->pid = pid;
+ strlcpy((*ch)->comm, (comm != NULL) ? comm : CHN_COMM_UNKNOWN,
+ sizeof((*ch)->comm));
+
+ if ((err = chn_reset(*ch, (*ch)->format, (*ch)->speed)) != 0)
+ return (err);
+ chn_vpc_reset(*ch, SND_VOL_C_PCM, 0);
+
+ CHN_UNLOCK(*ch);
+
+ return (0);
+}
static void
dsp_close(void *data)
{
struct dsp_cdevpriv *priv = data;
- struct pcm_channel *rdch, *wrch, *volch;
+ struct pcm_channel *rdch, *wrch, *parent;
struct snddev_info *d;
- int sg_ids, rdref, wdref;
+ int sg_ids;
if (priv == NULL)
return;
d = priv->sc;
/* At this point pcm_unregister() will destroy all channels anyway. */
- if (PCM_DETACHING(d))
+ if (!DSP_REGISTERED(d))
goto skip;
PCM_GIANT_ENTER(d);
@@ -289,22 +263,6 @@ dsp_close(void *data)
rdch = priv->rdch;
wrch = priv->wrch;
- volch = priv->volch;
-
- rdref = -1;
- wdref = -1;
-
- if (volch != NULL) {
- if (volch == rdch)
- rdref--;
- else if (volch == wrch)
- wdref--;
- else {
- CHN_LOCK(volch);
- chn_ref(volch, -1);
- CHN_UNLOCK(volch);
- }
- }
if (rdch != NULL)
CHN_REMOVE(d, rdch, channels.pcm.opened);
@@ -331,13 +289,26 @@ dsp_close(void *data)
if (sg_ids != 0)
free_unr(pcmsg_unrhdr, sg_ids);
+ /*
+ * Go through the channel abort/flush path for both
+ * primary and virtual channels to ensure that, in the
+ * case of vchans, the stream is always properly
+ * stopped, and the primary channels do not keep being
+ * interrupted even if all vchans are gone.
+ */
CHN_LOCK(rdch);
- chn_ref(rdch, rdref);
chn_abort(rdch); /* won't sleep */
rdch->flags &= ~(CHN_F_RUNNING | CHN_F_MMAP |
CHN_F_DEAD | CHN_F_EXCLUSIVE);
chn_reset(rdch, 0, 0);
chn_release(rdch);
+ if (rdch->flags & CHN_F_VIRTUAL) {
+ parent = rdch->parentchannel;
+ CHN_LOCK(parent);
+ CHN_LOCK(rdch);
+ vchan_destroy(rdch);
+ CHN_UNLOCK(parent);
+ }
}
if (wrch != NULL) {
/*
@@ -350,12 +321,18 @@ dsp_close(void *data)
free_unr(pcmsg_unrhdr, sg_ids);
CHN_LOCK(wrch);
- chn_ref(wrch, wdref);
chn_flush(wrch); /* may sleep */
wrch->flags &= ~(CHN_F_RUNNING | CHN_F_MMAP |
CHN_F_DEAD | CHN_F_EXCLUSIVE);
chn_reset(wrch, 0, 0);
chn_release(wrch);
+ if (wrch->flags & CHN_F_VIRTUAL) {
+ parent = wrch->parentchannel;
+ CHN_LOCK(parent);
+ CHN_LOCK(wrch);
+ vchan_destroy(wrch);
+ CHN_UNLOCK(parent);
+ }
}
PCM_LOCK(d);
}
@@ -369,43 +346,27 @@ skip:
priv = NULL;
}
-#define DSP_FIXUP_ERROR() do { \
- prio = pcm_getflags(d->dev); \
- if (!DSP_F_VALID(flags)) \
- error = EINVAL; \
- if (!DSP_F_DUPLEX(flags) && \
- ((DSP_F_READ(flags) && d->reccount == 0) || \
- (DSP_F_WRITE(flags) && d->playcount == 0))) \
- error = ENOTSUP; \
- else if (!DSP_F_DUPLEX(flags) && (prio & SD_F_SIMPLEX) && \
- ((DSP_F_READ(flags) && (prio & SD_F_PRIO_WR)) || \
- (DSP_F_WRITE(flags) && (prio & SD_F_PRIO_RD)))) \
- error = EBUSY; \
-} while (0)
-
static int
dsp_open(struct cdev *i_dev, int flags, int mode, struct thread *td)
{
struct dsp_cdevpriv *priv;
- struct pcm_channel *rdch, *wrch;
+ struct pcm_channel *ch;
struct snddev_info *d;
- uint32_t fmt, spd, prio;
- int error, rderror, wrerror;
+ int error, dir;
/* Kind of impossible.. */
if (i_dev == NULL || td == NULL)
return (ENODEV);
d = i_dev->si_drv1;
- if (PCM_DETACHING(d) || !PCM_REGISTERED(d))
+ if (!DSP_REGISTERED(d))
return (EBADF);
+ if (PCM_CHANCOUNT(d) >= PCM_MAXCHANS)
+ return (ENOMEM);
+
priv = malloc(sizeof(*priv), M_DEVBUF, M_WAITOK | M_ZERO);
priv->sc = d;
- priv->rdch = NULL;
- priv->wrch = NULL;
- priv->volch = NULL;
- priv->simplex = (pcm_getflags(d->dev) & SD_F_SIMPLEX) ? 1 : 0;
error = devfs_set_cdevpriv(priv, dsp_close);
if (error != 0)
@@ -418,7 +379,42 @@ dsp_open(struct cdev *i_dev, int flags, int mode, struct thread *td)
PCM_WAIT(d);
error = 0;
- DSP_FIXUP_ERROR();
+ if (!DSP_F_VALID(flags))
+ error = EINVAL;
+ else if (!DSP_F_DUPLEX(flags) &&
+ ((DSP_F_READ(flags) && d->reccount == 0) ||
+ (DSP_F_WRITE(flags) && d->playcount == 0)))
+ error = ENOTSUP;
+ if (pcm_getflags(d->dev) & SD_F_SIMPLEX) {
+ if (DSP_F_DUPLEX(flags)) {
+ /*
+ * If no channels are opened yet, and we request
+ * DUPLEX, limit to playback only, otherwise open one
+ * channel in a direction that already exists.
+ */
+ if (CHN_EMPTY(d, channels.pcm.opened)) {
+ if (d->playcount > 0)
+ flags &= ~FREAD;
+ else if (d->reccount > 0)
+ flags &= ~FWRITE;
+ } else {
+ ch = CHN_FIRST(d, channels.pcm.opened);
+ if (ch->direction == PCMDIR_PLAY)
+ flags &= ~FREAD;
+ else if (ch->direction == PCMDIR_REC)
+ flags &= ~FWRITE;
+ }
+ } else if (!CHN_EMPTY(d, channels.pcm.opened)) {
+ /*
+ * If we requested SIMPLEX, make sure we do not open a
+ * channel in the opposite direction.
+ */
+ ch = CHN_FIRST(d, channels.pcm.opened);
+ dir = DSP_F_READ(flags) ? PCMDIR_REC : PCMDIR_PLAY;
+ if (ch->direction != dir)
+ error = ENOTSUP;
+ }
+ }
if (error != 0) {
PCM_UNLOCK(d);
PCM_GIANT_EXIT(d);
@@ -433,101 +429,30 @@ dsp_open(struct cdev *i_dev, int flags, int mode, struct thread *td)
PCM_ACQUIRE(d);
PCM_UNLOCK(d);
- fmt = SND_FORMAT(AFMT_U8, 1, 0);
- spd = DSP_DEFAULT_SPEED;
-
- rdch = NULL;
- wrch = NULL;
- rderror = 0;
- wrerror = 0;
-
- if (DSP_F_READ(flags)) {
- /* open for read */
- rderror = pcm_chnalloc(d, &rdch, PCMDIR_REC,
- td->td_proc->p_pid, td->td_proc->p_comm);
-
- if (rderror == 0 && chn_reset(rdch, fmt, spd) != 0)
- rderror = ENXIO;
-
- if (rderror != 0) {
- if (rdch != NULL)
- chn_release(rdch);
- if (!DSP_F_DUPLEX(flags)) {
- PCM_RELEASE_QUICK(d);
- PCM_GIANT_EXIT(d);
- return (rderror);
- }
- rdch = NULL;
- } else {
- if (flags & O_NONBLOCK)
- rdch->flags |= CHN_F_NBIO;
- if (flags & O_EXCL)
- rdch->flags |= CHN_F_EXCLUSIVE;
- chn_ref(rdch, 1);
- chn_vpc_reset(rdch, SND_VOL_C_PCM, 0);
- CHN_UNLOCK(rdch);
- }
- }
-
if (DSP_F_WRITE(flags)) {
- /* open for write */
- wrerror = pcm_chnalloc(d, &wrch, PCMDIR_PLAY,
- td->td_proc->p_pid, td->td_proc->p_comm);
-
- if (wrerror == 0 && chn_reset(wrch, fmt, spd) != 0)
- wrerror = ENXIO;
-
- if (wrerror != 0) {
- if (wrch != NULL)
- chn_release(wrch);
- if (!DSP_F_DUPLEX(flags)) {
- if (rdch != NULL) {
- /*
- * Lock, deref and release previously
- * created record channel
- */
- CHN_LOCK(rdch);
- chn_ref(rdch, -1);
- chn_release(rdch);
- }
- PCM_RELEASE_QUICK(d);
- PCM_GIANT_EXIT(d);
- return (wrerror);
- }
- wrch = NULL;
- } else {
- if (flags & O_NONBLOCK)
- wrch->flags |= CHN_F_NBIO;
- if (flags & O_EXCL)
- wrch->flags |= CHN_F_EXCLUSIVE;
- chn_ref(wrch, 1);
- chn_vpc_reset(wrch, SND_VOL_C_PCM, 0);
- CHN_UNLOCK(wrch);
+ error = dsp_chn_alloc(d, &priv->wrch, PCMDIR_PLAY, flags, td);
+ if (error != 0) {
+ PCM_RELEASE_QUICK(d);
+ PCM_GIANT_EXIT(d);
+ return (error);
}
+ PCM_LOCK(d);
+ CHN_INSERT_HEAD(d, priv->wrch, channels.pcm.opened);
+ PCM_UNLOCK(d);
}
-
- PCM_LOCK(d);
-
- if (wrch == NULL && rdch == NULL) {
- PCM_RELEASE(d);
+ if (DSP_F_READ(flags)) {
+ error = dsp_chn_alloc(d, &priv->rdch, PCMDIR_REC, flags, td);
+ if (error != 0) {
+ PCM_RELEASE_QUICK(d);
+ PCM_GIANT_EXIT(d);
+ return (error);
+ }
+ PCM_LOCK(d);
+ CHN_INSERT_HEAD(d, priv->rdch, channels.pcm.opened);
PCM_UNLOCK(d);
- PCM_GIANT_EXIT(d);
- if (wrerror != 0)
- return (wrerror);
- if (rderror != 0)
- return (rderror);
- return (EINVAL);
}
- if (rdch != NULL)
- CHN_INSERT_HEAD(d, rdch, channels.pcm.opened);
- if (wrch != NULL)
- CHN_INSERT_HEAD(d, wrch, channels.pcm.opened);
- priv->rdch = rdch;
- priv->wrch = wrch;
-
- PCM_RELEASE(d);
- PCM_UNLOCK(d);
+ PCM_RELEASE_QUICK(d);
PCM_GIANT_LEAVE(d);
return (0);
@@ -547,19 +472,19 @@ dsp_io_ops(struct dsp_cdevpriv *priv, struct uio *buf)
("%s(): io train wreck!", __func__));
d = priv->sc;
- if (PCM_DETACHING(d) || !DSP_REGISTERED(d))
+ if (!DSP_REGISTERED(d))
return (EBADF);
PCM_GIANT_ENTER(d);
switch (buf->uio_rw) {
case UIO_READ:
- prio = SD_F_PRIO_RD;
+ prio = FREAD;
ch = &priv->rdch;
chn_io = chn_read;
break;
case UIO_WRITE:
- prio = SD_F_PRIO_WR;
+ prio = FWRITE;
ch = &priv->wrch;
chn_io = chn_write;
break;
@@ -570,18 +495,18 @@ dsp_io_ops(struct dsp_cdevpriv *priv, struct uio *buf)
runpid = buf->uio_td->td_proc->p_pid;
- getchns(priv, prio);
+ dsp_lock_chans(priv, prio);
if (*ch == NULL || !((*ch)->flags & CHN_F_BUSY)) {
if (priv->rdch != NULL || priv->wrch != NULL)
- relchns(priv, prio);
+ dsp_unlock_chans(priv, prio);
PCM_GIANT_EXIT(d);
return (EBADF);
}
if (((*ch)->flags & (CHN_F_MMAP | CHN_F_DEAD)) ||
(((*ch)->flags & CHN_F_RUNNING) && (*ch)->pid != runpid)) {
- relchns(priv, prio);
+ dsp_unlock_chans(priv, prio);
PCM_GIANT_EXIT(d);
return (EINVAL);
} else if (!((*ch)->flags & CHN_F_RUNNING)) {
@@ -600,7 +525,7 @@ dsp_io_ops(struct dsp_cdevpriv *priv, struct uio *buf)
CHN_BROADCAST(&(*ch)->cv);
- relchns(priv, prio);
+ dsp_unlock_chans(priv, prio);
PCM_GIANT_LEAVE(d);
@@ -630,7 +555,7 @@ dsp_write(struct cdev *i_dev, struct uio *buf, int flag)
}
static int
-dsp_ioctl_channel(struct dsp_cdevpriv *priv, struct pcm_channel *volch,
+dsp_ioctl_channel(struct dsp_cdevpriv *priv, struct pcm_channel *ch,
u_long cmd, caddr_t arg)
{
struct snddev_info *d;
@@ -648,25 +573,19 @@ dsp_ioctl_channel(struct dsp_cdevpriv *priv, struct pcm_channel *volch,
rdch = priv->rdch;
wrch = priv->wrch;
- /* No specific channel, look into cache */
- if (volch == NULL)
- volch = priv->volch;
-
- /* Look harder */
- if (volch == NULL) {
+ if (ch == NULL) {
if (j == SOUND_MIXER_RECLEV && rdch != NULL)
- volch = rdch;
+ ch = rdch;
else if (j == SOUND_MIXER_PCM && wrch != NULL)
- volch = wrch;
+ ch = wrch;
}
- /* Final validation */
- if (volch == NULL)
+ if (ch == NULL)
return (EINVAL);
- CHN_LOCK(volch);
- if (!(volch->feederflags & (1 << FEEDER_VOLUME))) {
- CHN_UNLOCK(volch);
+ CHN_LOCK(ch);
+ if (!(ch->feederflags & (1 << FEEDER_VOLUME))) {
+ CHN_UNLOCK(ch);
return (EINVAL);
}
@@ -674,28 +593,28 @@ dsp_ioctl_channel(struct dsp_cdevpriv *priv, struct pcm_channel *volch,
case MIXER_WRITE(0):
switch (j) {
case SOUND_MIXER_MUTE:
- if (volch->direction == PCMDIR_REC) {
- chn_setmute_multi(volch, SND_VOL_C_PCM, (*(int *)arg & SOUND_MASK_RECLEV) != 0);
+ if (ch->direction == PCMDIR_REC) {
+ chn_setmute_multi(ch, SND_VOL_C_PCM, (*(int *)arg & SOUND_MASK_RECLEV) != 0);
} else {
- chn_setmute_multi(volch, SND_VOL_C_PCM, (*(int *)arg & SOUND_MASK_PCM) != 0);
+ chn_setmute_multi(ch, SND_VOL_C_PCM, (*(int *)arg & SOUND_MASK_PCM) != 0);
}
break;
case SOUND_MIXER_PCM:
- if (volch->direction != PCMDIR_PLAY)
+ if (ch->direction != PCMDIR_PLAY)
break;
left = *(int *)arg & 0x7f;
right = ((*(int *)arg) >> 8) & 0x7f;
center = (left + right) >> 1;
- chn_setvolume_multi(volch, SND_VOL_C_PCM,
+ chn_setvolume_multi(ch, SND_VOL_C_PCM,
left, right, center);
break;
case SOUND_MIXER_RECLEV:
- if (volch->direction != PCMDIR_REC)
+ if (ch->direction != PCMDIR_REC)
break;
left = *(int *)arg & 0x7f;
right = ((*(int *)arg) >> 8) & 0x7f;
center = (left + right) >> 1;
- chn_setvolume_multi(volch, SND_VOL_C_PCM,
+ chn_setvolume_multi(ch, SND_VOL_C_PCM,
left, right, center);
break;
default:
@@ -707,34 +626,34 @@ dsp_ioctl_channel(struct dsp_cdevpriv *priv, struct pcm_channel *volch,
case MIXER_READ(0):
switch (j) {
case SOUND_MIXER_MUTE:
- mute = CHN_GETMUTE(volch, SND_VOL_C_PCM, SND_CHN_T_FL) ||
- CHN_GETMUTE(volch, SND_VOL_C_PCM, SND_CHN_T_FR);
- if (volch->direction == PCMDIR_REC) {
+ mute = CHN_GETMUTE(ch, SND_VOL_C_PCM, SND_CHN_T_FL) ||
+ CHN_GETMUTE(ch, SND_VOL_C_PCM, SND_CHN_T_FR);
+ if (ch->direction == PCMDIR_REC) {
*(int *)arg = mute << SOUND_MIXER_RECLEV;
} else {
*(int *)arg = mute << SOUND_MIXER_PCM;
}
break;
case SOUND_MIXER_PCM:
- if (volch->direction != PCMDIR_PLAY)
+ if (ch->direction != PCMDIR_PLAY)
break;
- *(int *)arg = CHN_GETVOLUME(volch,
+ *(int *)arg = CHN_GETVOLUME(ch,
SND_VOL_C_PCM, SND_CHN_T_FL);
- *(int *)arg |= CHN_GETVOLUME(volch,
+ *(int *)arg |= CHN_GETVOLUME(ch,
SND_VOL_C_PCM, SND_CHN_T_FR) << 8;
break;
case SOUND_MIXER_RECLEV:
- if (volch->direction != PCMDIR_REC)
+ if (ch->direction != PCMDIR_REC)
break;
- *(int *)arg = CHN_GETVOLUME(volch,
+ *(int *)arg = CHN_GETVOLUME(ch,
SND_VOL_C_PCM, SND_CHN_T_FL);
- *(int *)arg |= CHN_GETVOLUME(volch,
+ *(int *)arg |= CHN_GETVOLUME(ch,
SND_VOL_C_PCM, SND_CHN_T_FR) << 8;
break;
case SOUND_MIXER_DEVMASK:
case SOUND_MIXER_CAPS:
case SOUND_MIXER_STEREODEVS:
- if (volch->direction == PCMDIR_REC)
+ if (ch->direction == PCMDIR_REC)
*(int *)arg = SOUND_MASK_RECLEV;
else
*(int *)arg = SOUND_MASK_PCM;
@@ -748,7 +667,7 @@ dsp_ioctl_channel(struct dsp_cdevpriv *priv, struct pcm_channel *volch,
default:
break;
}
- CHN_UNLOCK(volch);
+ CHN_UNLOCK(ch);
return (0);
}
@@ -766,7 +685,7 @@ dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode,
return (err);
d = priv->sc;
- if (PCM_DETACHING(d) || !DSP_REGISTERED(d))
+ if (!DSP_REGISTERED(d))
return (EBADF);
PCM_GIANT_ENTER(d);
@@ -782,7 +701,7 @@ dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode,
PCM_GIANT_EXIT(d);
return (0);
}
- ret = dsp_ioctl_channel(priv, priv->volch, cmd, arg);
+ ret = dsp_ioctl_channel(priv, NULL, cmd, arg);
if (ret != -1) {
PCM_GIANT_EXIT(d);
return (ret);
@@ -815,9 +734,15 @@ dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode,
ret = sound_oss_card_info((oss_card_info *)arg);
break;
case SNDCTL_AUDIOINFO:
+ ret = dsp_oss_audioinfo(i_dev, (oss_audioinfo *)arg,
+ false);
+ break;
case SNDCTL_AUDIOINFO_EX:
+ ret = dsp_oss_audioinfo(i_dev, (oss_audioinfo *)arg,
+ true);
+ break;
case SNDCTL_ENGINEINFO:
- ret = dsp_oss_audioinfo(i_dev, (oss_audioinfo *)arg);
+ ret = dsp_oss_engineinfo(i_dev, (oss_audioinfo *)arg);
break;
case SNDCTL_MIXERINFO:
ret = mixer_oss_mixerinfo(i_dev, (oss_mixerinfo *)arg);
@@ -830,7 +755,6 @@ dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode,
return (ret);
}
- getchns(priv, 0);
rdch = priv->rdch;
wrch = priv->wrch;
@@ -1354,7 +1278,6 @@ dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode,
struct snd_dbuf *bs = wrch->bufsoft;
CHN_LOCK(wrch);
- /* XXX abusive DMA update: chn_wrupdate(wrch); */
a->bytes = sndbuf_getfree(bs);
a->fragments = a->bytes / sndbuf_getblksz(bs);
a->fragstotal = sndbuf_getblkcnt(bs);
@@ -1372,7 +1295,6 @@ dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode,
struct snd_dbuf *bs = rdch->bufsoft;
CHN_LOCK(rdch);
- /* XXX abusive DMA update: chn_rdupdate(rdch); */
a->bytes = sndbuf_gettotal(bs);
a->blocks = sndbuf_getblocks(bs) - rdch->blocks;
a->ptr = sndbuf_getfreeptr(bs);
@@ -1390,7 +1312,6 @@ dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode,
struct snd_dbuf *bs = wrch->bufsoft;
CHN_LOCK(wrch);
- /* XXX abusive DMA update: chn_wrupdate(wrch); */
a->bytes = sndbuf_gettotal(bs);
a->blocks = sndbuf_getblocks(bs) - wrch->blocks;
a->ptr = sndbuf_getreadyptr(bs);
@@ -1482,7 +1403,6 @@ dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode,
struct snd_dbuf *bs = wrch->bufsoft;
CHN_LOCK(wrch);
- /* XXX abusive DMA update: chn_wrupdate(wrch); */
*arg_i = sndbuf_getready(bs);
CHN_UNLOCK(wrch);
} else
@@ -1679,14 +1599,8 @@ dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode,
CHN_LOCK(chn);
bs = chn->bufsoft;
-#if 0
- tmp = (sndbuf_getsize(b) + chn_getptr(chn) - sndbuf_gethwptr(b)) % sndbuf_getsize(b);
- oc->samples = (sndbuf_gettotal(b) + tmp) / sndbuf_getalign(b);
- oc->fifo_samples = (sndbuf_getready(b) - tmp) / sndbuf_getalign(b);
-#else
oc->samples = sndbuf_gettotal(bs) / sndbuf_getalign(bs);
oc->fifo_samples = sndbuf_getready(bs) / sndbuf_getalign(bs);
-#endif
CHN_UNLOCK(chn);
}
break;
@@ -1835,18 +1749,6 @@ dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode,
case SNDCTL_SETNAME:
ret = dsp_oss_setname(wrch, rdch, (oss_longname_t *)arg);
break;
-#if 0
- /**
- * @note The S/PDIF interface ioctls, @c SNDCTL_DSP_READCTL and
- * @c SNDCTL_DSP_WRITECTL have been omitted at the suggestion of
- * 4Front Technologies.
- */
- case SNDCTL_DSP_READCTL:
- case SNDCTL_DSP_WRITECTL:
- ret = EINVAL;
- break;
-#endif /* !0 (explicitly omitted ioctls) */
-
#endif /* !OSSV4_EXPERIMENT */
case SNDCTL_DSP_MAPINBUF:
case SNDCTL_DSP_MAPOUTBUF:
@@ -1880,7 +1782,7 @@ dsp_poll(struct cdev *i_dev, int events, struct thread *td)
if ((err = devfs_get_cdevpriv((void **)&priv)) != 0)
return (err);
d = priv->sc;
- if (PCM_DETACHING(d) || !DSP_REGISTERED(d)) {
+ if (!DSP_REGISTERED(d)) {
/* XXX many clients don't understand POLLNVAL */
return (events & (POLLHUP | POLLPRI | POLLIN |
POLLRDNORM | POLLOUT | POLLWRNORM));
@@ -1889,7 +1791,7 @@ dsp_poll(struct cdev *i_dev, int events, struct thread *td)
ret = 0;
- getchns(priv, SD_F_PRIO_RD | SD_F_PRIO_WR);
+ dsp_lock_chans(priv, FREAD | FWRITE);
wrch = priv->wrch;
rdch = priv->rdch;
@@ -1905,7 +1807,7 @@ dsp_poll(struct cdev *i_dev, int events, struct thread *td)
ret |= chn_poll(rdch, e, td);
}
- relchns(priv, SD_F_PRIO_RD | SD_F_PRIO_WR);
+ dsp_unlock_chans(priv, FREAD | FWRITE);
PCM_GIANT_LEAVE(d);
@@ -1962,12 +1864,12 @@ dsp_mmap_single(struct cdev *i_dev, vm_ooffset_t *offset,
if ((err = devfs_get_cdevpriv((void **)&priv)) != 0)
return (err);
d = priv->sc;
- if (PCM_DETACHING(d) || !DSP_REGISTERED(d))
+ if (!DSP_REGISTERED(d))
return (EINVAL);
PCM_GIANT_ENTER(d);
- getchns(priv, SD_F_PRIO_RD | SD_F_PRIO_WR);
+ dsp_lock_chans(priv, FREAD | FWRITE);
wrch = priv->wrch;
rdch = priv->rdch;
@@ -1976,7 +1878,7 @@ dsp_mmap_single(struct cdev *i_dev, vm_ooffset_t *offset,
(*offset + size) > sndbuf_getallocsize(c->bufsoft) ||
(wrch != NULL && (wrch->flags & CHN_F_MMAP_INVALID)) ||
(rdch != NULL && (rdch->flags & CHN_F_MMAP_INVALID))) {
- relchns(priv, SD_F_PRIO_RD | SD_F_PRIO_WR);
+ dsp_unlock_chans(priv, FREAD | FWRITE);
PCM_GIANT_EXIT(d);
return (EINVAL);
}
@@ -1987,7 +1889,7 @@ dsp_mmap_single(struct cdev *i_dev, vm_ooffset_t *offset,
rdch->flags |= CHN_F_MMAP;
*offset = (uintptr_t)sndbuf_getbufofs(c->bufsoft, *offset);
- relchns(priv, SD_F_PRIO_RD | SD_F_PRIO_WR);
+ dsp_unlock_chans(priv, FREAD | FWRITE);
*object = vm_pager_allocate(OBJT_DEVICE, i_dev,
size, nprot, *offset, curthread->td_ucred);
@@ -1998,20 +1900,27 @@ dsp_mmap_single(struct cdev *i_dev, vm_ooffset_t *offset,
return (0);
}
+static const char *dsp_aliases[] = {
+ "dsp_ac3",
+ "dsp_mmap",
+ "dsp_multich",
+ "dsp_spdifout",
+ "dsp_spdifin",
+};
+
static void
dsp_clone(void *arg, struct ucred *cred, char *name, int namelen,
struct cdev **dev)
{
struct snddev_info *d;
- int i;
+ size_t i;
if (*dev != NULL)
return;
if (strcmp(name, "dsp") == 0 && dsp_basename_clone)
goto found;
- for (i = 0; i < nitems(dsp_cdevs); i++) {
- if (dsp_cdevs[i].alias != NULL &&
- strcmp(name, dsp_cdevs[i].name) == 0)
+ for (i = 0; i < nitems(dsp_aliases); i++) {
+ if (strcmp(name, dsp_aliases[i]) == 0)
goto found;
}
return;
@@ -2024,7 +1933,7 @@ found:
* have returned already, meaning it will have set snd_unit to -1, and
* thus devclass_get_softc() will return NULL here.
*/
- if (d != NULL && PCM_REGISTERED(d) && d->dsp_dev != NULL) {
+ if (DSP_REGISTERED(d)) {
*dev = d->dsp_dev;
dev_ref(*dev);
}
@@ -2051,28 +1960,189 @@ dsp_sysuninit(void *p)
SYSINIT(dsp_sysinit, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, dsp_sysinit, NULL);
SYSUNINIT(dsp_sysuninit, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, dsp_sysuninit, NULL);
-char *
-dsp_unit2name(char *buf, size_t len, struct pcm_channel *ch)
+static void
+dsp_oss_audioinfo_unavail(oss_audioinfo *ai, int unit)
+{
+ bzero(ai, sizeof(*ai));
+ ai->dev = unit;
+ snprintf(ai->name, sizeof(ai->name), "pcm%d (unavailable)", unit);
+ ai->pid = -1;
+ strlcpy(ai->cmd, CHN_COMM_UNUSED, sizeof(ai->cmd));
+ ai->card_number = unit;
+ ai->port_number = unit;
+ ai->mixer_dev = -1;
+ ai->legacy_device = unit;
+}
+
+/**
+ * @brief Handler for SNDCTL_AUDIOINFO.
+ *
+ * Gathers information about the audio device specified in ai->dev. If
+ * ai->dev == -1, then this function gathers information about the current
+ * device. If the call comes in on a non-audio device and ai->dev == -1,
+ * return EINVAL.
+ *
+ * This routine is supposed to go practically straight to the hardware,
+ * getting capabilities directly from the sound card driver, side-stepping
+ * the intermediate channel interface.
+ *
+ * @note
+ * Calling threads must not hold any snddev_info or pcm_channel locks.
+ *
+ * @param dev device on which the ioctl was issued
+ * @param ai ioctl request data container
+ * @param ex flag to distinguish between SNDCTL_AUDIOINFO from
+ * SNDCTL_AUDIOINFO_EX
+ *
+ * @retval 0 success
+ * @retval EINVAL ai->dev specifies an invalid device
+ */
+int
+dsp_oss_audioinfo(struct cdev *i_dev, oss_audioinfo *ai, bool ex)
{
- int i;
+ struct pcmchan_caps *caps;
+ struct pcm_channel *ch;
+ struct snddev_info *d;
+ uint32_t fmts;
+ int i, minch, maxch, unit;
+
+ /*
+ * If probing the device that received the ioctl, make sure it's a
+ * DSP device. (Users may use this ioctl with /dev/mixer and
+ * /dev/midi.)
+ */
+ if (ai->dev == -1 && i_dev->si_devsw != &dsp_cdevsw)
+ return (EINVAL);
+
+ bus_topo_lock();
+ for (unit = 0; pcm_devclass != NULL &&
+ unit < devclass_get_maxunit(pcm_devclass); unit++) {
+ d = devclass_get_softc(pcm_devclass, unit);
+ if (!PCM_REGISTERED(d)) {
+ if ((ai->dev == -1 && unit == snd_unit) ||
+ ai->dev == unit) {
+ dsp_oss_audioinfo_unavail(ai, unit);
+ bus_topo_unlock();
+ return (0);
+ } else {
+ d = NULL;
+ continue;
+ }
+ }
+
+ PCM_UNLOCKASSERT(d);
+ PCM_LOCK(d);
+ if ((ai->dev == -1 && d->dsp_dev == i_dev) ||
+ (ai->dev == unit)) {
+ PCM_UNLOCK(d);
+ break;
+ } else {
+ PCM_UNLOCK(d);
+ d = NULL;
+ }
+ }
+ bus_topo_unlock();
+
+ /* Exhausted the search -- nothing is locked, so return. */
+ if (d == NULL)
+ return (EINVAL);
+
+ /* XXX Need Giant magic entry ??? */
+
+ PCM_UNLOCKASSERT(d);
+ PCM_LOCK(d);
- KASSERT(buf != NULL && len != 0,
- ("bogus buf=%p len=%ju", buf, (uintmax_t)len));
+ bzero((void *)ai, sizeof(oss_audioinfo));
+ ai->dev = unit;
+ strlcpy(ai->name, device_get_desc(d->dev), sizeof(ai->name));
+ ai->pid = -1;
+ strlcpy(ai->cmd, CHN_COMM_UNKNOWN, sizeof(ai->cmd));
+ ai->card_number = unit;
+ ai->port_number = unit;
+ ai->mixer_dev = (d->mixer_dev != NULL) ? unit : -1;
+ ai->legacy_device = unit;
+ snprintf(ai->devnode, sizeof(ai->devnode), "/dev/dsp%d", unit);
+ ai->enabled = device_is_attached(d->dev) ? 1 : 0;
+ ai->next_play_engine = 0;
+ ai->next_rec_engine = 0;
+ ai->busy = 0;
+ ai->caps = PCM_CAP_REALTIME | PCM_CAP_MMAP | PCM_CAP_TRIGGER;
+ ai->iformats = 0;
+ ai->oformats = 0;
+ ai->min_rate = INT_MAX;
+ ai->max_rate = 0;
+ ai->min_channels = INT_MAX;
+ ai->max_channels = 0;
+
+ /* Gather global information about the device. */
+ CHN_FOREACH(ch, d, channels.pcm) {
+ CHN_UNLOCKASSERT(ch);
+ CHN_LOCK(ch);
- for (i = 0; i < nitems(dsp_cdevs); i++) {
- if (ch->type != dsp_cdevs[i].type || dsp_cdevs[i].alias != NULL)
+ /*
+ * Skip physical channels if we are servicing SNDCTL_AUDIOINFO,
+ * or VCHANs if we are servicing SNDCTL_AUDIOINFO_EX.
+ *
+ * For SNDCTL_AUDIOINFO do not skip the physical channels if
+ * there are no VCHANs.
+ */
+ if ((ex && (ch->flags & CHN_F_VIRTUAL) != 0) ||
+ ((!ex && (ch->flags & CHN_F_VIRTUAL) == 0) &&
+ (d->pvchancount > 0 || d->rvchancount > 0))) {
+ CHN_UNLOCK(ch);
continue;
- snprintf(buf, len, "%s%d%s%d",
- dsp_cdevs[i].name, device_get_unit(ch->dev),
- dsp_cdevs[i].sep, ch->unit);
- return (buf);
+ }
+
+ if ((ch->flags & CHN_F_BUSY) == 0) {
+ ai->busy |= (ch->direction == PCMDIR_PLAY) ?
+ OPEN_WRITE : OPEN_READ;
+ }
+
+ ai->caps |=
+ ((ch->flags & CHN_F_VIRTUAL) ? PCM_CAP_VIRTUAL : 0) |
+ ((ch->direction == PCMDIR_PLAY) ? PCM_CAP_OUTPUT :
+ PCM_CAP_INPUT);
+
+ caps = chn_getcaps(ch);
+
+ minch = INT_MAX;
+ maxch = 0;
+ fmts = 0;
+ for (i = 0; caps->fmtlist[i]; i++) {
+ fmts |= AFMT_ENCODING(caps->fmtlist[i]);
+ minch = min(AFMT_CHANNEL(caps->fmtlist[i]), minch);
+ maxch = max(AFMT_CHANNEL(caps->fmtlist[i]), maxch);
+ }
+
+ if (ch->direction == PCMDIR_PLAY)
+ ai->oformats |= fmts;
+ else
+ ai->iformats |= fmts;
+
+ if (ex || (pcm_getflags(d->dev) & SD_F_BITPERFECT)) {
+ ai->min_rate = min(ai->min_rate, caps->minspeed);
+ ai->max_rate = max(ai->max_rate, caps->maxspeed);
+ } else {
+ ai->min_rate = min(ai->min_rate, feeder_rate_min);
+ ai->max_rate = max(ai->max_rate, feeder_rate_max);
+ }
+ ai->min_channels = min(ai->min_channels, minch);
+ ai->max_channels = max(ai->max_channels, maxch);
+
+ CHN_UNLOCK(ch);
}
+ if (ai->min_rate == INT_MAX)
+ ai->min_rate = 0;
+ if (ai->min_channels == INT_MAX)
+ ai->min_channels = 0;
- return (NULL);
+ PCM_UNLOCK(d);
+
+ return (0);
}
static int
-dsp_oss_audioinfo_cb(void *data, void *arg)
+dsp_oss_engineinfo_cb(void *data, void *arg)
{
struct dsp_cdevpriv *priv = data;
struct pcm_channel *ch = arg;
@@ -2084,10 +2154,10 @@ dsp_oss_audioinfo_cb(void *data, void *arg)
}
/**
- * @brief Handler for SNDCTL_AUDIOINFO.
+ * @brief Handler for SNDCTL_ENGINEINFO
*
- * Gathers information about the audio device specified in ai->dev. If
- * ai->dev == -1, then this function gathers information about the current
+ * Gathers information about the audio device's engine specified in ai->dev.
+ * If ai->dev == -1, then this function gathers information about the current
* device. If the call comes in on a non-audio device and ai->dev == -1,
* return EINVAL.
*
@@ -2103,18 +2173,15 @@ dsp_oss_audioinfo_cb(void *data, void *arg)
*
* @retval 0 success
* @retval EINVAL ai->dev specifies an invalid device
- *
- * @todo Verify correctness of Doxygen tags. ;)
*/
int
-dsp_oss_audioinfo(struct cdev *i_dev, oss_audioinfo *ai)
+dsp_oss_engineinfo(struct cdev *i_dev, oss_audioinfo *ai)
{
struct pcmchan_caps *caps;
struct pcm_channel *ch;
struct snddev_info *d;
uint32_t fmts;
int i, nchan, *rates, minch, maxch, unit;
- char *devname, buf[CHN_NAMELEN];
/*
* If probing the device that received the ioctl, make sure it's a
@@ -2125,14 +2192,13 @@ dsp_oss_audioinfo(struct cdev *i_dev, oss_audioinfo *ai)
return (EINVAL);
ch = NULL;
- devname = NULL;
nchan = 0;
- bzero(buf, sizeof(buf));
/*
* Search for the requested audio device (channel). Start by
* iterating over pcm devices.
*/
+ bus_topo_lock();
for (unit = 0; pcm_devclass != NULL &&
unit < devclass_get_maxunit(pcm_devclass); unit++) {
d = devclass_get_softc(pcm_devclass, unit);
@@ -2148,158 +2214,145 @@ dsp_oss_audioinfo(struct cdev *i_dev, oss_audioinfo *ai)
CHN_FOREACH(ch, d, channels.pcm) {
CHN_UNLOCKASSERT(ch);
CHN_LOCK(ch);
- if (ai->dev == -1) {
- if (devfs_foreach_cdevpriv(i_dev,
- dsp_oss_audioinfo_cb, ch) != 0) {
- devname = dsp_unit2name(buf,
- sizeof(buf), ch);
- }
- } else if (ai->dev == nchan)
- devname = dsp_unit2name(buf, sizeof(buf), ch);
- if (devname != NULL)
+ if ((ai->dev == -1 && devfs_foreach_cdevpriv(
+ i_dev, dsp_oss_engineinfo_cb, ch) != 0) ||
+ ai->dev == nchan)
break;
CHN_UNLOCK(ch);
++nchan;
}
- if (devname != NULL) {
- /*
- * At this point, the following synchronization stuff
- * has happened:
- * - a specific PCM device is locked.
- * - a specific audio channel has been locked, so be
- * sure to unlock when exiting;
- */
+ if (ch == NULL) {
+ PCM_UNLOCK(d);
+ continue;
+ }
- caps = chn_getcaps(ch);
+ /*
+ * At this point, the following synchronization stuff
+ * has happened:
+ * - a specific PCM device is locked.
+ * - a specific audio channel has been locked, so be
+ * sure to unlock when exiting;
+ */
- /*
- * With all handles collected, zero out the user's
- * container and begin filling in its fields.
- */
- bzero((void *)ai, sizeof(oss_audioinfo));
+ caps = chn_getcaps(ch);
- ai->dev = nchan;
- strlcpy(ai->name, ch->name, sizeof(ai->name));
+ /*
+ * With all handles collected, zero out the user's
+ * container and begin filling in its fields.
+ */
+ bzero((void *)ai, sizeof(oss_audioinfo));
- if ((ch->flags & CHN_F_BUSY) == 0)
- ai->busy = 0;
- else
- ai->busy = (ch->direction == PCMDIR_PLAY) ? OPEN_WRITE : OPEN_READ;
-
- /**
- * @note
- * @c cmd - OSSv4 docs: "Only supported under Linux at
- * this moment." Cop-out, I know, but I'll save
- * running around in the process table for later.
- * Is there a risk of leaking information?
- */
- ai->pid = ch->pid;
+ ai->dev = nchan;
+ strlcpy(ai->name, ch->name, sizeof(ai->name));
- /*
- * These flags stolen from SNDCTL_DSP_GETCAPS handler.
- * Note, however, that a single channel operates in
- * only one direction, so PCM_CAP_DUPLEX is out.
- */
- /**
- * @todo @c SNDCTL_AUDIOINFO::caps - Make drivers keep
- * these in pcmchan::caps?
- */
- ai->caps = PCM_CAP_REALTIME | PCM_CAP_MMAP | PCM_CAP_TRIGGER |
- ((ch->flags & CHN_F_VIRTUAL) ? PCM_CAP_VIRTUAL : 0) |
- ((ch->direction == PCMDIR_PLAY) ? PCM_CAP_OUTPUT : PCM_CAP_INPUT);
+ if ((ch->flags & CHN_F_BUSY) == 0)
+ ai->busy = 0;
+ else
+ ai->busy = (ch->direction == PCMDIR_PLAY) ? OPEN_WRITE : OPEN_READ;
- /*
- * Collect formats supported @b natively by the
- * device. Also determine min/max channels. (I.e.,
- * mono, stereo, or both?)
- *
- * If any channel is stereo, maxch = 2;
- * if all channels are stereo, minch = 2, too;
- * if any channel is mono, minch = 1;
- * and if all channels are mono, maxch = 1.
- */
- minch = 0;
- maxch = 0;
- fmts = 0;
- for (i = 0; caps->fmtlist[i]; i++) {
- fmts |= caps->fmtlist[i];
- if (AFMT_CHANNEL(caps->fmtlist[i]) > 1) {
- minch = (minch == 0) ? 2 : minch;
- maxch = 2;
- } else {
- minch = 1;
- maxch = (maxch == 0) ? 1 : maxch;
- }
- }
+ ai->pid = ch->pid;
+ strlcpy(ai->cmd, ch->comm, sizeof(ai->cmd));
- if (ch->direction == PCMDIR_PLAY)
- ai->oformats = fmts;
- else
- ai->iformats = fmts;
-
- /**
- * @note
- * @c magic - OSSv4 docs: "Reserved for internal use
- * by OSS."
- *
- * @par
- * @c card_number - OSSv4 docs: "Number of the sound
- * card where this device belongs or -1 if this
- * information is not available. Applications
- * should normally not use this field for any
- * purpose."
- */
- ai->card_number = -1;
- /**
- * @todo @c song_name - depends first on
- * SNDCTL_[GS]ETSONG @todo @c label - depends
- * on SNDCTL_[GS]ETLABEL
- * @todo @c port_number - routing information?
- */
- ai->port_number = -1;
- ai->mixer_dev = (d->mixer_dev != NULL) ? unit : -1;
- /**
- * @note
- * @c real_device - OSSv4 docs: "Obsolete."
- */
- ai->real_device = -1;
- snprintf(ai->devnode, sizeof(ai->devnode), "/dev/dsp%d", unit);
- ai->enabled = device_is_attached(d->dev) ? 1 : 0;
- /**
- * @note
- * @c flags - OSSv4 docs: "Reserved for future use."
- *
- * @note
- * @c binding - OSSv4 docs: "Reserved for future use."
- *
- * @todo @c handle - haven't decided how to generate
- * this yet; bus, vendor, device IDs?
- */
+ /*
+ * These flags stolen from SNDCTL_DSP_GETCAPS handler.
+ * Note, however, that a single channel operates in
+ * only one direction, so PCM_CAP_DUPLEX is out.
+ */
+ /**
+ * @todo @c SNDCTL_AUDIOINFO::caps - Make drivers keep
+ * these in pcmchan::caps?
+ */
+ ai->caps = PCM_CAP_REALTIME | PCM_CAP_MMAP | PCM_CAP_TRIGGER |
+ ((ch->flags & CHN_F_VIRTUAL) ? PCM_CAP_VIRTUAL : 0) |
+ ((ch->direction == PCMDIR_PLAY) ? PCM_CAP_OUTPUT : PCM_CAP_INPUT);
+
+ /*
+ * Collect formats supported @b natively by the
+ * device. Also determine min/max channels.
+ */
+ minch = INT_MAX;
+ maxch = 0;
+ fmts = 0;
+ for (i = 0; caps->fmtlist[i]; i++) {
+ fmts |= AFMT_ENCODING(caps->fmtlist[i]);
+ minch = min(AFMT_CHANNEL(caps->fmtlist[i]), minch);
+ maxch = max(AFMT_CHANNEL(caps->fmtlist[i]), maxch);
+ }
+
+ if (ch->direction == PCMDIR_PLAY)
+ ai->oformats = fmts;
+ else
+ ai->iformats = fmts;
+
+ /**
+ * @note
+ * @c magic - OSSv4 docs: "Reserved for internal use
+ * by OSS."
+ *
+ * @par
+ * @c card_number - OSSv4 docs: "Number of the sound
+ * card where this device belongs or -1 if this
+ * information is not available. Applications
+ * should normally not use this field for any
+ * purpose."
+ */
+ ai->card_number = unit;
+ /**
+ * @todo @c song_name - depends first on
+ * SNDCTL_[GS]ETSONG @todo @c label - depends
+ * on SNDCTL_[GS]ETLABEL
+ * @todo @c port_number - routing information?
+ */
+ ai->port_number = unit;
+ ai->mixer_dev = (d->mixer_dev != NULL) ? unit : -1;
+ /**
+ * @note
+ * @c legacy_device - OSSv4 docs: "Obsolete."
+ */
+ ai->legacy_device = unit;
+ snprintf(ai->devnode, sizeof(ai->devnode), "/dev/dsp%d", unit);
+ ai->enabled = device_is_attached(d->dev) ? 1 : 0;
+ /**
+ * @note
+ * @c flags - OSSv4 docs: "Reserved for future use."
+ *
+ * @note
+ * @c binding - OSSv4 docs: "Reserved for future use."
+ *
+ * @todo @c handle - haven't decided how to generate
+ * this yet; bus, vendor, device IDs?
+ */
+
+ if ((ch->flags & CHN_F_EXCLUSIVE) ||
+ (pcm_getflags(d->dev) & SD_F_BITPERFECT)) {
ai->min_rate = caps->minspeed;
ai->max_rate = caps->maxspeed;
+ } else {
+ ai->min_rate = feeder_rate_min;
+ ai->max_rate = feeder_rate_max;
+ }
- ai->min_channels = minch;
- ai->max_channels = maxch;
+ ai->min_channels = minch;
+ ai->max_channels = maxch;
- ai->nrates = chn_getrates(ch, &rates);
- if (ai->nrates > OSS_MAX_SAMPLE_RATES)
- ai->nrates = OSS_MAX_SAMPLE_RATES;
+ ai->nrates = chn_getrates(ch, &rates);
+ if (ai->nrates > OSS_MAX_SAMPLE_RATES)
+ ai->nrates = OSS_MAX_SAMPLE_RATES;
- for (i = 0; i < ai->nrates; i++)
- ai->rates[i] = rates[i];
-
- ai->next_play_engine = 0;
- ai->next_rec_engine = 0;
+ for (i = 0; i < ai->nrates; i++)
+ ai->rates[i] = rates[i];
- CHN_UNLOCK(ch);
- }
+ ai->next_play_engine = 0;
+ ai->next_rec_engine = 0;
+ CHN_UNLOCK(ch);
PCM_UNLOCK(d);
+ bus_topo_unlock();
- if (devname != NULL)
- return (0);
+ return (0);
}
+ bus_topo_unlock();
/* Exhausted the search -- nothing is locked, so return. */
return (EINVAL);
@@ -2384,7 +2437,7 @@ dsp_oss_syncgroup(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_syncgr
* syncgroup.
*/
if (group->id == 0) {
- sg = (struct pcmchan_syncgroup *)malloc(sizeof(*sg), M_DEVBUF, M_NOWAIT);
+ sg = malloc(sizeof(*sg), M_DEVBUF, M_NOWAIT);
if (sg != NULL) {
SLIST_INIT(&sg->members);
sg->id = alloc_unr(pcmsg_unrhdr);
@@ -2411,7 +2464,7 @@ dsp_oss_syncgroup(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_syncgr
* insert into syncgroup.
*/
if (group->mode & PCM_ENABLE_INPUT) {
- smrd = (struct pcmchan_syncmember *)malloc(sizeof(*smrd), M_DEVBUF, M_NOWAIT);
+ smrd = malloc(sizeof(*smrd), M_DEVBUF, M_NOWAIT);
if (smrd == NULL) {
ret = ENOMEM;
goto out;
@@ -2427,7 +2480,7 @@ dsp_oss_syncgroup(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_syncgr
}
if (group->mode & PCM_ENABLE_OUTPUT) {
- smwr = (struct pcmchan_syncmember *)malloc(sizeof(*smwr), M_DEVBUF, M_NOWAIT);
+ smwr = malloc(sizeof(*smwr), M_DEVBUF, M_NOWAIT);
if (smwr == NULL) {
ret = ENOMEM;
goto out;
diff --git a/sys/dev/sound/pcm/dsp.h b/sys/dev/sound/pcm/dsp.h
index b81e60dc19b5..8c0786aad474 100644
--- a/sys/dev/sound/pcm/dsp.h
+++ b/sys/dev/sound/pcm/dsp.h
@@ -5,6 +5,10 @@
* Portions Copyright (c) Ryan Beasley <ryan.beasley@gmail.com> - GSoC 2006
* Copyright (c) 1999 Cameron Grant <cg@FreeBSD.org>
* All rights reserved.
+ * Copyright (c) 2024-2025 The FreeBSD Foundation
+ *
+ * Portions of this software were developed by Christos Margiolis
+ * <christos@FreeBSD.org> under sponsorship from the FreeBSD Foundation.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -31,11 +35,9 @@
#ifndef _PCMDSP_H_
#define _PCMDSP_H_
-extern struct cdevsw dsp_cdevsw;
-
int dsp_make_dev(device_t);
void dsp_destroy_dev(device_t);
-char *dsp_unit2name(char *, size_t, struct pcm_channel *);
-int dsp_oss_audioinfo(struct cdev *, oss_audioinfo *);
+int dsp_oss_audioinfo(struct cdev *, oss_audioinfo *, bool);
+int dsp_oss_engineinfo(struct cdev *, oss_audioinfo *);
#endif /* !_PCMDSP_H_ */
diff --git a/sys/dev/sound/pcm/feeder.c b/sys/dev/sound/pcm/feeder.c
index 78443ad76140..af3ada441e48 100644
--- a/sys/dev/sound/pcm/feeder.c
+++ b/sys/dev/sound/pcm/feeder.c
@@ -4,6 +4,10 @@
* Copyright (c) 2005-2009 Ariff Abdullah <ariff@FreeBSD.org>
* Copyright (c) 1999 Cameron Grant <cg@FreeBSD.org>
* All rights reserved.
+ * Copyright (c) 2024-2025 The FreeBSD Foundation
+ *
+ * Portions of this software were developed by Christos Margiolis
+ * <christos@FreeBSD.org> under sponsorship from the FreeBSD Foundation.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -32,13 +36,13 @@
#endif
#include <dev/sound/pcm/sound.h>
+#include <dev/sound/pcm/vchan.h>
#include "feeder_if.h"
static MALLOC_DEFINE(M_FEEDER, "feeder", "pcm feeder");
#define MAXFEEDERS 256
-#undef FEEDER_DEBUG
struct feedertab_entry {
SLIST_ENTRY(feedertab_entry) link;
@@ -48,96 +52,44 @@ struct feedertab_entry {
int idx;
};
static SLIST_HEAD(, feedertab_entry) feedertab;
+static int feedercnt = 0;
/*****************************************************************************/
-void
-feeder_register(void *p)
+static void
+feeder_register_root(void *p)
{
- static int feedercnt = 0;
-
struct feeder_class *fc = p;
struct feedertab_entry *fte;
- int i;
-
- if (feedercnt == 0) {
- KASSERT(fc->desc == NULL, ("first feeder not root: %s", fc->name));
-
- SLIST_INIT(&feedertab);
- fte = malloc(sizeof(*fte), M_FEEDER, M_NOWAIT | M_ZERO);
- if (fte == NULL) {
- printf("can't allocate memory for root feeder: %s\n",
- fc->name);
-
- return;
- }
- fte->feederclass = fc;
- fte->desc = NULL;
- fte->idx = feedercnt;
- SLIST_INSERT_HEAD(&feedertab, fte, link);
- feedercnt++;
-
- /* initialize global variables */
-
- if (snd_verbose < 0 || snd_verbose > 4)
- snd_verbose = 1;
-
- if (snd_unit < 0)
- snd_unit = -1;
-
- if (snd_maxautovchans < 0 ||
- snd_maxautovchans > SND_MAXVCHANS)
- snd_maxautovchans = 0;
-
- if (chn_latency < CHN_LATENCY_MIN ||
- chn_latency > CHN_LATENCY_MAX)
- chn_latency = CHN_LATENCY_DEFAULT;
-
- if (chn_latency_profile < CHN_LATENCY_PROFILE_MIN ||
- chn_latency_profile > CHN_LATENCY_PROFILE_MAX)
- chn_latency_profile = CHN_LATENCY_PROFILE_DEFAULT;
-
- if (feeder_rate_min < FEEDRATE_MIN ||
- feeder_rate_max < FEEDRATE_MIN ||
- feeder_rate_min > FEEDRATE_MAX ||
- feeder_rate_max > FEEDRATE_MAX ||
- !(feeder_rate_min < feeder_rate_max)) {
- feeder_rate_min = FEEDRATE_RATEMIN;
- feeder_rate_max = FEEDRATE_RATEMAX;
- }
- if (feeder_rate_round < FEEDRATE_ROUNDHZ_MIN ||
- feeder_rate_round > FEEDRATE_ROUNDHZ_MAX)
- feeder_rate_round = FEEDRATE_ROUNDHZ;
+ MPASS(feedercnt == 0);
+ KASSERT(fc->desc == NULL, ("first feeder not root: %s", fc->name));
- if (bootverbose)
- printf("%s: snd_unit=%d snd_maxautovchans=%d "
- "latency=%d "
- "feeder_rate_min=%d feeder_rate_max=%d "
- "feeder_rate_round=%d\n",
- __func__, snd_unit, snd_maxautovchans,
- chn_latency,
- feeder_rate_min, feeder_rate_max,
- feeder_rate_round);
-
- /* we've got our root feeder so don't veto pcm loading anymore */
- pcm_veto_load = 0;
+ SLIST_INIT(&feedertab);
+ fte = malloc(sizeof(*fte), M_FEEDER, M_WAITOK | M_ZERO);
+ fte->feederclass = fc;
+ fte->desc = NULL;
+ fte->idx = feedercnt;
+ SLIST_INSERT_HEAD(&feedertab, fte, link);
+ feedercnt++;
+}
- return;
- }
+void
+feeder_register(void *p)
+{
+ struct feeder_class *fc = p;
+ struct feedertab_entry *fte;
+ int i;
KASSERT(fc->desc != NULL, ("feeder '%s' has no descriptor", fc->name));
- /* beyond this point failure is non-fatal but may result in some translations being unavailable */
+ /*
+ * beyond this point failure is non-fatal but may result in some
+ * translations being unavailable
+ */
i = 0;
while ((feedercnt < MAXFEEDERS) && (fc->desc[i].type > 0)) {
- /* printf("adding feeder %s, %x -> %x\n", fc->name, fc->desc[i].in, fc->desc[i].out); */
- fte = malloc(sizeof(*fte), M_FEEDER, M_NOWAIT | M_ZERO);
- if (fte == NULL) {
- printf("can't allocate memory for feeder '%s', %x -> %x\n", fc->name, fc->desc[i].in, fc->desc[i].out);
-
- return;
- }
+ fte = malloc(sizeof(*fte), M_FEEDER, M_WAITOK | M_ZERO);
fte->feederclass = fc;
fte->desc = &fc->desc[i];
fte->idx = feedercnt;
@@ -146,8 +98,10 @@ feeder_register(void *p)
i++;
}
feedercnt++;
- if (feedercnt >= MAXFEEDERS)
- printf("MAXFEEDERS (%d >= %d) exceeded\n", feedercnt, MAXFEEDERS);
+ if (feedercnt >= MAXFEEDERS) {
+ printf("MAXFEEDERS (%d >= %d) exceeded\n",
+ feedercnt, MAXFEEDERS);
+ }
}
static void
@@ -231,7 +185,7 @@ feeder_getclass(struct pcm_feederdesc *desc)
}
int
-chn_addfeeder(struct pcm_channel *c, struct feeder_class *fc, struct pcm_feederdesc *desc)
+feeder_add(struct pcm_channel *c, struct feeder_class *fc, struct pcm_feederdesc *desc)
{
struct pcm_feeder *nf;
@@ -248,22 +202,20 @@ chn_addfeeder(struct pcm_channel *c, struct feeder_class *fc, struct pcm_feederd
return 0;
}
-int
-chn_removefeeder(struct pcm_channel *c)
+void
+feeder_remove(struct pcm_channel *c)
{
struct pcm_feeder *f;
- if (c->feeder == NULL)
- return -1;
- f = c->feeder;
- c->feeder = c->feeder->source;
- feeder_destroy(f);
-
- return 0;
+ while (c->feeder != NULL) {
+ f = c->feeder;
+ c->feeder = c->feeder->source;
+ feeder_destroy(f);
+ }
}
struct pcm_feeder *
-chn_findfeeder(struct pcm_channel *c, u_int32_t type)
+feeder_find(struct pcm_channel *c, u_int32_t type)
{
struct pcm_feeder *f;
@@ -514,5 +466,10 @@ static struct feeder_class feeder_root_class = {
.desc = NULL,
.data = NULL,
};
-SYSINIT(feeder_root, SI_SUB_DRIVERS, SI_ORDER_FIRST, feeder_register, &feeder_root_class);
+/*
+ * Register the root feeder first so that pcm_addchan() and subsequent
+ * functions can use it.
+ */
+SYSINIT(feeder_root, SI_SUB_DRIVERS, SI_ORDER_FIRST, feeder_register_root,
+ &feeder_root_class);
SYSUNINIT(feeder_root, SI_SUB_DRIVERS, SI_ORDER_FIRST, feeder_unregisterall, NULL);
diff --git a/sys/dev/sound/pcm/feeder.h b/sys/dev/sound/pcm/feeder.h
index c3d6f89d102e..60b8280e59ef 100644
--- a/sys/dev/sound/pcm/feeder.h
+++ b/sys/dev/sound/pcm/feeder.h
@@ -58,10 +58,10 @@ u_int32_t snd_fmtbestbit(u_int32_t fmt, u_int32_t *fmts);
u_int32_t snd_fmtbestchannel(u_int32_t fmt, u_int32_t *fmts);
u_int32_t snd_fmtbest(u_int32_t fmt, u_int32_t *fmts);
-int chn_addfeeder(struct pcm_channel *c, struct feeder_class *fc,
+int feeder_add(struct pcm_channel *c, struct feeder_class *fc,
struct pcm_feederdesc *desc);
-int chn_removefeeder(struct pcm_channel *c);
-struct pcm_feeder *chn_findfeeder(struct pcm_channel *c, u_int32_t type);
+void feeder_remove(struct pcm_channel *c);
+struct pcm_feeder *feeder_find(struct pcm_channel *c, u_int32_t type);
void feeder_printchain(struct pcm_feeder *head);
int feeder_chain(struct pcm_channel *);
@@ -167,32 +167,6 @@ int feeder_matrix_oss_get_channel_order(struct pcmchan_matrix *,
int feeder_matrix_oss_set_channel_order(struct pcmchan_matrix *,
unsigned long long *);
-#if 0
-/* feeder_matrix */
-enum {
- FEEDMATRIX_TYPE,
- FEEDMATRIX_RESET,
- FEEDMATRIX_CHANNELS_IN,
- FEEDMATRIX_CHANNELS_OUT,
- FEEDMATRIX_SET_MAP
-};
-
-enum {
- FEEDMATRIX_TYPE_NONE,
- FEEDMATRIX_TYPE_AUTO,
- FEEDMATRIX_TYPE_2X1,
- FEEDMATRIX_TYPE_1X2,
- FEEDMATRIX_TYPE_2X2
-};
-
-#define FEEDMATRIX_TYPE_STEREO_TO_MONO FEEDMATRIX_TYPE_2X1
-#define FEEDMATRIX_TYPE_MONO_TO_STEREO FEEDMATRIX_TYPE_1X2
-#define FEEDMATRIX_TYPE_SWAP_STEREO FEEDMATRIX_TYPE_2X2
-#define FEEDMATRIX_MAP(x, y) ((((x) & 0x3f) << 6) | ((y) & 0x3f))
-#define FEEDMATRIX_MAP_SRC(x) ((x) & 0x3f)
-#define FEEDMATRIX_MAP_DST(x) (((x) >> 6) & 0x3f)
-#endif
-
/*
* By default, various feeders only deal with sign 16/32 bit native-endian
* since it should provide the fastest processing path. Processing 8bit samples
diff --git a/sys/dev/sound/pcm/feeder_chain.c b/sys/dev/sound/pcm/feeder_chain.c
index 52351ef58510..56de32441de7 100644
--- a/sys/dev/sound/pcm/feeder_chain.c
+++ b/sys/dev/sound/pcm/feeder_chain.c
@@ -102,6 +102,7 @@ static uint32_t feeder_chain_formats_multi[] = {
AFMT_S16_LE, AFMT_S16_BE, AFMT_U16_LE, AFMT_U16_BE,
AFMT_S24_LE, AFMT_S24_BE, AFMT_U24_LE, AFMT_U24_BE,
AFMT_S32_LE, AFMT_S32_BE, AFMT_U32_LE, AFMT_U32_BE,
+ AFMT_F32_LE, AFMT_F32_BE,
0
};
@@ -111,6 +112,7 @@ static uint32_t feeder_chain_formats_fullmulti[] = {
AFMT_S16_LE, AFMT_S16_BE, AFMT_U16_LE, AFMT_U16_BE,
AFMT_S24_LE, AFMT_S24_BE, AFMT_U24_LE, AFMT_U24_BE,
AFMT_S32_LE, AFMT_S32_BE, AFMT_U32_LE, AFMT_U32_BE,
+ AFMT_F32_LE, AFMT_F32_BE,
0
};
@@ -157,7 +159,7 @@ feeder_build_format(struct pcm_channel *c, struct feeder_chain_desc *cdesc)
desc->in = cdesc->current.afmt;
desc->out = cdesc->target.afmt;
- ret = chn_addfeeder(c, fc, desc);
+ ret = feeder_add(c, fc, desc);
if (ret != 0) {
device_printf(c->dev,
"%s(): can't add feeder_format\n", __func__);
@@ -230,7 +232,7 @@ feeder_build_rate(struct pcm_channel *c, struct feeder_chain_desc *cdesc)
desc->in = cdesc->current.afmt;
desc->out = desc->in;
- ret = chn_addfeeder(c, fc, desc);
+ ret = feeder_add(c, fc, desc);
if (ret != 0) {
device_printf(c->dev,
"%s(): can't add feeder_rate\n", __func__);
@@ -309,7 +311,7 @@ feeder_build_matrix(struct pcm_channel *c, struct feeder_chain_desc *cdesc)
desc->out = SND_FORMAT(cdesc->current.afmt,
cdesc->target.matrix->channels, cdesc->target.matrix->ext);
- ret = chn_addfeeder(c, fc, desc);
+ ret = feeder_add(c, fc, desc);
if (ret != 0) {
device_printf(c->dev,
"%s(): can't add feeder_matrix\n", __func__);
@@ -365,7 +367,7 @@ feeder_build_volume(struct pcm_channel *c, struct feeder_chain_desc *cdesc)
desc->in = cdesc->current.afmt;
desc->out = desc->in;
- ret = chn_addfeeder(c, fc, desc);
+ ret = feeder_add(c, fc, desc);
if (ret != 0) {
device_printf(c->dev,
"%s(): can't add feeder_volume\n", __func__);
@@ -433,7 +435,7 @@ feeder_build_eq(struct pcm_channel *c, struct feeder_chain_desc *cdesc)
desc->in = cdesc->current.afmt;
desc->out = desc->in;
- ret = chn_addfeeder(c, fc, desc);
+ ret = feeder_add(c, fc, desc);
if (ret != 0) {
device_printf(c->dev,
"%s(): can't add feeder_eq\n", __func__);
@@ -472,7 +474,7 @@ feeder_build_root(struct pcm_channel *c, struct feeder_chain_desc *cdesc)
return (ENOTSUP);
}
- ret = chn_addfeeder(c, fc, NULL);
+ ret = feeder_add(c, fc, NULL);
if (ret != 0) {
device_printf(c->dev,
"%s(): can't add feeder_root\n", __func__);
@@ -513,7 +515,7 @@ feeder_build_mixer(struct pcm_channel *c, struct feeder_chain_desc *cdesc)
desc->in = cdesc->current.afmt;
desc->out = desc->in;
- ret = chn_addfeeder(c, fc, desc);
+ ret = feeder_add(c, fc, desc);
if (ret != 0) {
device_printf(c->dev,
"%s(): can't add feeder_mixer\n", __func__);
@@ -588,8 +590,7 @@ feeder_chain(struct pcm_channel *c)
CHN_LOCKASSERT(c);
/* Remove everything first. */
- while (chn_removefeeder(c) == 0)
- ;
+ feeder_remove(c);
KASSERT(c->feeder == NULL, ("feeder chain not empty"));
@@ -719,6 +720,17 @@ feeder_chain(struct pcm_channel *c)
c->format = cdesc.target.afmt;
c->speed = cdesc.target.rate;
} else {
+ /*
+ * Bail out early if we do not support either of those formats.
+ */
+ if ((cdesc.origin.afmt & AFMT_CONVERTIBLE) == 0 ||
+ (cdesc.target.afmt & AFMT_CONVERTIBLE) == 0) {
+ device_printf(c->dev,
+ "%s(): unsupported formats: in=0x%08x, out=0x%08x\n",
+ __func__, cdesc.origin.afmt, cdesc.target.afmt);
+ return (ENODEV);
+ }
+
/* hwfmt is not convertible, so 'dummy' it. */
if (hwfmt & AFMT_PASSTHROUGH)
cdesc.dummy = 1;
diff --git a/sys/dev/sound/pcm/feeder_eq.c b/sys/dev/sound/pcm/feeder_eq.c
index a097b13cd986..23e27b922486 100644
--- a/sys/dev/sound/pcm/feeder_eq.c
+++ b/sys/dev/sound/pcm/feeder_eq.c
@@ -3,6 +3,10 @@
*
* Copyright (c) 2008-2009 Ariff Abdullah <ariff@FreeBSD.org>
* All rights reserved.
+ * Copyright (c) 2024-2025 The FreeBSD Foundation
+ *
+ * Portions of this software were developed by Christos Margiolis
+ * <christos@FreeBSD.org> under sponsorship from the FreeBSD Foundation.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -101,10 +105,6 @@ SYSCTL_INT(_hw_snd, OID_AUTO, feeder_eq_exact_rate, CTLFLAG_RWTUN,
&feeder_eq_exact_rate, 0, "force exact rate validation");
#endif
-struct feed_eq_info;
-
-typedef void (*feed_eq_t)(struct feed_eq_info *, uint8_t *, uint32_t);
-
struct feed_eq_tone {
intpcm_t o1[SND_CHN_MAX];
intpcm_t o2[SND_CHN_MAX];
@@ -117,7 +117,7 @@ struct feed_eq_info {
struct feed_eq_tone treble;
struct feed_eq_tone bass;
struct feed_eq_coeff *coeff;
- feed_eq_t biquad;
+ uint32_t fmt;
uint32_t channels;
uint32_t rate;
uint32_t align;
@@ -135,137 +135,74 @@ struct feed_eq_info {
#define FEEDEQ_ERR_CLIP_CHECK(...)
#endif
-#define FEEDEQ_CLAMP(v) (((v) > PCM_S32_MAX) ? PCM_S32_MAX : \
- (((v) < PCM_S32_MIN) ? PCM_S32_MIN : \
- (v)))
-
-#define FEEDEQ_DECLARE(SIGN, BIT, ENDIAN) \
-static void \
-feed_eq_biquad_##SIGN##BIT##ENDIAN(struct feed_eq_info *info, \
- uint8_t *dst, uint32_t count) \
-{ \
- struct feed_eq_coeff_tone *treble, *bass; \
- intpcm64_t w; \
- intpcm_t v; \
- uint32_t i, j; \
- int32_t pmul, pshift; \
- \
- pmul = feed_eq_preamp[info->preamp].mul; \
- pshift = feed_eq_preamp[info->preamp].shift; \
- \
- if (info->state == FEEDEQ_DISABLE) { \
- j = count * info->channels; \
- dst += j * PCM_##BIT##_BPS; \
- do { \
- dst -= PCM_##BIT##_BPS; \
- v = _PCM_READ_##SIGN##BIT##_##ENDIAN(dst); \
- v = ((intpcm64_t)pmul * v) >> pshift; \
- _PCM_WRITE_##SIGN##BIT##_##ENDIAN(dst, v); \
- } while (--j != 0); \
- \
- return; \
- } \
- \
- treble = &(info->coeff[info->treble.gain].treble); \
- bass = &(info->coeff[info->bass.gain].bass); \
- \
- do { \
- i = 0; \
- j = info->channels; \
- do { \
- v = _PCM_READ_##SIGN##BIT##_##ENDIAN(dst); \
- v <<= 32 - BIT; \
- v = ((intpcm64_t)pmul * v) >> pshift; \
- \
- w = (intpcm64_t)v * treble->b0; \
- w += (intpcm64_t)info->treble.i1[i] * treble->b1; \
- w += (intpcm64_t)info->treble.i2[i] * treble->b2; \
- w -= (intpcm64_t)info->treble.o1[i] * treble->a1; \
- w -= (intpcm64_t)info->treble.o2[i] * treble->a2; \
- info->treble.i2[i] = info->treble.i1[i]; \
- info->treble.i1[i] = v; \
- info->treble.o2[i] = info->treble.o1[i]; \
- w >>= FEEDEQ_COEFF_SHIFT; \
- FEEDEQ_ERR_CLIP_CHECK(treble, w); \
- v = FEEDEQ_CLAMP(w); \
- info->treble.o1[i] = v; \
- \
- w = (intpcm64_t)v * bass->b0; \
- w += (intpcm64_t)info->bass.i1[i] * bass->b1; \
- w += (intpcm64_t)info->bass.i2[i] * bass->b2; \
- w -= (intpcm64_t)info->bass.o1[i] * bass->a1; \
- w -= (intpcm64_t)info->bass.o2[i] * bass->a2; \
- info->bass.i2[i] = info->bass.i1[i]; \
- info->bass.i1[i] = v; \
- info->bass.o2[i] = info->bass.o1[i]; \
- w >>= FEEDEQ_COEFF_SHIFT; \
- FEEDEQ_ERR_CLIP_CHECK(bass, w); \
- v = FEEDEQ_CLAMP(w); \
- info->bass.o1[i] = v; \
- \
- v >>= 32 - BIT; \
- _PCM_WRITE_##SIGN##BIT##_##ENDIAN(dst, v); \
- dst += PCM_##BIT##_BPS; \
- i++; \
- } while (--j != 0); \
- } while (--count != 0); \
-}
-
-#if BYTE_ORDER == LITTLE_ENDIAN || defined(SND_FEEDER_MULTIFORMAT)
-FEEDEQ_DECLARE(S, 16, LE)
-FEEDEQ_DECLARE(S, 32, LE)
-#endif
-#if BYTE_ORDER == BIG_ENDIAN || defined(SND_FEEDER_MULTIFORMAT)
-FEEDEQ_DECLARE(S, 16, BE)
-FEEDEQ_DECLARE(S, 32, BE)
-#endif
-#ifdef SND_FEEDER_MULTIFORMAT
-FEEDEQ_DECLARE(S, 8, NE)
-FEEDEQ_DECLARE(S, 24, LE)
-FEEDEQ_DECLARE(S, 24, BE)
-FEEDEQ_DECLARE(U, 8, NE)
-FEEDEQ_DECLARE(U, 16, LE)
-FEEDEQ_DECLARE(U, 24, LE)
-FEEDEQ_DECLARE(U, 32, LE)
-FEEDEQ_DECLARE(U, 16, BE)
-FEEDEQ_DECLARE(U, 24, BE)
-FEEDEQ_DECLARE(U, 32, BE)
-#endif
-
-#define FEEDEQ_ENTRY(SIGN, BIT, ENDIAN) \
- { \
- AFMT_##SIGN##BIT##_##ENDIAN, \
- feed_eq_biquad_##SIGN##BIT##ENDIAN \
+__always_inline static void
+feed_eq_biquad(struct feed_eq_info *info, uint8_t *dst, uint32_t count,
+ const uint32_t fmt)
+{
+ struct feed_eq_coeff_tone *treble, *bass;
+ intpcm64_t w;
+ intpcm_t v;
+ uint32_t i, j;
+ int32_t pmul, pshift;
+
+ pmul = feed_eq_preamp[info->preamp].mul;
+ pshift = feed_eq_preamp[info->preamp].shift;
+
+ if (info->state == FEEDEQ_DISABLE) {
+ j = count * info->channels;
+ dst += j * AFMT_BPS(fmt);
+ do {
+ dst -= AFMT_BPS(fmt);
+ v = pcm_sample_read(dst, fmt);
+ v = ((intpcm64_t)pmul * v) >> pshift;
+ pcm_sample_write(dst, v, fmt);
+ } while (--j != 0);
+
+ return;
}
-static const struct {
- uint32_t format;
- feed_eq_t biquad;
-} feed_eq_biquad_tab[] = {
-#if BYTE_ORDER == LITTLE_ENDIAN || defined(SND_FEEDER_MULTIFORMAT)
- FEEDEQ_ENTRY(S, 16, LE),
- FEEDEQ_ENTRY(S, 32, LE),
-#endif
-#if BYTE_ORDER == BIG_ENDIAN || defined(SND_FEEDER_MULTIFORMAT)
- FEEDEQ_ENTRY(S, 16, BE),
- FEEDEQ_ENTRY(S, 32, BE),
-#endif
-#ifdef SND_FEEDER_MULTIFORMAT
- FEEDEQ_ENTRY(S, 8, NE),
- FEEDEQ_ENTRY(S, 24, LE),
- FEEDEQ_ENTRY(S, 24, BE),
- FEEDEQ_ENTRY(U, 8, NE),
- FEEDEQ_ENTRY(U, 16, LE),
- FEEDEQ_ENTRY(U, 24, LE),
- FEEDEQ_ENTRY(U, 32, LE),
- FEEDEQ_ENTRY(U, 16, BE),
- FEEDEQ_ENTRY(U, 24, BE),
- FEEDEQ_ENTRY(U, 32, BE)
-#endif
-};
+ treble = &(info->coeff[info->treble.gain].treble);
+ bass = &(info->coeff[info->bass.gain].bass);
-#define FEEDEQ_BIQUAD_TAB_SIZE \
- ((int32_t)(sizeof(feed_eq_biquad_tab) / sizeof(feed_eq_biquad_tab[0])))
+ do {
+ i = 0;
+ j = info->channels;
+ do {
+ v = pcm_sample_read_norm(dst, fmt);
+ v = ((intpcm64_t)pmul * v) >> pshift;
+
+ w = (intpcm64_t)v * treble->b0;
+ w += (intpcm64_t)info->treble.i1[i] * treble->b1;
+ w += (intpcm64_t)info->treble.i2[i] * treble->b2;
+ w -= (intpcm64_t)info->treble.o1[i] * treble->a1;
+ w -= (intpcm64_t)info->treble.o2[i] * treble->a2;
+ info->treble.i2[i] = info->treble.i1[i];
+ info->treble.i1[i] = v;
+ info->treble.o2[i] = info->treble.o1[i];
+ w >>= FEEDEQ_COEFF_SHIFT;
+ FEEDEQ_ERR_CLIP_CHECK(treble, w);
+ v = pcm_clamp(w, AFMT_S32_NE);
+ info->treble.o1[i] = v;
+
+ w = (intpcm64_t)v * bass->b0;
+ w += (intpcm64_t)info->bass.i1[i] * bass->b1;
+ w += (intpcm64_t)info->bass.i2[i] * bass->b2;
+ w -= (intpcm64_t)info->bass.o1[i] * bass->a1;
+ w -= (intpcm64_t)info->bass.o2[i] * bass->a2;
+ info->bass.i2[i] = info->bass.i1[i];
+ info->bass.i1[i] = v;
+ info->bass.o2[i] = info->bass.o1[i];
+ w >>= FEEDEQ_COEFF_SHIFT;
+ FEEDEQ_ERR_CLIP_CHECK(bass, w);
+ v = pcm_clamp(w, AFMT_S32_NE);
+ info->bass.o1[i] = v;
+
+ pcm_sample_write_norm(dst, v, fmt);
+ dst += AFMT_BPS(fmt);
+ i++;
+ } while (--j != 0);
+ } while (--count != 0);
+}
static struct feed_eq_coeff *
feed_eq_coeff_rate(uint32_t rate)
@@ -337,26 +274,15 @@ static int
feed_eq_init(struct pcm_feeder *f)
{
struct feed_eq_info *info;
- feed_eq_t biquad_op;
- int i;
if (f->desc->in != f->desc->out)
return (EINVAL);
- biquad_op = NULL;
-
- for (i = 0; i < FEEDEQ_BIQUAD_TAB_SIZE && biquad_op == NULL; i++) {
- if (AFMT_ENCODING(f->desc->in) == feed_eq_biquad_tab[i].format)
- biquad_op = feed_eq_biquad_tab[i].biquad;
- }
-
- if (biquad_op == NULL)
- return (EINVAL);
-
info = malloc(sizeof(*info), M_DEVBUF, M_NOWAIT | M_ZERO);
if (info == NULL)
return (ENOMEM);
+ info->fmt = AFMT_ENCODING(f->desc->in);
info->channels = AFMT_CHANNEL(f->desc->in);
info->align = info->channels * AFMT_BPS(f->desc->in);
@@ -366,8 +292,6 @@ feed_eq_init(struct pcm_feeder *f)
info->preamp = FEEDEQ_PREAMP2IDX(FEEDEQ_PREAMP_DEFAULT);
info->state = FEEDEQ_UNKNOWN;
- info->biquad = biquad_op;
-
f->data = info;
return (feed_eq_setup(info));
@@ -470,7 +394,21 @@ feed_eq_feed(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
if (j == 0)
break;
- info->biquad(info, dst, j);
+ /* Optimize some common formats. */
+ switch (info->fmt) {
+ case AFMT_S16_NE:
+ feed_eq_biquad(info, dst, j, AFMT_S16_NE);
+ break;
+ case AFMT_S24_NE:
+ feed_eq_biquad(info, dst, j, AFMT_S24_NE);
+ break;
+ case AFMT_S32_NE:
+ feed_eq_biquad(info, dst, j, AFMT_S32_NE);
+ break;
+ default:
+ feed_eq_biquad(info, dst, j, info->fmt);
+ break;
+ }
j *= info->align;
dst += j;
@@ -584,7 +522,7 @@ sysctl_dev_pcm_eq(SYSCTL_HANDLER_ARGS)
CHN_FOREACH(c, d, channels.pcm.busy) {
CHN_LOCK(c);
- f = chn_findfeeder(c, FEEDER_EQ);
+ f = feeder_find(c, FEEDER_EQ);
if (f != NULL)
(void)FEEDER_SET(f, FEEDEQ_STATE, val);
CHN_UNLOCK(c);
@@ -643,7 +581,7 @@ sysctl_dev_pcm_eq_preamp(SYSCTL_HANDLER_ARGS)
CHN_FOREACH(c, d, channels.pcm.busy) {
CHN_LOCK(c);
- f = chn_findfeeder(c, FEEDER_EQ);
+ f = feeder_find(c, FEEDER_EQ);
if (f != NULL)
(void)FEEDER_SET(f, FEEDEQ_PREAMP, val);
CHN_UNLOCK(c);
diff --git a/sys/dev/sound/pcm/feeder_format.c b/sys/dev/sound/pcm/feeder_format.c
index 1e18e3e07450..0feac43374b8 100644
--- a/sys/dev/sound/pcm/feeder_format.c
+++ b/sys/dev/sound/pcm/feeder_format.c
@@ -3,6 +3,10 @@
*
* Copyright (c) 2008-2009 Ariff Abdullah <ariff@FreeBSD.org>
* All rights reserved.
+ * Copyright (c) 2024-2025 The FreeBSD Foundation
+ *
+ * Portions of this software were developed by Christos Margiolis
+ * <christos@FreeBSD.org> under sponsorship from the FreeBSD Foundation.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -37,8 +41,6 @@
#endif
#include <dev/sound/pcm/sound.h>
#include <dev/sound/pcm/pcm.h>
-#include <dev/sound/pcm/g711.h>
-#include <dev/sound/pcm/intpcm.h>
#include "feeder_if.h"
#define SND_USE_FXDIV
@@ -47,109 +49,22 @@
#define FEEDFORMAT_RESERVOIR (SND_CHN_MAX * PCM_32_BPS)
-INTPCM_DECLARE(intpcm_conv_tables)
-
struct feed_format_info {
uint32_t ibps, obps;
uint32_t ialign, oalign, channels;
- intpcm_read_t *read;
- intpcm_write_t *write;
+ uint32_t rdfmt, wrfmt;
uint8_t reservoir[FEEDFORMAT_RESERVOIR];
};
-/*
- * dummy ac3/dts passthrough, etc.
- * XXX assume as s16le.
- */
-static __inline intpcm_t
-intpcm_read_null(uint8_t *src __unused)
-{
-
- return (0);
-}
-
-static __inline void
-intpcm_write_null(uint8_t *dst, intpcm_t v __unused)
-{
-
- _PCM_WRITE_S16_LE(dst, 0);
-}
-
-#define FEEDFORMAT_ENTRY(SIGN, BIT, ENDIAN) \
- { \
- AFMT_##SIGN##BIT##_##ENDIAN, \
- intpcm_read_##SIGN##BIT##ENDIAN, \
- intpcm_write_##SIGN##BIT##ENDIAN \
- }
-
-static const struct {
- uint32_t format;
- intpcm_read_t *read;
- intpcm_write_t *write;
-} feed_format_ops[] = {
- FEEDFORMAT_ENTRY(S, 8, NE),
- FEEDFORMAT_ENTRY(S, 16, LE),
- FEEDFORMAT_ENTRY(S, 24, LE),
- FEEDFORMAT_ENTRY(S, 32, LE),
- FEEDFORMAT_ENTRY(S, 16, BE),
- FEEDFORMAT_ENTRY(S, 24, BE),
- FEEDFORMAT_ENTRY(S, 32, BE),
- FEEDFORMAT_ENTRY(U, 8, NE),
- FEEDFORMAT_ENTRY(U, 16, LE),
- FEEDFORMAT_ENTRY(U, 24, LE),
- FEEDFORMAT_ENTRY(U, 32, LE),
- FEEDFORMAT_ENTRY(U, 16, BE),
- FEEDFORMAT_ENTRY(U, 24, BE),
- FEEDFORMAT_ENTRY(U, 32, BE),
- {
- AFMT_MU_LAW,
- intpcm_read_ulaw, intpcm_write_ulaw
- },
- {
- AFMT_A_LAW,
- intpcm_read_alaw, intpcm_write_alaw
- },
- {
- AFMT_AC3,
- intpcm_read_null, intpcm_write_null
- }
-};
-
-#define FEEDFORMAT_TAB_SIZE \
- ((int32_t)(sizeof(feed_format_ops) / sizeof(feed_format_ops[0])))
-
static int
feed_format_init(struct pcm_feeder *f)
{
struct feed_format_info *info;
- intpcm_read_t *rd_op;
- intpcm_write_t *wr_op;
- int i;
if (f->desc->in == f->desc->out ||
AFMT_CHANNEL(f->desc->in) != AFMT_CHANNEL(f->desc->out))
return (EINVAL);
- rd_op = NULL;
- wr_op = NULL;
-
- for (i = 0; i < FEEDFORMAT_TAB_SIZE &&
- (rd_op == NULL || wr_op == NULL); i++) {
- if (rd_op == NULL &&
- AFMT_ENCODING(f->desc->in) == feed_format_ops[i].format)
- rd_op = feed_format_ops[i].read;
- if (wr_op == NULL &&
- AFMT_ENCODING(f->desc->out) == feed_format_ops[i].format)
- wr_op = feed_format_ops[i].write;
- }
-
- if (rd_op == NULL || wr_op == NULL) {
- printf("%s(): failed to initialize io ops "
- "in=0x%08x out=0x%08x\n",
- __func__, f->desc->in, f->desc->out);
- return (EINVAL);
- }
-
info = malloc(sizeof(*info), M_DEVBUF, M_NOWAIT | M_ZERO);
if (info == NULL)
return (ENOMEM);
@@ -158,11 +73,11 @@ feed_format_init(struct pcm_feeder *f)
info->ibps = AFMT_BPS(f->desc->in);
info->ialign = info->ibps * info->channels;
- info->read = rd_op;
+ info->rdfmt = AFMT_ENCODING(f->desc->in);
info->obps = AFMT_BPS(f->desc->out);
info->oalign = info->obps * info->channels;
- info->write = wr_op;
+ info->wrfmt = AFMT_ENCODING(f->desc->out);
f->data = info;
@@ -246,8 +161,8 @@ feed_format_feed(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
count -= j * info->obps;
do {
- v = info->read(src);
- info->write(dst, v);
+ v = pcm_sample_read_norm(src, info->rdfmt);
+ pcm_sample_write_norm(dst, v, info->wrfmt);
dst += info->obps;
src += info->ibps;
} while (--j != 0);
@@ -271,30 +186,3 @@ static kobj_method_t feeder_format_methods[] = {
};
FEEDER_DECLARE(feeder_format, NULL);
-
-/* Extern */
-intpcm_read_t *
-feeder_format_read_op(uint32_t format)
-{
- int i;
-
- for (i = 0; i < FEEDFORMAT_TAB_SIZE; i++) {
- if (AFMT_ENCODING(format) == feed_format_ops[i].format)
- return (feed_format_ops[i].read);
- }
-
- return (NULL);
-}
-
-intpcm_write_t *
-feeder_format_write_op(uint32_t format)
-{
- int i;
-
- for (i = 0; i < FEEDFORMAT_TAB_SIZE; i++) {
- if (AFMT_ENCODING(format) == feed_format_ops[i].format)
- return (feed_format_ops[i].write);
- }
-
- return (NULL);
-}
diff --git a/sys/dev/sound/pcm/feeder_matrix.c b/sys/dev/sound/pcm/feeder_matrix.c
index f5f02e2bf4f5..43258a311d82 100644
--- a/sys/dev/sound/pcm/feeder_matrix.c
+++ b/sys/dev/sound/pcm/feeder_matrix.c
@@ -3,6 +3,10 @@
*
* Copyright (c) 2008-2009 Ariff Abdullah <ariff@FreeBSD.org>
* All rights reserved.
+ * Copyright (c) 2024-2025 The FreeBSD Foundation
+ *
+ * Portions of this software were developed by Christos Margiolis
+ * <christos@FreeBSD.org> under sponsorship from the FreeBSD Foundation.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -60,20 +64,11 @@
#define SND_CHN_T_EOF 0x00e0fe0f
#define SND_CHN_T_NULL 0x0e0e0e0e
-struct feed_matrix_info;
-
-typedef void (*feed_matrix_t)(struct feed_matrix_info *, uint8_t *,
- uint8_t *, uint32_t);
-
struct feed_matrix_info {
+ uint32_t fmt;
uint32_t bps;
uint32_t ialign, oalign;
uint32_t in, out;
- feed_matrix_t apply;
-#ifdef FEEDMATRIX_GENERIC
- intpcm_read_t *rd;
- intpcm_write_t *wr;
-#endif
struct {
int chn[SND_CHN_T_MAX + 1];
int mul, shift;
@@ -119,174 +114,64 @@ static int feeder_matrix_default_ids[9] = {
} while (0)
#endif
-#define FEEDMATRIX_DECLARE(SIGN, BIT, ENDIAN) \
-static void \
-feed_matrix_##SIGN##BIT##ENDIAN(struct feed_matrix_info *info, \
- uint8_t *src, uint8_t *dst, uint32_t count) \
-{ \
- intpcm64_t accum; \
- intpcm_t v; \
- int i, j; \
- \
- do { \
- for (i = 0; info->matrix[i].chn[0] != SND_CHN_T_EOF; \
- i++) { \
- if (info->matrix[i].chn[0] == SND_CHN_T_NULL) { \
- _PCM_WRITE_##SIGN##BIT##_##ENDIAN(dst, \
- 0); \
- dst += PCM_##BIT##_BPS; \
- continue; \
- } else if (info->matrix[i].chn[1] == \
- SND_CHN_T_EOF) { \
- v = _PCM_READ_##SIGN##BIT##_##ENDIAN( \
- src + info->matrix[i].chn[0]); \
- _PCM_WRITE_##SIGN##BIT##_##ENDIAN(dst, \
- v); \
- dst += PCM_##BIT##_BPS; \
- continue; \
- } \
- \
- accum = 0; \
- for (j = 0; \
- info->matrix[i].chn[j] != SND_CHN_T_EOF; \
- j++) { \
- v = _PCM_READ_##SIGN##BIT##_##ENDIAN( \
- src + info->matrix[i].chn[j]); \
- accum += v; \
- } \
- \
- accum = (accum * info->matrix[i].mul) >> \
- info->matrix[i].shift; \
- \
- FEEDMATRIX_CLIP_CHECK(accum, BIT); \
- \
- v = (accum > PCM_S##BIT##_MAX) ? \
- PCM_S##BIT##_MAX : \
- ((accum < PCM_S##BIT##_MIN) ? \
- PCM_S##BIT##_MIN : \
- accum); \
- _PCM_WRITE_##SIGN##BIT##_##ENDIAN(dst, v); \
- dst += PCM_##BIT##_BPS; \
- } \
- src += info->ialign; \
- } while (--count != 0); \
-}
-
-#if BYTE_ORDER == LITTLE_ENDIAN || defined(SND_FEEDER_MULTIFORMAT)
-FEEDMATRIX_DECLARE(S, 16, LE)
-FEEDMATRIX_DECLARE(S, 32, LE)
-#endif
-#if BYTE_ORDER == BIG_ENDIAN || defined(SND_FEEDER_MULTIFORMAT)
-FEEDMATRIX_DECLARE(S, 16, BE)
-FEEDMATRIX_DECLARE(S, 32, BE)
-#endif
-#ifdef SND_FEEDER_MULTIFORMAT
-FEEDMATRIX_DECLARE(S, 8, NE)
-FEEDMATRIX_DECLARE(S, 24, LE)
-FEEDMATRIX_DECLARE(S, 24, BE)
-FEEDMATRIX_DECLARE(U, 8, NE)
-FEEDMATRIX_DECLARE(U, 16, LE)
-FEEDMATRIX_DECLARE(U, 24, LE)
-FEEDMATRIX_DECLARE(U, 32, LE)
-FEEDMATRIX_DECLARE(U, 16, BE)
-FEEDMATRIX_DECLARE(U, 24, BE)
-FEEDMATRIX_DECLARE(U, 32, BE)
-#endif
-
-#define FEEDMATRIX_ENTRY(SIGN, BIT, ENDIAN) \
- { \
- AFMT_##SIGN##BIT##_##ENDIAN, \
- feed_matrix_##SIGN##BIT##ENDIAN \
- }
-
-static const struct {
- uint32_t format;
- feed_matrix_t apply;
-} feed_matrix_tab[] = {
-#if BYTE_ORDER == LITTLE_ENDIAN || defined(SND_FEEDER_MULTIFORMAT)
- FEEDMATRIX_ENTRY(S, 16, LE),
- FEEDMATRIX_ENTRY(S, 32, LE),
-#endif
-#if BYTE_ORDER == BIG_ENDIAN || defined(SND_FEEDER_MULTIFORMAT)
- FEEDMATRIX_ENTRY(S, 16, BE),
- FEEDMATRIX_ENTRY(S, 32, BE),
-#endif
-#ifdef SND_FEEDER_MULTIFORMAT
- FEEDMATRIX_ENTRY(S, 8, NE),
- FEEDMATRIX_ENTRY(S, 24, LE),
- FEEDMATRIX_ENTRY(S, 24, BE),
- FEEDMATRIX_ENTRY(U, 8, NE),
- FEEDMATRIX_ENTRY(U, 16, LE),
- FEEDMATRIX_ENTRY(U, 24, LE),
- FEEDMATRIX_ENTRY(U, 32, LE),
- FEEDMATRIX_ENTRY(U, 16, BE),
- FEEDMATRIX_ENTRY(U, 24, BE),
- FEEDMATRIX_ENTRY(U, 32, BE)
-#endif
-};
-
-static void
-feed_matrix_reset(struct feed_matrix_info *info)
-{
- uint32_t i, j;
-
- for (i = 0; i < (sizeof(info->matrix) / sizeof(info->matrix[0])); i++) {
- for (j = 0;
- j < (sizeof(info->matrix[i].chn) /
- sizeof(info->matrix[i].chn[0])); j++) {
- info->matrix[i].chn[j] = SND_CHN_T_EOF;
- }
- info->matrix[i].mul = 1;
- info->matrix[i].shift = 0;
- }
-}
-
-#ifdef FEEDMATRIX_GENERIC
-static void
-feed_matrix_apply_generic(struct feed_matrix_info *info,
- uint8_t *src, uint8_t *dst, uint32_t count)
+__always_inline static void
+feed_matrix_apply(struct feed_matrix_info *info, uint8_t *src, uint8_t *dst,
+ uint32_t count, const uint32_t fmt)
{
intpcm64_t accum;
intpcm_t v;
int i, j;
do {
- for (i = 0; info->matrix[i].chn[0] != SND_CHN_T_EOF;
- i++) {
+ for (i = 0; info->matrix[i].chn[0] != SND_CHN_T_EOF; i++) {
if (info->matrix[i].chn[0] == SND_CHN_T_NULL) {
- info->wr(dst, 0);
+ pcm_sample_write(dst, 0, fmt);
dst += info->bps;
continue;
- } else if (info->matrix[i].chn[1] ==
- SND_CHN_T_EOF) {
- v = info->rd(src + info->matrix[i].chn[0]);
- info->wr(dst, v);
+ } else if (info->matrix[i].chn[1] == SND_CHN_T_EOF) {
+ v = pcm_sample_read(src +
+ info->matrix[i].chn[0], fmt);
+ pcm_sample_write(dst, v, fmt);
dst += info->bps;
continue;
}
accum = 0;
- for (j = 0;
- info->matrix[i].chn[j] != SND_CHN_T_EOF;
+ for (j = 0; info->matrix[i].chn[j] != SND_CHN_T_EOF;
j++) {
- v = info->rd(src + info->matrix[i].chn[j]);
+ v = pcm_sample_read(src +
+ info->matrix[i].chn[j], fmt);
accum += v;
}
accum = (accum * info->matrix[i].mul) >>
info->matrix[i].shift;
- FEEDMATRIX_CLIP_CHECK(accum, 32);
+ FEEDMATRIX_CLIP_CHECK(accum, AFMT_BIT(fmt));
- v = (accum > PCM_S32_MAX) ? PCM_S32_MAX :
- ((accum < PCM_S32_MIN) ? PCM_S32_MIN : accum);
- info->wr(dst, v);
+ v = pcm_clamp(accum, fmt);
+ pcm_sample_write(dst, v, fmt);
dst += info->bps;
}
src += info->ialign;
} while (--count != 0);
}
-#endif
+
+static void
+feed_matrix_reset(struct feed_matrix_info *info)
+{
+ uint32_t i, j;
+
+ for (i = 0; i < nitems(info->matrix); i++) {
+ for (j = 0;
+ j < (sizeof(info->matrix[i].chn) /
+ sizeof(info->matrix[i].chn[0])); j++) {
+ info->matrix[i].chn[j] = SND_CHN_T_EOF;
+ }
+ info->matrix[i].mul = 1;
+ info->matrix[i].shift = 0;
+ }
+}
static int
feed_matrix_setup(struct feed_matrix_info *info, struct pcmchan_matrix *m_in,
@@ -396,7 +281,6 @@ feed_matrix_init(struct pcm_feeder *f)
{
struct feed_matrix_info *info;
struct pcmchan_matrix *m_in, *m_out;
- uint32_t i;
int ret;
if (AFMT_ENCODING(f->desc->in) != AFMT_ENCODING(f->desc->out))
@@ -408,31 +292,10 @@ feed_matrix_init(struct pcm_feeder *f)
info->in = f->desc->in;
info->out = f->desc->out;
+ info->fmt = AFMT_ENCODING(info->in);
info->bps = AFMT_BPS(info->in);
info->ialign = AFMT_ALIGN(info->in);
info->oalign = AFMT_ALIGN(info->out);
- info->apply = NULL;
-
- for (i = 0; info->apply == NULL &&
- i < (sizeof(feed_matrix_tab) / sizeof(feed_matrix_tab[0])); i++) {
- if (AFMT_ENCODING(info->in) == feed_matrix_tab[i].format)
- info->apply = feed_matrix_tab[i].apply;
- }
-
- if (info->apply == NULL) {
-#ifdef FEEDMATRIX_GENERIC
- info->rd = feeder_format_read_op(info->in);
- info->wr = feeder_format_write_op(info->out);
- if (info->rd == NULL || info->wr == NULL) {
- free(info, M_DEVBUF);
- return (EINVAL);
- }
- info->apply = feed_matrix_apply_generic;
-#else
- free(info, M_DEVBUF);
- return (EINVAL);
-#endif
- }
m_in = feeder_matrix_format_map(info->in);
m_out = feeder_matrix_format_map(info->out);
@@ -510,7 +373,21 @@ feed_matrix_feed(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
if (j == 0)
break;
- info->apply(info, src, dst, j);
+ /* Optimize some common formats. */
+ switch (info->fmt) {
+ case AFMT_S16_NE:
+ feed_matrix_apply(info, src, dst, j, AFMT_S16_NE);
+ break;
+ case AFMT_S24_NE:
+ feed_matrix_apply(info, src, dst, j, AFMT_S24_NE);
+ break;
+ case AFMT_S32_NE:
+ feed_matrix_apply(info, src, dst, j, AFMT_S32_NE);
+ break;
+ default:
+ feed_matrix_apply(info, src, dst, j, info->fmt);
+ break;
+ }
j *= info->oalign;
dst += j;
@@ -679,7 +556,7 @@ feeder_matrix_compare(struct pcmchan_matrix *m_in, struct pcmchan_matrix *m_out)
m_in->mask != m_out->mask)
return (1);
- for (i = 0; i < (sizeof(m_in->map) / sizeof(m_in->map[0])); i++) {
+ for (i = 0; i < nitems(m_in->map); i++) {
if (m_in->map[i].type != m_out->map[i].type)
return (1);
if (m_in->map[i].type == SND_CHN_T_MAX)
diff --git a/sys/dev/sound/pcm/feeder_mixer.c b/sys/dev/sound/pcm/feeder_mixer.c
index 9f6b653effa3..b6b81ad9a51c 100644
--- a/sys/dev/sound/pcm/feeder_mixer.c
+++ b/sys/dev/sound/pcm/feeder_mixer.c
@@ -3,6 +3,10 @@
*
* Copyright (c) 2008-2009 Ariff Abdullah <ariff@FreeBSD.org>
* All rights reserved.
+ * Copyright (c) 2024-2025 The FreeBSD Foundation
+ *
+ * Portions of this software were developed by Christos Margiolis
+ * <christos@FreeBSD.org> under sponsorship from the FreeBSD Foundation.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -42,133 +46,83 @@
#undef SND_FEEDER_MULTIFORMAT
#define SND_FEEDER_MULTIFORMAT 1
-typedef void (*feed_mixer_t)(uint8_t *, uint8_t *, uint32_t);
-
-#define FEEDMIXER_DECLARE(SIGN, BIT, ENDIAN) \
-static void \
-feed_mixer_##SIGN##BIT##ENDIAN(uint8_t *src, uint8_t *dst, \
- uint32_t count) \
-{ \
- intpcm##BIT##_t z; \
- intpcm_t x, y; \
- \
- src += count; \
- dst += count; \
- \
- do { \
- src -= PCM_##BIT##_BPS; \
- dst -= PCM_##BIT##_BPS; \
- count -= PCM_##BIT##_BPS; \
- x = PCM_READ_##SIGN##BIT##_##ENDIAN(src); \
- y = PCM_READ_##SIGN##BIT##_##ENDIAN(dst); \
- z = INTPCM##BIT##_T(x) + y; \
- x = PCM_CLAMP_##SIGN##BIT(z); \
- _PCM_WRITE_##SIGN##BIT##_##ENDIAN(dst, x); \
- } while (count != 0); \
-}
-
-#if BYTE_ORDER == LITTLE_ENDIAN || defined(SND_FEEDER_MULTIFORMAT)
-FEEDMIXER_DECLARE(S, 16, LE)
-FEEDMIXER_DECLARE(S, 32, LE)
-#endif
-#if BYTE_ORDER == BIG_ENDIAN || defined(SND_FEEDER_MULTIFORMAT)
-FEEDMIXER_DECLARE(S, 16, BE)
-FEEDMIXER_DECLARE(S, 32, BE)
-#endif
-#ifdef SND_FEEDER_MULTIFORMAT
-FEEDMIXER_DECLARE(S, 8, NE)
-FEEDMIXER_DECLARE(S, 24, LE)
-FEEDMIXER_DECLARE(S, 24, BE)
-FEEDMIXER_DECLARE(U, 8, NE)
-FEEDMIXER_DECLARE(U, 16, LE)
-FEEDMIXER_DECLARE(U, 24, LE)
-FEEDMIXER_DECLARE(U, 32, LE)
-FEEDMIXER_DECLARE(U, 16, BE)
-FEEDMIXER_DECLARE(U, 24, BE)
-FEEDMIXER_DECLARE(U, 32, BE)
-#endif
-
struct feed_mixer_info {
uint32_t format;
+ uint32_t channels;
int bps;
- feed_mixer_t mix;
};
-#define FEEDMIXER_ENTRY(SIGN, BIT, ENDIAN) \
- { \
- AFMT_##SIGN##BIT##_##ENDIAN, PCM_##BIT##_BPS, \
- feed_mixer_##SIGN##BIT##ENDIAN \
- }
-
-static struct feed_mixer_info feed_mixer_info_tab[] = {
- FEEDMIXER_ENTRY(S, 8, NE),
-#if BYTE_ORDER == LITTLE_ENDIAN || defined(SND_FEEDER_MULTIFORMAT)
- FEEDMIXER_ENTRY(S, 16, LE),
- FEEDMIXER_ENTRY(S, 32, LE),
-#endif
-#if BYTE_ORDER == BIG_ENDIAN || defined(SND_FEEDER_MULTIFORMAT)
- FEEDMIXER_ENTRY(S, 16, BE),
- FEEDMIXER_ENTRY(S, 32, BE),
-#endif
-#ifdef SND_FEEDER_MULTIFORMAT
- FEEDMIXER_ENTRY(S, 24, LE),
- FEEDMIXER_ENTRY(S, 24, BE),
- FEEDMIXER_ENTRY(U, 8, NE),
- FEEDMIXER_ENTRY(U, 16, LE),
- FEEDMIXER_ENTRY(U, 24, LE),
- FEEDMIXER_ENTRY(U, 32, LE),
- FEEDMIXER_ENTRY(U, 16, BE),
- FEEDMIXER_ENTRY(U, 24, BE),
- FEEDMIXER_ENTRY(U, 32, BE),
-#endif
- { AFMT_AC3, PCM_16_BPS, NULL },
- { AFMT_MU_LAW, PCM_8_BPS, feed_mixer_U8NE }, /* dummy */
- { AFMT_A_LAW, PCM_8_BPS, feed_mixer_U8NE } /* dummy */
-};
+__always_inline static void
+feed_mixer_apply(uint8_t *src, uint8_t *dst, uint32_t count, const uint32_t fmt)
+{
+ intpcm32_t z;
+ intpcm_t x, y;
-#define FEEDMIXER_TAB_SIZE ((int32_t) \
- (sizeof(feed_mixer_info_tab) / \
- sizeof(feed_mixer_info_tab[0])))
+ src += count;
+ dst += count;
-#define FEEDMIXER_DATA(i, c) ((void *) \
- ((uintptr_t)((((i) & 0x1f) << 7) | \
- ((c) & 0x7f))))
-#define FEEDMIXER_INFOIDX(d) ((uint32_t)((uintptr_t)(d) >> 7) & 0x1f)
-#define FEEDMIXER_CHANNELS(d) ((uint32_t)((uintptr_t)(d)) & 0x7f)
+ do {
+ src -= AFMT_BPS(fmt);
+ dst -= AFMT_BPS(fmt);
+ count -= AFMT_BPS(fmt);
+ x = pcm_sample_read_calc(src, fmt);
+ y = pcm_sample_read_calc(dst, fmt);
+ z = INTPCM_T(x) + y;
+ x = pcm_clamp_calc(z, fmt);
+ pcm_sample_write(dst, x, fmt);
+ } while (count != 0);
+}
static int
feed_mixer_init(struct pcm_feeder *f)
{
- int i;
+ struct feed_mixer_info *info;
if (f->desc->in != f->desc->out)
return (EINVAL);
- for (i = 0; i < FEEDMIXER_TAB_SIZE; i++) {
- if (AFMT_ENCODING(f->desc->in) ==
- feed_mixer_info_tab[i].format) {
- f->data =
- FEEDMIXER_DATA(i, AFMT_CHANNEL(f->desc->in));
- return (0);
- }
- }
+ info = malloc(sizeof(*info), M_DEVBUF, M_NOWAIT | M_ZERO);
+ if (info == NULL)
+ return (ENOMEM);
+
+ info->format = AFMT_ENCODING(f->desc->in);
+ info->channels = AFMT_CHANNEL(f->desc->in);
+ info->bps = AFMT_BPS(f->desc->in);
+
+ f->data = info;
- return (EINVAL);
+ return (0);
+}
+
+static int
+feed_mixer_free(struct pcm_feeder *f)
+{
+ struct feed_mixer_info *info;
+
+ info = f->data;
+ if (info != NULL)
+ free(info, M_DEVBUF);
+
+ f->data = NULL;
+
+ return (0);
}
static int
feed_mixer_set(struct pcm_feeder *f, int what, int value)
{
+ struct feed_mixer_info *info;
+
+ info = f->data;
switch (what) {
case FEEDMIXER_CHANNELS:
if (value < SND_CHN_MIN || value > SND_CHN_MAX)
return (EINVAL);
- f->data = FEEDMIXER_DATA(FEEDMIXER_INFOIDX(f->data), value);
+ info->channels = (uint32_t)value;
break;
default:
return (EINVAL);
- break;
}
return (0);
@@ -294,8 +248,8 @@ feed_mixer_feed(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
if (sz < count)
count = sz;
- info = &feed_mixer_info_tab[FEEDMIXER_INFOIDX(f->data)];
- sz = info->bps * FEEDMIXER_CHANNELS(f->data);
+ info = f->data;
+ sz = info->bps * info->channels;
count = SND_FXROUND(count, sz);
if (count < sz)
return (0);
@@ -328,7 +282,7 @@ feed_mixer_feed(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
if ((ch->flags & CHN_F_MMAP) && !(ch->flags & CHN_F_CLOSING))
sndbuf_acquire(ch->bufsoft, NULL,
sndbuf_getfree(ch->bufsoft));
- if (info->mix == NULL) {
+ if (c->flags & CHN_F_PASSTHROUGH) {
/*
* Passthrough. Dump the first digital/passthrough
* channel into destination buffer, and the rest into
@@ -370,7 +324,24 @@ feed_mixer_feed(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
f->desc->out), mcnt);
mcnt = 0;
}
- info->mix(tmp, b, cnt);
+ switch (info->format) {
+ case AFMT_S16_NE:
+ feed_mixer_apply(tmp, b, cnt,
+ AFMT_S16_NE);
+ break;
+ case AFMT_S24_NE:
+ feed_mixer_apply(tmp, b, cnt,
+ AFMT_S24_NE);
+ break;
+ case AFMT_S32_NE:
+ feed_mixer_apply(tmp, b, cnt,
+ AFMT_S32_NE);
+ break;
+ default:
+ feed_mixer_apply(tmp, b, cnt,
+ info->format);
+ break;
+ }
if (cnt > rcnt)
rcnt = cnt;
}
@@ -394,6 +365,7 @@ static struct pcm_feederdesc feeder_mixer_desc[] = {
static kobj_method_t feeder_mixer_methods[] = {
KOBJMETHOD(feeder_init, feed_mixer_init),
+ KOBJMETHOD(feeder_free, feed_mixer_free),
KOBJMETHOD(feeder_set, feed_mixer_set),
KOBJMETHOD(feeder_feed, feed_mixer_feed),
KOBJMETHOD_END
diff --git a/sys/dev/sound/pcm/feeder_rate.c b/sys/dev/sound/pcm/feeder_rate.c
index c8cc67e8fa80..9c29142b9d6b 100644
--- a/sys/dev/sound/pcm/feeder_rate.c
+++ b/sys/dev/sound/pcm/feeder_rate.c
@@ -258,6 +258,7 @@ sysctl_hw_snd_feeder_rate_quality(SYSCTL_HANDLER_ARGS)
* set resampler quality if and only if it is exist as
* part of feeder chains and the channel is idle.
*/
+ bus_topo_lock();
for (i = 0; pcm_devclass != NULL &&
i < devclass_get_maxunit(pcm_devclass); i++) {
d = devclass_get_softc(pcm_devclass, i);
@@ -268,7 +269,7 @@ sysctl_hw_snd_feeder_rate_quality(SYSCTL_HANDLER_ARGS)
PCM_ACQUIRE(d);
CHN_FOREACH(c, d, channels.pcm) {
CHN_LOCK(c);
- f = chn_findfeeder(c, FEEDER_RATE);
+ f = feeder_find(c, FEEDER_RATE);
if (f == NULL || f->data == NULL || CHN_STARTED(c)) {
CHN_UNLOCK(c);
continue;
@@ -279,11 +280,12 @@ sysctl_hw_snd_feeder_rate_quality(SYSCTL_HANDLER_ARGS)
PCM_RELEASE(d);
PCM_UNLOCK(d);
}
+ bus_topo_unlock();
return (0);
}
SYSCTL_PROC(_hw_snd, OID_AUTO, feeder_rate_quality,
- CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_NEEDGIANT, 0, sizeof(int),
+ CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, 0, sizeof(int),
sysctl_hw_snd_feeder_rate_quality, "I",
"sample rate converter quality ("__XSTRING(Z_QUALITY_MIN)"=low .. "
__XSTRING(Z_QUALITY_MAX)"=high)");
@@ -431,11 +433,6 @@ z_roundpow2(int32_t v)
static void
z_feed_zoh(struct z_info *info, uint8_t *dst)
{
-#if 0
- z_copy(info->z_delay +
- (info->z_start * info->channels * info->bps), dst,
- info->channels * info->bps);
-#else
uint32_t cnt;
uint8_t *src;
@@ -449,7 +446,6 @@ z_feed_zoh(struct z_info *info, uint8_t *dst)
do {
*dst++ = *src++;
} while (--cnt != 0);
-#endif
}
/*
@@ -477,10 +473,10 @@ z_feed_linear_##SIGN##BIT##ENDIAN(struct z_info *info, uint8_t *dst) \
ch = info->channels; \
\
do { \
- x = _PCM_READ_##SIGN##BIT##_##ENDIAN(sx); \
- y = _PCM_READ_##SIGN##BIT##_##ENDIAN(sy); \
+ x = pcm_sample_read(sx, AFMT_##SIGN##BIT##_##ENDIAN); \
+ y = pcm_sample_read(sy, AFMT_##SIGN##BIT##_##ENDIAN); \
x = Z_LINEAR_INTERPOLATE_##BIT(z, x, y); \
- _PCM_WRITE_##SIGN##BIT##_##ENDIAN(dst, x); \
+ pcm_sample_write(dst, x, AFMT_##SIGN##BIT##_##ENDIAN); \
sx += PCM_##BIT##_BPS; \
sy += PCM_##BIT##_BPS; \
dst += PCM_##BIT##_BPS; \
@@ -508,10 +504,6 @@ z_feed_linear_##SIGN##BIT##ENDIAN(struct z_info *info, uint8_t *dst) \
#define Z_CLIP_CHECK(...)
#endif
-#define Z_CLAMP(v, BIT) \
- (((v) > PCM_S##BIT##_MAX) ? PCM_S##BIT##_MAX : \
- (((v) < PCM_S##BIT##_MIN) ? PCM_S##BIT##_MIN : (v)))
-
/*
* Sine Cardinal (SINC) Interpolation. Scaling is done in 64 bit, so
* there's no point to hold the plate any longer. All samples will be
@@ -522,7 +514,7 @@ z_feed_linear_##SIGN##BIT##ENDIAN(struct z_info *info, uint8_t *dst) \
c += z >> Z_SHIFT; \
z &= Z_MASK; \
coeff = Z_COEFF_INTERPOLATE(z, z_coeff[c], z_dcoeff[c]); \
- x = _PCM_READ_##SIGN##BIT##_##ENDIAN(p); \
+ x = pcm_sample_read(p, AFMT_##SIGN##BIT##_##ENDIAN); \
v += Z_NORM_##BIT((intpcm64_t)x * coeff); \
z += info->z_dy; \
p adv##= info->channels * PCM_##BIT##_BPS
@@ -580,7 +572,8 @@ z_feed_sinc_##SIGN##BIT##ENDIAN(struct z_info *info, uint8_t *dst) \
else \
v >>= Z_COEFF_SHIFT - Z_GUARD_BIT_##BIT; \
Z_CLIP_CHECK(v, BIT); \
- _PCM_WRITE_##SIGN##BIT##_##ENDIAN(dst, Z_CLAMP(v, BIT)); \
+ pcm_sample_write(dst, pcm_clamp(v, AFMT_##SIGN##BIT##_##ENDIAN),\
+ AFMT_##SIGN##BIT##_##ENDIAN); \
} while (ch != 0); \
}
@@ -605,11 +598,11 @@ z_feed_sinc_polyphase_##SIGN##BIT##ENDIAN(struct z_info *info, uint8_t *dst) \
z_pcoeff = info->z_pcoeff + \
((info->z_alpha * info->z_size) << 1); \
for (i = info->z_size; i != 0; i--) { \
- x = _PCM_READ_##SIGN##BIT##_##ENDIAN(p); \
+ x = pcm_sample_read(p, AFMT_##SIGN##BIT##_##ENDIAN); \
v += Z_NORM_##BIT((intpcm64_t)x * *z_pcoeff); \
z_pcoeff++; \
p += info->channels * PCM_##BIT##_BPS; \
- x = _PCM_READ_##SIGN##BIT##_##ENDIAN(p); \
+ x = pcm_sample_read(p, AFMT_##SIGN##BIT##_##ENDIAN); \
v += Z_NORM_##BIT((intpcm64_t)x * *z_pcoeff); \
z_pcoeff++; \
p += info->channels * PCM_##BIT##_BPS; \
@@ -619,7 +612,8 @@ z_feed_sinc_polyphase_##SIGN##BIT##ENDIAN(struct z_info *info, uint8_t *dst) \
else \
v >>= Z_COEFF_SHIFT - Z_GUARD_BIT_##BIT; \
Z_CLIP_CHECK(v, BIT); \
- _PCM_WRITE_##SIGN##BIT##_##ENDIAN(dst, Z_CLAMP(v, BIT)); \
+ pcm_sample_write(dst, pcm_clamp(v, AFMT_##SIGN##BIT##_##ENDIAN),\
+ AFMT_##SIGN##BIT##_##ENDIAN); \
} while (ch != 0); \
}
@@ -647,6 +641,8 @@ Z_DECLARE(U, 32, LE)
Z_DECLARE(U, 16, BE)
Z_DECLARE(U, 24, BE)
Z_DECLARE(U, 32, BE)
+Z_DECLARE(F, 32, LE)
+Z_DECLARE(F, 32, BE)
#endif
enum {
@@ -695,6 +691,8 @@ static const struct {
Z_RESAMPLER_ENTRY(U, 16, BE),
Z_RESAMPLER_ENTRY(U, 24, BE),
Z_RESAMPLER_ENTRY(U, 32, BE),
+ Z_RESAMPLER_ENTRY(F, 32, LE),
+ Z_RESAMPLER_ENTRY(F, 32, BE),
#endif
};
@@ -1171,14 +1169,6 @@ z_setup_adaptive_sinc:
info->z_scale = Z_ONE;
}
-#if 0
-#define Z_SCALE_DIV 10000
-#define Z_SCALE_LIMIT(s, v) \
- ((((uint64_t)(s) * (v)) + (Z_SCALE_DIV >> 1)) / Z_SCALE_DIV)
-
- info->z_scale = Z_SCALE_LIMIT(info->z_scale, 9780);
-#endif
-
/* Smallest drift increment. */
info->z_dx = info->z_dy / info->z_gy;
@@ -1672,12 +1662,6 @@ z_resampler_feed_internal(struct pcm_feeder *f, struct pcm_channel *c,
*/
do {
info->z_resample(info, dst);
-#if 0
- startdrift = z_gy2gx(info, 1);
- alphadrift = z_drift(info, startdrift, 1);
- info->z_start += startdrift;
- info->z_alpha += alphadrift;
-#else
info->z_alpha += alphadrift;
if (info->z_alpha < info->z_gy)
info->z_start += startdrift;
@@ -1685,7 +1669,6 @@ z_resampler_feed_internal(struct pcm_feeder *f, struct pcm_channel *c,
info->z_start += startdrift - 1;
info->z_alpha -= info->z_gy;
}
-#endif
dst += align;
#ifdef Z_DIAGNOSTIC
info->z_cycle++;
diff --git a/sys/dev/sound/pcm/feeder_volume.c b/sys/dev/sound/pcm/feeder_volume.c
index 452d8788a5a5..ddcbf29804f3 100644
--- a/sys/dev/sound/pcm/feeder_volume.c
+++ b/sys/dev/sound/pcm/feeder_volume.c
@@ -63,10 +63,13 @@ feed_volume_##SIGN##BIT##ENDIAN(int *vol, int *matrix, \
do { \
dst -= PCM_##BIT##_BPS; \
i--; \
- x = PCM_READ_##SIGN##BIT##_##ENDIAN(dst); \
+ x = pcm_sample_read_calc(dst, \
+ AFMT_##SIGN##BIT##_##ENDIAN); \
v = FEEDVOLUME_CALC##BIT(x, vol[matrix[i]]); \
- x = PCM_CLAMP_##SIGN##BIT(v); \
- _PCM_WRITE_##SIGN##BIT##_##ENDIAN(dst, x); \
+ x = pcm_clamp_calc(v, \
+ AFMT_##SIGN##BIT##_##ENDIAN); \
+ pcm_sample_write(dst, x, \
+ AFMT_##SIGN##BIT##_##ENDIAN); \
} while (i != 0); \
} while (--count != 0); \
}
@@ -90,6 +93,8 @@ FEEDVOLUME_DECLARE(U, 32, LE)
FEEDVOLUME_DECLARE(U, 16, BE)
FEEDVOLUME_DECLARE(U, 24, BE)
FEEDVOLUME_DECLARE(U, 32, BE)
+FEEDVOLUME_DECLARE(F, 32, LE)
+FEEDVOLUME_DECLARE(F, 32, BE)
#endif
struct feed_volume_info {
@@ -128,7 +133,9 @@ static const struct {
FEEDVOLUME_ENTRY(U, 32, LE),
FEEDVOLUME_ENTRY(U, 16, BE),
FEEDVOLUME_ENTRY(U, 24, BE),
- FEEDVOLUME_ENTRY(U, 32, BE)
+ FEEDVOLUME_ENTRY(U, 32, BE),
+ FEEDVOLUME_ENTRY(F, 32, LE),
+ FEEDVOLUME_ENTRY(F, 32, BE),
#endif
};
@@ -337,7 +344,7 @@ feeder_volume_apply_matrix(struct pcm_feeder *f, struct pcmchan_matrix *m)
info = f->data;
- for (i = 0; i < (sizeof(info->matrix) / sizeof(info->matrix[0])); i++) {
+ for (i = 0; i < nitems(info->matrix); i++) {
if (i < m->channels)
info->matrix[i] = m->map[i].type;
else
diff --git a/sys/dev/sound/pcm/g711.h b/sys/dev/sound/pcm/g711.h
index fe9ddb651c8b..481ef368e144 100644
--- a/sys/dev/sound/pcm/g711.h
+++ b/sys/dev/sound/pcm/g711.h
@@ -176,48 +176,4 @@
#define _INTPCM_TO_G711(t, v) ((t)[(uint8_t)((v) ^ 0x80)])
-#define G711_DECLARE_TABLE(t) \
-static const struct { \
- const uint8_t ulaw_to_u8[G711_TABLE_SIZE]; \
- const uint8_t alaw_to_u8[G711_TABLE_SIZE]; \
- const uint8_t u8_to_ulaw[G711_TABLE_SIZE]; \
- const uint8_t u8_to_alaw[G711_TABLE_SIZE]; \
-} t = { \
- ULAW_TO_U8, ALAW_TO_U8, \
- U8_TO_ULAW, U8_TO_ALAW \
-}
-
-#define G711_DECLARE_OP(t) \
-static __inline intpcm_t \
-pcm_read_ulaw(uint8_t v) \
-{ \
- \
- return (_G711_TO_INTPCM((t).ulaw_to_u8, v)); \
-} \
- \
-static __inline intpcm_t \
-pcm_read_alaw(uint8_t v) \
-{ \
- \
- return (_G711_TO_INTPCM((t).alaw_to_u8, v)); \
-} \
- \
-static __inline void \
-pcm_write_ulaw(uint8_t *dst, intpcm_t v) \
-{ \
- \
- *dst = _INTPCM_TO_G711((t).u8_to_ulaw, v); \
-} \
- \
-static __inline void \
-pcm_write_alaw(uint8_t *dst, intpcm_t v) \
-{ \
- \
- *dst = _INTPCM_TO_G711((t).u8_to_alaw, v); \
-}
-
-#define G711_DECLARE(t) \
- G711_DECLARE_TABLE(t); \
- G711_DECLARE_OP(t)
-
#endif /* !_SND_G711_H_ */
diff --git a/sys/dev/sound/pcm/intpcm.h b/sys/dev/sound/pcm/intpcm.h
deleted file mode 100644
index 1e85535feec1..000000000000
--- a/sys/dev/sound/pcm/intpcm.h
+++ /dev/null
@@ -1,135 +0,0 @@
-/*-
- * SPDX-License-Identifier: BSD-2-Clause
- *
- * Copyright (c) 2008-2009 Ariff Abdullah <ariff@FreeBSD.org>
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#ifndef _SND_INTPCM_H_
-#define _SND_INTPCM_H_
-
-typedef intpcm_t intpcm_read_t(uint8_t *);
-typedef void intpcm_write_t(uint8_t *, intpcm_t);
-
-extern intpcm_read_t *feeder_format_read_op(uint32_t);
-extern intpcm_write_t *feeder_format_write_op(uint32_t);
-
-#define INTPCM_DECLARE_OP_WRITE(SIGN, BIT, ENDIAN, SHIFT) \
-static __inline void \
-intpcm_write_##SIGN##BIT##ENDIAN(uint8_t *dst, intpcm_t v) \
-{ \
- \
- _PCM_WRITE_##SIGN##BIT##_##ENDIAN(dst, v >> SHIFT); \
-}
-
-#define INTPCM_DECLARE_OP_8(SIGN, ENDIAN) \
-static __inline intpcm_t \
-intpcm_read_##SIGN##8##ENDIAN(uint8_t *src) \
-{ \
- \
- return (_PCM_READ_##SIGN##8##_##ENDIAN(src) << 24); \
-} \
-INTPCM_DECLARE_OP_WRITE(SIGN, 8, ENDIAN, 24)
-
-#define INTPCM_DECLARE_OP_16(SIGN, ENDIAN) \
-static __inline intpcm_t \
-intpcm_read_##SIGN##16##ENDIAN(uint8_t *src) \
-{ \
- \
- return (_PCM_READ_##SIGN##16##_##ENDIAN(src) << 16); \
-} \
-INTPCM_DECLARE_OP_WRITE(SIGN, 16, ENDIAN, 16)
-
-#define INTPCM_DECLARE_OP_24(SIGN, ENDIAN) \
-static __inline intpcm_t \
-intpcm_read_##SIGN##24##ENDIAN(uint8_t *src) \
-{ \
- \
- return (_PCM_READ_##SIGN##24##_##ENDIAN(src) << 8); \
-} \
-INTPCM_DECLARE_OP_WRITE(SIGN, 24, ENDIAN, 8)
-
-#define INTPCM_DECLARE_OP_32(SIGN, ENDIAN) \
-static __inline intpcm_t \
-intpcm_read_##SIGN##32##ENDIAN(uint8_t *src) \
-{ \
- \
- return (_PCM_READ_##SIGN##32##_##ENDIAN(src)); \
-} \
- \
-static __inline void \
-intpcm_write_##SIGN##32##ENDIAN(uint8_t *dst, intpcm_t v) \
-{ \
- \
- _PCM_WRITE_##SIGN##32##_##ENDIAN(dst, v); \
-}
-
-#define INTPCM_DECLARE(t) \
- \
-G711_DECLARE_TABLE(t); \
- \
-static __inline intpcm_t \
-intpcm_read_ulaw(uint8_t *src) \
-{ \
- \
- return (_G711_TO_INTPCM((t).ulaw_to_u8, *src) << 24); \
-} \
- \
-static __inline intpcm_t \
-intpcm_read_alaw(uint8_t *src) \
-{ \
- \
- return (_G711_TO_INTPCM((t).alaw_to_u8, *src) << 24); \
-} \
- \
-static __inline void \
-intpcm_write_ulaw(uint8_t *dst, intpcm_t v) \
-{ \
- \
- *dst = _INTPCM_TO_G711((t).u8_to_ulaw, v >> 24); \
-} \
- \
-static __inline void \
-intpcm_write_alaw(uint8_t *dst, intpcm_t v) \
-{ \
- \
- *dst = _INTPCM_TO_G711((t).u8_to_alaw, v >> 24); \
-} \
- \
-INTPCM_DECLARE_OP_8(S, NE) \
-INTPCM_DECLARE_OP_16(S, LE) \
-INTPCM_DECLARE_OP_16(S, BE) \
-INTPCM_DECLARE_OP_24(S, LE) \
-INTPCM_DECLARE_OP_24(S, BE) \
-INTPCM_DECLARE_OP_32(S, LE) \
-INTPCM_DECLARE_OP_32(S, BE) \
-INTPCM_DECLARE_OP_8(U, NE) \
-INTPCM_DECLARE_OP_16(U, LE) \
-INTPCM_DECLARE_OP_16(U, BE) \
-INTPCM_DECLARE_OP_24(U, LE) \
-INTPCM_DECLARE_OP_24(U, BE) \
-INTPCM_DECLARE_OP_32(U, LE) \
-INTPCM_DECLARE_OP_32(U, BE)
-
-#endif /* !_SND_INTPCM_H_ */
diff --git a/sys/dev/sound/pcm/matrix.h b/sys/dev/sound/pcm/matrix.h
index 14f3a3410a70..e2798c651536 100644
--- a/sys/dev/sound/pcm/matrix.h
+++ b/sys/dev/sound/pcm/matrix.h
@@ -217,4 +217,644 @@
(x)[SND_VOL_C_MASTER][z]) / \
(x)[SND_VOL_C_MASTER][SND_CHN_T_VOL_0DB]) \
+/*
+ * Standard matrix maps:
+ *
+ * struct pcmchan_matrix {
+ * .id = Matrix identity (see matrix.h). Custom defined should use
+ * one of SND_CHN_MATRIX_MISC (for whatever purposes) or
+ * SND_CHN_MATRIX_DRV (hardware driver).
+ * .channels = Total number of channels, including whatever 'extended'
+ * (the X.ext notions, mostly LFE).
+ * .ext = Total number of extended channels (LFE).
+ * .map = {
+ * Sequences of channel type and interleave structure.
+ * [interleave offset] = {
+ * .type = channel type (see matrix.h).
+ * .members = Masks of channels that is acceptable as a
+ * member of this channel type.
+ * },
+ * [total channels] = {
+ * .type = Maximum channels marker (SND_CHN_T_MAX).
+ * .members = 0 (no channels allowed here).
+ * },
+ * },
+ * .mask = Mask of channels that exist in this map.
+ * .offset = {
+ * channel offset that directly translate to the above interleave
+ * offset according to SND_CHN_T_* definitions.
+ * }
+ * };
+ *
+ * Rule of thumb: Avoid using SND_CHN_T_* that is marked with XXX (matrix.h),
+ * or be prepared for the horror to come.
+ *
+ */
+
+#define SND_CHN_MATRIX_MAP_1_0 { \
+ .id = SND_CHN_MATRIX_1_0, \
+ .channels = 1, \
+ .ext = 0, \
+ .map = { \
+ /* Mono, center, etc. */ \
+ [0] = { \
+ .type = SND_CHN_T_FL, \
+ .members = \
+ SND_CHN_T_MASK_FL | SND_CHN_T_MASK_FR | \
+ SND_CHN_T_MASK_FC | SND_CHN_T_MASK_LF | \
+ SND_CHN_T_MASK_BL | SND_CHN_T_MASK_BR | \
+ SND_CHN_T_MASK_BC | SND_CHN_T_MASK_SL | \
+ SND_CHN_T_MASK_SR \
+ }, \
+ [1] = { \
+ .type = SND_CHN_T_MAX, \
+ .members = 0 \
+ } \
+ }, \
+ .mask = SND_CHN_T_MASK_FL | SND_CHN_T_MASK_FR | \
+ SND_CHN_T_MASK_FC, \
+ .offset = { 0, 0, 0, 0, 0, 0, -1, -1, 0, \
+ 0, 0, -1, -1, -1, -1, -1, -1, -1 } \
+}
+
+#define SND_CHN_MATRIX_MAP_2_0 { \
+ .id = SND_CHN_MATRIX_2_0, \
+ .channels = 2, \
+ .ext = 0, \
+ .map = { \
+ /* Left */ \
+ [0] = { \
+ .type = SND_CHN_T_FL, \
+ .members = \
+ SND_CHN_T_MASK_FL | SND_CHN_T_MASK_FC | \
+ SND_CHN_T_MASK_LF | SND_CHN_T_MASK_BL | \
+ SND_CHN_T_MASK_BC | SND_CHN_T_MASK_SL \
+ }, \
+ /* Right */ \
+ [1] = { \
+ .type = SND_CHN_T_FR, \
+ .members = \
+ SND_CHN_T_MASK_FR | SND_CHN_T_MASK_FC | \
+ SND_CHN_T_MASK_LF | SND_CHN_T_MASK_BR | \
+ SND_CHN_T_MASK_BC | SND_CHN_T_MASK_SR \
+ }, \
+ [2] = { \
+ .type = SND_CHN_T_MAX, \
+ .members = 0 \
+ } \
+ }, \
+ .mask = SND_CHN_T_MASK_FL | SND_CHN_T_MASK_FR, \
+ .offset = { 0, 1, -1, -1, -1, -1, -1, -1, -1, \
+ -1, -1, -1, -1, -1, -1, -1, -1, -1 } \
+}
+
+#define SND_CHN_MATRIX_MAP_2_1 { \
+ .id = SND_CHN_MATRIX_2_1, \
+ .channels = 3, \
+ .ext = 1, \
+ .map = { \
+ /* Left */ \
+ [0] = { \
+ .type = SND_CHN_T_FL, \
+ .members = \
+ SND_CHN_T_MASK_FL | SND_CHN_T_MASK_FC | \
+ SND_CHN_T_MASK_BL | SND_CHN_T_MASK_BC | \
+ SND_CHN_T_MASK_SL \
+ }, \
+ /* Right */ \
+ [1] = { \
+ .type = SND_CHN_T_FR, \
+ .members = \
+ SND_CHN_T_MASK_FR | SND_CHN_T_MASK_FC | \
+ SND_CHN_T_MASK_BR | SND_CHN_T_MASK_BC | \
+ SND_CHN_T_MASK_SR \
+ }, \
+ /* LFE */ \
+ [2] = { \
+ .type = SND_CHN_T_LF, \
+ .members = SND_CHN_T_MASK_LF \
+ }, \
+ [3] = { \
+ .type = SND_CHN_T_MAX, \
+ .members = 0 \
+ } \
+ }, \
+ .mask = SND_CHN_T_MASK_FL | SND_CHN_T_MASK_FR | \
+ SND_CHN_T_MASK_LF, \
+ .offset = { 0, 1, -1, 2, -1, -1, -1, -1, -1, \
+ -1, -1, -1, -1, -1, -1, -1, -1, -1 } \
+}
+
+#define SND_CHN_MATRIX_MAP_3_0 { /* 3 channels default */ \
+ .id = SND_CHN_MATRIX_3_0, \
+ .channels = 3, \
+ .ext = 0, \
+ .map = { \
+ /* Left */ \
+ [0] = { \
+ .type = SND_CHN_T_FL, \
+ .members = \
+ SND_CHN_T_MASK_FL | SND_CHN_T_MASK_FC | \
+ SND_CHN_T_MASK_LF | SND_CHN_T_MASK_SL \
+ }, \
+ /* Right */ \
+ [1] = { \
+ .type = SND_CHN_T_FR, \
+ .members = \
+ SND_CHN_T_MASK_FR | SND_CHN_T_MASK_FC | \
+ SND_CHN_T_MASK_LF | SND_CHN_T_MASK_SR \
+ }, \
+ /* Rear Center */ \
+ [2] = { \
+ .type = SND_CHN_T_BC, \
+ .members = \
+ SND_CHN_T_MASK_LF | SND_CHN_T_MASK_BL | \
+ SND_CHN_T_MASK_BR | SND_CHN_T_MASK_BC | \
+ SND_CHN_T_MASK_SL | SND_CHN_T_MASK_SR \
+ }, \
+ [3] = { \
+ .type = SND_CHN_T_MAX, \
+ .members = 0 \
+ } \
+ }, \
+ .mask = SND_CHN_T_MASK_FL | SND_CHN_T_MASK_FR | \
+ SND_CHN_T_MASK_BC, \
+ .offset = { 0, 1, -1, -1, -1, -1, -1, -1, 2, \
+ -1, -1, -1, -1, -1, -1, -1, -1, -1 } \
+}
+
+#define SND_CHN_MATRIX_MAP_3_1 { \
+ .id = SND_CHN_MATRIX_3_1, \
+ .channels = 4, \
+ .ext = 1, \
+ .map = { \
+ /* Left */ \
+ [0] = { \
+ .type = SND_CHN_T_FL, \
+ .members = \
+ SND_CHN_T_MASK_FL | SND_CHN_T_MASK_FC | \
+ SND_CHN_T_MASK_SL \
+ }, \
+ /* Right */ \
+ [1] = { \
+ .type = SND_CHN_T_FR, \
+ .members = \
+ SND_CHN_T_MASK_FR | SND_CHN_T_MASK_FC | \
+ SND_CHN_T_MASK_SR \
+ }, \
+ /* LFE */ \
+ [2] = { \
+ .type = SND_CHN_T_LF, \
+ .members = SND_CHN_T_MASK_LF \
+ }, \
+ /* Rear Center */ \
+ [3] = { \
+ .type = SND_CHN_T_BC, \
+ .members = \
+ SND_CHN_T_MASK_BL | SND_CHN_T_MASK_BR | \
+ SND_CHN_T_MASK_BC | \
+ SND_CHN_T_MASK_SL | SND_CHN_T_MASK_SR \
+ }, \
+ [4] = { \
+ .type = SND_CHN_T_MAX, \
+ .members = 0 \
+ } \
+ }, \
+ .mask = SND_CHN_T_MASK_FL | SND_CHN_T_MASK_FR | \
+ SND_CHN_T_MASK_LF | SND_CHN_T_MASK_BC, \
+ .offset = { 0, 1, -1, 2, -1, -1, -1, -1, 3, \
+ -1, -1, -1, -1, -1, -1, -1, -1, -1 } \
+}
+
+#define SND_CHN_MATRIX_MAP_4_0 { \
+ .id = SND_CHN_MATRIX_4_0, \
+ .channels = 4, \
+ .ext = 0, \
+ .map = { \
+ /* Left */ \
+ [0] = { \
+ .type = SND_CHN_T_FL, \
+ .members = \
+ SND_CHN_T_MASK_FL | SND_CHN_T_MASK_FC | \
+ SND_CHN_T_MASK_LF | SND_CHN_T_MASK_SL \
+ }, \
+ /* Right */ \
+ [1] = { \
+ .type = SND_CHN_T_FR, \
+ .members = \
+ SND_CHN_T_MASK_FR | SND_CHN_T_MASK_FC | \
+ SND_CHN_T_MASK_LF | SND_CHN_T_MASK_SR \
+ }, \
+ /* Rear Left */ \
+ [2] = { \
+ .type = SND_CHN_T_BL, \
+ .members = \
+ SND_CHN_T_MASK_LF | SND_CHN_T_MASK_BL | \
+ SND_CHN_T_MASK_BC | SND_CHN_T_MASK_SL \
+ }, \
+ /* Rear Right */ \
+ [3] = { \
+ .type = SND_CHN_T_BR, \
+ .members = \
+ SND_CHN_T_MASK_LF | SND_CHN_T_MASK_BR | \
+ SND_CHN_T_MASK_BC | SND_CHN_T_MASK_SR \
+ }, \
+ [4] = { \
+ .type = SND_CHN_T_MAX, \
+ .members = 0 \
+ } \
+ }, \
+ .mask = SND_CHN_T_MASK_FL | SND_CHN_T_MASK_FR | \
+ SND_CHN_T_MASK_BL | SND_CHN_T_MASK_BR, \
+ .offset = { 0, 1, -1, -1, 2, 3, -1, -1, -1, \
+ -1, -1, -1, -1, -1, -1, -1, -1, -1 } \
+}
+
+#define SND_CHN_MATRIX_MAP_4_1 { \
+ .id = SND_CHN_MATRIX_4_1, \
+ .channels = 5, \
+ .ext = 1, \
+ .map = { \
+ /* Left */ \
+ [0] = { \
+ .type = SND_CHN_T_FL, \
+ .members = \
+ SND_CHN_T_MASK_FL | SND_CHN_T_MASK_FC | \
+ SND_CHN_T_MASK_SL \
+ }, \
+ /* Right */ \
+ [1] = { \
+ .type = SND_CHN_T_FR, \
+ .members = \
+ SND_CHN_T_MASK_FR | SND_CHN_T_MASK_FC | \
+ SND_CHN_T_MASK_SR \
+ }, \
+ /* Rear Left */ \
+ [2] = { \
+ .type = SND_CHN_T_BL, \
+ .members = \
+ SND_CHN_T_MASK_BL | SND_CHN_T_MASK_BC | \
+ SND_CHN_T_MASK_SL \
+ }, \
+ /* Rear Right */ \
+ [3] = { \
+ .type = SND_CHN_T_BR, \
+ .members = \
+ SND_CHN_T_MASK_BR | SND_CHN_T_MASK_BC | \
+ SND_CHN_T_MASK_SR \
+ }, \
+ /* LFE */ \
+ [4] = { \
+ .type = SND_CHN_T_LF, \
+ .members = SND_CHN_T_MASK_LF \
+ }, \
+ [5] = { \
+ .type = SND_CHN_T_MAX, \
+ .members = 0 \
+ } \
+ }, \
+ .mask = SND_CHN_T_MASK_FL | SND_CHN_T_MASK_FR | \
+ SND_CHN_T_MASK_BL | SND_CHN_T_MASK_BR | \
+ SND_CHN_T_MASK_LF, \
+ .offset = { 0, 1, -1, 4, 2, 3, -1, -1, -1, \
+ -1, -1, -1, -1, -1, -1, -1, -1, -1 } \
+}
+
+#define SND_CHN_MATRIX_MAP_5_0 { /* 5 channels default */ \
+ .id = SND_CHN_MATRIX_5_0, \
+ .channels = 5, \
+ .ext = 0, \
+ .map = { \
+ /* Left */ \
+ [0] = { \
+ .type = SND_CHN_T_FL, \
+ .members = \
+ SND_CHN_T_MASK_FL | SND_CHN_T_MASK_LF | \
+ SND_CHN_T_MASK_SL \
+ }, \
+ /* Right */ \
+ [1] = { \
+ .type = SND_CHN_T_FR, \
+ .members = \
+ SND_CHN_T_MASK_FR | SND_CHN_T_MASK_LF | \
+ SND_CHN_T_MASK_SR \
+ }, \
+ /* Rear Left */ \
+ [2] = { \
+ .type = SND_CHN_T_BL, \
+ .members = \
+ SND_CHN_T_MASK_LF | SND_CHN_T_MASK_BL | \
+ SND_CHN_T_MASK_BC | SND_CHN_T_MASK_SL \
+ }, \
+ /* Rear Right */ \
+ [3] = { \
+ .type = SND_CHN_T_BR, \
+ .members = \
+ SND_CHN_T_MASK_LF | SND_CHN_T_MASK_BR | \
+ SND_CHN_T_MASK_BC | SND_CHN_T_MASK_SR \
+ }, \
+ /* Center */ \
+ [4] = { \
+ .type = SND_CHN_T_FC, \
+ .members = SND_CHN_T_MASK_FC \
+ }, \
+ [5] = { \
+ .type = SND_CHN_T_MAX, \
+ .members = 0 \
+ } \
+ }, \
+ .mask = SND_CHN_T_MASK_FL | SND_CHN_T_MASK_FR | \
+ SND_CHN_T_MASK_BL | SND_CHN_T_MASK_BR | \
+ SND_CHN_T_MASK_FC, \
+ .offset = { 0, 1, 4, -1, 2, 3, -1, -1, -1, \
+ -1, -1, -1, -1, -1, -1, -1, -1, -1 } \
+}
+
+#define SND_CHN_MATRIX_MAP_5_1 { /* 6 channels default */ \
+ .id = SND_CHN_MATRIX_5_1, \
+ .channels = 6, \
+ .ext = 1, \
+ .map = { \
+ /* Left */ \
+ [0] = { \
+ .type = SND_CHN_T_FL, \
+ .members = \
+ SND_CHN_T_MASK_FL | SND_CHN_T_MASK_SL \
+ }, \
+ /* Right */ \
+ [1] = { \
+ .type = SND_CHN_T_FR, \
+ .members = \
+ SND_CHN_T_MASK_FR | SND_CHN_T_MASK_SR \
+ }, \
+ /* Rear Left */ \
+ [2] = { \
+ .type = SND_CHN_T_BL, \
+ .members = \
+ SND_CHN_T_MASK_BL | SND_CHN_T_MASK_BC | \
+ SND_CHN_T_MASK_SL \
+ }, \
+ /* Rear Right */ \
+ [3] = { \
+ .type = SND_CHN_T_BR, \
+ .members = \
+ SND_CHN_T_MASK_BR | SND_CHN_T_MASK_BC | \
+ SND_CHN_T_MASK_SR \
+ }, \
+ /* Center */ \
+ [4] = { \
+ .type = SND_CHN_T_FC, \
+ .members = SND_CHN_T_MASK_FC \
+ }, \
+ /* LFE */ \
+ [5] = { \
+ .type = SND_CHN_T_LF, \
+ .members = SND_CHN_T_MASK_LF \
+ }, \
+ [6] = { \
+ .type = SND_CHN_T_MAX, \
+ .members = 0 \
+ } \
+ }, \
+ .mask = SND_CHN_T_MASK_FL | SND_CHN_T_MASK_FR | \
+ SND_CHN_T_MASK_BL | SND_CHN_T_MASK_BR | \
+ SND_CHN_T_MASK_FC | SND_CHN_T_MASK_LF, \
+ .offset = { 0, 1, 4, 5, 2, 3, -1, -1, -1, \
+ -1, -1, -1, -1, -1, -1, -1, -1, -1 } \
+}
+
+#define SND_CHN_MATRIX_MAP_6_0 { \
+ .id = SND_CHN_MATRIX_6_0, \
+ .channels = 6, \
+ .ext = 0, \
+ .map = { \
+ /* Left */ \
+ [0] = { \
+ .type = SND_CHN_T_FL, \
+ .members = \
+ SND_CHN_T_MASK_FL | SND_CHN_T_MASK_LF | \
+ SND_CHN_T_MASK_SL \
+ }, \
+ /* Right */ \
+ [1] = { \
+ .type = SND_CHN_T_FR, \
+ .members = \
+ SND_CHN_T_MASK_FR | SND_CHN_T_MASK_LF | \
+ SND_CHN_T_MASK_SR \
+ }, \
+ /* Rear Left */ \
+ [2] = { \
+ .type = SND_CHN_T_BL, \
+ .members = \
+ SND_CHN_T_MASK_BL | SND_CHN_T_MASK_LF | \
+ SND_CHN_T_MASK_SL \
+ }, \
+ /* Rear Right */ \
+ [3] = { \
+ .type = SND_CHN_T_BR, \
+ .members = \
+ SND_CHN_T_MASK_BR | SND_CHN_T_MASK_LF | \
+ SND_CHN_T_MASK_SR \
+ }, \
+ /* Center */ \
+ [4] = { \
+ .type = SND_CHN_T_FC, \
+ .members = SND_CHN_T_MASK_FC \
+ }, \
+ /* Rear Center */ \
+ [5] = { \
+ .type = SND_CHN_T_BC, \
+ .members = SND_CHN_T_MASK_BC \
+ }, \
+ [6] = { \
+ .type = SND_CHN_T_MAX, \
+ .members = 0 \
+ } \
+ }, \
+ .mask = SND_CHN_T_MASK_FL | SND_CHN_T_MASK_FR | \
+ SND_CHN_T_MASK_BL | SND_CHN_T_MASK_BR | \
+ SND_CHN_T_MASK_FC | SND_CHN_T_MASK_BC, \
+ .offset = { 0, 1, 4, -1, 2, 3, -1, -1, 5, \
+ -1, -1, -1, -1, -1, -1, -1, -1, -1 } \
+}
+
+#define SND_CHN_MATRIX_MAP_6_1 { \
+ .id = SND_CHN_MATRIX_6_1, \
+ .channels = 7, \
+ .ext = 1, \
+ .map = { \
+ /* Left */ \
+ [0] = { \
+ .type = SND_CHN_T_FL, \
+ .members = \
+ SND_CHN_T_MASK_FL | SND_CHN_T_MASK_SL \
+ }, \
+ /* Right */ \
+ [1] = { \
+ .type = SND_CHN_T_FR, \
+ .members = \
+ SND_CHN_T_MASK_FR | SND_CHN_T_MASK_SR \
+ }, \
+ /* Rear Left */ \
+ [2] = { \
+ .type = SND_CHN_T_BL, \
+ .members = \
+ SND_CHN_T_MASK_BL | SND_CHN_T_MASK_SL \
+ }, \
+ /* Rear Right */ \
+ [3] = { \
+ .type = SND_CHN_T_BR, \
+ .members = \
+ SND_CHN_T_MASK_BR | SND_CHN_T_MASK_SR \
+ }, \
+ /* Center */ \
+ [4] = { \
+ .type = SND_CHN_T_FC, \
+ .members = SND_CHN_T_MASK_FC \
+ }, \
+ /* LFE */ \
+ [5] = { \
+ .type = SND_CHN_T_LF, \
+ .members = SND_CHN_T_MASK_LF \
+ }, \
+ /* Rear Center */ \
+ [6] = { \
+ .type = SND_CHN_T_BC, \
+ .members = SND_CHN_T_MASK_BC \
+ }, \
+ [7] = { \
+ .type = SND_CHN_T_MAX, \
+ .members = 0 \
+ } \
+ }, \
+ .mask = SND_CHN_T_MASK_FL | SND_CHN_T_MASK_FR | \
+ SND_CHN_T_MASK_BL | SND_CHN_T_MASK_BR | \
+ SND_CHN_T_MASK_FC | SND_CHN_T_MASK_LF | \
+ SND_CHN_T_MASK_BC, \
+ .offset = { 0, 1, 4, 5, 2, 3, -1, -1, 6, \
+ -1, -1, -1, -1, -1, -1, -1, -1, -1 } \
+}
+
+#define SND_CHN_MATRIX_MAP_7_0 { \
+ .id = SND_CHN_MATRIX_7_0, \
+ .channels = 7, \
+ .ext = 0, \
+ .map = { \
+ /* Left */ \
+ [0] = { \
+ .type = SND_CHN_T_FL, \
+ .members = \
+ SND_CHN_T_MASK_FL | SND_CHN_T_MASK_LF \
+ }, \
+ /* Right */ \
+ [1] = { \
+ .type = SND_CHN_T_FR, \
+ .members = \
+ SND_CHN_T_MASK_FR | SND_CHN_T_MASK_LF \
+ }, \
+ /* Rear Left */ \
+ [2] = { \
+ .type = SND_CHN_T_BL, \
+ .members = \
+ SND_CHN_T_MASK_BL | SND_CHN_T_MASK_BC | \
+ SND_CHN_T_MASK_LF \
+ }, \
+ /* Rear Right */ \
+ [3] = { \
+ .type = SND_CHN_T_BR, \
+ .members = \
+ SND_CHN_T_MASK_BR | SND_CHN_T_MASK_BC | \
+ SND_CHN_T_MASK_LF \
+ }, \
+ /* Center */ \
+ [4] = { \
+ .type = SND_CHN_T_FC, \
+ .members = \
+ SND_CHN_T_MASK_FC | SND_CHN_T_MASK_LF \
+ }, \
+ /* Side Left */ \
+ [5] = { \
+ .type = SND_CHN_T_SL, \
+ .members = \
+ SND_CHN_T_MASK_SL | SND_CHN_T_MASK_LF \
+ }, \
+ /* Side Right */ \
+ [6] = { \
+ .type = SND_CHN_T_SR, \
+ .members = \
+ SND_CHN_T_MASK_SR | SND_CHN_T_MASK_LF \
+ }, \
+ [7] = { \
+ .type = SND_CHN_T_MAX, \
+ .members = 0 \
+ } \
+ }, \
+ .mask = SND_CHN_T_MASK_FL | SND_CHN_T_MASK_FR | \
+ SND_CHN_T_MASK_BL | SND_CHN_T_MASK_BR | \
+ SND_CHN_T_MASK_FC | \
+ SND_CHN_T_MASK_SL | SND_CHN_T_MASK_SR, \
+ .offset = { 0, 1, 4, -1, 2, 3, -1, -1, -1, \
+ 5, 6, -1, -1, -1, -1, -1, -1, -1 } \
+}
+
+#define SND_CHN_MATRIX_MAP_7_1 { \
+ .id = SND_CHN_MATRIX_7_1, \
+ .channels = 8, \
+ .ext = 1, \
+ .map = { \
+ /* Left */ \
+ [0] = { \
+ .type = SND_CHN_T_FL, \
+ .members = SND_CHN_T_MASK_FL \
+ }, \
+ /* Right */ \
+ [1] = { \
+ .type = SND_CHN_T_FR, \
+ .members = SND_CHN_T_MASK_FR \
+ }, \
+ /* Rear Left */ \
+ [2] = { \
+ .type = SND_CHN_T_BL, \
+ .members = \
+ SND_CHN_T_MASK_BL | SND_CHN_T_MASK_BC \
+ }, \
+ /* Rear Right */ \
+ [3] = { \
+ .type = SND_CHN_T_BR, \
+ .members = \
+ SND_CHN_T_MASK_BR | SND_CHN_T_MASK_BC \
+ }, \
+ /* Center */ \
+ [4] = { \
+ .type = SND_CHN_T_FC, \
+ .members = SND_CHN_T_MASK_FC \
+ }, \
+ /* LFE */ \
+ [5] = { \
+ .type = SND_CHN_T_LF, \
+ .members = SND_CHN_T_MASK_LF \
+ }, \
+ /* Side Left */ \
+ [6] = { \
+ .type = SND_CHN_T_SL, \
+ .members = SND_CHN_T_MASK_SL \
+ }, \
+ /* Side Right */ \
+ [7] = { \
+ .type = SND_CHN_T_SR, \
+ .members = SND_CHN_T_MASK_SR \
+ }, \
+ [8] = { \
+ .type = SND_CHN_T_MAX, \
+ .members = 0 \
+ } \
+ }, \
+ .mask = SND_CHN_T_MASK_FL | SND_CHN_T_MASK_FR | \
+ SND_CHN_T_MASK_BL | SND_CHN_T_MASK_BR | \
+ SND_CHN_T_MASK_FC | SND_CHN_T_MASK_LF | \
+ SND_CHN_T_MASK_SL | SND_CHN_T_MASK_SR, \
+ .offset = { 0, 1, 4, 5, 2, 3, -1, -1, -1, \
+ 6, 7, -1, -1, -1, -1, -1, -1, -1 } \
+}
+
#endif /* !_SND_MATRIX_H_ */
diff --git a/sys/dev/sound/pcm/matrix_map.h b/sys/dev/sound/pcm/matrix_map.h
deleted file mode 100644
index ec0619614273..000000000000
--- a/sys/dev/sound/pcm/matrix_map.h
+++ /dev/null
@@ -1,672 +0,0 @@
-/*-
- * SPDX-License-Identifier: BSD-2-Clause
- *
- * Copyright (c) 2009 Ariff Abdullah <ariff@FreeBSD.org>
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#ifndef _SND_MATRIX_MAP_H_
-#define _SND_MATRIX_MAP_H_
-
-/*
- * Standard matrix maps:
- *
- * struct pcmchan_matrix {
- * .id = Matrix identity (see matrix.h). Custom defined should use
- * one of SND_CHN_MATRIX_MISC (for whatever purposes) or
- * SND_CHN_MATRIX_DRV (hardware driver).
- * .channels = Total number of channels, including whatever 'extended'
- * (the X.ext notions, mostly LFE).
- * .ext = Total number of extended channels (LFE).
- * .map = {
- * Sequences of channel type and interleave structure.
- * [interleave offset] = {
- * .type = channel type (see matrix.h).
- * .members = Masks of channels that is acceptable as a
- * member of this channel type.
- * },
- * [total channels] = {
- * .type = Maximum channels marker (SND_CHN_T_MAX).
- * .members = 0 (no channels allowed here).
- * },
- * },
- * .mask = Mask of channels that exist in this map.
- * .offset = {
- * channel offset that directly translate to the above interleave
- * offset according to SND_CHN_T_* definitions.
- * }
- * };
- *
- * Rule of thumb: Avoid using SND_CHN_T_* that is marked with XXX (matrix.h),
- * or be prepared for the horror to come.
- *
- */
-
-#define SND_CHN_MATRIX_MAP_1_0 { \
- .id = SND_CHN_MATRIX_1_0, \
- .channels = 1, \
- .ext = 0, \
- .map = { \
- /* Mono, center, etc. */ \
- [0] = { \
- .type = SND_CHN_T_FL, \
- .members = \
- SND_CHN_T_MASK_FL | SND_CHN_T_MASK_FR | \
- SND_CHN_T_MASK_FC | SND_CHN_T_MASK_LF | \
- SND_CHN_T_MASK_BL | SND_CHN_T_MASK_BR | \
- SND_CHN_T_MASK_BC | SND_CHN_T_MASK_SL | \
- SND_CHN_T_MASK_SR \
- }, \
- [1] = { \
- .type = SND_CHN_T_MAX, \
- .members = 0 \
- } \
- }, \
- .mask = SND_CHN_T_MASK_FL | SND_CHN_T_MASK_FR | \
- SND_CHN_T_MASK_FC, \
- .offset = { 0, 0, 0, 0, 0, 0, -1, -1, 0, \
- 0, 0, -1, -1, -1, -1, -1, -1, -1 } \
-}
-
-#define SND_CHN_MATRIX_MAP_2_0 { \
- .id = SND_CHN_MATRIX_2_0, \
- .channels = 2, \
- .ext = 0, \
- .map = { \
- /* Left */ \
- [0] = { \
- .type = SND_CHN_T_FL, \
- .members = \
- SND_CHN_T_MASK_FL | SND_CHN_T_MASK_FC | \
- SND_CHN_T_MASK_LF | SND_CHN_T_MASK_BL | \
- SND_CHN_T_MASK_BC | SND_CHN_T_MASK_SL \
- }, \
- /* Right */ \
- [1] = { \
- .type = SND_CHN_T_FR, \
- .members = \
- SND_CHN_T_MASK_FR | SND_CHN_T_MASK_FC | \
- SND_CHN_T_MASK_LF | SND_CHN_T_MASK_BR | \
- SND_CHN_T_MASK_BC | SND_CHN_T_MASK_SR \
- }, \
- [2] = { \
- .type = SND_CHN_T_MAX, \
- .members = 0 \
- } \
- }, \
- .mask = SND_CHN_T_MASK_FL | SND_CHN_T_MASK_FR, \
- .offset = { 0, 1, -1, -1, -1, -1, -1, -1, -1, \
- -1, -1, -1, -1, -1, -1, -1, -1, -1 } \
-}
-
-#define SND_CHN_MATRIX_MAP_2_1 { \
- .id = SND_CHN_MATRIX_2_1, \
- .channels = 3, \
- .ext = 1, \
- .map = { \
- /* Left */ \
- [0] = { \
- .type = SND_CHN_T_FL, \
- .members = \
- SND_CHN_T_MASK_FL | SND_CHN_T_MASK_FC | \
- SND_CHN_T_MASK_BL | SND_CHN_T_MASK_BC | \
- SND_CHN_T_MASK_SL \
- }, \
- /* Right */ \
- [1] = { \
- .type = SND_CHN_T_FR, \
- .members = \
- SND_CHN_T_MASK_FR | SND_CHN_T_MASK_FC | \
- SND_CHN_T_MASK_BR | SND_CHN_T_MASK_BC | \
- SND_CHN_T_MASK_SR \
- }, \
- /* LFE */ \
- [2] = { \
- .type = SND_CHN_T_LF, \
- .members = SND_CHN_T_MASK_LF \
- }, \
- [3] = { \
- .type = SND_CHN_T_MAX, \
- .members = 0 \
- } \
- }, \
- .mask = SND_CHN_T_MASK_FL | SND_CHN_T_MASK_FR | \
- SND_CHN_T_MASK_LF, \
- .offset = { 0, 1, -1, 2, -1, -1, -1, -1, -1, \
- -1, -1, -1, -1, -1, -1, -1, -1, -1 } \
-}
-
-#define SND_CHN_MATRIX_MAP_3_0 { /* 3 channels default */ \
- .id = SND_CHN_MATRIX_3_0, \
- .channels = 3, \
- .ext = 0, \
- .map = { \
- /* Left */ \
- [0] = { \
- .type = SND_CHN_T_FL, \
- .members = \
- SND_CHN_T_MASK_FL | SND_CHN_T_MASK_FC | \
- SND_CHN_T_MASK_LF | SND_CHN_T_MASK_SL \
- }, \
- /* Right */ \
- [1] = { \
- .type = SND_CHN_T_FR, \
- .members = \
- SND_CHN_T_MASK_FR | SND_CHN_T_MASK_FC | \
- SND_CHN_T_MASK_LF | SND_CHN_T_MASK_SR \
- }, \
- /* Rear Center */ \
- [2] = { \
- .type = SND_CHN_T_BC, \
- .members = \
- SND_CHN_T_MASK_LF | SND_CHN_T_MASK_BL | \
- SND_CHN_T_MASK_BR | SND_CHN_T_MASK_BC | \
- SND_CHN_T_MASK_SL | SND_CHN_T_MASK_SR \
- }, \
- [3] = { \
- .type = SND_CHN_T_MAX, \
- .members = 0 \
- } \
- }, \
- .mask = SND_CHN_T_MASK_FL | SND_CHN_T_MASK_FR | \
- SND_CHN_T_MASK_BC, \
- .offset = { 0, 1, -1, -1, -1, -1, -1, -1, 2, \
- -1, -1, -1, -1, -1, -1, -1, -1, -1 } \
-}
-
-#define SND_CHN_MATRIX_MAP_3_1 { \
- .id = SND_CHN_MATRIX_3_1, \
- .channels = 4, \
- .ext = 1, \
- .map = { \
- /* Left */ \
- [0] = { \
- .type = SND_CHN_T_FL, \
- .members = \
- SND_CHN_T_MASK_FL | SND_CHN_T_MASK_FC | \
- SND_CHN_T_MASK_SL \
- }, \
- /* Right */ \
- [1] = { \
- .type = SND_CHN_T_FR, \
- .members = \
- SND_CHN_T_MASK_FR | SND_CHN_T_MASK_FC | \
- SND_CHN_T_MASK_SR \
- }, \
- /* LFE */ \
- [2] = { \
- .type = SND_CHN_T_LF, \
- .members = SND_CHN_T_MASK_LF \
- }, \
- /* Rear Center */ \
- [3] = { \
- .type = SND_CHN_T_BC, \
- .members = \
- SND_CHN_T_MASK_BL | SND_CHN_T_MASK_BR | \
- SND_CHN_T_MASK_BC | \
- SND_CHN_T_MASK_SL | SND_CHN_T_MASK_SR \
- }, \
- [4] = { \
- .type = SND_CHN_T_MAX, \
- .members = 0 \
- } \
- }, \
- .mask = SND_CHN_T_MASK_FL | SND_CHN_T_MASK_FR | \
- SND_CHN_T_MASK_LF | SND_CHN_T_MASK_BC, \
- .offset = { 0, 1, -1, 2, -1, -1, -1, -1, 3, \
- -1, -1, -1, -1, -1, -1, -1, -1, -1 } \
-}
-
-#define SND_CHN_MATRIX_MAP_4_0 { \
- .id = SND_CHN_MATRIX_4_0, \
- .channels = 4, \
- .ext = 0, \
- .map = { \
- /* Left */ \
- [0] = { \
- .type = SND_CHN_T_FL, \
- .members = \
- SND_CHN_T_MASK_FL | SND_CHN_T_MASK_FC | \
- SND_CHN_T_MASK_LF | SND_CHN_T_MASK_SL \
- }, \
- /* Right */ \
- [1] = { \
- .type = SND_CHN_T_FR, \
- .members = \
- SND_CHN_T_MASK_FR | SND_CHN_T_MASK_FC | \
- SND_CHN_T_MASK_LF | SND_CHN_T_MASK_SR \
- }, \
- /* Rear Left */ \
- [2] = { \
- .type = SND_CHN_T_BL, \
- .members = \
- SND_CHN_T_MASK_LF | SND_CHN_T_MASK_BL | \
- SND_CHN_T_MASK_BC | SND_CHN_T_MASK_SL \
- }, \
- /* Rear Right */ \
- [3] = { \
- .type = SND_CHN_T_BR, \
- .members = \
- SND_CHN_T_MASK_LF | SND_CHN_T_MASK_BR | \
- SND_CHN_T_MASK_BC | SND_CHN_T_MASK_SR \
- }, \
- [4] = { \
- .type = SND_CHN_T_MAX, \
- .members = 0 \
- } \
- }, \
- .mask = SND_CHN_T_MASK_FL | SND_CHN_T_MASK_FR | \
- SND_CHN_T_MASK_BL | SND_CHN_T_MASK_BR, \
- .offset = { 0, 1, -1, -1, 2, 3, -1, -1, -1, \
- -1, -1, -1, -1, -1, -1, -1, -1, -1 } \
-}
-
-#define SND_CHN_MATRIX_MAP_4_1 { \
- .id = SND_CHN_MATRIX_4_1, \
- .channels = 5, \
- .ext = 1, \
- .map = { \
- /* Left */ \
- [0] = { \
- .type = SND_CHN_T_FL, \
- .members = \
- SND_CHN_T_MASK_FL | SND_CHN_T_MASK_FC | \
- SND_CHN_T_MASK_SL \
- }, \
- /* Right */ \
- [1] = { \
- .type = SND_CHN_T_FR, \
- .members = \
- SND_CHN_T_MASK_FR | SND_CHN_T_MASK_FC | \
- SND_CHN_T_MASK_SR \
- }, \
- /* Rear Left */ \
- [2] = { \
- .type = SND_CHN_T_BL, \
- .members = \
- SND_CHN_T_MASK_BL | SND_CHN_T_MASK_BC | \
- SND_CHN_T_MASK_SL \
- }, \
- /* Rear Right */ \
- [3] = { \
- .type = SND_CHN_T_BR, \
- .members = \
- SND_CHN_T_MASK_BR | SND_CHN_T_MASK_BC | \
- SND_CHN_T_MASK_SR \
- }, \
- /* LFE */ \
- [4] = { \
- .type = SND_CHN_T_LF, \
- .members = SND_CHN_T_MASK_LF \
- }, \
- [5] = { \
- .type = SND_CHN_T_MAX, \
- .members = 0 \
- } \
- }, \
- .mask = SND_CHN_T_MASK_FL | SND_CHN_T_MASK_FR | \
- SND_CHN_T_MASK_BL | SND_CHN_T_MASK_BR | \
- SND_CHN_T_MASK_LF, \
- .offset = { 0, 1, -1, 4, 2, 3, -1, -1, -1, \
- -1, -1, -1, -1, -1, -1, -1, -1, -1 } \
-}
-
-#define SND_CHN_MATRIX_MAP_5_0 { /* 5 channels default */ \
- .id = SND_CHN_MATRIX_5_0, \
- .channels = 5, \
- .ext = 0, \
- .map = { \
- /* Left */ \
- [0] = { \
- .type = SND_CHN_T_FL, \
- .members = \
- SND_CHN_T_MASK_FL | SND_CHN_T_MASK_LF | \
- SND_CHN_T_MASK_SL \
- }, \
- /* Right */ \
- [1] = { \
- .type = SND_CHN_T_FR, \
- .members = \
- SND_CHN_T_MASK_FR | SND_CHN_T_MASK_LF | \
- SND_CHN_T_MASK_SR \
- }, \
- /* Rear Left */ \
- [2] = { \
- .type = SND_CHN_T_BL, \
- .members = \
- SND_CHN_T_MASK_LF | SND_CHN_T_MASK_BL | \
- SND_CHN_T_MASK_BC | SND_CHN_T_MASK_SL \
- }, \
- /* Rear Right */ \
- [3] = { \
- .type = SND_CHN_T_BR, \
- .members = \
- SND_CHN_T_MASK_LF | SND_CHN_T_MASK_BR | \
- SND_CHN_T_MASK_BC | SND_CHN_T_MASK_SR \
- }, \
- /* Center */ \
- [4] = { \
- .type = SND_CHN_T_FC, \
- .members = SND_CHN_T_MASK_FC \
- }, \
- [5] = { \
- .type = SND_CHN_T_MAX, \
- .members = 0 \
- } \
- }, \
- .mask = SND_CHN_T_MASK_FL | SND_CHN_T_MASK_FR | \
- SND_CHN_T_MASK_BL | SND_CHN_T_MASK_BR | \
- SND_CHN_T_MASK_FC, \
- .offset = { 0, 1, 4, -1, 2, 3, -1, -1, -1, \
- -1, -1, -1, -1, -1, -1, -1, -1, -1 } \
-}
-
-#define SND_CHN_MATRIX_MAP_5_1 { /* 6 channels default */ \
- .id = SND_CHN_MATRIX_5_1, \
- .channels = 6, \
- .ext = 1, \
- .map = { \
- /* Left */ \
- [0] = { \
- .type = SND_CHN_T_FL, \
- .members = \
- SND_CHN_T_MASK_FL | SND_CHN_T_MASK_SL \
- }, \
- /* Right */ \
- [1] = { \
- .type = SND_CHN_T_FR, \
- .members = \
- SND_CHN_T_MASK_FR | SND_CHN_T_MASK_SR \
- }, \
- /* Rear Left */ \
- [2] = { \
- .type = SND_CHN_T_BL, \
- .members = \
- SND_CHN_T_MASK_BL | SND_CHN_T_MASK_BC | \
- SND_CHN_T_MASK_SL \
- }, \
- /* Rear Right */ \
- [3] = { \
- .type = SND_CHN_T_BR, \
- .members = \
- SND_CHN_T_MASK_BR | SND_CHN_T_MASK_BC | \
- SND_CHN_T_MASK_SR \
- }, \
- /* Center */ \
- [4] = { \
- .type = SND_CHN_T_FC, \
- .members = SND_CHN_T_MASK_FC \
- }, \
- /* LFE */ \
- [5] = { \
- .type = SND_CHN_T_LF, \
- .members = SND_CHN_T_MASK_LF \
- }, \
- [6] = { \
- .type = SND_CHN_T_MAX, \
- .members = 0 \
- } \
- }, \
- .mask = SND_CHN_T_MASK_FL | SND_CHN_T_MASK_FR | \
- SND_CHN_T_MASK_BL | SND_CHN_T_MASK_BR | \
- SND_CHN_T_MASK_FC | SND_CHN_T_MASK_LF, \
- .offset = { 0, 1, 4, 5, 2, 3, -1, -1, -1, \
- -1, -1, -1, -1, -1, -1, -1, -1, -1 } \
-}
-
-#define SND_CHN_MATRIX_MAP_6_0 { \
- .id = SND_CHN_MATRIX_6_0, \
- .channels = 6, \
- .ext = 0, \
- .map = { \
- /* Left */ \
- [0] = { \
- .type = SND_CHN_T_FL, \
- .members = \
- SND_CHN_T_MASK_FL | SND_CHN_T_MASK_LF | \
- SND_CHN_T_MASK_SL \
- }, \
- /* Right */ \
- [1] = { \
- .type = SND_CHN_T_FR, \
- .members = \
- SND_CHN_T_MASK_FR | SND_CHN_T_MASK_LF | \
- SND_CHN_T_MASK_SR \
- }, \
- /* Rear Left */ \
- [2] = { \
- .type = SND_CHN_T_BL, \
- .members = \
- SND_CHN_T_MASK_BL | SND_CHN_T_MASK_LF | \
- SND_CHN_T_MASK_SL \
- }, \
- /* Rear Right */ \
- [3] = { \
- .type = SND_CHN_T_BR, \
- .members = \
- SND_CHN_T_MASK_BR | SND_CHN_T_MASK_LF | \
- SND_CHN_T_MASK_SR \
- }, \
- /* Center */ \
- [4] = { \
- .type = SND_CHN_T_FC, \
- .members = SND_CHN_T_MASK_FC \
- }, \
- /* Rear Center */ \
- [5] = { \
- .type = SND_CHN_T_BC, \
- .members = SND_CHN_T_MASK_BC \
- }, \
- [6] = { \
- .type = SND_CHN_T_MAX, \
- .members = 0 \
- } \
- }, \
- .mask = SND_CHN_T_MASK_FL | SND_CHN_T_MASK_FR | \
- SND_CHN_T_MASK_BL | SND_CHN_T_MASK_BR | \
- SND_CHN_T_MASK_FC | SND_CHN_T_MASK_BC, \
- .offset = { 0, 1, 4, -1, 2, 3, -1, -1, 5, \
- -1, -1, -1, -1, -1, -1, -1, -1, -1 } \
-}
-
-#define SND_CHN_MATRIX_MAP_6_1 { \
- .id = SND_CHN_MATRIX_6_1, \
- .channels = 7, \
- .ext = 1, \
- .map = { \
- /* Left */ \
- [0] = { \
- .type = SND_CHN_T_FL, \
- .members = \
- SND_CHN_T_MASK_FL | SND_CHN_T_MASK_SL \
- }, \
- /* Right */ \
- [1] = { \
- .type = SND_CHN_T_FR, \
- .members = \
- SND_CHN_T_MASK_FR | SND_CHN_T_MASK_SR \
- }, \
- /* Rear Left */ \
- [2] = { \
- .type = SND_CHN_T_BL, \
- .members = \
- SND_CHN_T_MASK_BL | SND_CHN_T_MASK_SL \
- }, \
- /* Rear Right */ \
- [3] = { \
- .type = SND_CHN_T_BR, \
- .members = \
- SND_CHN_T_MASK_BR | SND_CHN_T_MASK_SR \
- }, \
- /* Center */ \
- [4] = { \
- .type = SND_CHN_T_FC, \
- .members = SND_CHN_T_MASK_FC \
- }, \
- /* LFE */ \
- [5] = { \
- .type = SND_CHN_T_LF, \
- .members = SND_CHN_T_MASK_LF \
- }, \
- /* Rear Center */ \
- [6] = { \
- .type = SND_CHN_T_BC, \
- .members = SND_CHN_T_MASK_BC \
- }, \
- [7] = { \
- .type = SND_CHN_T_MAX, \
- .members = 0 \
- } \
- }, \
- .mask = SND_CHN_T_MASK_FL | SND_CHN_T_MASK_FR | \
- SND_CHN_T_MASK_BL | SND_CHN_T_MASK_BR | \
- SND_CHN_T_MASK_FC | SND_CHN_T_MASK_LF | \
- SND_CHN_T_MASK_BC, \
- .offset = { 0, 1, 4, 5, 2, 3, -1, -1, 6, \
- -1, -1, -1, -1, -1, -1, -1, -1, -1 } \
-}
-
-#define SND_CHN_MATRIX_MAP_7_0 { \
- .id = SND_CHN_MATRIX_7_0, \
- .channels = 7, \
- .ext = 0, \
- .map = { \
- /* Left */ \
- [0] = { \
- .type = SND_CHN_T_FL, \
- .members = \
- SND_CHN_T_MASK_FL | SND_CHN_T_MASK_LF \
- }, \
- /* Right */ \
- [1] = { \
- .type = SND_CHN_T_FR, \
- .members = \
- SND_CHN_T_MASK_FR | SND_CHN_T_MASK_LF \
- }, \
- /* Rear Left */ \
- [2] = { \
- .type = SND_CHN_T_BL, \
- .members = \
- SND_CHN_T_MASK_BL | SND_CHN_T_MASK_BC | \
- SND_CHN_T_MASK_LF \
- }, \
- /* Rear Right */ \
- [3] = { \
- .type = SND_CHN_T_BR, \
- .members = \
- SND_CHN_T_MASK_BR | SND_CHN_T_MASK_BC | \
- SND_CHN_T_MASK_LF \
- }, \
- /* Center */ \
- [4] = { \
- .type = SND_CHN_T_FC, \
- .members = \
- SND_CHN_T_MASK_FC | SND_CHN_T_MASK_LF \
- }, \
- /* Side Left */ \
- [5] = { \
- .type = SND_CHN_T_SL, \
- .members = \
- SND_CHN_T_MASK_SL | SND_CHN_T_MASK_LF \
- }, \
- /* Side Right */ \
- [6] = { \
- .type = SND_CHN_T_SR, \
- .members = \
- SND_CHN_T_MASK_SR | SND_CHN_T_MASK_LF \
- }, \
- [7] = { \
- .type = SND_CHN_T_MAX, \
- .members = 0 \
- } \
- }, \
- .mask = SND_CHN_T_MASK_FL | SND_CHN_T_MASK_FR | \
- SND_CHN_T_MASK_BL | SND_CHN_T_MASK_BR | \
- SND_CHN_T_MASK_FC | \
- SND_CHN_T_MASK_SL | SND_CHN_T_MASK_SR, \
- .offset = { 0, 1, 4, -1, 2, 3, -1, -1, -1, \
- 5, 6, -1, -1, -1, -1, -1, -1, -1 } \
-}
-
-#define SND_CHN_MATRIX_MAP_7_1 { \
- .id = SND_CHN_MATRIX_7_1, \
- .channels = 8, \
- .ext = 1, \
- .map = { \
- /* Left */ \
- [0] = { \
- .type = SND_CHN_T_FL, \
- .members = SND_CHN_T_MASK_FL \
- }, \
- /* Right */ \
- [1] = { \
- .type = SND_CHN_T_FR, \
- .members = SND_CHN_T_MASK_FR \
- }, \
- /* Rear Left */ \
- [2] = { \
- .type = SND_CHN_T_BL, \
- .members = \
- SND_CHN_T_MASK_BL | SND_CHN_T_MASK_BC \
- }, \
- /* Rear Right */ \
- [3] = { \
- .type = SND_CHN_T_BR, \
- .members = \
- SND_CHN_T_MASK_BR | SND_CHN_T_MASK_BC \
- }, \
- /* Center */ \
- [4] = { \
- .type = SND_CHN_T_FC, \
- .members = SND_CHN_T_MASK_FC \
- }, \
- /* LFE */ \
- [5] = { \
- .type = SND_CHN_T_LF, \
- .members = SND_CHN_T_MASK_LF \
- }, \
- /* Side Left */ \
- [6] = { \
- .type = SND_CHN_T_SL, \
- .members = SND_CHN_T_MASK_SL \
- }, \
- /* Side Right */ \
- [7] = { \
- .type = SND_CHN_T_SR, \
- .members = SND_CHN_T_MASK_SR \
- }, \
- [8] = { \
- .type = SND_CHN_T_MAX, \
- .members = 0 \
- } \
- }, \
- .mask = SND_CHN_T_MASK_FL | SND_CHN_T_MASK_FR | \
- SND_CHN_T_MASK_BL | SND_CHN_T_MASK_BR | \
- SND_CHN_T_MASK_FC | SND_CHN_T_MASK_LF | \
- SND_CHN_T_MASK_SL | SND_CHN_T_MASK_SR, \
- .offset = { 0, 1, 4, 5, 2, 3, -1, -1, -1, \
- 6, 7, -1, -1, -1, -1, -1, -1, -1 } \
-}
-
-#endif /* !_SND_MATRIX_MAP_H_ */
diff --git a/sys/dev/sound/pcm/mixer.c b/sys/dev/sound/pcm/mixer.c
index 3e197b120c9d..092af3298f0e 100644
--- a/sys/dev/sound/pcm/mixer.c
+++ b/sys/dev/sound/pcm/mixer.c
@@ -105,11 +105,6 @@ static struct cdevsw mixer_cdevsw = {
.d_name = "mixer",
};
-/**
- * Keeps a count of mixer devices; used only by OSSv4 SNDCTL_SYSINFO ioctl.
- */
-int mixer_count = 0;
-
static eventhandler_tag mixer_ehtag = NULL;
static struct cdev *
@@ -151,7 +146,7 @@ mixer_set_softpcmvol(struct snd_mixer *m, struct snddev_info *d,
struct pcm_channel *c;
int dropmtx, acquiremtx;
- if (!PCM_REGISTERED(d) || PCM_DETACHING(d))
+ if (!PCM_REGISTERED(d))
return (EINVAL);
if (mtx_owned(m->lock))
@@ -204,7 +199,7 @@ mixer_set_eq(struct snd_mixer *m, struct snddev_info *d,
else
return (EINVAL);
- if (!PCM_REGISTERED(d) || PCM_DETACHING(d))
+ if (!PCM_REGISTERED(d))
return (EINVAL);
if (mtx_owned(m->lock))
@@ -229,7 +224,7 @@ mixer_set_eq(struct snd_mixer *m, struct snddev_info *d,
CHN_FOREACH(c, d, channels.pcm.busy) {
CHN_LOCK(c);
- f = chn_findfeeder(c, FEEDER_EQ);
+ f = feeder_find(c, FEEDER_EQ);
if (f != NULL)
(void)FEEDER_SET(f, tone, level);
CHN_UNLOCK(c);
@@ -650,7 +645,7 @@ mixer_obj_create(device_t dev, kobj_class_t cls, void *devinfo,
int type, const char *desc)
{
struct snd_mixer *m;
- int i;
+ size_t i;
KASSERT(dev != NULL && cls != NULL && devinfo != NULL,
("%s(): NULL data dev=%p cls=%p devinfo=%p",
@@ -671,7 +666,7 @@ mixer_obj_create(device_t dev, kobj_class_t cls, void *devinfo,
m->devinfo = devinfo;
m->busy = 0;
m->dev = dev;
- for (i = 0; i < (sizeof(m->parent) / sizeof(m->parent[0])); i++) {
+ for (i = 0; i < nitems(m->parent); i++) {
m->parent[i] = SOUND_MIXER_NONE;
m->child[i] = 0;
m->realdev[i] = i;
@@ -701,22 +696,13 @@ mixer_delete(struct snd_mixer *m)
snd_mtxfree(m->lock);
kobj_delete((kobj_t)m, M_MIXER);
- --mixer_count;
-
return (0);
}
struct snd_mixer *
mixer_create(device_t dev, kobj_class_t cls, void *devinfo, const char *desc)
{
- struct snd_mixer *m;
-
- m = mixer_obj_create(dev, cls, devinfo, MIXER_TYPE_SECONDARY, desc);
-
- if (m != NULL)
- ++mixer_count;
-
- return (m);
+ return (mixer_obj_create(dev, cls, devinfo, MIXER_TYPE_SECONDARY, desc));
}
int
@@ -769,8 +755,6 @@ mixer_init(device_t dev, kobj_class_t cls, void *devinfo)
pdev->si_drv1 = m;
snddev->mixer_dev = pdev;
- ++mixer_count;
-
if (bootverbose) {
for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
if (!(m->devs & (1 << i)))
@@ -839,8 +823,6 @@ mixer_uninit(device_t dev)
d->mixer_dev = NULL;
- --mixer_count;
-
return 0;
}
@@ -1071,7 +1053,7 @@ mixer_open(struct cdev *i_dev, int flags, int mode, struct thread *td)
m = i_dev->si_drv1;
d = device_get_softc(m->dev);
- if (!PCM_REGISTERED(d) || PCM_DETACHING(d))
+ if (!PCM_REGISTERED(d))
return (EBADF);
/* XXX Need Giant magic entry ??? */
@@ -1227,7 +1209,7 @@ mixer_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode,
return (EBADF);
d = device_get_softc(((struct snd_mixer *)i_dev->si_drv1)->dev);
- if (!PCM_REGISTERED(d) || PCM_DETACHING(d))
+ if (!PCM_REGISTERED(d))
return (EBADF);
PCM_GIANT_ENTER(d);
@@ -1282,9 +1264,13 @@ mixer_ioctl_cmd(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode,
case SNDCTL_CARDINFO:
return (sound_oss_card_info((oss_card_info *)arg));
case SNDCTL_AUDIOINFO:
+ return (dsp_oss_audioinfo(i_dev, (oss_audioinfo *)arg,
+ false));
case SNDCTL_AUDIOINFO_EX:
- case SNDCTL_ENGINEINFO:
- return (dsp_oss_audioinfo(i_dev, (oss_audioinfo *)arg));
+ return (dsp_oss_audioinfo(i_dev, (oss_audioinfo *)arg,
+ true));
+ case SNDCTL_ENGINEINFO:
+ return (dsp_oss_engineinfo(i_dev, (oss_audioinfo *)arg));
case SNDCTL_MIXERINFO:
return (mixer_oss_mixerinfo(i_dev, (oss_mixerinfo *)arg));
}
@@ -1379,7 +1365,7 @@ mixer_clone(void *arg,
bus_topo_lock();
d = devclass_get_softc(pcm_devclass, snd_unit);
/* See related comment in dsp_clone(). */
- if (d != NULL && PCM_REGISTERED(d) && d->mixer_dev != NULL) {
+ if (PCM_REGISTERED(d) && d->mixer_dev != NULL) {
*dev = d->mixer_dev;
dev_ref(*dev);
}
@@ -1407,6 +1393,17 @@ mixer_sysuninit(void *p)
SYSINIT(mixer_sysinit, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, mixer_sysinit, NULL);
SYSUNINIT(mixer_sysuninit, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, mixer_sysuninit, NULL);
+static void
+mixer_oss_mixerinfo_unavail(oss_mixerinfo *mi, int unit)
+{
+ bzero(mi, sizeof(*mi));
+ mi->dev = unit;
+ snprintf(mi->id, sizeof(mi->id), "mixer%d (n/a)", unit);
+ snprintf(mi->name, sizeof(mi->name), "pcm%d:mixer (unavailable)", unit);
+ mi->card_number = unit;
+ mi->legacy_device = unit;
+}
+
/**
* @brief Handler for SNDCTL_MIXERINFO
*
@@ -1431,7 +1428,7 @@ mixer_oss_mixerinfo(struct cdev *i_dev, oss_mixerinfo *mi)
{
struct snddev_info *d;
struct snd_mixer *m;
- int nmix, i;
+ int i;
/*
* If probing the device handling the ioctl, make sure it's a mixer
@@ -1442,17 +1439,23 @@ mixer_oss_mixerinfo(struct cdev *i_dev, oss_mixerinfo *mi)
d = NULL;
m = NULL;
- nmix = 0;
/*
* There's a 1:1 relationship between mixers and PCM devices, so
* begin by iterating over PCM devices and search for our mixer.
*/
+ bus_topo_lock();
for (i = 0; pcm_devclass != NULL &&
i < devclass_get_maxunit(pcm_devclass); i++) {
d = devclass_get_softc(pcm_devclass, i);
- if (!PCM_REGISTERED(d) || PCM_DETACHING(d))
- continue;
+ if (!PCM_REGISTERED(d)) {
+ if ((mi->dev == -1 && i == snd_unit) || mi->dev == i) {
+ mixer_oss_mixerinfo_unavail(mi, i);
+ bus_topo_unlock();
+ return (0);
+ } else
+ continue;
+ }
/* XXX Need Giant magic entry */
@@ -1460,92 +1463,100 @@ mixer_oss_mixerinfo(struct cdev *i_dev, oss_mixerinfo *mi)
PCM_UNLOCKASSERT(d);
PCM_LOCK(d);
- if (d->mixer_dev != NULL && d->mixer_dev->si_drv1 != NULL &&
- ((mi->dev == -1 && d->mixer_dev == i_dev) ||
- mi->dev == nmix)) {
- m = d->mixer_dev->si_drv1;
- mtx_lock(m->lock);
-
- /*
- * At this point, the following synchronization stuff
- * has happened:
- * - a specific PCM device is locked.
- * - a specific mixer device has been locked, so be
- * sure to unlock when existing.
- */
- bzero((void *)mi, sizeof(*mi));
- mi->dev = nmix;
- snprintf(mi->id, sizeof(mi->id), "mixer%d", i);
- strlcpy(mi->name, m->name, sizeof(mi->name));
- mi->modify_counter = m->modify_counter;
- mi->card_number = i;
- /*
- * Currently, FreeBSD assumes 1:1 relationship between
- * a pcm and mixer devices, so this is hardcoded to 0.
- */
- mi->port_number = 0;
-
- /**
- * @todo Fill in @sa oss_mixerinfo::mixerhandle.
- * @note From 4Front: "mixerhandle is an arbitrary
- * string that identifies the mixer better than
- * the device number (mixerinfo.dev). Device
- * numbers may change depending on the order the
- * drivers are loaded. However the handle should
- * remain the same provided that the sound card
- * is not moved to another PCI slot."
- */
+ if (!((d->mixer_dev == i_dev && mi->dev == -1) ||
+ mi->dev == i)) {
+ PCM_UNLOCK(d);
+ continue;
+ }
- /**
- * @note
- * @sa oss_mixerinfo::magic is a reserved field.
- *
- * @par
- * From 4Front: "magic is usually 0. However some
- * devices may have dedicated setup utilities and the
- * magic field may contain an unique driver specific
- * value (managed by [4Front])."
- */
+ if (d->mixer_dev->si_drv1 == NULL) {
+ mixer_oss_mixerinfo_unavail(mi, i);
+ PCM_UNLOCK(d);
+ bus_topo_unlock();
+ return (0);
+ }
- mi->enabled = device_is_attached(m->dev) ? 1 : 0;
- /**
- * The only flag for @sa oss_mixerinfo::caps is
- * currently MIXER_CAP_VIRTUAL, which I'm not sure we
- * really worry about.
- */
- /**
- * Mixer extensions currently aren't supported, so
- * leave @sa oss_mixerinfo::nrext blank for now.
- */
- /**
- * @todo Fill in @sa oss_mixerinfo::priority (requires
- * touching drivers?)
- * @note The priority field is for mixer applets to
- * determine which mixer should be the default, with 0
- * being least preferred and 10 being most preferred.
- * From 4Front: "OSS drivers like ICH use higher
- * values (10) because such chips are known to be used
- * only on motherboards. Drivers for high end pro
- * devices use 0 because they will never be the
- * default mixer. Other devices use values 1 to 9
- * depending on the estimated probability of being the
- * default device.
- *
- * XXX Described by Hannu@4Front, but not found in
- * soundcard.h.
- strlcpy(mi->devnode, devtoname(d->mixer_dev),
- sizeof(mi->devnode));
- mi->legacy_device = i;
- */
- mtx_unlock(m->lock);
- } else
- ++nmix;
+ m = d->mixer_dev->si_drv1;
+ mtx_lock(m->lock);
+
+ /*
+ * At this point, the following synchronization stuff
+ * has happened:
+ * - a specific PCM device is locked.
+ * - a specific mixer device has been locked, so be
+ * sure to unlock when existing.
+ */
+ bzero((void *)mi, sizeof(*mi));
+ mi->dev = i;
+ snprintf(mi->id, sizeof(mi->id), "mixer%d", i);
+ strlcpy(mi->name, m->name, sizeof(mi->name));
+ mi->modify_counter = m->modify_counter;
+ mi->card_number = i;
+ /*
+ * Currently, FreeBSD assumes 1:1 relationship between
+ * a pcm and mixer devices, so this is hardcoded to 0.
+ */
+ mi->port_number = 0;
+
+ /**
+ * @todo Fill in @sa oss_mixerinfo::mixerhandle.
+ * @note From 4Front: "mixerhandle is an arbitrary
+ * string that identifies the mixer better than
+ * the device number (mixerinfo.dev). Device
+ * numbers may change depending on the order the
+ * drivers are loaded. However the handle should
+ * remain the same provided that the sound card
+ * is not moved to another PCI slot."
+ */
+
+ /**
+ * @note
+ * @sa oss_mixerinfo::magic is a reserved field.
+ *
+ * @par
+ * From 4Front: "magic is usually 0. However some
+ * devices may have dedicated setup utilities and the
+ * magic field may contain an unique driver specific
+ * value (managed by [4Front])."
+ */
+
+ mi->enabled = device_is_attached(m->dev) ? 1 : 0;
+ /**
+ * The only flag for @sa oss_mixerinfo::caps is
+ * currently MIXER_CAP_VIRTUAL, which I'm not sure we
+ * really worry about.
+ */
+ /**
+ * Mixer extensions currently aren't supported, so
+ * leave @sa oss_mixerinfo::nrext blank for now.
+ */
+
+ /**
+ * @todo Fill in @sa oss_mixerinfo::priority (requires
+ * touching drivers?)
+ * @note The priority field is for mixer applets to
+ * determine which mixer should be the default, with 0
+ * being least preferred and 10 being most preferred.
+ * From 4Front: "OSS drivers like ICH use higher
+ * values (10) because such chips are known to be used
+ * only on motherboards. Drivers for high end pro
+ * devices use 0 because they will never be the
+ * default mixer. Other devices use values 1 to 9
+ * depending on the estimated probability of being the
+ * default device.
+ */
+
+ snprintf(mi->devnode, sizeof(mi->devnode), "/dev/mixer%d", i);
+ mi->legacy_device = i;
+
+ mtx_unlock(m->lock);
PCM_UNLOCK(d);
- if (m != NULL)
- return (0);
+ bus_topo_unlock();
+ return (0);
}
+ bus_topo_unlock();
return (EINVAL);
}
diff --git a/sys/dev/sound/pcm/mixer.h b/sys/dev/sound/pcm/mixer.h
index 6c5c8f3ec3fe..7139a766b392 100644
--- a/sys/dev/sound/pcm/mixer.h
+++ b/sys/dev/sound/pcm/mixer.h
@@ -69,8 +69,6 @@ u_int32_t mix_getchild(struct snd_mixer *m, u_int32_t dev);
void *mix_getdevinfo(struct snd_mixer *m);
struct mtx *mixer_get_lock(struct snd_mixer *m);
-extern int mixer_count;
-
#define MIXER_CMD_DIRECT 0 /* send command within driver */
#define MIXER_CMD_CDEV 1 /* send command from cdev/ioctl */
diff --git a/sys/dev/sound/pcm/pcm.h b/sys/dev/sound/pcm/pcm.h
index 3165822e3c85..7d0a8f0f431b 100644
--- a/sys/dev/sound/pcm/pcm.h
+++ b/sys/dev/sound/pcm/pcm.h
@@ -3,6 +3,10 @@
*
* Copyright (c) 2006-2009 Ariff Abdullah <ariff@FreeBSD.org>
* All rights reserved.
+ * Copyright (c) 2024-2025 The FreeBSD Foundation
+ *
+ * Portions of this software were developed by Christos Margiolis
+ * <christos@FreeBSD.org> under sponsorship from the FreeBSD Foundation.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -31,14 +35,11 @@
#include <sys/param.h>
-/*
- * Macros for reading/writing PCM sample / int values from bytes array.
- * Since every process is done using signed integer (and to make our life
- * less miserable), unsigned sample will be converted to its signed
- * counterpart and restored during writing back. To avoid overflow,
- * we truncate 32bit (and only 32bit) samples down to 24bit (see below
- * for the reason), unless SND_PCM_64 is defined.
- */
+#include <dev/sound/pcm/g711.h>
+
+#ifndef _KERNEL
+#include <assert.h> /* for __assert_unreachable() */
+#endif
/*
* Automatically turn on 64bit arithmetic on suitable archs
@@ -106,333 +107,344 @@ typedef uint64_t uintpcm64_t;
#define INTPCM24_T(v) ((intpcm24_t)(v))
#define INTPCM32_T(v) ((intpcm32_t)(v))
-#if BYTE_ORDER == LITTLE_ENDIAN
-#define _PCM_READ_S16_LE(b8) INTPCM_T(*((int16_t *)(b8)))
-#define _PCM_READ_S32_LE(b8) INTPCM_T(*((int32_t *)(b8)))
-#define _PCM_READ_S16_BE(b8) \
- INTPCM_T((b8)[1] | (((int8_t)((b8)[0])) << 8))
-#define _PCM_READ_S32_BE(b8) \
- INTPCM_T((b8)[3] | ((b8)[2] << 8) | ((b8)[1] << 16) | \
- (((int8_t)((b8)[0])) << 24))
-
-#define _PCM_WRITE_S16_LE(b8, val) do { \
- *((int16_t *)(b8)) = (val); \
-} while (0)
-#define _PCM_WRITE_S32_LE(b8, val) do { \
- *((int32_t *)(b8)) = (val); \
-} while (0)
-#define _PCM_WRITE_S16_BE(bb8, vval) do { \
- intpcm_t val = (vval); \
- uint8_t *b8 = (bb8); \
- b8[1] = val; \
- b8[0] = val >> 8; \
-} while (0)
-#define _PCM_WRITE_S32_BE(bb8, vval) do { \
- intpcm_t val = (vval); \
- uint8_t *b8 = (bb8); \
- b8[3] = val; \
- b8[2] = val >> 8; \
- b8[1] = val >> 16; \
- b8[0] = val >> 24; \
-} while (0)
-
-#define _PCM_READ_U16_LE(b8) \
- INTPCM_T((int16_t)(*((uint16_t *)(b8)) ^ 0x8000))
-#define _PCM_READ_U32_LE(b8) \
- INTPCM_T((int32_t)(*((uint32_t *)(b8)) ^ 0x80000000))
-#define _PCM_READ_U16_BE(b8) \
- INTPCM_T((b8)[1] | (((int8_t)((b8)[0] ^ 0x80)) << 8))
-#define _PCM_READ_U32_BE(b8) \
- INTPCM_T((b8)[3] | ((b8)[2] << 8) | ((b8)[1] << 16) | \
- (((int8_t)((b8)[0] ^ 0x80)) << 24))
-
-#define _PCM_WRITE_U16_LE(b8, val) do { \
- *((uint16_t *)(b8)) = (val) ^ 0x8000; \
-} while (0)
-#define _PCM_WRITE_U32_LE(b8, val) do { \
- *((uint32_t *)(b8)) = (val) ^ 0x80000000; \
-} while (0)
-#define _PCM_WRITE_U16_BE(bb8, vval) do { \
- intpcm_t val = (vval); \
- uint8_t *b8 = (bb8); \
- b8[1] = val; \
- b8[0] = (val >> 8) ^ 0x80; \
-} while (0)
-#define _PCM_WRITE_U32_BE(bb8, vval) do { \
- intpcm_t val = (vval); \
- uint8_t *b8 = (bb8); \
- b8[3] = val; \
- b8[2] = val >> 8; \
- b8[1] = val >> 16; \
- b8[0] = (val >> 24) ^ 0x80; \
-} while (0)
-
-#define _PCM_READ_S16_NE(b8) _PCM_READ_S16_LE(b8)
-#define _PCM_READ_U16_NE(b8) _PCM_READ_U16_LE(b8)
-#define _PCM_READ_S32_NE(b8) _PCM_READ_S32_LE(b8)
-#define _PCM_READ_U32_NE(b8) _PCM_READ_U32_LE(b8)
-#define _PCM_WRITE_S16_NE(b6) _PCM_WRITE_S16_LE(b8)
-#define _PCM_WRITE_U16_NE(b6) _PCM_WRITE_U16_LE(b8)
-#define _PCM_WRITE_S32_NE(b6) _PCM_WRITE_S32_LE(b8)
-#define _PCM_WRITE_U32_NE(b6) _PCM_WRITE_U32_LE(b8)
-#else /* !LITTLE_ENDIAN */
-#define _PCM_READ_S16_LE(b8) \
- INTPCM_T((b8)[0] | (((int8_t)((b8)[1])) << 8))
-#define _PCM_READ_S32_LE(b8) \
- INTPCM_T((b8)[0] | ((b8)[1] << 8) | ((b8)[2] << 16) | \
- (((int8_t)((b8)[3])) << 24))
-#define _PCM_READ_S16_BE(b8) INTPCM_T(*((int16_t *)(b8)))
-#define _PCM_READ_S32_BE(b8) INTPCM_T(*((int32_t *)(b8)))
-
-#define _PCM_WRITE_S16_LE(bb8, vval) do { \
- intpcm_t val = (vval); \
- uint8_t *b8 = (bb8); \
- b8[0] = val; \
- b8[1] = val >> 8; \
-} while (0)
-#define _PCM_WRITE_S32_LE(bb8, vval) do { \
- intpcm_t val = (vval); \
- uint8_t *b8 = (bb8); \
- b8[0] = val; \
- b8[1] = val >> 8; \
- b8[2] = val >> 16; \
- b8[3] = val >> 24; \
-} while (0)
-#define _PCM_WRITE_S16_BE(b8, val) do { \
- *((int16_t *)(b8)) = (val); \
-} while (0)
-#define _PCM_WRITE_S32_BE(b8, val) do { \
- *((int32_t *)(b8)) = (val); \
-} while (0)
-
-#define _PCM_READ_U16_LE(b8) \
- INTPCM_T((b8)[0] | (((int8_t)((b8)[1] ^ 0x80)) << 8))
-#define _PCM_READ_U32_LE(b8) \
- INTPCM_T((b8)[0] | ((b8)[1] << 8) | ((b8)[2] << 16) | \
- (((int8_t)((b8)[3] ^ 0x80)) << 24))
-#define _PCM_READ_U16_BE(b8) \
- INTPCM_T((int16_t)(*((uint16_t *)(b8)) ^ 0x8000))
-#define _PCM_READ_U32_BE(b8) \
- INTPCM_T((int32_t)(*((uint32_t *)(b8)) ^ 0x80000000))
-
-#define _PCM_WRITE_U16_LE(bb8, vval) do { \
- intpcm_t val = (vval); \
- uint8_t *b8 = (bb8); \
- b8[0] = val; \
- b8[1] = (val >> 8) ^ 0x80; \
-} while (0)
-#define _PCM_WRITE_U32_LE(bb8, vval) do { \
- intpcm_t val = (vval); \
- uint8_t *b8 = (bb8); \
- b8[0] = val; \
- b8[1] = val >> 8; \
- b8[2] = val >> 16; \
- b8[3] = (val >> 24) ^ 0x80; \
-} while (0)
-#define _PCM_WRITE_U16_BE(b8, val) do { \
- *((uint16_t *)(b8)) = (val) ^ 0x8000; \
-} while (0)
-#define _PCM_WRITE_U32_BE(b8, val) do { \
- *((uint32_t *)(b8)) = (val) ^ 0x80000000; \
-} while (0)
-
-#define _PCM_READ_S16_NE(b8) _PCM_READ_S16_BE(b8)
-#define _PCM_READ_U16_NE(b8) _PCM_READ_U16_BE(b8)
-#define _PCM_READ_S32_NE(b8) _PCM_READ_S32_BE(b8)
-#define _PCM_READ_U32_NE(b8) _PCM_READ_U32_BE(b8)
-#define _PCM_WRITE_S16_NE(b6) _PCM_WRITE_S16_BE(b8)
-#define _PCM_WRITE_U16_NE(b6) _PCM_WRITE_U16_BE(b8)
-#define _PCM_WRITE_S32_NE(b6) _PCM_WRITE_S32_BE(b8)
-#define _PCM_WRITE_U32_NE(b6) _PCM_WRITE_U32_BE(b8)
-#endif /* LITTLE_ENDIAN */
-
-#define _PCM_READ_S24_LE(b8) \
- INTPCM_T((b8)[0] | ((b8)[1] << 8) | (((int8_t)((b8)[2])) << 16))
-#define _PCM_READ_S24_BE(b8) \
- INTPCM_T((b8)[2] | ((b8)[1] << 8) | (((int8_t)((b8)[0])) << 16))
-
-#define _PCM_WRITE_S24_LE(bb8, vval) do { \
- intpcm_t val = (vval); \
- uint8_t *b8 = (bb8); \
- b8[0] = val; \
- b8[1] = val >> 8; \
- b8[2] = val >> 16; \
-} while (0)
-#define _PCM_WRITE_S24_BE(bb8, vval) do { \
- intpcm_t val = (vval); \
- uint8_t *b8 = (bb8); \
- b8[2] = val; \
- b8[1] = val >> 8; \
- b8[0] = val >> 16; \
-} while (0)
-
-#define _PCM_READ_U24_LE(b8) \
- INTPCM_T((b8)[0] | ((b8)[1] << 8) | \
- (((int8_t)((b8)[2] ^ 0x80)) << 16))
-#define _PCM_READ_U24_BE(b8) \
- INTPCM_T((b8)[2] | ((b8)[1] << 8) | \
- (((int8_t)((b8)[0] ^ 0x80)) << 16))
-
-#define _PCM_WRITE_U24_LE(bb8, vval) do { \
- intpcm_t val = (vval); \
- uint8_t *b8 = (bb8); \
- b8[0] = val; \
- b8[1] = val >> 8; \
- b8[2] = (val >> 16) ^ 0x80; \
-} while (0)
-#define _PCM_WRITE_U24_BE(bb8, vval) do { \
- intpcm_t val = (vval); \
- uint8_t *b8 = (bb8); \
- b8[2] = val; \
- b8[1] = val >> 8; \
- b8[0] = (val >> 16) ^ 0x80; \
-} while (0)
-
-#if BYTE_ORDER == LITTLE_ENDIAN
-#define _PCM_READ_S24_NE(b8) _PCM_READ_S24_LE(b8)
-#define _PCM_READ_U24_NE(b8) _PCM_READ_U24_LE(b8)
-#define _PCM_WRITE_S24_NE(b6) _PCM_WRITE_S24_LE(b8)
-#define _PCM_WRITE_U24_NE(b6) _PCM_WRITE_U24_LE(b8)
-#else /* !LITTLE_ENDIAN */
-#define _PCM_READ_S24_NE(b8) _PCM_READ_S24_BE(b8)
-#define _PCM_READ_U24_NE(b8) _PCM_READ_U24_BE(b8)
-#define _PCM_WRITE_S24_NE(b6) _PCM_WRITE_S24_BE(b8)
-#define _PCM_WRITE_U24_NE(b6) _PCM_WRITE_U24_BE(b8)
-#endif /* LITTLE_ENDIAN */
+static const struct {
+ const uint8_t ulaw_to_u8[G711_TABLE_SIZE];
+ const uint8_t alaw_to_u8[G711_TABLE_SIZE];
+ const uint8_t u8_to_ulaw[G711_TABLE_SIZE];
+ const uint8_t u8_to_alaw[G711_TABLE_SIZE];
+} xlaw_conv_tables = {
+ ULAW_TO_U8,
+ ALAW_TO_U8,
+ U8_TO_ULAW,
+ U8_TO_ALAW
+};
+
/*
- * 8bit sample is pretty much useless since it doesn't provide
- * sufficient dynamic range throughout our filtering process.
- * For the sake of completeness, declare it anyway.
+ * Functions for reading/writing PCM integer sample values from bytes array.
+ * Since every process is done using signed integer (and to make our life less
+ * miserable), unsigned sample will be converted to its signed counterpart and
+ * restored during writing back.
*/
-#define _PCM_READ_S8_NE(b8) INTPCM_T(*((int8_t *)(b8)))
-#define _PCM_READ_U8_NE(b8) \
- INTPCM_T((int8_t)(*((uint8_t *)(b8)) ^ 0x80))
+static __always_inline __unused intpcm_t
+pcm_sample_read(const uint8_t *src, uint32_t fmt)
+{
+ intpcm_t v, e, m;
+ bool s;
+
+ fmt = AFMT_ENCODING(fmt);
+
+ switch (fmt) {
+ case AFMT_AC3:
+ v = 0;
+ break;
+ case AFMT_MU_LAW:
+ v = _G711_TO_INTPCM(xlaw_conv_tables.ulaw_to_u8, *src);
+ break;
+ case AFMT_A_LAW:
+ v = _G711_TO_INTPCM(xlaw_conv_tables.alaw_to_u8, *src);
+ break;
+ case AFMT_S8:
+ v = INTPCM_T((int8_t)*src);
+ break;
+ case AFMT_U8:
+ v = INTPCM_T((int8_t)(*src ^ 0x80));
+ break;
+ case AFMT_S16_LE:
+ v = INTPCM_T(src[0] | (int8_t)src[1] << 8);
+ break;
+ case AFMT_S16_BE:
+ v = INTPCM_T(src[1] | (int8_t)src[0] << 8);
+ break;
+ case AFMT_U16_LE:
+ v = INTPCM_T(src[0] | (int8_t)(src[1] ^ 0x80) << 8);
+ break;
+ case AFMT_U16_BE:
+ v = INTPCM_T(src[1] | (int8_t)(src[0] ^ 0x80) << 8);
+ break;
+ case AFMT_S24_LE:
+ v = INTPCM_T(src[0] | src[1] << 8 | (int8_t)src[2] << 16);
+ break;
+ case AFMT_S24_BE:
+ v = INTPCM_T(src[2] | src[1] << 8 | (int8_t)src[0] << 16);
+ break;
+ case AFMT_U24_LE:
+ v = INTPCM_T(src[0] | src[1] << 8 |
+ (int8_t)(src[2] ^ 0x80) << 16);
+ break;
+ case AFMT_U24_BE:
+ v = INTPCM_T(src[2] | src[1] << 8 |
+ (int8_t)(src[0] ^ 0x80) << 16);
+ break;
+ case AFMT_S32_LE:
+ v = INTPCM_T(src[0] | src[1] << 8 | src[2] << 16 |
+ (int8_t)src[3] << 24);
+ break;
+ case AFMT_S32_BE:
+ v = INTPCM_T(src[3] | src[2] << 8 | src[1] << 16 |
+ (int8_t)src[0] << 24);
+ break;
+ case AFMT_U32_LE:
+ v = INTPCM_T(src[0] | src[1] << 8 | src[2] << 16 |
+ (int8_t)(src[3] ^ 0x80) << 24);
+ break;
+ case AFMT_U32_BE:
+ v = INTPCM_T(src[3] | src[2] << 8 | src[1] << 16 |
+ (int8_t)(src[0] ^ 0x80) << 24);
+ break;
+ case AFMT_F32_LE: /* FALLTHROUGH */
+ case AFMT_F32_BE:
+ if (fmt == AFMT_F32_LE) {
+ v = INTPCM_T(src[0] | src[1] << 8 | src[2] << 16 |
+ (int8_t)src[3] << 24);
+ } else {
+ v = INTPCM_T(src[3] | src[2] << 8 | src[1] << 16 |
+ (int8_t)src[0] << 24);
+ }
+ e = (v >> 23) & 0xff;
+ /* NaN, +/- Inf or too small */
+ if (e == 0xff || e < 96) {
+ v = INTPCM_T(0);
+ break;
+ }
+ s = v & 0x80000000U;
+ if (e > 126) {
+ v = INTPCM_T((s == 0) ? PCM_S32_MAX : PCM_S32_MIN);
+ break;
+ }
+ m = 0x800000 | (v & 0x7fffff);
+ e += 8 - 127;
+ if (e < 0)
+ m >>= -e;
+ else
+ m <<= e;
+ v = INTPCM_T((s == 0) ? m : -m);
+ break;
+ default:
+ v = 0;
+ printf("%s(): unknown format: 0x%08x\n", __func__, fmt);
+ __assert_unreachable();
+ }
+
+ return (v);
+}
-#define _PCM_WRITE_S8_NE(b8, val) do { \
- *((int8_t *)(b8)) = (val); \
-} while (0)
-#define _PCM_WRITE_U8_NE(b8, val) do { \
- *((uint8_t *)(b8)) = (val) ^ 0x80; \
-} while (0)
+/*
+ * Read sample and normalize to 32-bit magnitude.
+ */
+static __always_inline __unused intpcm_t
+pcm_sample_read_norm(const uint8_t *src, uint32_t fmt)
+{
+ return (pcm_sample_read(src, fmt) << (32 - AFMT_BIT(fmt)));
+}
/*
- * Common macross. Use this instead of "_", unless we want
- * the real sample value.
+ * Read sample and restrict magnitude to 24 bits.
*/
+static __always_inline __unused intpcm_t
+pcm_sample_read_calc(const uint8_t *src, uint32_t fmt)
+{
+ intpcm_t v;
+
+ v = pcm_sample_read(src, fmt);
+
+#ifndef SND_PCM_64
+ /*
+ * Dynamic range for humans: ~140db.
+ *
+ * 16bit = 96db (close enough)
+ * 24bit = 144db (perfect)
+ * 32bit = 196db (way too much)
+ *
+ * 24bit is pretty much sufficient for our signed integer processing.
+ * Also, to avoid overflow, we truncate 32bit (and only 32bit) samples
+ * down to 24bit (see below for the reason), unless SND_PCM_64 is
+ * defined.
+ */
+ if (fmt & AFMT_32BIT)
+ v >>= PCM_FXSHIFT;
+#endif
+
+ return (v);
+}
+
+static __always_inline __unused void
+pcm_sample_write(uint8_t *dst, intpcm_t v, uint32_t fmt)
+{
+ intpcm_t r, e;
+
+ fmt = AFMT_ENCODING(fmt);
+
+ if (fmt & (AFMT_F32_LE | AFMT_F32_BE)) {
+ if (v == 0)
+ r = 0;
+ else if (v == PCM_S32_MAX)
+ r = 0x3f800000;
+ else if (v == PCM_S32_MIN)
+ r = 0x80000000U | 0x3f800000;
+ else {
+ r = 0;
+ if (v < 0) {
+ r |= 0x80000000U;
+ v = -v;
+ }
+ e = 127 - 8;
+ while ((v & 0x7f000000) != 0) {
+ v >>= 1;
+ e++;
+ }
+ while ((v & 0x7f800000) == 0) {
+ v <<= 1;
+ e--;
+ }
+ r |= (e & 0xff) << 23;
+ r |= v & 0x7fffff;
+ }
+ v = r;
+ }
+
+ switch (fmt) {
+ case AFMT_AC3:
+ *(int16_t *)dst = 0;
+ break;
+ case AFMT_MU_LAW:
+ *dst = _INTPCM_TO_G711(xlaw_conv_tables.u8_to_ulaw, v);
+ break;
+ case AFMT_A_LAW:
+ *dst = _INTPCM_TO_G711(xlaw_conv_tables.u8_to_alaw, v);
+ break;
+ case AFMT_S8:
+ *(int8_t *)dst = v;
+ break;
+ case AFMT_U8:
+ *(int8_t *)dst = v ^ 0x80;
+ break;
+ case AFMT_S16_LE:
+ dst[0] = v;
+ dst[1] = v >> 8;
+ break;
+ case AFMT_S16_BE:
+ dst[1] = v;
+ dst[0] = v >> 8;
+ break;
+ case AFMT_U16_LE:
+ dst[0] = v;
+ dst[1] = (v >> 8) ^ 0x80;
+ break;
+ case AFMT_U16_BE:
+ dst[1] = v;
+ dst[0] = (v >> 8) ^ 0x80;
+ break;
+ case AFMT_S24_LE:
+ dst[0] = v;
+ dst[1] = v >> 8;
+ dst[2] = v >> 16;
+ break;
+ case AFMT_S24_BE:
+ dst[2] = v;
+ dst[1] = v >> 8;
+ dst[0] = v >> 16;
+ break;
+ case AFMT_U24_LE:
+ dst[0] = v;
+ dst[1] = v >> 8;
+ dst[2] = (v >> 16) ^ 0x80;
+ break;
+ case AFMT_U24_BE:
+ dst[2] = v;
+ dst[1] = v >> 8;
+ dst[0] = (v >> 16) ^ 0x80;
+ break;
+ case AFMT_S32_LE: /* FALLTHROUGH */
+ case AFMT_F32_LE:
+ dst[0] = v;
+ dst[1] = v >> 8;
+ dst[2] = v >> 16;
+ dst[3] = v >> 24;
+ break;
+ case AFMT_S32_BE: /* FALLTHROUGH */
+ case AFMT_F32_BE:
+ dst[3] = v;
+ dst[2] = v >> 8;
+ dst[1] = v >> 16;
+ dst[0] = v >> 24;
+ break;
+ case AFMT_U32_LE:
+ dst[0] = v;
+ dst[1] = v >> 8;
+ dst[2] = v >> 16;
+ dst[3] = (v >> 24) ^ 0x80;
+ break;
+ case AFMT_U32_BE:
+ dst[3] = v;
+ dst[2] = v >> 8;
+ dst[1] = v >> 16;
+ dst[0] = (v >> 24) ^ 0x80;
+ break;
+ default:
+ printf("%s(): unknown format: 0x%08x\n", __func__, fmt);
+ __assert_unreachable();
+ }
+}
-/* 8bit */
-#define PCM_READ_S8_NE(b8) _PCM_READ_S8_NE(b8)
-#define PCM_READ_U8_NE(b8) _PCM_READ_U8_NE(b8)
-#define PCM_WRITE_S8_NE(b8, val) _PCM_WRITE_S8_NE(b8, val)
-#define PCM_WRITE_U8_NE(b8, val) _PCM_WRITE_U8_NE(b8, val)
-
-/* 16bit */
-#define PCM_READ_S16_LE(b8) _PCM_READ_S16_LE(b8)
-#define PCM_READ_S16_BE(b8) _PCM_READ_S16_BE(b8)
-#define PCM_READ_U16_LE(b8) _PCM_READ_U16_LE(b8)
-#define PCM_READ_U16_BE(b8) _PCM_READ_U16_BE(b8)
-
-#define PCM_WRITE_S16_LE(b8, val) _PCM_WRITE_S16_LE(b8, val)
-#define PCM_WRITE_S16_BE(b8, val) _PCM_WRITE_S16_BE(b8, val)
-#define PCM_WRITE_U16_LE(b8, val) _PCM_WRITE_U16_LE(b8, val)
-#define PCM_WRITE_U16_BE(b8, val) _PCM_WRITE_U16_BE(b8, val)
-
-#define PCM_READ_S16_NE(b8) _PCM_READ_S16_NE(b8)
-#define PCM_READ_U16_NE(b8) _PCM_READ_U16_NE(b8)
-#define PCM_WRITE_S16_NE(b8) _PCM_WRITE_S16_NE(b8)
-#define PCM_WRITE_U16_NE(b8) _PCM_WRITE_U16_NE(b8)
-
-/* 24bit */
-#define PCM_READ_S24_LE(b8) _PCM_READ_S24_LE(b8)
-#define PCM_READ_S24_BE(b8) _PCM_READ_S24_BE(b8)
-#define PCM_READ_U24_LE(b8) _PCM_READ_U24_LE(b8)
-#define PCM_READ_U24_BE(b8) _PCM_READ_U24_BE(b8)
-
-#define PCM_WRITE_S24_LE(b8, val) _PCM_WRITE_S24_LE(b8, val)
-#define PCM_WRITE_S24_BE(b8, val) _PCM_WRITE_S24_BE(b8, val)
-#define PCM_WRITE_U24_LE(b8, val) _PCM_WRITE_U24_LE(b8, val)
-#define PCM_WRITE_U24_BE(b8, val) _PCM_WRITE_U24_BE(b8, val)
-
-#define PCM_READ_S24_NE(b8) _PCM_READ_S24_NE(b8)
-#define PCM_READ_U24_NE(b8) _PCM_READ_U24_NE(b8)
-#define PCM_WRITE_S24_NE(b8) _PCM_WRITE_S24_NE(b8)
-#define PCM_WRITE_U24_NE(b8) _PCM_WRITE_U24_NE(b8)
-
-/* 32bit */
-#ifdef SND_PCM_64
-#define PCM_READ_S32_LE(b8) _PCM_READ_S32_LE(b8)
-#define PCM_READ_S32_BE(b8) _PCM_READ_S32_BE(b8)
-#define PCM_READ_U32_LE(b8) _PCM_READ_U32_LE(b8)
-#define PCM_READ_U32_BE(b8) _PCM_READ_U32_BE(b8)
-
-#define PCM_WRITE_S32_LE(b8, val) _PCM_WRITE_S32_LE(b8, val)
-#define PCM_WRITE_S32_BE(b8, val) _PCM_WRITE_S32_BE(b8, val)
-#define PCM_WRITE_U32_LE(b8, val) _PCM_WRITE_U32_LE(b8, val)
-#define PCM_WRITE_U32_BE(b8, val) _PCM_WRITE_U32_BE(b8, val)
-
-#define PCM_READ_S32_NE(b8) _PCM_READ_S32_NE(b8)
-#define PCM_READ_U32_NE(b8) _PCM_READ_U32_NE(b8)
-#define PCM_WRITE_S32_NE(b8) _PCM_WRITE_S32_NE(b8)
-#define PCM_WRITE_U32_NE(b8) _PCM_WRITE_U32_NE(b8)
-#else /* !SND_PCM_64 */
/*
- * 24bit integer ?!? This is quite unfortunate, eh? Get the fact straight:
- * Dynamic range for:
- * 1) Human =~ 140db
- * 2) 16bit = 96db (close enough)
- * 3) 24bit = 144db (perfect)
- * 4) 32bit = 196db (way too much)
- * 5) Bugs Bunny = Gazillion!@%$Erbzzztt-EINVAL db
- * Since we're not Bugs Bunny ..uh..err.. avoiding 64bit arithmetic, 24bit
- * is pretty much sufficient for our signed integer processing.
+ * Write sample and normalize to original magnitude.
*/
-#define PCM_READ_S32_LE(b8) (_PCM_READ_S32_LE(b8) >> PCM_FXSHIFT)
-#define PCM_READ_S32_BE(b8) (_PCM_READ_S32_BE(b8) >> PCM_FXSHIFT)
-#define PCM_READ_U32_LE(b8) (_PCM_READ_U32_LE(b8) >> PCM_FXSHIFT)
-#define PCM_READ_U32_BE(b8) (_PCM_READ_U32_BE(b8) >> PCM_FXSHIFT)
-
-#define PCM_READ_S32_NE(b8) (_PCM_READ_S32_NE(b8) >> PCM_FXSHIFT)
-#define PCM_READ_U32_NE(b8) (_PCM_READ_U32_NE(b8) >> PCM_FXSHIFT)
-
-#define PCM_WRITE_S32_LE(b8, val) \
- _PCM_WRITE_S32_LE(b8, (val) << PCM_FXSHIFT)
-#define PCM_WRITE_S32_BE(b8, val) \
- _PCM_WRITE_S32_BE(b8, (val) << PCM_FXSHIFT)
-#define PCM_WRITE_U32_LE(b8, val) \
- _PCM_WRITE_U32_LE(b8, (val) << PCM_FXSHIFT)
-#define PCM_WRITE_U32_BE(b8, val) \
- _PCM_WRITE_U32_BE(b8, (val) << PCM_FXSHIFT)
-
-#define PCM_WRITE_S32_NE(b8, val) \
- _PCM_WRITE_S32_NE(b8, (val) << PCM_FXSHIFT)
-#define PCM_WRITE_U32_NE(b8, val) \
- _PCM_WRITE_U32_NE(b8, (val) << PCM_FXSHIFT)
-#endif /* SND_PCM_64 */
-
-#define PCM_CLAMP_S8(val) \
- (((val) > PCM_S8_MAX) ? PCM_S8_MAX : \
- (((val) < PCM_S8_MIN) ? PCM_S8_MIN : (val)))
-#define PCM_CLAMP_S16(val) \
- (((val) > PCM_S16_MAX) ? PCM_S16_MAX : \
- (((val) < PCM_S16_MIN) ? PCM_S16_MIN : (val)))
-#define PCM_CLAMP_S24(val) \
- (((val) > PCM_S24_MAX) ? PCM_S24_MAX : \
- (((val) < PCM_S24_MIN) ? PCM_S24_MIN : (val)))
+static __always_inline __unused void
+pcm_sample_write_norm(uint8_t *dst, intpcm_t v, uint32_t fmt)
+{
+ pcm_sample_write(dst, v >> (32 - AFMT_BIT(fmt)), fmt);
+}
-#ifdef SND_PCM_64
-#define PCM_CLAMP_S32(val) \
- (((val) > PCM_S32_MAX) ? PCM_S32_MAX : \
- (((val) < PCM_S32_MIN) ? PCM_S32_MIN : (val)))
-#else /* !SND_PCM_64 */
-#define PCM_CLAMP_S32(val) \
- (((val) > PCM_S24_MAX) ? PCM_S32_MAX : \
- (((val) < PCM_S24_MIN) ? PCM_S32_MIN : \
- ((val) << PCM_FXSHIFT)))
-#endif /* SND_PCM_64 */
-
-#define PCM_CLAMP_U8(val) PCM_CLAMP_S8(val)
-#define PCM_CLAMP_U16(val) PCM_CLAMP_S16(val)
-#define PCM_CLAMP_U24(val) PCM_CLAMP_S24(val)
-#define PCM_CLAMP_U32(val) PCM_CLAMP_S32(val)
+/*
+ * To be used with pcm_sample_read_calc().
+ */
+static __always_inline __unused void
+pcm_sample_write_calc(uint8_t *dst, intpcm_t v, uint32_t fmt)
+{
+#ifndef SND_PCM_64
+ /* Shift back to 32-bit magnitude. */
+ if (fmt & AFMT_32BIT)
+ v <<= PCM_FXSHIFT;
+#endif
+ pcm_sample_write(dst, v, fmt);
+}
+
+static __always_inline __unused intpcm_t
+pcm_clamp(intpcm32_t sample, uint32_t fmt)
+{
+ fmt = AFMT_ENCODING(fmt);
+
+ switch (AFMT_BIT(fmt)) {
+ case 8:
+ return ((sample > PCM_S8_MAX) ? PCM_S8_MAX :
+ ((sample < PCM_S8_MIN) ? PCM_S8_MIN : sample));
+ case 16:
+ return ((sample > PCM_S16_MAX) ? PCM_S16_MAX :
+ ((sample < PCM_S16_MIN) ? PCM_S16_MIN : sample));
+ case 24:
+ return ((sample > PCM_S24_MAX) ? PCM_S24_MAX :
+ ((sample < PCM_S24_MIN) ? PCM_S24_MIN : sample));
+ case 32:
+ return ((sample > PCM_S32_MAX) ? PCM_S32_MAX :
+ ((sample < PCM_S32_MIN) ? PCM_S32_MIN : sample));
+ default:
+ printf("%s(): unknown format: 0x%08x\n", __func__, fmt);
+ __assert_unreachable();
+ }
+}
+
+static __always_inline __unused intpcm_t
+pcm_clamp_calc(intpcm32_t sample, uint32_t fmt)
+{
+#ifndef SND_PCM_64
+ if (fmt & AFMT_32BIT) {
+ return ((sample > PCM_S24_MAX) ? PCM_S32_MAX :
+ ((sample < PCM_S24_MIN) ? PCM_S32_MIN :
+ sample << PCM_FXSHIFT));
+ }
+#endif
+
+ return (pcm_clamp(sample, fmt));
+}
#endif /* !_SND_PCM_H_ */
diff --git a/sys/dev/sound/pcm/sndstat.c b/sys/dev/sound/pcm/sndstat.c
index ef006a580d40..509a35c5a038 100644
--- a/sys/dev/sound/pcm/sndstat.c
+++ b/sys/dev/sound/pcm/sndstat.c
@@ -5,7 +5,7 @@
* Copyright (c) 2001 Cameron Grant <cg@FreeBSD.org>
* Copyright (c) 2020 The FreeBSD Foundation
* All rights reserved.
- * Copyright (c) 2024 The FreeBSD Foundation
+ * Copyright (c) 2024-2025 The FreeBSD Foundation
*
* Portions of this software were developed by Christos Margiolis
* <christos@FreeBSD.org> under sponsorship from the FreeBSD Foundation.
@@ -45,13 +45,8 @@
#include <sys/nv.h>
#include <sys/dnv.h>
#include <sys/sx.h>
-#ifdef COMPAT_FREEBSD32
-#include <sys/sysent.h>
-#endif
#include <dev/sound/pcm/sound.h>
-#include <dev/sound/pcm/pcm.h>
-#include <dev/sound/version.h>
#include "feeder_if.h"
@@ -154,10 +149,7 @@ sndstat_open(struct cdev *i_dev, int flags, int mode, struct thread *td)
pf = malloc(sizeof(*pf), M_DEVBUF, M_WAITOK | M_ZERO);
- if (sbuf_new(&pf->sbuf, NULL, 4096, SBUF_AUTOEXTEND) == NULL) {
- free(pf, M_DEVBUF);
- return (ENOMEM);
- }
+ sbuf_new(&pf->sbuf, NULL, 4096, SBUF_AUTOEXTEND);
pf->fflags = flags;
TAILQ_INIT(&pf->userdev_list);
@@ -327,47 +319,36 @@ sndstat_write(struct cdev *i_dev, struct uio *buf, int flag)
}
static void
-sndstat_get_caps(struct snddev_info *d, bool play, uint32_t *min_rate,
+sndstat_get_caps(struct snddev_info *d, int dir, uint32_t *min_rate,
uint32_t *max_rate, uint32_t *fmts, uint32_t *minchn, uint32_t *maxchn)
{
struct pcm_channel *c;
- unsigned int encoding;
- int dir;
-
- dir = play ? PCMDIR_PLAY : PCMDIR_REC;
-
- if (play && d->pvchancount > 0) {
- *min_rate = *max_rate = d->pvchanrate;
- *fmts = AFMT_ENCODING(d->pvchanformat);
- *minchn = *maxchn = AFMT_CHANNEL(d->pvchanformat);
- return;
- } else if (!play && d->rvchancount > 0) {
- *min_rate = *max_rate = d->rvchanrate;
- *fmts = AFMT_ENCODING(d->rvchanformat);
- *minchn = *maxchn = AFMT_CHANNEL(d->rvchanformat);
- return;
- }
+ struct pcmchan_caps *caps;
+ int i;
+ *fmts = 0;
*min_rate = UINT32_MAX;
*max_rate = 0;
*minchn = UINT32_MAX;
*maxchn = 0;
- encoding = 0;
- CHN_FOREACH(c, d, channels.pcm) {
- struct pcmchan_caps *caps;
- int i;
- if (c->direction != dir || (c->flags & CHN_F_VIRTUAL) != 0)
+ CHN_FOREACH(c, d, channels.pcm) {
+ if (c->direction != dir)
continue;
-
CHN_LOCK(c);
caps = chn_getcaps(c);
- *min_rate = min(caps->minspeed, *min_rate);
- *max_rate = max(caps->maxspeed, *max_rate);
for (i = 0; caps->fmtlist[i]; i++) {
- encoding |= AFMT_ENCODING(caps->fmtlist[i]);
- *minchn = min(AFMT_CHANNEL(encoding), *minchn);
- *maxchn = max(AFMT_CHANNEL(encoding), *maxchn);
+ *fmts |= AFMT_ENCODING(caps->fmtlist[i]);
+ *minchn = min(AFMT_CHANNEL(caps->fmtlist[i]), *minchn);
+ *maxchn = max(AFMT_CHANNEL(caps->fmtlist[i]), *maxchn);
+ }
+ if ((c->flags & CHN_F_EXCLUSIVE) ||
+ (pcm_getflags(d->dev) & SD_F_BITPERFECT)) {
+ *min_rate = min(*min_rate, caps->minspeed);
+ *max_rate = max(*max_rate, caps->maxspeed);
+ } else {
+ *min_rate = min(*min_rate, feeder_rate_min);
+ *max_rate = max(*max_rate, feeder_rate_max);
}
CHN_UNLOCK(c);
}
@@ -397,9 +378,13 @@ sndstat_create_diinfo_nv(uint32_t min_rate, uint32_t max_rate, uint32_t formats,
static int
sndstat_build_sound4_nvlist(struct snddev_info *d, nvlist_t **dip)
{
- uint32_t maxrate, minrate, fmts, minchn, maxchn;
- nvlist_t *di = NULL, *sound4di = NULL, *diinfo = NULL;
- int err;
+ struct pcm_channel *c;
+ struct pcm_feeder *f;
+ struct sbuf sb;
+ uint32_t maxrate, minrate, fmts, minchn, maxchn, caps;
+ nvlist_t *di = NULL, *sound4di = NULL, *diinfo = NULL, *cdi = NULL;
+ int err, nchan;
+ char buf[AFMTSTR_LEN];
di = nvlist_create(0);
if (di == NULL) {
@@ -424,8 +409,8 @@ sndstat_build_sound4_nvlist(struct snddev_info *d, nvlist_t **dip)
nvlist_add_number(di, SNDST_DSPS_PCHAN, d->playcount);
nvlist_add_number(di, SNDST_DSPS_RCHAN, d->reccount);
if (d->playcount > 0) {
- sndstat_get_caps(d, true, &minrate, &maxrate, &fmts, &minchn,
- &maxchn);
+ sndstat_get_caps(d, PCMDIR_PLAY, &minrate, &maxrate, &fmts,
+ &minchn, &maxchn);
nvlist_add_number(di, "pminrate", minrate);
nvlist_add_number(di, "pmaxrate", maxrate);
nvlist_add_number(di, "pfmts", fmts);
@@ -437,8 +422,8 @@ sndstat_build_sound4_nvlist(struct snddev_info *d, nvlist_t **dip)
nvlist_move_nvlist(di, SNDST_DSPS_INFO_PLAY, diinfo);
}
if (d->reccount > 0) {
- sndstat_get_caps(d, false, &minrate, &maxrate, &fmts, &minchn,
- &maxchn);
+ sndstat_get_caps(d, PCMDIR_REC, &minrate, &maxrate, &fmts,
+ &minchn, &maxchn);
nvlist_add_number(di, "rminrate", minrate);
nvlist_add_number(di, "rmaxrate", maxrate);
nvlist_add_number(di, "rfmts", fmts);
@@ -452,12 +437,153 @@ sndstat_build_sound4_nvlist(struct snddev_info *d, nvlist_t **dip)
nvlist_add_number(sound4di, SNDST_DSPS_SOUND4_UNIT,
device_get_unit(d->dev)); // XXX: I want signed integer here
+ nvlist_add_string(sound4di, SNDST_DSPS_SOUND4_STATUS, d->status);
nvlist_add_bool(
sound4di, SNDST_DSPS_SOUND4_BITPERFECT, d->flags & SD_F_BITPERFECT);
- nvlist_add_number(sound4di, SNDST_DSPS_SOUND4_PVCHAN, d->pvchancount);
- nvlist_add_number(sound4di, SNDST_DSPS_SOUND4_RVCHAN, d->rvchancount);
+ nvlist_add_bool(sound4di, SNDST_DSPS_SOUND4_PVCHAN,
+ d->flags & SD_F_PVCHANS);
+ nvlist_add_number(sound4di, SNDST_DSPS_SOUND4_PVCHANRATE,
+ d->pvchanrate);
+ nvlist_add_number(sound4di, SNDST_DSPS_SOUND4_PVCHANFORMAT,
+ d->pvchanformat);
+ nvlist_add_bool(sound4di, SNDST_DSPS_SOUND4_RVCHAN,
+ d->flags & SD_F_RVCHANS);
+ nvlist_add_number(sound4di, SNDST_DSPS_SOUND4_RVCHANRATE,
+ d->rvchanrate);
+ nvlist_add_number(sound4di, SNDST_DSPS_SOUND4_RVCHANFORMAT,
+ d->rvchanformat);
+
+ nchan = 0;
+ CHN_FOREACH(c, d, channels.pcm) {
+ sbuf_new(&sb, NULL, 4096, SBUF_AUTOEXTEND);
+ cdi = nvlist_create(0);
+ if (cdi == NULL) {
+ sbuf_delete(&sb);
+ PCM_RELEASE_QUICK(d);
+ err = ENOMEM;
+ goto done;
+ }
+
+ CHN_LOCK(c);
+
+ caps = PCM_CAP_REALTIME | PCM_CAP_MMAP | PCM_CAP_TRIGGER |
+ ((c->flags & CHN_F_VIRTUAL) ? PCM_CAP_VIRTUAL : 0) |
+ ((c->direction == PCMDIR_PLAY) ? PCM_CAP_OUTPUT : PCM_CAP_INPUT);
+
+ nvlist_add_string(cdi, SNDST_DSPS_SOUND4_CHAN_NAME, c->name);
+ nvlist_add_string(cdi, SNDST_DSPS_SOUND4_CHAN_PARENTCHAN,
+ c->parentchannel != NULL ? c->parentchannel->name : "");
+ nvlist_add_number(cdi, SNDST_DSPS_SOUND4_CHAN_UNIT, nchan++);
+ nvlist_add_number(cdi, SNDST_DSPS_SOUND4_CHAN_CAPS, caps);
+ nvlist_add_number(cdi, SNDST_DSPS_SOUND4_CHAN_LATENCY,
+ c->latency);
+ nvlist_add_number(cdi, SNDST_DSPS_SOUND4_CHAN_RATE, c->speed);
+ nvlist_add_number(cdi, SNDST_DSPS_SOUND4_CHAN_FORMAT,
+ c->format);
+ nvlist_add_number(cdi, SNDST_DSPS_SOUND4_CHAN_PID, c->pid);
+ nvlist_add_string(cdi, SNDST_DSPS_SOUND4_CHAN_COMM, c->comm);
+ nvlist_add_number(cdi, SNDST_DSPS_SOUND4_CHAN_INTR,
+ c->interrupts);
+ nvlist_add_number(cdi, SNDST_DSPS_SOUND4_CHAN_FEEDCNT,
+ c->feedcount);
+ nvlist_add_number(cdi, SNDST_DSPS_SOUND4_CHAN_XRUNS, c->xruns);
+ nvlist_add_number(cdi, SNDST_DSPS_SOUND4_CHAN_LEFTVOL,
+ CHN_GETVOLUME(c, SND_VOL_C_PCM, SND_CHN_T_FL));
+ nvlist_add_number(cdi, SNDST_DSPS_SOUND4_CHAN_RIGHTVOL,
+ CHN_GETVOLUME(c, SND_VOL_C_PCM, SND_CHN_T_FR));
+ nvlist_add_number(cdi, SNDST_DSPS_SOUND4_CHAN_HWBUF_FORMAT,
+ sndbuf_getfmt(c->bufhard));
+ nvlist_add_number(cdi, SNDST_DSPS_SOUND4_CHAN_HWBUF_RATE,
+ sndbuf_getspd(c->bufhard));
+ nvlist_add_number(cdi, SNDST_DSPS_SOUND4_CHAN_HWBUF_SIZE,
+ sndbuf_getsize(c->bufhard));
+ nvlist_add_number(cdi, SNDST_DSPS_SOUND4_CHAN_HWBUF_BLKSZ,
+ sndbuf_getblksz(c->bufhard));
+ nvlist_add_number(cdi, SNDST_DSPS_SOUND4_CHAN_HWBUF_BLKCNT,
+ sndbuf_getblkcnt(c->bufhard));
+ nvlist_add_number(cdi, SNDST_DSPS_SOUND4_CHAN_HWBUF_FREE,
+ sndbuf_getfree(c->bufhard));
+ nvlist_add_number(cdi, SNDST_DSPS_SOUND4_CHAN_HWBUF_READY,
+ sndbuf_getready(c->bufhard));
+ nvlist_add_number(cdi, SNDST_DSPS_SOUND4_CHAN_SWBUF_FORMAT,
+ sndbuf_getfmt(c->bufsoft));
+ nvlist_add_number(cdi, SNDST_DSPS_SOUND4_CHAN_SWBUF_RATE,
+ sndbuf_getspd(c->bufsoft));
+ nvlist_add_number(cdi, SNDST_DSPS_SOUND4_CHAN_SWBUF_SIZE,
+ sndbuf_getsize(c->bufsoft));
+ nvlist_add_number(cdi, SNDST_DSPS_SOUND4_CHAN_SWBUF_BLKSZ,
+ sndbuf_getblksz(c->bufsoft));
+ nvlist_add_number(cdi, SNDST_DSPS_SOUND4_CHAN_SWBUF_BLKCNT,
+ sndbuf_getblkcnt(c->bufsoft));
+ nvlist_add_number(cdi, SNDST_DSPS_SOUND4_CHAN_SWBUF_FREE,
+ sndbuf_getfree(c->bufsoft));
+ nvlist_add_number(cdi, SNDST_DSPS_SOUND4_CHAN_SWBUF_READY,
+ sndbuf_getready(c->bufsoft));
+
+ if (c->parentchannel != NULL) {
+ sbuf_printf(&sb, "[%s", (c->direction == PCMDIR_REC) ?
+ c->parentchannel->name : "userland");
+ } else {
+ sbuf_printf(&sb, "[%s", (c->direction == PCMDIR_REC) ?
+ "hardware" : "userland");
+ }
+ sbuf_printf(&sb, " -> ");
+ f = c->feeder;
+ while (f->source != NULL)
+ f = f->source;
+ while (f != NULL) {
+ sbuf_printf(&sb, "%s", f->class->name);
+ if (f->desc->type == FEEDER_FORMAT) {
+ snd_afmt2str(f->desc->in, buf, sizeof(buf));
+ sbuf_printf(&sb, "(%s -> ", buf);
+ snd_afmt2str(f->desc->out, buf, sizeof(buf));
+ sbuf_printf(&sb, "%s)", buf);
+ } else if (f->desc->type == FEEDER_MATRIX) {
+ sbuf_printf(&sb, "(%d.%dch -> %d.%dch)",
+ AFMT_CHANNEL(f->desc->in) -
+ AFMT_EXTCHANNEL(f->desc->in),
+ AFMT_EXTCHANNEL(f->desc->in),
+ AFMT_CHANNEL(f->desc->out) -
+ AFMT_EXTCHANNEL(f->desc->out),
+ AFMT_EXTCHANNEL(f->desc->out));
+ } else if (f->desc->type == FEEDER_RATE) {
+ sbuf_printf(&sb, "(%d -> %d)",
+ FEEDER_GET(f, FEEDRATE_SRC),
+ FEEDER_GET(f, FEEDRATE_DST));
+ } else {
+ snd_afmt2str(f->desc->out, buf, sizeof(buf));
+ sbuf_printf(&sb, "(%s)", buf);
+ }
+ sbuf_printf(&sb, " -> ");
+ f = f->parent;
+ }
+ if (c->parentchannel != NULL) {
+ sbuf_printf(&sb, "%s]", (c->direction == PCMDIR_REC) ?
+ "userland" : c->parentchannel->name);
+ } else {
+ sbuf_printf(&sb, "%s]", (c->direction == PCMDIR_REC) ?
+ "userland" : "hardware");
+ }
+
+ CHN_UNLOCK(c);
+
+ sbuf_finish(&sb);
+ nvlist_add_string(cdi, SNDST_DSPS_SOUND4_CHAN_FEEDERCHAIN,
+ sbuf_data(&sb));
+ sbuf_delete(&sb);
+
+ nvlist_append_nvlist_array(sound4di,
+ SNDST_DSPS_SOUND4_CHAN_INFO, cdi);
+ nvlist_destroy(cdi);
+ err = nvlist_error(sound4di);
+ if (err) {
+ PCM_RELEASE_QUICK(d);
+ goto done;
+ }
+ }
nvlist_move_nvlist(di, SNDST_DSPS_PROVIDER_INFO, sound4di);
sound4di = NULL;
+
PCM_RELEASE_QUICK(d);
nvlist_add_string(di, SNDST_DSPS_PROVIDER, SNDST_DSPS_SOUND4_PROVIDER);
@@ -624,10 +750,9 @@ sndstat_refresh_devs(struct sndstat_file *pf)
}
static int
-sndstat_get_devs(struct sndstat_file *pf, caddr_t data)
+sndstat_get_devs(struct sndstat_file *pf, void *arg_buf, size_t *arg_nbytes)
{
int err;
- struct sndstioc_nv_arg *arg = (struct sndstioc_nv_arg *)data;
SNDSTAT_LOCK();
sx_xlock(&pf->lock);
@@ -666,22 +791,22 @@ sndstat_get_devs(struct sndstat_file *pf, caddr_t data)
SNDSTAT_UNLOCK();
- if (!arg->nbytes) {
- arg->nbytes = pf->devs_nbytes;
+ if (*arg_nbytes == 0) {
+ *arg_nbytes = pf->devs_nbytes;
err = 0;
goto done;
}
- if (arg->nbytes < pf->devs_nbytes) {
- arg->nbytes = 0;
+ if (*arg_nbytes < pf->devs_nbytes) {
+ *arg_nbytes = 0;
err = 0;
goto done;
}
- err = copyout(pf->devs_nvlbuf, arg->buf, pf->devs_nbytes);
+ err = copyout(pf->devs_nvlbuf, arg_buf, pf->devs_nbytes);
if (err)
goto done;
- arg->nbytes = pf->devs_nbytes;
+ *arg_nbytes = pf->devs_nbytes;
free(pf->devs_nvlbuf, M_NVLIST);
pf->devs_nvlbuf = NULL;
@@ -706,7 +831,7 @@ sndstat_unpack_user_nvlbuf(const void *unvlbuf, size_t nbytes, nvlist_t **nvl)
}
*nvl = nvlist_unpack(nvlbuf, nbytes, 0);
free(nvlbuf, M_DEVBUF);
- if (nvl == NULL) {
+ if (*nvl == NULL) {
return (EINVAL);
}
@@ -852,20 +977,24 @@ sndstat_dsp_unpack_nvlist(const nvlist_t *nvlist, struct sndstat_userdev *ud)
}
static int
-sndstat_add_user_devs(struct sndstat_file *pf, caddr_t data)
+sndstat_add_user_devs(struct sndstat_file *pf, void *nvlbuf, size_t nbytes)
{
int err;
nvlist_t *nvl = NULL;
const nvlist_t * const *dsps;
size_t i, ndsps;
- struct sndstioc_nv_arg *arg = (struct sndstioc_nv_arg *)data;
if ((pf->fflags & FWRITE) == 0) {
err = EPERM;
goto done;
}
- err = sndstat_unpack_user_nvlbuf(arg->buf, arg->nbytes, &nvl);
+ if (nbytes > SNDST_UNVLBUF_MAX) {
+ err = ENOMEM;
+ goto done;
+ }
+
+ err = sndstat_unpack_user_nvlbuf(nvlbuf, nbytes, &nvl);
if (err != 0)
goto done;
@@ -911,52 +1040,17 @@ sndstat_flush_user_devs(struct sndstat_file *pf)
return (0);
}
-#ifdef COMPAT_FREEBSD32
-static int
-compat_sndstat_get_devs32(struct sndstat_file *pf, caddr_t data)
-{
- struct sndstioc_nv_arg32 *arg32 = (struct sndstioc_nv_arg32 *)data;
- struct sndstioc_nv_arg arg;
- int err;
-
- arg.buf = (void *)(uintptr_t)arg32->buf;
- arg.nbytes = arg32->nbytes;
-
- err = sndstat_get_devs(pf, (caddr_t)&arg);
- if (err == 0) {
- arg32->buf = (uint32_t)(uintptr_t)arg.buf;
- arg32->nbytes = arg.nbytes;
- }
-
- return (err);
-}
-
-static int
-compat_sndstat_add_user_devs32(struct sndstat_file *pf, caddr_t data)
-{
- struct sndstioc_nv_arg32 *arg32 = (struct sndstioc_nv_arg32 *)data;
- struct sndstioc_nv_arg arg;
- int err;
-
- arg.buf = (void *)(uintptr_t)arg32->buf;
- arg.nbytes = arg32->nbytes;
-
- err = sndstat_add_user_devs(pf, (caddr_t)&arg);
- if (err == 0) {
- arg32->buf = (uint32_t)(uintptr_t)arg.buf;
- arg32->nbytes = arg.nbytes;
- }
-
- return (err);
-}
-#endif
-
static int
sndstat_ioctl(
struct cdev *dev, u_long cmd, caddr_t data, int fflag, struct thread *td)
{
int err;
struct sndstat_file *pf;
+ struct sndstioc_nv_arg *arg;
+#ifdef COMPAT_FREEBSD32
+ struct sndstioc_nv_arg32 *arg32;
+ size_t nbytes;
+#endif
err = devfs_get_cdevpriv((void **)&pf);
if (err != 0)
@@ -964,27 +1058,30 @@ sndstat_ioctl(
switch (cmd) {
case SNDSTIOC_GET_DEVS:
- err = sndstat_get_devs(pf, data);
+ arg = (struct sndstioc_nv_arg *)data;
+ err = sndstat_get_devs(pf, arg->buf, &arg->nbytes);
break;
#ifdef COMPAT_FREEBSD32
case SNDSTIOC_GET_DEVS32:
- if (!SV_CURPROC_FLAG(SV_ILP32)) {
- err = ENODEV;
- break;
+ arg32 = (struct sndstioc_nv_arg32 *)data;
+ nbytes = arg32->nbytes;
+ err = sndstat_get_devs(pf, (void *)(uintptr_t)arg32->buf,
+ &nbytes);
+ if (err == 0) {
+ KASSERT(nbytes < UINT_MAX, ("impossibly many bytes"));
+ arg32->nbytes = nbytes;
}
- err = compat_sndstat_get_devs32(pf, data);
break;
#endif
case SNDSTIOC_ADD_USER_DEVS:
- err = sndstat_add_user_devs(pf, data);
+ arg = (struct sndstioc_nv_arg *)data;
+ err = sndstat_add_user_devs(pf, arg->buf, arg->nbytes);
break;
#ifdef COMPAT_FREEBSD32
case SNDSTIOC_ADD_USER_DEVS32:
- if (!SV_CURPROC_FLAG(SV_ILP32)) {
- err = ENODEV;
- break;
- }
- err = compat_sndstat_add_user_devs32(pf, data);
+ arg32 = (struct sndstioc_nv_arg32 *)data;
+ err = sndstat_add_user_devs(pf, (void *)(uintptr_t)arg32->buf,
+ arg32->nbytes);
break;
#endif
case SNDSTIOC_REFRESH_DEVS:
@@ -1014,7 +1111,7 @@ sndstat_line2userdev(struct sndstat_file *pf, const char *line, int n)
if (e == NULL)
goto fail;
ud->nameunit = strndup(line, e - line, M_DEVBUF);
- ud->devnode = (char *)malloc(e - line + 1, M_DEVBUF, M_WAITOK | M_ZERO);
+ ud->devnode = malloc(e - line + 1, M_DEVBUF, M_WAITOK | M_ZERO);
strlcat(ud->devnode, ud->nameunit, e - line + 1);
line = e + 1;
@@ -1163,6 +1260,8 @@ sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose)
KASSERT(c->bufhard != NULL && c->bufsoft != NULL,
("hosed pcm channel setup"));
+ CHN_LOCK(c);
+
sbuf_printf(s, "\n\t");
sbuf_printf(s, "%s[%s]: ",
@@ -1186,12 +1285,12 @@ sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose)
}
sbuf_printf(s, "\n\t");
- sbuf_printf(s, "interrupts %d, ", c->interrupts);
+ sbuf_printf(s, "\tinterrupts %d, ", c->interrupts);
if (c->direction == PCMDIR_REC) {
sbuf_printf(s,
"overruns %d, feed %u, hfree %d, "
- "sfree %d [b:%d/%d/%d|bs:%d/%d/%d]",
+ "sfree %d\n\t\t[b:%d/%d/%d|bs:%d/%d/%d]",
c->xruns, c->feedcount,
sndbuf_getfree(c->bufhard),
sndbuf_getfree(c->bufsoft),
@@ -1204,7 +1303,7 @@ sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose)
} else {
sbuf_printf(s,
"underruns %d, feed %u, ready %d "
- "[b:%d/%d/%d|bs:%d/%d/%d]",
+ "\n\t\t[b:%d/%d/%d|bs:%d/%d/%d]",
c->xruns, c->feedcount,
sndbuf_getready(c->bufsoft),
sndbuf_getsize(c->bufhard),
@@ -1216,11 +1315,16 @@ sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose)
}
sbuf_printf(s, "\n\t");
- sbuf_printf(s, "channel flags=0x%b", c->flags, CHN_F_BITS);
+ sbuf_printf(s, "\tchannel flags=0x%b", c->flags, CHN_F_BITS);
sbuf_printf(s, "\n\t");
- sbuf_printf(s, "{%s}",
- (c->direction == PCMDIR_REC) ? "hardware" : "userland");
+ if (c->parentchannel != NULL) {
+ sbuf_printf(s, "\t{%s}", (c->direction == PCMDIR_REC) ?
+ c->parentchannel->name : "userland");
+ } else {
+ sbuf_printf(s, "\t{%s}", (c->direction == PCMDIR_REC) ?
+ "hardware" : "userland");
+ }
sbuf_printf(s, " -> ");
f = c->feeder;
while (f->source != NULL)
@@ -1252,8 +1356,15 @@ sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose)
sbuf_printf(s, " -> ");
f = f->parent;
}
- sbuf_printf(s, "{%s}",
- (c->direction == PCMDIR_REC) ? "userland" : "hardware");
+ if (c->parentchannel != NULL) {
+ sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC) ?
+ "userland" : c->parentchannel->name);
+ } else {
+ sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC) ?
+ "userland" : "hardware");
+ }
+
+ CHN_UNLOCK(c);
}
return (0);
@@ -1271,11 +1382,8 @@ sndstat_prepare(struct sndstat_file *pf_self)
/* make sure buffer is reset */
sbuf_clear(s);
- if (snd_verbose > 0) {
- sbuf_printf(s, "FreeBSD Audio Driver (%ubit %d/%s)\n",
- (u_int)sizeof(intpcm32_t) << 3, SND_DRV_VERSION,
- MACHINE_ARCH);
- }
+ if (snd_verbose > 0)
+ sbuf_printf(s, "FreeBSD Audio Driver\n");
/* generate list of installed devices */
k = 0;
diff --git a/sys/dev/sound/pcm/sound.c b/sys/dev/sound/pcm/sound.c
index 9d5eaf3f5ad7..cb510d526fa8 100644
--- a/sys/dev/sound/pcm/sound.c
+++ b/sys/dev/sound/pcm/sound.c
@@ -6,7 +6,7 @@
* Copyright (c) 1999 Cameron Grant <cg@FreeBSD.org>
* Copyright (c) 1997 Luigi Rizzo
* All rights reserved.
- * Copyright (c) 2024 The FreeBSD Foundation
+ * Copyright (c) 2024-2025 The FreeBSD Foundation
*
* Portions of this software were developed by Christos Margiolis
* <christos@FreeBSD.org> under sponsorship from the FreeBSD Foundation.
@@ -41,7 +41,6 @@
#include <dev/sound/pcm/ac97.h>
#include <dev/sound/pcm/vchan.h>
#include <dev/sound/pcm/dsp.h>
-#include <dev/sound/version.h>
#include <sys/limits.h>
#include <sys/sysctl.h>
@@ -49,30 +48,15 @@
devclass_t pcm_devclass;
-int pcm_veto_load = 1;
-
int snd_unit = -1;
static int snd_unit_auto = -1;
SYSCTL_INT(_hw_snd, OID_AUTO, default_auto, CTLFLAG_RWTUN,
&snd_unit_auto, 0, "assign default unit to a newly attached device");
-int snd_maxautovchans = 16;
-
SYSCTL_NODE(_hw, OID_AUTO, snd, CTLFLAG_RD | CTLFLAG_MPSAFE, 0,
"Sound driver");
-static void pcm_sysinit(device_t);
-
-/*
- * XXX I've had enough with people not telling proper version/arch
- * while reporting problems, not after 387397913213th questions/requests.
- */
-static char snd_driver_version[] =
- __XSTRING(SND_DRV_VERSION)"/"MACHINE_ARCH;
-SYSCTL_STRING(_hw_snd, OID_AUTO, version, CTLFLAG_RD, &snd_driver_version,
- 0, "driver version/arch");
-
/**
* @brief Unit number allocator for syncgroup IDs
*/
@@ -121,210 +105,6 @@ snd_setup_intr(device_t dev, struct resource *res, int flags, driver_intr_t hand
return bus_setup_intr(dev, res, flags, NULL, hand, param, cookiep);
}
-int
-pcm_setvchans(struct snddev_info *d, int direction, int newcnt, int num)
-{
- struct pcm_channel *c, *ch, *nch;
- struct pcmchan_caps *caps;
- int i, err, vcnt;
-
- PCM_BUSYASSERT(d);
-
- if ((direction == PCMDIR_PLAY && d->playcount < 1) ||
- (direction == PCMDIR_REC && d->reccount < 1))
- return (ENODEV);
-
- if (!(d->flags & SD_F_AUTOVCHAN))
- return (EINVAL);
-
- if (newcnt < 0 || newcnt > SND_MAXVCHANS)
- return (E2BIG);
-
- if (direction == PCMDIR_PLAY)
- vcnt = d->pvchancount;
- else if (direction == PCMDIR_REC)
- vcnt = d->rvchancount;
- else
- return (EINVAL);
-
- if (newcnt > vcnt) {
- KASSERT(num == -1 ||
- (num >= 0 && num < SND_MAXVCHANS && (newcnt - 1) == vcnt),
- ("bogus vchan_create() request num=%d newcnt=%d vcnt=%d",
- num, newcnt, vcnt));
- /* add new vchans - find a parent channel first */
- ch = NULL;
- CHN_FOREACH(c, d, channels.pcm) {
- CHN_LOCK(c);
- if (c->direction == direction &&
- ((c->flags & CHN_F_HAS_VCHAN) || (vcnt == 0 &&
- c->refcount < 1 &&
- !(c->flags & (CHN_F_BUSY | CHN_F_VIRTUAL))))) {
- /*
- * Reuse hw channel with vchans already
- * created.
- */
- if (c->flags & CHN_F_HAS_VCHAN) {
- ch = c;
- break;
- }
- /*
- * No vchans ever created, look for
- * channels with supported formats.
- */
- caps = chn_getcaps(c);
- if (caps == NULL) {
- CHN_UNLOCK(c);
- continue;
- }
- for (i = 0; caps->fmtlist[i] != 0; i++) {
- if (caps->fmtlist[i] & AFMT_CONVERTIBLE)
- break;
- }
- if (caps->fmtlist[i] != 0) {
- ch = c;
- break;
- }
- }
- CHN_UNLOCK(c);
- }
- if (ch == NULL)
- return (EBUSY);
- ch->flags |= CHN_F_BUSY;
- err = 0;
- while (err == 0 && newcnt > vcnt) {
- err = vchan_create(ch, num);
- if (err == 0)
- vcnt++;
- else if (err == E2BIG && newcnt > vcnt)
- device_printf(d->dev,
- "%s: err=%d Maximum channel reached.\n",
- __func__, err);
- }
- if (vcnt == 0)
- ch->flags &= ~CHN_F_BUSY;
- CHN_UNLOCK(ch);
- if (err != 0)
- return (err);
- } else if (newcnt < vcnt) {
- KASSERT(num == -1,
- ("bogus vchan_destroy() request num=%d", num));
- CHN_FOREACH(c, d, channels.pcm) {
- CHN_LOCK(c);
- if (c->direction != direction ||
- CHN_EMPTY(c, children) ||
- !(c->flags & CHN_F_HAS_VCHAN)) {
- CHN_UNLOCK(c);
- continue;
- }
- CHN_FOREACH_SAFE(ch, c, nch, children) {
- CHN_LOCK(ch);
- if (vcnt == 1 && c->refcount > 0) {
- CHN_UNLOCK(ch);
- break;
- }
- if (!(ch->flags & CHN_F_BUSY) &&
- ch->refcount < 1) {
- err = vchan_destroy(ch);
- if (err == 0)
- vcnt--;
- } else
- CHN_UNLOCK(ch);
- if (vcnt == newcnt)
- break;
- }
- CHN_UNLOCK(c);
- break;
- }
- }
-
- return (0);
-}
-
-/* return error status and a locked channel */
-int
-pcm_chnalloc(struct snddev_info *d, struct pcm_channel **ch, int direction,
- pid_t pid, char *comm)
-{
- struct pcm_channel *c;
- int err, vchancount, vchan_num;
- bool retry;
-
- KASSERT(d != NULL && ch != NULL &&
- (direction == PCMDIR_PLAY || direction == PCMDIR_REC),
- ("%s(): invalid d=%p ch=%p direction=%d pid=%d",
- __func__, d, ch, direction, pid));
- PCM_BUSYASSERT(d);
-
- *ch = NULL;
- vchan_num = 0;
- vchancount = (direction == PCMDIR_PLAY) ? d->pvchancount :
- d->rvchancount;
-
- retry = false;
-retry_chnalloc:
- err = ENOTSUP;
- /* scan for a free channel */
- CHN_FOREACH(c, d, channels.pcm) {
- CHN_LOCK(c);
- if (c->direction == direction && (c->flags & CHN_F_VIRTUAL)) {
- if (vchancount < snd_maxautovchans &&
- vchan_num < c->unit) {
- CHN_UNLOCK(c);
- goto vchan_alloc;
- }
- vchan_num++;
- }
- if (c->direction == direction && !(c->flags & CHN_F_BUSY)) {
- c->flags |= CHN_F_BUSY;
- c->pid = pid;
- strlcpy(c->comm, (comm != NULL) ? comm :
- CHN_COMM_UNKNOWN, sizeof(c->comm));
- *ch = c;
- return (0);
- } else if (c->direction == direction && (c->flags & CHN_F_BUSY))
- err = EBUSY;
- CHN_UNLOCK(c);
- }
-
- /*
- * We came from retry_chnalloc and still didn't find a free channel.
- */
- if (retry)
- return (err);
-
-vchan_alloc:
- /* no channel available */
- if (!(vchancount > 0 && vchancount < snd_maxautovchans))
- return (err);
- err = pcm_setvchans(d, direction, vchancount + 1, -1);
- if (err == 0) {
- retry = true;
- goto retry_chnalloc;
- }
-
- return (err);
-}
-
-static void
-pcm_setmaxautovchans(struct snddev_info *d, int num)
-{
- PCM_BUSYASSERT(d);
-
- if (num < 0)
- return;
-
- if (num >= 0 && d->pvchancount > num)
- (void)pcm_setvchans(d, PCMDIR_PLAY, num, -1);
- else if (num > 0 && d->pvchancount == 0)
- (void)pcm_setvchans(d, PCMDIR_PLAY, 1, -1);
-
- if (num >= 0 && d->rvchancount > num)
- (void)pcm_setvchans(d, PCMDIR_REC, num, -1);
- else if (num > 0 && d->rvchancount == 0)
- (void)pcm_setvchans(d, PCMDIR_REC, 1, -1);
-}
-
static int
sysctl_hw_snd_default_unit(SYSCTL_HANDLER_ARGS)
{
@@ -334,256 +114,42 @@ sysctl_hw_snd_default_unit(SYSCTL_HANDLER_ARGS)
unit = snd_unit;
error = sysctl_handle_int(oidp, &unit, 0, req);
if (error == 0 && req->newptr != NULL) {
+ bus_topo_lock();
d = devclass_get_softc(pcm_devclass, unit);
- if (!PCM_REGISTERED(d) || CHN_EMPTY(d, channels.pcm))
+ if (!PCM_REGISTERED(d) || CHN_EMPTY(d, channels.pcm)) {
+ bus_topo_unlock();
return EINVAL;
+ }
snd_unit = unit;
snd_unit_auto = 0;
+ bus_topo_unlock();
}
return (error);
}
/* XXX: do we need a way to let the user change the default unit? */
SYSCTL_PROC(_hw_snd, OID_AUTO, default_unit,
- CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_ANYBODY | CTLFLAG_NEEDGIANT, 0,
+ CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_ANYBODY | CTLFLAG_MPSAFE, 0,
sizeof(int), sysctl_hw_snd_default_unit, "I",
"default sound device");
-static int
-sysctl_hw_snd_maxautovchans(SYSCTL_HANDLER_ARGS)
-{
- struct snddev_info *d;
- int i, v, error;
-
- v = snd_maxautovchans;
- error = sysctl_handle_int(oidp, &v, 0, req);
- if (error == 0 && req->newptr != NULL) {
- if (v < 0)
- v = 0;
- if (v > SND_MAXVCHANS)
- v = SND_MAXVCHANS;
- snd_maxautovchans = v;
- for (i = 0; pcm_devclass != NULL &&
- i < devclass_get_maxunit(pcm_devclass); i++) {
- d = devclass_get_softc(pcm_devclass, i);
- if (!PCM_REGISTERED(d))
- continue;
- PCM_ACQUIRE_QUICK(d);
- pcm_setmaxautovchans(d, v);
- PCM_RELEASE_QUICK(d);
- }
- }
- return (error);
-}
-SYSCTL_PROC(_hw_snd, OID_AUTO, maxautovchans,
- CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_NEEDGIANT, 0, sizeof(int),
- sysctl_hw_snd_maxautovchans, "I",
- "maximum virtual channel");
-
-struct pcm_channel *
-pcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t cls, int dir, int num, void *devinfo)
-{
- struct pcm_channel *ch;
- int direction, err, rpnum, *pnum, max;
- int type, unit;
- char *dirs, *devname, buf[CHN_NAMELEN];
-
- PCM_BUSYASSERT(d);
- PCM_LOCKASSERT(d);
- KASSERT(num >= -1, ("invalid num=%d", num));
-
- switch (dir) {
- case PCMDIR_PLAY:
- dirs = "play";
- direction = PCMDIR_PLAY;
- pnum = &d->playcount;
- type = SND_DEV_DSPHW_PLAY;
- max = SND_MAXHWCHAN;
- break;
- case PCMDIR_PLAY_VIRTUAL:
- dirs = "virtual_play";
- direction = PCMDIR_PLAY;
- pnum = &d->pvchancount;
- type = SND_DEV_DSPHW_VPLAY;
- max = SND_MAXVCHANS;
- break;
- case PCMDIR_REC:
- dirs = "record";
- direction = PCMDIR_REC;
- pnum = &d->reccount;
- type = SND_DEV_DSPHW_REC;
- max = SND_MAXHWCHAN;
- break;
- case PCMDIR_REC_VIRTUAL:
- dirs = "virtual_record";
- direction = PCMDIR_REC;
- pnum = &d->rvchancount;
- type = SND_DEV_DSPHW_VREC;
- max = SND_MAXVCHANS;
- break;
- default:
- return (NULL);
- }
-
- unit = (num == -1) ? 0 : num;
-
- if (*pnum >= max || unit >= max)
- return (NULL);
-
- rpnum = 0;
-
- CHN_FOREACH(ch, d, channels.pcm) {
- if (ch->type != type)
- continue;
- if (unit == ch->unit && num != -1) {
- device_printf(d->dev,
- "channel num=%d allocated!\n", unit);
- return (NULL);
- }
- unit++;
- if (unit >= max) {
- device_printf(d->dev,
- "chan=%d > %d\n", unit, max);
- return (NULL);
- }
- rpnum++;
- }
-
- if (*pnum != rpnum) {
- device_printf(d->dev,
- "%s(): WARNING: pnum screwed : dirs=%s pnum=%d rpnum=%d\n",
- __func__, dirs, *pnum, rpnum);
- return (NULL);
- }
-
- PCM_UNLOCK(d);
- ch = malloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO);
- ch->methods = kobj_create(cls, M_DEVBUF, M_WAITOK | M_ZERO);
- ch->type = type;
- ch->unit = unit;
- ch->pid = -1;
- strlcpy(ch->comm, CHN_COMM_UNUSED, sizeof(ch->comm));
- ch->parentsnddev = d;
- ch->parentchannel = parent;
- ch->dev = d->dev;
- ch->trigger = PCMTRIG_STOP;
- devname = dsp_unit2name(buf, sizeof(buf), ch);
- if (devname == NULL) {
- device_printf(d->dev, "Failed to query device name");
- kobj_delete(ch->methods, M_DEVBUF);
- free(ch, M_DEVBUF);
- return (NULL);
- }
- snprintf(ch->name, sizeof(ch->name), "%s:%s:%s",
- device_get_nameunit(ch->dev), dirs, devname);
-
- err = chn_init(ch, devinfo, dir, direction);
- PCM_LOCK(d);
- if (err) {
- device_printf(d->dev, "chn_init(%s) failed: err = %d\n",
- ch->name, err);
- kobj_delete(ch->methods, M_DEVBUF);
- free(ch, M_DEVBUF);
- return (NULL);
- }
-
- return (ch);
-}
-
-int
-pcm_chn_add(struct snddev_info *d, struct pcm_channel *ch)
-{
- PCM_BUSYASSERT(d);
- PCM_LOCKASSERT(d);
- KASSERT(ch != NULL && (ch->direction == PCMDIR_PLAY ||
- ch->direction == PCMDIR_REC), ("Invalid pcm channel"));
-
- CHN_INSERT_SORT_ASCEND(d, ch, channels.pcm);
-
- switch (ch->type) {
- case SND_DEV_DSPHW_PLAY:
- d->playcount++;
- break;
- case SND_DEV_DSPHW_VPLAY:
- d->pvchancount++;
- break;
- case SND_DEV_DSPHW_REC:
- d->reccount++;
- break;
- case SND_DEV_DSPHW_VREC:
- d->rvchancount++;
- break;
- default:
- break;
- }
-
- return (0);
-}
-
-int
-pcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch)
-{
- struct pcm_channel *tmp;
-
- PCM_BUSYASSERT(d);
- PCM_LOCKASSERT(d);
-
- tmp = NULL;
-
- CHN_FOREACH(tmp, d, channels.pcm) {
- if (tmp == ch)
- break;
- }
-
- if (tmp != ch)
- return (EINVAL);
-
- CHN_REMOVE(d, ch, channels.pcm);
-
- switch (ch->type) {
- case SND_DEV_DSPHW_PLAY:
- d->playcount--;
- break;
- case SND_DEV_DSPHW_VPLAY:
- d->pvchancount--;
- break;
- case SND_DEV_DSPHW_REC:
- d->reccount--;
- break;
- case SND_DEV_DSPHW_VREC:
- d->rvchancount--;
- break;
- default:
- break;
- }
-
- return (0);
-}
-
int
pcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo)
{
struct snddev_info *d = device_get_softc(dev);
struct pcm_channel *ch;
- int err;
-
- PCM_BUSYASSERT(d);
+ int err = 0;
PCM_LOCK(d);
- ch = pcm_chn_create(d, NULL, cls, dir, -1, devinfo);
+ PCM_WAIT(d);
+ PCM_ACQUIRE(d);
+ ch = chn_init(d, NULL, cls, dir, devinfo);
if (!ch) {
- device_printf(d->dev, "pcm_chn_create(%s, %d, %p) failed\n",
+ device_printf(d->dev, "chn_init(%s, %d, %p) failed\n",
cls->name, dir, devinfo);
- PCM_UNLOCK(d);
- return (ENODEV);
+ err = ENODEV;
}
-
- err = pcm_chn_add(d, ch);
+ PCM_RELEASE(d);
PCM_UNLOCK(d);
- if (err) {
- device_printf(d->dev, "pcm_chn_add(%s) failed, err=%d\n",
- ch->name, err);
- chn_kill(ch);
- }
return (err);
}
@@ -592,46 +158,53 @@ static void
pcm_killchans(struct snddev_info *d)
{
struct pcm_channel *ch;
- int error;
- bool found;
+ bool again;
PCM_BUSYASSERT(d);
- do {
- found = false;
+ KASSERT(!PCM_REGISTERED(d), ("%s(): still registered\n", __func__));
+
+ for (;;) {
+ again = false;
+ /* Make sure all channels are stopped. */
CHN_FOREACH(ch, d, channels.pcm) {
CHN_LOCK(ch);
- /*
- * Make sure no channel has went to sleep in the
- * meantime.
- */
- chn_shutdown(ch);
- /*
- * We have to give a thread sleeping in chn_sleep() a
- * chance to observe that the channel is dead.
- */
- if ((ch->flags & CHN_F_SLEEPING) == 0) {
- found = true;
+ if (ch->inprog == 0 && ch->sleeping == 0 &&
+ CHN_STOPPED(ch)) {
CHN_UNLOCK(ch);
- break;
+ continue;
}
+ chn_shutdown(ch);
+ if (ch->direction == PCMDIR_PLAY)
+ chn_flush(ch);
+ else
+ chn_abort(ch);
CHN_UNLOCK(ch);
+ again = true;
}
-
/*
- * All channels are still sleeping. Sleep for a bit and try
- * again to see if any of them is awake now.
+ * Some channels are still active. Sleep for a bit and try
+ * again.
*/
- if (!found) {
- pause_sbt("pcmkillchans", SBT_1MS * 5, 0, 0);
- continue;
- }
+ if (again)
+ pause_sbt("pcmkillchans", mstosbt(5), 0, 0);
+ else
+ break;
+ }
- PCM_LOCK(d);
- error = pcm_chn_remove(d, ch);
- PCM_UNLOCK(d);
- if (error == 0)
- chn_kill(ch);
- } while (!CHN_EMPTY(d, channels.pcm));
+ /* All channels are finally dead. */
+ while (!CHN_EMPTY(d, channels.pcm)) {
+ ch = CHN_FIRST(d, channels.pcm);
+ chn_kill(ch);
+ }
+
+ if (d->p_unr != NULL)
+ delete_unrhdr(d->p_unr);
+ if (d->vp_unr != NULL)
+ delete_unrhdr(d->vp_unr);
+ if (d->r_unr != NULL)
+ delete_unrhdr(d->r_unr);
+ if (d->vr_unr != NULL)
+ delete_unrhdr(d->vr_unr);
}
static int
@@ -642,6 +215,7 @@ pcm_best_unit(int old)
best = -1;
bestprio = -100;
+ bus_topo_lock();
for (i = 0; pcm_devclass != NULL &&
i < devclass_get_maxunit(pcm_devclass); i++) {
d = devclass_get_softc(pcm_devclass, i);
@@ -657,53 +231,9 @@ pcm_best_unit(int old)
bestprio = prio;
}
}
- return (best);
-}
+ bus_topo_unlock();
-int
-pcm_setstatus(device_t dev, char *str)
-{
- struct snddev_info *d = device_get_softc(dev);
-
- /* should only be called once */
- if (d->flags & SD_F_REGISTERED)
- return (EINVAL);
-
- PCM_BUSYASSERT(d);
-
- if (d->playcount == 0 || d->reccount == 0)
- d->flags |= SD_F_SIMPLEX;
-
- if (d->playcount > 0 || d->reccount > 0)
- d->flags |= SD_F_AUTOVCHAN;
-
- pcm_setmaxautovchans(d, snd_maxautovchans);
-
- strlcpy(d->status, str, SND_STATUSLEN);
-
- PCM_LOCK(d);
-
- /* Done, we're ready.. */
- d->flags |= SD_F_REGISTERED;
-
- PCM_RELEASE(d);
-
- PCM_UNLOCK(d);
-
- /*
- * Create all sysctls once SD_F_REGISTERED is set else
- * tunable sysctls won't work:
- */
- pcm_sysinit(dev);
-
- if (snd_unit_auto < 0)
- snd_unit_auto = (snd_unit < 0) ? 1 : 0;
- if (snd_unit < 0 || snd_unit_auto > 1)
- snd_unit = device_get_unit(dev);
- else if (snd_unit_auto == 1)
- snd_unit = pcm_best_unit(snd_unit);
-
- return (0);
+ return (best);
}
uint32_t
@@ -797,75 +327,43 @@ sysctl_dev_pcm_bitperfect(SYSCTL_HANDLER_ARGS)
return (err);
}
-static u_int8_t
-pcm_mode_init(struct snddev_info *d)
+static int
+sysctl_dev_pcm_mode(SYSCTL_HANDLER_ARGS)
{
- u_int8_t mode = 0;
+ struct snddev_info *d;
+ int mode = 0;
+
+ d = oidp->oid_arg1;
+ if (!PCM_REGISTERED(d))
+ return (ENODEV);
+ PCM_LOCK(d);
if (d->playcount > 0)
mode |= PCM_MODE_PLAY;
if (d->reccount > 0)
mode |= PCM_MODE_REC;
if (d->mixer_dev != NULL)
mode |= PCM_MODE_MIXER;
+ PCM_UNLOCK(d);
- return (mode);
+ return (sysctl_handle_int(oidp, &mode, 0, req));
}
-static void
-pcm_sysinit(device_t dev)
-{
- struct snddev_info *d = device_get_softc(dev);
- u_int8_t mode;
-
- mode = pcm_mode_init(d);
-
- /* XXX: a user should be able to set this with a control tool, the
- sysadmin then needs min+max sysctls for this */
- SYSCTL_ADD_UINT(device_get_sysctl_ctx(dev),
- SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
- OID_AUTO, "buffersize", CTLFLAG_RD, &d->bufsz, 0, "allocated buffer size");
- SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
- SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
- "bitperfect", CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, d,
- sizeof(d), sysctl_dev_pcm_bitperfect, "I",
- "bit-perfect playback/recording (0=disable, 1=enable)");
- SYSCTL_ADD_UINT(device_get_sysctl_ctx(dev),
- SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
- OID_AUTO, "mode", CTLFLAG_RD, NULL, mode,
- "mode (1=mixer, 2=play, 4=rec. The values are OR'ed if more than "
- "one mode is supported)");
- if (d->flags & SD_F_AUTOVCHAN)
- vchan_initsys(dev);
- if (d->flags & SD_F_EQ)
- feeder_eq_initsys(dev);
-}
-
-int
-pcm_register(device_t dev, void *devinfo, int numplay, int numrec)
+/*
+ * Basic initialization so that drivers can use pcm_addchan() before
+ * pcm_register().
+ */
+void
+pcm_init(device_t dev, void *devinfo)
{
struct snddev_info *d;
int i;
- if (pcm_veto_load) {
- device_printf(dev, "disabled due to an error while initialising: %d\n", pcm_veto_load);
-
- return EINVAL;
- }
-
d = device_get_softc(dev);
d->dev = dev;
d->lock = snd_mtxcreate(device_get_nameunit(dev), "sound cdev");
cv_init(&d->cv, device_get_nameunit(dev));
- PCM_ACQUIRE_QUICK(d);
-#if 0
- /*
- * d->flags should be cleared by the allocator of the softc.
- * We cannot clear this field here because several devices set
- * this flag before calling pcm_register().
- */
- d->flags = 0;
-#endif
+
i = 0;
if (resource_int_value(device_get_name(dev), device_get_unit(dev),
"vpc", &i) != 0 || i != 0)
@@ -884,15 +382,42 @@ pcm_register(device_t dev, void *devinfo, int numplay, int numrec)
d->pvchanformat = 0;
d->rvchanrate = 0;
d->rvchanformat = 0;
+ d->p_unr = new_unrhdr(0, INT_MAX, NULL);
+ d->vp_unr = new_unrhdr(0, INT_MAX, NULL);
+ d->r_unr = new_unrhdr(0, INT_MAX, NULL);
+ d->vr_unr = new_unrhdr(0, INT_MAX, NULL);
CHN_INIT(d, channels.pcm);
CHN_INIT(d, channels.pcm.busy);
CHN_INIT(d, channels.pcm.opened);
+ CHN_INIT(d, channels.pcm.primary);
+}
+
+int
+pcm_register(device_t dev, char *str)
+{
+ struct snddev_info *d = device_get_softc(dev);
+
+ /* should only be called once */
+ if (d->flags & SD_F_REGISTERED)
+ return (EINVAL);
- /* XXX This is incorrect, but lets play along for now. */
- if ((numplay == 0 || numrec == 0) && numplay != numrec)
+ if (d->playcount == 0 || d->reccount == 0)
d->flags |= SD_F_SIMPLEX;
+ if (d->playcount > 0)
+ d->flags |= SD_F_PVCHANS;
+ if (d->reccount > 0)
+ d->flags |= SD_F_RVCHANS;
+ strlcpy(d->status, str, SND_STATUSLEN);
+
+ /* Done, we're ready.. */
+ d->flags |= SD_F_REGISTERED;
+
+ /*
+ * Create all sysctls once SD_F_REGISTERED is set else
+ * tunable sysctls won't work:
+ */
sysctl_ctx_init(&d->play_sysctl_ctx);
d->play_sysctl_tree = SYSCTL_ADD_NODE(&d->play_sysctl_ctx,
SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "play",
@@ -902,8 +427,33 @@ pcm_register(device_t dev, void *devinfo, int numplay, int numrec)
SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "rec",
CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "recording channels node");
- if (numplay > 0 || numrec > 0)
- d->flags |= SD_F_AUTOVCHAN;
+ /* XXX: a user should be able to set this with a control tool, the
+ sysadmin then needs min+max sysctls for this */
+ SYSCTL_ADD_UINT(device_get_sysctl_ctx(dev),
+ SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
+ OID_AUTO, "buffersize", CTLFLAG_RD, &d->bufsz, 0,
+ "allocated buffer size");
+ SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
+ SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
+ "bitperfect", CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, d,
+ sizeof(d), sysctl_dev_pcm_bitperfect, "I",
+ "bit-perfect playback/recording (0=disable, 1=enable)");
+ SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
+ SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
+ "mode", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, d, sizeof(d),
+ sysctl_dev_pcm_mode, "I",
+ "mode (1=mixer, 2=play, 4=rec. The values are OR'ed if more than "
+ "one mode is supported)");
+ vchan_initsys(dev);
+ if (d->flags & SD_F_EQ)
+ feeder_eq_initsys(dev);
+
+ if (snd_unit_auto < 0)
+ snd_unit_auto = (snd_unit < 0) ? 1 : 0;
+ if (snd_unit < 0 || snd_unit_auto > 1)
+ snd_unit = device_get_unit(dev);
+ else if (snd_unit_auto == 1)
+ snd_unit = pcm_best_unit(snd_unit);
sndstat_register(dev, d->status);
@@ -914,7 +464,6 @@ int
pcm_unregister(device_t dev)
{
struct snddev_info *d;
- struct pcm_channel *ch;
d = device_get_softc(dev);
@@ -926,29 +475,14 @@ pcm_unregister(device_t dev)
PCM_LOCK(d);
PCM_WAIT(d);
- d->flags |= SD_F_DETACHING;
+ d->flags &= ~SD_F_REGISTERED;
PCM_ACQUIRE(d);
PCM_UNLOCK(d);
- CHN_FOREACH(ch, d, channels.pcm) {
- CHN_LOCK(ch);
- /*
- * Do not wait for the timeout in chn_read()/chn_write(). Wake
- * up the sleeping thread and kill the channel.
- */
- chn_shutdown(ch);
- chn_abort(ch);
- CHN_UNLOCK(ch);
- }
+ pcm_killchans(d);
- /* remove /dev/sndstat entry first */
- sndstat_unregister(dev);
-
- PCM_LOCK(d);
- d->flags |= SD_F_DYING;
- d->flags &= ~SD_F_REGISTERED;
- PCM_UNLOCK(d);
+ PCM_RELEASE_QUICK(d);
if (d->play_sysctl_tree != NULL) {
sysctl_ctx_free(&d->play_sysctl_ctx);
@@ -959,15 +493,11 @@ pcm_unregister(device_t dev)
d->rec_sysctl_tree = NULL;
}
+ sndstat_unregister(dev);
+ mixer_uninit(dev);
dsp_destroy_dev(dev);
- (void)mixer_uninit(dev);
- pcm_killchans(d);
-
- PCM_LOCK(d);
- PCM_RELEASE(d);
cv_destroy(&d->cv);
- PCM_UNLOCK(d);
snd_mtxfree(d->lock);
if (snd_unit == device_get_unit(dev)) {
@@ -1008,9 +538,8 @@ sound_oss_sysinfo(oss_sysinfo *si)
struct snddev_info *d;
struct pcm_channel *c;
- int i, j, ncards;
-
- ncards = 0;
+ int j;
+ size_t i;
strlcpy(si->product, si_product, sizeof(si->product));
strlcpy(si->version, si_version, sizeof(si->version));
@@ -1019,13 +548,14 @@ sound_oss_sysinfo(oss_sysinfo *si)
/*
* Iterate over PCM devices and their channels, gathering up data
- * for the numaudios, ncards, and openedaudio fields.
+ * for the numaudioengines and openedaudio fields.
*/
- si->numaudios = 0;
+ si->numaudioengines = 0;
bzero((void *)&si->openedaudio, sizeof(si->openedaudio));
j = 0;
+ bus_topo_lock();
for (i = 0; pcm_devclass != NULL &&
i < devclass_get_maxunit(pcm_devclass); i++) {
d = devclass_get_softc(pcm_devclass, i);
@@ -1038,8 +568,7 @@ sound_oss_sysinfo(oss_sysinfo *si)
PCM_UNLOCKASSERT(d);
PCM_LOCK(d);
- si->numaudios += PCM_CHANCOUNT(d);
- ++ncards;
+ si->numaudioengines += PCM_CHANCOUNT(d);
CHN_FOREACH(c, d, channels.pcm) {
CHN_UNLOCKASSERT(c);
@@ -1053,7 +582,7 @@ sound_oss_sysinfo(oss_sysinfo *si)
PCM_UNLOCK(d);
}
- si->numaudioengines = si->numaudios;
+ bus_topo_unlock();
si->numsynths = 0; /* OSSv4 docs: this field is obsolete */
/**
@@ -1069,8 +598,16 @@ sound_oss_sysinfo(oss_sysinfo *si)
*/
si->nummidis = 0;
si->numtimers = 0;
- si->nummixers = mixer_count;
- si->numcards = ncards;
+ /*
+ * Set this to the maximum unit number so that applications will not
+ * break if they try to loop through all mixers and some of them are
+ * not available.
+ */
+ bus_topo_lock();
+ si->nummixers = devclass_get_maxunit(pcm_devclass);
+ si->numcards = devclass_get_maxunit(pcm_devclass);
+ si->numaudios = devclass_get_maxunit(pcm_devclass);
+ bus_topo_unlock();
/* OSSv4 docs: Intended only for test apps; API doesn't
really have much of a concept of cards. Shouldn't be
used by applications. */
@@ -1086,7 +623,7 @@ sound_oss_sysinfo(oss_sysinfo *si)
* Si->filler is a reserved array, but according to docs each
* element should be set to -1.
*/
- for (i = 0; i < sizeof(si->filler)/sizeof(si->filler[0]); i++)
+ for (i = 0; i < nitems(si->filler); i++)
si->filler[i] = -1;
}
@@ -1094,38 +631,89 @@ int
sound_oss_card_info(oss_card_info *si)
{
struct snddev_info *d;
- int i, ncards;
-
- ncards = 0;
+ int i;
+ bus_topo_lock();
for (i = 0; pcm_devclass != NULL &&
i < devclass_get_maxunit(pcm_devclass); i++) {
d = devclass_get_softc(pcm_devclass, i);
- if (!PCM_REGISTERED(d))
- continue;
-
- if (ncards++ != si->card)
+ if (i != si->card)
continue;
- PCM_UNLOCKASSERT(d);
- PCM_LOCK(d);
-
- strlcpy(si->shortname, device_get_nameunit(d->dev),
- sizeof(si->shortname));
- strlcpy(si->longname, device_get_desc(d->dev),
- sizeof(si->longname));
- strlcpy(si->hw_info, d->status, sizeof(si->hw_info));
- si->intr_count = si->ack_count = 0;
-
- PCM_UNLOCK(d);
+ if (!PCM_REGISTERED(d)) {
+ snprintf(si->shortname, sizeof(si->shortname),
+ "pcm%d (n/a)", i);
+ strlcpy(si->longname, "Device unavailable",
+ sizeof(si->longname));
+ si->hw_info[0] = '\0';
+ si->intr_count = si->ack_count = 0;
+ } else {
+ PCM_UNLOCKASSERT(d);
+ PCM_LOCK(d);
+
+ strlcpy(si->shortname, device_get_nameunit(d->dev),
+ sizeof(si->shortname));
+ strlcpy(si->longname, device_get_desc(d->dev),
+ sizeof(si->longname));
+ strlcpy(si->hw_info, d->status, sizeof(si->hw_info));
+ si->intr_count = si->ack_count = 0;
+
+ PCM_UNLOCK(d);
+ }
+ bus_topo_unlock();
return (0);
}
+ bus_topo_unlock();
+
return (ENXIO);
}
/************************************************************************/
+static void
+sound_global_init(void)
+{
+ if (snd_verbose < 0 || snd_verbose > 4)
+ snd_verbose = 1;
+
+ if (snd_unit < 0)
+ snd_unit = -1;
+
+ snd_vchans_enable = true;
+
+ if (chn_latency < CHN_LATENCY_MIN ||
+ chn_latency > CHN_LATENCY_MAX)
+ chn_latency = CHN_LATENCY_DEFAULT;
+
+ if (chn_latency_profile < CHN_LATENCY_PROFILE_MIN ||
+ chn_latency_profile > CHN_LATENCY_PROFILE_MAX)
+ chn_latency_profile = CHN_LATENCY_PROFILE_DEFAULT;
+
+ if (feeder_rate_min < FEEDRATE_MIN ||
+ feeder_rate_max < FEEDRATE_MIN ||
+ feeder_rate_min > FEEDRATE_MAX ||
+ feeder_rate_max > FEEDRATE_MAX ||
+ !(feeder_rate_min < feeder_rate_max)) {
+ feeder_rate_min = FEEDRATE_RATEMIN;
+ feeder_rate_max = FEEDRATE_RATEMAX;
+ }
+
+ if (feeder_rate_round < FEEDRATE_ROUNDHZ_MIN ||
+ feeder_rate_round > FEEDRATE_ROUNDHZ_MAX)
+ feeder_rate_round = FEEDRATE_ROUNDHZ;
+
+ if (bootverbose)
+ printf("%s: snd_unit=%d snd_vchans_enable=%d "
+ "latency=%d "
+ "feeder_rate_min=%d feeder_rate_max=%d "
+ "feeder_rate_round=%d\n",
+ __func__, snd_unit, snd_vchans_enable,
+ chn_latency,
+ feeder_rate_min, feeder_rate_max,
+ feeder_rate_round);
+}
+
static int
sound_modevent(module_t mod, int type, void *data)
{
@@ -1133,20 +721,21 @@ sound_modevent(module_t mod, int type, void *data)
ret = 0;
switch (type) {
- case MOD_LOAD:
- pcm_devclass = devclass_create("pcm");
- pcmsg_unrhdr = new_unrhdr(1, INT_MAX, NULL);
- break;
- case MOD_UNLOAD:
- if (pcmsg_unrhdr != NULL) {
- delete_unrhdr(pcmsg_unrhdr);
- pcmsg_unrhdr = NULL;
- }
- break;
- case MOD_SHUTDOWN:
- break;
- default:
- ret = ENOTSUP;
+ case MOD_LOAD:
+ pcm_devclass = devclass_create("pcm");
+ pcmsg_unrhdr = new_unrhdr(1, INT_MAX, NULL);
+ sound_global_init();
+ break;
+ case MOD_UNLOAD:
+ if (pcmsg_unrhdr != NULL) {
+ delete_unrhdr(pcmsg_unrhdr);
+ pcmsg_unrhdr = NULL;
+ }
+ break;
+ case MOD_SHUTDOWN:
+ break;
+ default:
+ ret = ENOTSUP;
}
return ret;
diff --git a/sys/dev/sound/pcm/sound.h b/sys/dev/sound/pcm/sound.h
index 08aa56cc96f7..315452e294d1 100644
--- a/sys/dev/sound/pcm/sound.h
+++ b/sys/dev/sound/pcm/sound.h
@@ -5,6 +5,10 @@
* Copyright (c) 1999 Cameron Grant <cg@FreeBSD.org>
* Copyright (c) 1995 Hannu Savolainen
* All rights reserved.
+ * Copyright (c) 2024-2025 The FreeBSD Foundation
+ *
+ * Portions of this software were developed by Christos Margiolis
+ * <christos@FreeBSD.org> under sponsorship from the FreeBSD Foundation.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -83,7 +87,6 @@ struct snd_mixer;
#include <dev/sound/pcm/buffer.h>
#include <dev/sound/pcm/matrix.h>
-#include <dev/sound/pcm/matrix_map.h>
#include <dev/sound/pcm/channel.h>
#include <dev/sound/pcm/feeder.h>
#include <dev/sound/pcm/mixer.h>
@@ -99,213 +102,78 @@ struct snd_mixer;
#define SOUND_PREFVER SOUND_MODVER
#define SOUND_MAXVER SOUND_MODVER
-/*
- * By design, limit possible channels for each direction.
- */
-#define SND_MAXHWCHAN 256
-#define SND_MAXVCHANS SND_MAXHWCHAN
-
#define SD_F_SIMPLEX 0x00000001
-#define SD_F_AUTOVCHAN 0x00000002
+/* unused 0x00000002 */
#define SD_F_SOFTPCMVOL 0x00000004
-#define SD_F_DYING 0x00000008
-#define SD_F_DETACHING 0x00000010
-#define SD_F_BUSY 0x00000020
-#define SD_F_MPSAFE 0x00000040
-#define SD_F_REGISTERED 0x00000080
-#define SD_F_BITPERFECT 0x00000100
-#define SD_F_VPC 0x00000200 /* volume-per-channel */
-#define SD_F_EQ 0x00000400 /* EQ */
-#define SD_F_EQ_ENABLED 0x00000800 /* EQ enabled */
-#define SD_F_EQ_BYPASSED 0x00001000 /* EQ bypassed */
-#define SD_F_EQ_PC 0x00002000 /* EQ per-channel */
+#define SD_F_BUSY 0x00000008
+#define SD_F_MPSAFE 0x00000010
+#define SD_F_REGISTERED 0x00000020
+#define SD_F_BITPERFECT 0x00000040
+#define SD_F_VPC 0x00000080 /* volume-per-channel */
+#define SD_F_EQ 0x00000100 /* EQ */
+#define SD_F_EQ_ENABLED 0x00000200 /* EQ enabled */
+#define SD_F_EQ_BYPASSED 0x00000400 /* EQ bypassed */
+#define SD_F_EQ_PC 0x00000800 /* EQ per-channel */
+#define SD_F_PVCHANS 0x00001000 /* Playback vchans enabled */
+#define SD_F_RVCHANS 0x00002000 /* Recording vchans enabled */
#define SD_F_EQ_DEFAULT (SD_F_EQ | SD_F_EQ_ENABLED)
#define SD_F_EQ_MASK (SD_F_EQ | SD_F_EQ_ENABLED | \
SD_F_EQ_BYPASSED | SD_F_EQ_PC)
-#define SD_F_PRIO_RD 0x10000000
-#define SD_F_PRIO_WR 0x20000000
-#define SD_F_PRIO_SET (SD_F_PRIO_RD | SD_F_PRIO_WR)
-#define SD_F_DIR_SET 0x40000000
-#define SD_F_TRANSIENT 0xf0000000
-
#define SD_F_BITS "\020" \
"\001SIMPLEX" \
- "\002AUTOVCHAN" \
+ /* "\002 */ \
"\003SOFTPCMVOL" \
- "\004DYING" \
- "\005DETACHING" \
- "\006BUSY" \
- "\007MPSAFE" \
- "\010REGISTERED" \
- "\011BITPERFECT" \
- "\012VPC" \
- "\013EQ" \
- "\014EQ_ENABLED" \
- "\015EQ_BYPASSED" \
- "\016EQ_PC" \
- "\035PRIO_RD" \
- "\036PRIO_WR" \
- "\037DIR_SET"
-
-#define PCM_ALIVE(x) ((x) != NULL && (x)->lock != NULL && \
- !((x)->flags & SD_F_DYING))
-#define PCM_REGISTERED(x) (PCM_ALIVE(x) && \
- ((x)->flags & SD_F_REGISTERED))
-
-#define PCM_DETACHING(x) ((x)->flags & SD_F_DETACHING)
-
+ "\004BUSY" \
+ "\005MPSAFE" \
+ "\006REGISTERED" \
+ "\007BITPERFECT" \
+ "\010VPC" \
+ "\011EQ" \
+ "\012EQ_ENABLED" \
+ "\013EQ_BYPASSED" \
+ "\014EQ_PC" \
+ "\015PVCHANS" \
+ "\016RVCHANS"
+
+#define PCM_ALIVE(x) ((x) != NULL && (x)->lock != NULL)
+#define PCM_REGISTERED(x) (PCM_ALIVE(x) && ((x)->flags & SD_F_REGISTERED))
+
+#define PCM_MAXCHANS 10000
#define PCM_CHANCOUNT(d) \
(d->playcount + d->pvchancount + d->reccount + d->rvchancount)
/* many variables should be reduced to a range. Here define a macro */
#define RANGE(var, low, high) (var) = \
(((var)<(low))? (low) : ((var)>(high))? (high) : (var))
-#define DSP_BUFFSIZE (8192)
-
-/* make figuring out what a format is easier. got AFMT_STEREO already */
-#define AFMT_32BIT (AFMT_S32_LE | AFMT_S32_BE | AFMT_U32_LE | AFMT_U32_BE)
-#define AFMT_24BIT (AFMT_S24_LE | AFMT_S24_BE | AFMT_U24_LE | AFMT_U24_BE)
-#define AFMT_16BIT (AFMT_S16_LE | AFMT_S16_BE | AFMT_U16_LE | AFMT_U16_BE)
-#define AFMT_G711 (AFMT_MU_LAW | AFMT_A_LAW)
-#define AFMT_8BIT (AFMT_G711 | AFMT_U8 | AFMT_S8)
-#define AFMT_SIGNED (AFMT_S32_LE | AFMT_S32_BE | AFMT_S24_LE | AFMT_S24_BE | \
- AFMT_S16_LE | AFMT_S16_BE | AFMT_S8)
-#define AFMT_BIGENDIAN (AFMT_S32_BE | AFMT_U32_BE | AFMT_S24_BE | AFMT_U24_BE | \
- AFMT_S16_BE | AFMT_U16_BE)
-
-#define AFMT_CONVERTIBLE (AFMT_8BIT | AFMT_16BIT | AFMT_24BIT | \
- AFMT_32BIT)
-
-/* Supported vchan mixing formats */
-#define AFMT_VCHAN (AFMT_CONVERTIBLE & ~AFMT_G711)
-#define AFMT_PASSTHROUGH AFMT_AC3
-#define AFMT_PASSTHROUGH_RATE 48000
-#define AFMT_PASSTHROUGH_CHANNEL 2
-#define AFMT_PASSTHROUGH_EXTCHANNEL 0
-
-/*
- * We're simply using unused, contiguous bits from various AFMT_ definitions.
- * ~(0xb00ff7ff)
- */
-#define AFMT_ENCODING_MASK 0xf00fffff
-#define AFMT_CHANNEL_MASK 0x07f00000
-#define AFMT_CHANNEL_SHIFT 20
-#define AFMT_CHANNEL_MAX 0x7f
-#define AFMT_EXTCHANNEL_MASK 0x08000000
-#define AFMT_EXTCHANNEL_SHIFT 27
-#define AFMT_EXTCHANNEL_MAX 1
-
-#define AFMT_ENCODING(v) ((v) & AFMT_ENCODING_MASK)
-
-#define AFMT_EXTCHANNEL(v) (((v) & AFMT_EXTCHANNEL_MASK) >> \
- AFMT_EXTCHANNEL_SHIFT)
-
-#define AFMT_CHANNEL(v) (((v) & AFMT_CHANNEL_MASK) >> \
- AFMT_CHANNEL_SHIFT)
-
-#define AFMT_BIT(v) (((v) & AFMT_32BIT) ? 32 : \
- (((v) & AFMT_24BIT) ? 24 : \
- ((((v) & AFMT_16BIT) || \
- ((v) & AFMT_PASSTHROUGH)) ? 16 : 8)))
-
-#define AFMT_BPS(v) (AFMT_BIT(v) >> 3)
-#define AFMT_ALIGN(v) (AFMT_BPS(v) * AFMT_CHANNEL(v))
-
-#define SND_FORMAT(f, c, e) (AFMT_ENCODING(f) | \
- (((c) << AFMT_CHANNEL_SHIFT) & \
- AFMT_CHANNEL_MASK) | \
- (((e) << AFMT_EXTCHANNEL_SHIFT) & \
- AFMT_EXTCHANNEL_MASK))
-
-#define AFMT_U8_NE AFMT_U8
-#define AFMT_S8_NE AFMT_S8
-
-#define AFMT_SIGNED_NE (AFMT_S8_NE | AFMT_S16_NE | AFMT_S24_NE | AFMT_S32_NE)
-
-#define AFMT_NE (AFMT_SIGNED_NE | AFMT_U8_NE | AFMT_U16_NE | \
- AFMT_U24_NE | AFMT_U32_NE)
-
-/*
- * Minor numbers for the sound driver.
- *
- * Unfortunately Creative called the codec chip of SB as a DSP. For this
- * reason the /dev/dsp is reserved for digitized audio use. There is a
- * device for true DSP processors but it will be called something else.
- * In v3.0 it's /dev/sndproc but this could be a temporary solution.
- */
-
-#define SND_DEV_CTL 0 /* Control port /dev/mixer */
-#define SND_DEV_SEQ 1 /* Sequencer /dev/sequencer */
-#define SND_DEV_MIDIN 2 /* Raw midi access */
-#define SND_DEV_DSP 3 /* Digitized voice /dev/dsp */
-#define SND_DEV_AUDIO 4 /* Sparc compatible /dev/audio */
-#define SND_DEV_DSP16 5 /* Like /dev/dsp but 16 bits/sample */
-#define SND_DEV_STATUS 6 /* /dev/sndstat */
- /* #7 not in use now. */
-#define SND_DEV_SEQ2 8 /* /dev/sequencer, level 2 interface */
-#define SND_DEV_SNDPROC 9 /* /dev/sndproc for programmable devices */
-#define SND_DEV_PSS SND_DEV_SNDPROC /* ? */
-#define SND_DEV_NORESET 10
-
-#define SND_DEV_DSPHW_PLAY 11 /* specific playback channel */
-#define SND_DEV_DSPHW_VPLAY 12 /* specific virtual playback channel */
-#define SND_DEV_DSPHW_REC 13 /* specific record channel */
-#define SND_DEV_DSPHW_VREC 14 /* specific virtual record channel */
-
-#define SND_DEV_DSPHW_CD 15 /* s16le/stereo 44100Hz CD */
-
-/*
- * OSSv4 compatible device. For now, it serve no purpose and
- * the cloning itself will forward the request to ordinary /dev/dsp
- * instead.
- */
-#define SND_DEV_DSP_MMAP 16 /* /dev/dsp_mmap */
-#define SND_DEV_DSP_AC3 17 /* /dev/dsp_ac3 */
-#define SND_DEV_DSP_MULTICH 18 /* /dev/dsp_multich */
-#define SND_DEV_DSP_SPDIFOUT 19 /* /dev/dsp_spdifout */
-#define SND_DEV_DSP_SPDIFIN 20 /* /dev/dsp_spdifin */
+enum {
+ SND_DEV_CTL = 0, /* Control port /dev/mixer */
+ SND_DEV_SEQ, /* Sequencer /dev/sequencer */
+ SND_DEV_MIDIN, /* Raw midi access */
+ SND_DEV_DSP, /* Digitized voice /dev/dsp */
+ SND_DEV_STATUS, /* /dev/sndstat */
+};
#define DSP_DEFAULT_SPEED 8000
-#define ON 1
-#define OFF 0
-
-extern int pcm_veto_load;
extern int snd_unit;
-extern int snd_maxautovchans;
extern int snd_verbose;
extern devclass_t pcm_devclass;
extern struct unrhdr *pcmsg_unrhdr;
-/*
- * some macros for debugging purposes
- * DDB/DEB to enable/disable debugging stuff
- * BVDDB to enable debugging when bootverbose
- */
-#define BVDDB(x) if (bootverbose) x
-
#ifndef DEB
#define DEB(x)
#endif
SYSCTL_DECL(_hw_snd);
-int pcm_setvchans(struct snddev_info *d, int direction, int newcnt, int num);
-int pcm_chnalloc(struct snddev_info *d, struct pcm_channel **ch, int direction,
- pid_t pid, char *comm);
-
-struct pcm_channel *pcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t cls, int dir, int num, void *devinfo);
-int pcm_chn_add(struct snddev_info *d, struct pcm_channel *ch);
-int pcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch);
-
int pcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo);
unsigned int pcm_getbuffersize(device_t dev, unsigned int minbufsz, unsigned int deflt, unsigned int maxbufsz);
-int pcm_register(device_t dev, void *devinfo, int numplay, int numrec);
+void pcm_init(device_t dev, void *devinfo);
+int pcm_register(device_t dev, char *str);
int pcm_unregister(device_t dev);
-int pcm_setstatus(device_t dev, char *str);
u_int32_t pcm_getflags(device_t dev);
void pcm_setflags(device_t dev, u_int32_t val);
void *pcm_getdevinfo(device_t dev);
@@ -322,6 +190,22 @@ void snd_mtxassert(void *m);
int sndstat_register(device_t dev, char *str);
int sndstat_unregister(device_t dev);
+/* These are the function codes assigned to the children of sound cards. */
+enum {
+ SCF_PCM,
+ SCF_MIDI,
+ SCF_SYNTH,
+};
+
+/*
+ * This is the device information struct, used by a bridge device to pass the
+ * device function code to the children.
+ */
+struct sndcard_func {
+ int func; /* The function code. */
+ void *varinfo; /* Bridge-specific information. */
+};
+
/*
* this is rather kludgey- we need to duplicate these struct def'ns from sound.c
* so that the macro versions of pcm_{,un}lock can dereference them.
@@ -338,6 +222,9 @@ struct snddev_info {
struct {
SLIST_HEAD(, pcm_channel) head;
} opened;
+ struct {
+ SLIST_HEAD(, pcm_channel) head;
+ } primary;
} pcm;
} channels;
unsigned playcount, reccount, pvchancount, rvchancount;
@@ -349,12 +236,16 @@ struct snddev_info {
struct mtx *lock;
struct cdev *mixer_dev;
struct cdev *dsp_dev;
- uint32_t pvchanrate, pvchanformat;
- uint32_t rvchanrate, rvchanformat;
+ uint32_t pvchanrate, pvchanformat, pvchanmode;
+ uint32_t rvchanrate, rvchanformat, rvchanmode;
int32_t eqpreamp;
struct sysctl_ctx_list play_sysctl_ctx, rec_sysctl_ctx;
struct sysctl_oid *play_sysctl_tree, *rec_sysctl_tree;
struct cv cv;
+ struct unrhdr *p_unr;
+ struct unrhdr *vp_unr;
+ struct unrhdr *r_unr;
+ struct unrhdr *vr_unr;
};
void sound_oss_sysinfo(oss_sysinfo *);
@@ -405,15 +296,7 @@ int sound_oss_card_info(oss_card_info *);
__func__, __LINE__); \
if ((x)->flags & SD_F_BUSY) { \
(x)->flags &= ~SD_F_BUSY; \
- if ((x)->cv.cv_waiters != 0) { \
- if ((x)->cv.cv_waiters > 1 && snd_verbose > 3) \
- device_printf((x)->dev, \
- "%s(%d): [PCM RELEASE] " \
- "cv_waiters=%d > 1!\n", \
- __func__, __LINE__, \
- (x)->cv.cv_waiters); \
- cv_broadcast(&(x)->cv); \
- } \
+ cv_broadcast(&(x)->cv); \
} else \
panic("%s(%d): [PCM RELEASE] Releasing non-BUSY cv!", \
__func__, __LINE__); \
@@ -505,8 +388,7 @@ int sound_oss_card_info(oss_card_info *);
("%s(%d): [PCM RELEASE] Releasing non-BUSY cv!", \
__func__, __LINE__)); \
(x)->flags &= ~SD_F_BUSY; \
- if ((x)->cv.cv_waiters != 0) \
- cv_broadcast(&(x)->cv); \
+ cv_broadcast(&(x)->cv); \
} while (0)
/* Quick version, for shorter path. */
@@ -563,4 +445,71 @@ int sound_oss_card_info(oss_card_info *);
#endif /* _KERNEL */
+/* make figuring out what a format is easier. got AFMT_STEREO already */
+#define AFMT_32BIT (AFMT_S32_LE | AFMT_S32_BE | AFMT_U32_LE | AFMT_U32_BE | \
+ AFMT_F32_LE | AFMT_F32_BE)
+#define AFMT_24BIT (AFMT_S24_LE | AFMT_S24_BE | AFMT_U24_LE | AFMT_U24_BE)
+#define AFMT_16BIT (AFMT_S16_LE | AFMT_S16_BE | AFMT_U16_LE | AFMT_U16_BE)
+#define AFMT_G711 (AFMT_MU_LAW | AFMT_A_LAW)
+#define AFMT_8BIT (AFMT_G711 | AFMT_U8 | AFMT_S8)
+#define AFMT_SIGNED (AFMT_S32_LE | AFMT_S32_BE | AFMT_F32_LE | AFMT_F32_BE | \
+ AFMT_S24_LE | AFMT_S24_BE | \
+ AFMT_S16_LE | AFMT_S16_BE | AFMT_S8)
+#define AFMT_BIGENDIAN (AFMT_S32_BE | AFMT_U32_BE | AFMT_F32_BE | \
+ AFMT_S24_BE | AFMT_U24_BE | AFMT_S16_BE | AFMT_U16_BE)
+
+#define AFMT_CONVERTIBLE (AFMT_8BIT | AFMT_16BIT | AFMT_24BIT | \
+ AFMT_32BIT)
+
+/* Supported vchan mixing formats */
+#define AFMT_VCHAN (AFMT_CONVERTIBLE & ~AFMT_G711)
+
+#define AFMT_PASSTHROUGH AFMT_AC3
+#define AFMT_PASSTHROUGH_RATE 48000
+#define AFMT_PASSTHROUGH_CHANNEL 2
+#define AFMT_PASSTHROUGH_EXTCHANNEL 0
+
+/*
+ * We're simply using unused, contiguous bits from various AFMT_ definitions.
+ * ~(0xb00ff7ff)
+ */
+#define AFMT_ENCODING_MASK 0xf00fffff
+#define AFMT_CHANNEL_MASK 0x07f00000
+#define AFMT_CHANNEL_SHIFT 20
+#define AFMT_CHANNEL_MAX 0x7f
+#define AFMT_EXTCHANNEL_MASK 0x08000000
+#define AFMT_EXTCHANNEL_SHIFT 27
+#define AFMT_EXTCHANNEL_MAX 1
+
+#define AFMT_ENCODING(v) ((v) & AFMT_ENCODING_MASK)
+
+#define AFMT_EXTCHANNEL(v) (((v) & AFMT_EXTCHANNEL_MASK) >> \
+ AFMT_EXTCHANNEL_SHIFT)
+
+#define AFMT_CHANNEL(v) (((v) & AFMT_CHANNEL_MASK) >> \
+ AFMT_CHANNEL_SHIFT)
+
+#define AFMT_BIT(v) (((v) & AFMT_32BIT) ? 32 : \
+ (((v) & AFMT_24BIT) ? 24 : \
+ ((((v) & AFMT_16BIT) || \
+ ((v) & AFMT_PASSTHROUGH)) ? 16 : 8)))
+
+#define AFMT_BPS(v) (AFMT_BIT(v) >> 3)
+#define AFMT_ALIGN(v) (AFMT_BPS(v) * AFMT_CHANNEL(v))
+
+#define SND_FORMAT(f, c, e) (AFMT_ENCODING(f) | \
+ (((c) << AFMT_CHANNEL_SHIFT) & \
+ AFMT_CHANNEL_MASK) | \
+ (((e) << AFMT_EXTCHANNEL_SHIFT) & \
+ AFMT_EXTCHANNEL_MASK))
+
+#define AFMT_U8_NE AFMT_U8
+#define AFMT_S8_NE AFMT_S8
+
+#define AFMT_SIGNED_NE (AFMT_S8_NE | AFMT_S16_NE | AFMT_S24_NE | \
+ AFMT_S32_NE | AFMT_F32_NE)
+
+#define AFMT_NE (AFMT_SIGNED_NE | AFMT_U8_NE | AFMT_U16_NE | \
+ AFMT_U24_NE | AFMT_U32_NE)
+
#endif /* _OS_H_ */
diff --git a/sys/dev/sound/pcm/vchan.c b/sys/dev/sound/pcm/vchan.c
index c3bc36d924bd..31a4f7db8d70 100644
--- a/sys/dev/sound/pcm/vchan.c
+++ b/sys/dev/sound/pcm/vchan.c
@@ -4,6 +4,10 @@
* Copyright (c) 2006-2009 Ariff Abdullah <ariff@FreeBSD.org>
* Copyright (c) 2001 Cameron Grant <cg@FreeBSD.org>
* All rights reserved.
+ * Copyright (c) 2024-2025 The FreeBSD Foundation
+ *
+ * Portions of this software were developed by Christos Margiolis
+ * <christos@FreeBSD.org> under sponsorship from the FreeBSD Foundation.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -57,6 +61,8 @@ struct vchan_info {
int trigger;
};
+bool snd_vchans_enable = true;
+
static void *
vchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b,
struct pcm_channel *c, int dir)
@@ -140,20 +146,19 @@ vchan_trigger(kobj_t obj, void *data, int go)
int ret, otrigger;
info = data;
-
- if (!PCMTRIG_COMMON(go) || go == info->trigger)
- return (0);
-
c = info->channel;
p = c->parentchannel;
- otrigger = info->trigger;
- info->trigger = go;
CHN_LOCKASSERT(c);
+ if (!PCMTRIG_COMMON(go) || go == info->trigger)
+ return (0);
CHN_UNLOCK(c);
CHN_LOCK(p);
+ otrigger = info->trigger;
+ info->trigger = go;
+
switch (go) {
case PCMTRIG_START:
if (otrigger != PCMTRIG_START)
@@ -248,101 +253,63 @@ static kobj_method_t vchan_methods[] = {
};
CHANNEL_DECLARE(vchan);
-static void
-pcm_getparentchannel(struct snddev_info *d,
- struct pcm_channel **wrch, struct pcm_channel **rdch)
-{
- struct pcm_channel **ch, *wch, *rch, *c;
-
- KASSERT(d != NULL, ("%s(): NULL snddev_info", __func__));
-
- PCM_BUSYASSERT(d);
- PCM_UNLOCKASSERT(d);
-
- wch = NULL;
- rch = NULL;
-
- CHN_FOREACH(c, d, channels.pcm) {
- CHN_LOCK(c);
- ch = (c->direction == PCMDIR_PLAY) ? &wch : &rch;
- if (c->flags & CHN_F_VIRTUAL) {
- /* Sanity check */
- if (*ch != NULL && *ch != c->parentchannel) {
- CHN_UNLOCK(c);
- *ch = NULL;
- break;
- }
- } else if (c->flags & CHN_F_HAS_VCHAN) {
- /* No way!! */
- if (*ch != NULL) {
- CHN_UNLOCK(c);
- *ch = NULL;
- break;
- }
- *ch = c;
- }
- CHN_UNLOCK(c);
- }
-
- if (wrch != NULL)
- *wrch = wch;
- if (rdch != NULL)
- *rdch = rch;
-}
-
static int
sysctl_dev_pcm_vchans(SYSCTL_HANDLER_ARGS)
{
struct snddev_info *d;
- int direction, vchancount;
- int err, cnt;
+ int err, enabled, flag;
+ bus_topo_lock();
d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1));
- if (!PCM_REGISTERED(d) || !(d->flags & SD_F_AUTOVCHAN))
+ if (!PCM_REGISTERED(d)) {
+ bus_topo_unlock();
return (EINVAL);
+ }
+ bus_topo_unlock();
PCM_LOCK(d);
PCM_WAIT(d);
switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) {
case VCHAN_PLAY:
- direction = PCMDIR_PLAY;
- vchancount = d->pvchancount;
- cnt = d->playcount;
+ /* Exit if we do not support this direction. */
+ if (d->playcount < 1) {
+ PCM_UNLOCK(d);
+ return (ENODEV);
+ }
+ flag = SD_F_PVCHANS;
break;
case VCHAN_REC:
- direction = PCMDIR_REC;
- vchancount = d->rvchancount;
- cnt = d->reccount;
+ if (d->reccount < 1) {
+ PCM_UNLOCK(d);
+ return (ENODEV);
+ }
+ flag = SD_F_RVCHANS;
break;
default:
PCM_UNLOCK(d);
return (EINVAL);
- break;
}
- if (cnt < 1) {
- PCM_UNLOCK(d);
- return (ENODEV);
- }
+ enabled = (d->flags & flag) != 0;
PCM_ACQUIRE(d);
PCM_UNLOCK(d);
- cnt = vchancount;
- err = sysctl_handle_int(oidp, &cnt, 0, req);
-
- if (err == 0 && req->newptr != NULL && vchancount != cnt) {
- if (cnt < 0)
- cnt = 0;
- if (cnt > SND_MAXVCHANS)
- cnt = SND_MAXVCHANS;
- err = pcm_setvchans(d, direction, cnt, -1);
+ err = sysctl_handle_int(oidp, &enabled, 0, req);
+ if (err != 0 || req->newptr == NULL) {
+ PCM_RELEASE_QUICK(d);
+ return (err);
}
+ if (enabled <= 0)
+ d->flags &= ~flag;
+ else
+ d->flags |= flag;
+
PCM_RELEASE_QUICK(d);
- return err;
+ return (0);
}
static int
@@ -351,79 +318,81 @@ sysctl_dev_pcm_vchanmode(SYSCTL_HANDLER_ARGS)
struct snddev_info *d;
struct pcm_channel *c;
uint32_t dflags;
- int direction, ret;
+ int *vchanmode, direction, ret;
char dtype[16];
+ bus_topo_lock();
d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1));
- if (!PCM_REGISTERED(d) || !(d->flags & SD_F_AUTOVCHAN))
+ if (!PCM_REGISTERED(d)) {
+ bus_topo_unlock();
return (EINVAL);
+ }
+ bus_topo_unlock();
PCM_LOCK(d);
PCM_WAIT(d);
switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) {
case VCHAN_PLAY:
+ if ((d->flags & SD_F_PVCHANS) == 0) {
+ PCM_UNLOCK(d);
+ return (ENODEV);
+ }
direction = PCMDIR_PLAY;
+ vchanmode = &d->pvchanmode;
break;
case VCHAN_REC:
+ if ((d->flags & SD_F_RVCHANS) == 0) {
+ PCM_UNLOCK(d);
+ return (ENODEV);
+ }
direction = PCMDIR_REC;
+ vchanmode = &d->rvchanmode;
break;
default:
PCM_UNLOCK(d);
return (EINVAL);
- break;
}
PCM_ACQUIRE(d);
PCM_UNLOCK(d);
- if (direction == PCMDIR_PLAY)
- pcm_getparentchannel(d, &c, NULL);
- else
- pcm_getparentchannel(d, NULL, &c);
-
- if (c == NULL) {
- PCM_RELEASE_QUICK(d);
- return (EINVAL);
- }
-
- KASSERT(direction == c->direction, ("%s(): invalid direction %d/%d",
- __func__, direction, c->direction));
-
- CHN_LOCK(c);
- if (c->flags & CHN_F_VCHAN_PASSTHROUGH)
+ if (*vchanmode & CHN_F_VCHAN_PASSTHROUGH)
strlcpy(dtype, "passthrough", sizeof(dtype));
- else if (c->flags & CHN_F_VCHAN_ADAPTIVE)
+ else if (*vchanmode & CHN_F_VCHAN_ADAPTIVE)
strlcpy(dtype, "adaptive", sizeof(dtype));
else
strlcpy(dtype, "fixed", sizeof(dtype));
- CHN_UNLOCK(c);
ret = sysctl_handle_string(oidp, dtype, sizeof(dtype), req);
- if (ret == 0 && req->newptr != NULL) {
- if (strcasecmp(dtype, "passthrough") == 0 ||
- strcmp(dtype, "1") == 0)
- dflags = CHN_F_VCHAN_PASSTHROUGH;
- else if (strcasecmp(dtype, "adaptive") == 0 ||
- strcmp(dtype, "2") == 0)
- dflags = CHN_F_VCHAN_ADAPTIVE;
- else if (strcasecmp(dtype, "fixed") == 0 ||
- strcmp(dtype, "0") == 0)
- dflags = 0;
- else {
- PCM_RELEASE_QUICK(d);
- return (EINVAL);
- }
+ if (ret != 0 || req->newptr == NULL) {
+ PCM_RELEASE_QUICK(d);
+ return (ret);
+ }
+
+ if (strcasecmp(dtype, "passthrough") == 0 || strcmp(dtype, "1") == 0)
+ dflags = CHN_F_VCHAN_PASSTHROUGH;
+ else if (strcasecmp(dtype, "adaptive") == 0 || strcmp(dtype, "2") == 0)
+ dflags = CHN_F_VCHAN_ADAPTIVE;
+ else if (strcasecmp(dtype, "fixed") == 0 || strcmp(dtype, "0") == 0)
+ dflags = 0;
+ else {
+ PCM_RELEASE_QUICK(d);
+ return (EINVAL);
+ }
+
+ CHN_FOREACH(c, d, channels.pcm.primary) {
CHN_LOCK(c);
- if (dflags == (c->flags & CHN_F_VCHAN_DYNAMIC) ||
+ if (c->direction != direction ||
+ dflags == (c->flags & CHN_F_VCHAN_DYNAMIC) ||
(c->flags & CHN_F_PASSTHROUGH)) {
CHN_UNLOCK(c);
- PCM_RELEASE_QUICK(d);
- return (0);
+ continue;
}
c->flags &= ~CHN_F_VCHAN_DYNAMIC;
c->flags |= dflags;
CHN_UNLOCK(c);
+ *vchanmode = dflags;
}
PCM_RELEASE_QUICK(d);
@@ -444,57 +413,45 @@ sysctl_dev_pcm_vchanrate(SYSCTL_HANDLER_ARGS)
{
struct snddev_info *d;
struct pcm_channel *c, *ch;
- struct pcmchan_caps *caps;
- int *vchanrate, vchancount, direction, ret, newspd, restart;
+ int *vchanrate, direction, ret, newspd, restart;
+ bus_topo_lock();
d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1));
- if (!PCM_REGISTERED(d) || !(d->flags & SD_F_AUTOVCHAN))
+ if (!PCM_REGISTERED(d)) {
+ bus_topo_unlock();
return (EINVAL);
+ }
+ bus_topo_unlock();
PCM_LOCK(d);
PCM_WAIT(d);
switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) {
case VCHAN_PLAY:
+ if ((d->flags & SD_F_PVCHANS) == 0) {
+ PCM_UNLOCK(d);
+ return (ENODEV);
+ }
direction = PCMDIR_PLAY;
- vchancount = d->pvchancount;
vchanrate = &d->pvchanrate;
break;
case VCHAN_REC:
+ if ((d->flags & SD_F_RVCHANS) == 0) {
+ PCM_UNLOCK(d);
+ return (ENODEV);
+ }
direction = PCMDIR_REC;
- vchancount = d->rvchancount;
vchanrate = &d->rvchanrate;
break;
default:
PCM_UNLOCK(d);
return (EINVAL);
- break;
- }
-
- if (vchancount < 1) {
- PCM_UNLOCK(d);
- return (EINVAL);
}
PCM_ACQUIRE(d);
PCM_UNLOCK(d);
- if (direction == PCMDIR_PLAY)
- pcm_getparentchannel(d, &c, NULL);
- else
- pcm_getparentchannel(d, NULL, &c);
-
- if (c == NULL) {
- PCM_RELEASE_QUICK(d);
- return (EINVAL);
- }
-
- KASSERT(direction == c->direction, ("%s(): invalid direction %d/%d",
- __func__, direction, c->direction));
-
- CHN_LOCK(c);
- newspd = c->speed;
- CHN_UNLOCK(c);
+ newspd = *vchanrate;
ret = sysctl_handle_int(oidp, &newspd, 0, req);
if (ret != 0 || req->newptr == NULL) {
@@ -502,45 +459,43 @@ sysctl_dev_pcm_vchanrate(SYSCTL_HANDLER_ARGS)
return (ret);
}
- if (newspd < 1 || newspd < feeder_rate_min ||
- newspd > feeder_rate_max) {
+ if (newspd < feeder_rate_min || newspd > feeder_rate_max) {
PCM_RELEASE_QUICK(d);
return (EINVAL);
}
- CHN_LOCK(c);
-
- if (newspd != c->speed && VCHAN_ACCESSIBLE(c)) {
- if (CHN_STARTED(c)) {
- chn_abort(c);
- restart = 1;
- } else
- restart = 0;
-
- if (feeder_rate_round) {
- caps = chn_getcaps(c);
- RANGE(newspd, caps->minspeed, caps->maxspeed);
- newspd = CHANNEL_SETSPEED(c->methods,
- c->devinfo, newspd);
+ CHN_FOREACH(c, d, channels.pcm.primary) {
+ CHN_LOCK(c);
+ if (c->direction != direction) {
+ CHN_UNLOCK(c);
+ continue;
}
- ret = chn_reset(c, c->format, newspd);
- if (ret == 0) {
- *vchanrate = c->speed;
- if (restart != 0) {
- CHN_FOREACH(ch, c, children.busy) {
- CHN_LOCK(ch);
- if (VCHAN_SYNC_REQUIRED(ch))
- vchan_sync(ch);
- CHN_UNLOCK(ch);
+ if (newspd != c->speed && VCHAN_ACCESSIBLE(c)) {
+ if (CHN_STARTED(c)) {
+ chn_abort(c);
+ restart = 1;
+ } else
+ restart = 0;
+
+ ret = chn_reset(c, c->format, newspd);
+ if (ret == 0) {
+ if (restart != 0) {
+ CHN_FOREACH(ch, c, children.busy) {
+ CHN_LOCK(ch);
+ if (VCHAN_SYNC_REQUIRED(ch))
+ vchan_sync(ch);
+ CHN_UNLOCK(ch);
+ }
+ c->flags |= CHN_F_DIRTY;
+ ret = chn_start(c, 1);
}
- c->flags |= CHN_F_DIRTY;
- ret = chn_start(c, 1);
}
}
- }
+ *vchanrate = sndbuf_getspd(c->bufsoft);
- CHN_UNLOCK(c);
+ CHN_UNLOCK(c);
+ }
PCM_RELEASE_QUICK(d);
@@ -553,63 +508,50 @@ sysctl_dev_pcm_vchanformat(SYSCTL_HANDLER_ARGS)
struct snddev_info *d;
struct pcm_channel *c, *ch;
uint32_t newfmt;
- int *vchanformat, vchancount, direction, ret, restart;
+ int *vchanformat, direction, ret, restart;
char fmtstr[AFMTSTR_LEN];
+ bus_topo_lock();
d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1));
- if (!PCM_REGISTERED(d) || !(d->flags & SD_F_AUTOVCHAN))
+ if (!PCM_REGISTERED(d)) {
+ bus_topo_unlock();
return (EINVAL);
+ }
+ bus_topo_unlock();
PCM_LOCK(d);
PCM_WAIT(d);
switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) {
case VCHAN_PLAY:
+ if ((d->flags & SD_F_PVCHANS) == 0) {
+ PCM_UNLOCK(d);
+ return (ENODEV);
+ }
direction = PCMDIR_PLAY;
- vchancount = d->pvchancount;
vchanformat = &d->pvchanformat;
break;
case VCHAN_REC:
+ if ((d->flags & SD_F_RVCHANS) == 0) {
+ PCM_UNLOCK(d);
+ return (ENODEV);
+ }
direction = PCMDIR_REC;
- vchancount = d->rvchancount;
vchanformat = &d->rvchanformat;
break;
default:
PCM_UNLOCK(d);
return (EINVAL);
- break;
- }
-
- if (vchancount < 1) {
- PCM_UNLOCK(d);
- return (EINVAL);
}
PCM_ACQUIRE(d);
PCM_UNLOCK(d);
- if (direction == PCMDIR_PLAY)
- pcm_getparentchannel(d, &c, NULL);
- else
- pcm_getparentchannel(d, NULL, &c);
-
- if (c == NULL) {
- PCM_RELEASE_QUICK(d);
- return (EINVAL);
- }
-
- KASSERT(direction == c->direction, ("%s(): invalid direction %d/%d",
- __func__, direction, c->direction));
-
- CHN_LOCK(c);
-
bzero(fmtstr, sizeof(fmtstr));
- if (snd_afmt2str(c->format, fmtstr, sizeof(fmtstr)) != c->format)
+ if (snd_afmt2str(*vchanformat, fmtstr, sizeof(fmtstr)) != *vchanformat)
strlcpy(fmtstr, "<ERROR>", sizeof(fmtstr));
- CHN_UNLOCK(c);
-
ret = sysctl_handle_string(oidp, fmtstr, sizeof(fmtstr), req);
if (ret != 0 || req->newptr == NULL) {
PCM_RELEASE_QUICK(d);
@@ -622,32 +564,37 @@ sysctl_dev_pcm_vchanformat(SYSCTL_HANDLER_ARGS)
return (EINVAL);
}
- CHN_LOCK(c);
-
- if (newfmt != c->format && VCHAN_ACCESSIBLE(c)) {
- if (CHN_STARTED(c)) {
- chn_abort(c);
- restart = 1;
- } else
- restart = 0;
-
- ret = chn_reset(c, newfmt, c->speed);
- if (ret == 0) {
- *vchanformat = c->format;
- if (restart != 0) {
- CHN_FOREACH(ch, c, children.busy) {
- CHN_LOCK(ch);
- if (VCHAN_SYNC_REQUIRED(ch))
- vchan_sync(ch);
- CHN_UNLOCK(ch);
+ CHN_FOREACH(c, d, channels.pcm.primary) {
+ CHN_LOCK(c);
+ if (c->direction != direction) {
+ CHN_UNLOCK(c);
+ continue;
+ }
+ if (newfmt != c->format && VCHAN_ACCESSIBLE(c)) {
+ if (CHN_STARTED(c)) {
+ chn_abort(c);
+ restart = 1;
+ } else
+ restart = 0;
+
+ ret = chn_reset(c, newfmt, c->speed);
+ if (ret == 0) {
+ if (restart != 0) {
+ CHN_FOREACH(ch, c, children.busy) {
+ CHN_LOCK(ch);
+ if (VCHAN_SYNC_REQUIRED(ch))
+ vchan_sync(ch);
+ CHN_UNLOCK(ch);
+ }
+ c->flags |= CHN_F_DIRTY;
+ ret = chn_start(c, 1);
}
- c->flags |= CHN_F_DIRTY;
- ret = chn_start(c, 1);
}
}
- }
+ *vchanformat = sndbuf_getfmt(c->bufsoft);
- CHN_UNLOCK(c);
+ CHN_UNLOCK(c);
+ }
PCM_RELEASE_QUICK(d);
@@ -662,28 +609,24 @@ sysctl_dev_pcm_vchanformat(SYSCTL_HANDLER_ARGS)
"play.vchanrate" : "rec.vchanrate"
int
-vchan_create(struct pcm_channel *parent, int num)
+vchan_create(struct pcm_channel *parent, struct pcm_channel **child)
{
struct snddev_info *d;
struct pcm_channel *ch;
struct pcmchan_caps *parent_caps;
uint32_t vchanfmt, vchanspd;
- int ret, direction, r, save;
+ int ret, direction;
+ ret = 0;
d = parent->parentsnddev;
PCM_BUSYASSERT(d);
CHN_LOCKASSERT(parent);
- if (!(parent->flags & CHN_F_BUSY))
- return (EBUSY);
-
if (!(parent->direction == PCMDIR_PLAY ||
parent->direction == PCMDIR_REC))
return (EINVAL);
- d = parent->parentsnddev;
-
CHN_UNLOCK(parent);
PCM_LOCK(d);
@@ -698,150 +641,48 @@ vchan_create(struct pcm_channel *parent, int num)
}
/* create a new playback channel */
- ch = pcm_chn_create(d, parent, &vchan_class, direction, num, parent);
+ ch = chn_init(d, parent, &vchan_class, direction, parent);
if (ch == NULL) {
PCM_UNLOCK(d);
CHN_LOCK(parent);
return (ENODEV);
}
-
- /* add us to our grandparent's channel list */
- ret = pcm_chn_add(d, ch);
PCM_UNLOCK(d);
- if (ret != 0) {
- chn_kill(ch);
- CHN_LOCK(parent);
- return (ret);
- }
CHN_LOCK(parent);
- /*
- * Add us to our parent channel's children in reverse order
- * so future destruction will pick the last (biggest number)
- * channel.
- */
- CHN_INSERT_SORT_DESCEND(parent, ch, children);
+ CHN_INSERT_SORT_ASCEND(parent, ch, children);
+
+ *child = ch;
if (parent->flags & CHN_F_HAS_VCHAN)
return (0);
- parent->flags |= CHN_F_HAS_VCHAN;
+ parent->flags |= CHN_F_HAS_VCHAN | CHN_F_BUSY;
parent_caps = chn_getcaps(parent);
- if (parent_caps == NULL)
+ if (parent_caps == NULL) {
ret = EINVAL;
-
- save = 0;
-
- if (ret == 0 && vchanfmt == 0) {
- const char *vfmt;
-
- CHN_UNLOCK(parent);
- r = resource_string_value(device_get_name(parent->dev),
- device_get_unit(parent->dev), VCHAN_FMT_HINT(direction),
- &vfmt);
- CHN_LOCK(parent);
- if (r != 0)
- vfmt = NULL;
- if (vfmt != NULL) {
- vchanfmt = snd_str2afmt(vfmt);
- if (vchanfmt != 0 && !(vchanfmt & AFMT_VCHAN))
- vchanfmt = 0;
- }
- if (vchanfmt == 0)
- vchanfmt = VCHAN_DEFAULT_FORMAT;
- save = 1;
- }
-
- if (ret == 0 && vchanspd == 0) {
- /*
- * This is very sad. Few soundcards advertised as being
- * able to do (insanely) higher/lower speed, but in
- * reality, they simply can't. At least, we give user chance
- * to set sane value via kernel hints or sysctl.
- */
- CHN_UNLOCK(parent);
- r = resource_int_value(device_get_name(parent->dev),
- device_get_unit(parent->dev), VCHAN_SPD_HINT(direction),
- &vchanspd);
- CHN_LOCK(parent);
- if (r != 0) {
- /*
- * No saved value, no hint, NOTHING.
- *
- * Workaround for sb16 running
- * poorly at 45k / 49k.
- */
- switch (parent_caps->maxspeed) {
- case 45000:
- case 49000:
- vchanspd = 44100;
- break;
- default:
- vchanspd = VCHAN_DEFAULT_RATE;
- if (vchanspd > parent_caps->maxspeed)
- vchanspd = parent_caps->maxspeed;
- break;
- }
- if (vchanspd < parent_caps->minspeed)
- vchanspd = parent_caps->minspeed;
- }
- save = 1;
+ goto fail;
}
- if (ret == 0) {
- /*
- * Limit the speed between feeder_rate_min <-> feeder_rate_max.
- */
- if (vchanspd < feeder_rate_min)
- vchanspd = feeder_rate_min;
- if (vchanspd > feeder_rate_max)
- vchanspd = feeder_rate_max;
-
- if (feeder_rate_round) {
- RANGE(vchanspd, parent_caps->minspeed,
- parent_caps->maxspeed);
- vchanspd = CHANNEL_SETSPEED(parent->methods,
- parent->devinfo, vchanspd);
- }
-
- ret = chn_reset(parent, vchanfmt, vchanspd);
- }
-
- if (ret == 0 && save) {
- /*
- * Save new value.
- */
- if (direction == PCMDIR_PLAY_VIRTUAL) {
- d->pvchanformat = parent->format;
- d->pvchanrate = parent->speed;
- } else {
- d->rvchanformat = parent->format;
- d->rvchanrate = parent->speed;
- }
- }
+ if ((ret = chn_reset(parent, vchanfmt, vchanspd)) != 0)
+ goto fail;
/*
* If the parent channel supports digital format,
* enable passthrough mode.
*/
- if (ret == 0 && snd_fmtvalid(AFMT_PASSTHROUGH, parent_caps->fmtlist)) {
+ if (snd_fmtvalid(AFMT_PASSTHROUGH, parent_caps->fmtlist)) {
parent->flags &= ~CHN_F_VCHAN_DYNAMIC;
parent->flags |= CHN_F_VCHAN_PASSTHROUGH;
}
- if (ret != 0) {
- CHN_REMOVE(parent, ch, children);
- parent->flags &= ~CHN_F_HAS_VCHAN;
- CHN_UNLOCK(parent);
- PCM_LOCK(d);
- if (pcm_chn_remove(d, ch) == 0) {
- PCM_UNLOCK(d);
- chn_kill(ch);
- } else
- PCM_UNLOCK(d);
- CHN_LOCK(parent);
- }
+ return (ret);
+
+fail:
+ CHN_LOCK(ch);
+ vchan_destroy(ch);
+ *child = NULL;
return (ret);
}
@@ -850,8 +691,6 @@ int
vchan_destroy(struct pcm_channel *c)
{
struct pcm_channel *parent;
- struct snddev_info *d;
- int ret;
KASSERT(c != NULL && c->parentchannel != NULL &&
c->parentsnddev != NULL, ("%s(): invalid channel=%p",
@@ -859,23 +698,15 @@ vchan_destroy(struct pcm_channel *c)
CHN_LOCKASSERT(c);
- d = c->parentsnddev;
parent = c->parentchannel;
- PCM_BUSYASSERT(d);
+ PCM_BUSYASSERT(c->parentsnddev);
CHN_LOCKASSERT(parent);
CHN_UNLOCK(c);
- if (!(parent->flags & CHN_F_BUSY))
- return (EBUSY);
-
- if (CHN_EMPTY(parent, children))
- return (EINVAL);
-
/* remove us from our parent's children list */
CHN_REMOVE(parent, c, children);
-
if (CHN_EMPTY(parent, children)) {
parent->flags &= ~(CHN_F_BUSY | CHN_F_HAS_VCHAN);
chn_reset(parent, parent->format, parent->speed);
@@ -883,18 +714,12 @@ vchan_destroy(struct pcm_channel *c)
CHN_UNLOCK(parent);
- /* remove us from our grandparent's channel list */
- PCM_LOCK(d);
- ret = pcm_chn_remove(d, c);
- PCM_UNLOCK(d);
-
/* destroy ourselves */
- if (ret == 0)
- chn_kill(c);
+ chn_kill(c);
CHN_LOCK(parent);
- return (ret);
+ return (0);
}
int
@@ -920,20 +745,52 @@ vchan_sync(struct pcm_channel *c)
c->flags |= CHN_F_DIRTY;
#ifdef SND_DEBUG
- if (snd_passthrough_verbose != 0) {
- char *devname, buf[CHN_NAMELEN];
-
- devname = dsp_unit2name(buf, sizeof(buf), c);
- device_printf(c->dev,
- "%s(%s/%s) %s() -> re-sync err=%d\n",
- __func__, (devname != NULL) ? devname : "dspX", c->comm,
- caller, ret);
+ if (snd_passthrough_verbose) {
+ device_printf(c->dev, "%s(%s/%s) %s() -> re-sync err=%d\n",
+ __func__, c->name, c->comm, caller, ret);
}
#endif
return (ret);
}
+static int
+sysctl_hw_snd_vchans_enable(SYSCTL_HANDLER_ARGS)
+{
+ struct snddev_info *d;
+ int i, v, error;
+
+ v = snd_vchans_enable;
+ error = sysctl_handle_int(oidp, &v, 0, req);
+ if (error != 0 || req->newptr == NULL)
+ return (error);
+
+ bus_topo_lock();
+ snd_vchans_enable = v >= 1;
+
+ for (i = 0; pcm_devclass != NULL &&
+ i < devclass_get_maxunit(pcm_devclass); i++) {
+ d = devclass_get_softc(pcm_devclass, i);
+ if (!PCM_REGISTERED(d))
+ continue;
+ PCM_ACQUIRE_QUICK(d);
+ if (snd_vchans_enable) {
+ if (d->playcount > 0)
+ d->flags |= SD_F_PVCHANS;
+ if (d->reccount > 0)
+ d->flags |= SD_F_RVCHANS;
+ } else
+ d->flags &= ~(SD_F_PVCHANS | SD_F_RVCHANS);
+ PCM_RELEASE_QUICK(d);
+ }
+ bus_topo_unlock();
+
+ return (0);
+}
+SYSCTL_PROC(_hw_snd, OID_AUTO, vchans_enable,
+ CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, 0, sizeof(int),
+ sysctl_hw_snd_vchans_enable, "I", "global virtual channel switch");
+
void
vchan_initsys(device_t dev)
{
@@ -948,7 +805,7 @@ vchan_initsys(device_t dev)
SYSCTL_CHILDREN(d->play_sysctl_tree),
OID_AUTO, "vchans", CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE,
- sysctl_dev_pcm_vchans, "I", "total allocated virtual channel");
+ sysctl_dev_pcm_vchans, "I", "virtual channels enabled");
SYSCTL_ADD_PROC(&d->play_sysctl_ctx,
SYSCTL_CHILDREN(d->play_sysctl_tree),
OID_AUTO, "vchanmode",
@@ -974,7 +831,7 @@ vchan_initsys(device_t dev)
OID_AUTO, "vchans",
CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE,
- sysctl_dev_pcm_vchans, "I", "total allocated virtual channel");
+ sysctl_dev_pcm_vchans, "I", "virtual channels enabled");
SYSCTL_ADD_PROC(&d->rec_sysctl_ctx,
SYSCTL_CHILDREN(d->rec_sysctl_tree),
OID_AUTO, "vchanmode",
diff --git a/sys/dev/sound/pcm/vchan.h b/sys/dev/sound/pcm/vchan.h
index e2dcc9761261..8c1de9496ef3 100644
--- a/sys/dev/sound/pcm/vchan.h
+++ b/sys/dev/sound/pcm/vchan.h
@@ -4,6 +4,10 @@
* Copyright (c) 2005-2009 Ariff Abdullah <ariff@FreeBSD.org>
* Copyright (c) 2001 Cameron Grant <cg@FreeBSD.org>
* All rights reserved.
+ * Copyright (c) 2024-2025 The FreeBSD Foundation
+ *
+ * Portions of this software were developed by Christos Margiolis
+ * <christos@FreeBSD.org> under sponsorship from the FreeBSD Foundation.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -30,7 +34,9 @@
#ifndef _SND_VCHAN_H_
#define _SND_VCHAN_H_
-int vchan_create(struct pcm_channel *, int);
+extern bool snd_vchans_enable;
+
+int vchan_create(struct pcm_channel *, struct pcm_channel **);
int vchan_destroy(struct pcm_channel *);
#ifdef SND_DEBUG
diff --git a/sys/dev/sound/usb/uaudio.c b/sys/dev/sound/usb/uaudio.c
index 2351c2522021..ff9f59fe42ab 100644
--- a/sys/dev/sound/usb/uaudio.c
+++ b/sys/dev/sound/usb/uaudio.c
@@ -90,7 +90,6 @@
#include <dev/sound/pcm/sound.h>
#include <dev/sound/usb/uaudioreg.h>
#include <dev/sound/usb/uaudio.h>
-#include <dev/sound/chip.h>
#include "feeder_if.h"
static int uaudio_default_rate = 0; /* use rate list */
@@ -369,7 +368,6 @@ struct uaudio_softc_child {
};
struct uaudio_softc {
- struct sbuf sc_sndstat;
struct sndcard_func sc_sndcard_func;
struct uaudio_chan sc_rec_chan[UAUDIO_MAX_CHILD];
struct uaudio_chan sc_play_chan[UAUDIO_MAX_CHILD];
@@ -392,7 +390,6 @@ struct uaudio_softc {
uint8_t sc_mixer_iface_index;
uint8_t sc_mixer_iface_no;
uint8_t sc_mixer_chan;
- uint8_t sc_sndstat_valid:1;
uint8_t sc_uq_audio_swap_lr:1;
uint8_t sc_uq_au_inp_async:1;
uint8_t sc_uq_au_no_xu:1;
@@ -1128,7 +1125,7 @@ uaudio_attach(device_t dev)
sc->sc_child[i].mix_info == 0)
continue;
sc->sc_child[i].pcm_device =
- device_add_child(dev, "pcm", -1);
+ device_add_child(dev, "pcm", DEVICE_UNIT_ANY);
if (sc->sc_child[i].pcm_device == NULL) {
DPRINTF("out of memory\n");
@@ -1138,10 +1135,7 @@ uaudio_attach(device_t dev)
&sc->sc_sndcard_func);
}
- if (bus_generic_attach(dev)) {
- DPRINTF("child attach failed\n");
- goto detach;
- }
+ bus_attach_children(dev);
if (uaudio_handle_hid) {
if (uaudio_hid_probe(sc, uaa) == 0) {
@@ -1212,14 +1206,9 @@ uaudio_attach_sub(device_t dev, kobj_class_t mixer_class, kobj_class_t chan_clas
snprintf(status, sizeof(status), "on %s",
device_get_nameunit(device_get_parent(dev)));
- if (pcm_register(dev, sc,
- (sc->sc_play_chan[i].num_alt > 0) ? 1 : 0,
- (sc->sc_rec_chan[i].num_alt > 0) ? 1 : 0)) {
- goto detach;
- }
+ pcm_init(dev, sc);
uaudio_pcm_setflags(dev, SD_F_MPSAFE);
- sc->sc_child[i].pcm_registered = 1;
if (sc->sc_play_chan[i].num_alt > 0) {
sc->sc_play_chan[i].priv_sc = sc;
@@ -1232,7 +1221,9 @@ uaudio_attach_sub(device_t dev, kobj_class_t mixer_class, kobj_class_t chan_clas
pcm_addchan(dev, PCMDIR_REC, chan_class,
&sc->sc_rec_chan[i]);
}
- pcm_setstatus(dev, status);
+ if (pcm_register(dev, status))
+ goto detach;
+ sc->sc_child[i].pcm_registered = 1;
uaudio_mixer_register_sysctl(sc, dev, i);
@@ -1294,8 +1285,6 @@ uaudio_detach(device_t dev)
if (bus_generic_detach(dev) != 0) {
DPRINTF("detach failed!\n");
}
- sbuf_delete(&sc->sc_sndstat);
- sc->sc_sndstat_valid = 0;
umidi_detach(dev);
@@ -2150,15 +2139,6 @@ uaudio_chan_fill_info_sub(struct uaudio_softc *sc, struct usb_device *udev,
if (rate > chan->pcm_cap.maxspeed || chan->pcm_cap.maxspeed == 0)
chan->pcm_cap.maxspeed = rate;
- if (sc->sc_sndstat_valid != 0) {
- sbuf_printf(&sc->sc_sndstat, "\n\t"
- "mode %d.%d:(%s) %dch, %dbit, %s, %dHz",
- curidx, alt_index,
- (ep_dir == UE_DIR_IN) ? "input" : "output",
- channels, p_fmt->bPrecision,
- p_fmt->description, rate);
- }
-
next_ep:
sed.v1 = NULL;
ed1 = NULL;
@@ -2231,9 +2211,6 @@ uaudio_chan_fill_info(struct uaudio_softc *sc, struct usb_device *udev)
if (channels == 0)
channels = channels_max;
- if (sbuf_new(&sc->sc_sndstat, NULL, 4096, SBUF_AUTOEXTEND))
- sc->sc_sndstat_valid = 1;
-
/* try to search for a valid config */
for (x = channels; x; x--) {
@@ -2264,8 +2241,6 @@ uaudio_chan_fill_info(struct uaudio_softc *sc, struct usb_device *udev)
if (x == (channels + 1))
x--;
}
- if (sc->sc_sndstat_valid)
- sbuf_finish(&sc->sc_sndstat);
}
static void
@@ -2706,8 +2681,6 @@ uaudio_chan_init(struct uaudio_chan *ch, struct snd_dbuf *b,
DPRINTF("Worst case buffer is %d bytes\n", (int)buf_size);
ch->buf = malloc(buf_size, M_DEVBUF, M_WAITOK | M_ZERO);
- if (ch->buf == NULL)
- goto error;
if (sndbuf_setup(b, ch->buf, buf_size) != 0)
goto error;
@@ -3275,31 +3248,27 @@ uaudio_mixer_add_ctl_sub(struct uaudio_softc *sc, struct uaudio_mixer_node *mc)
malloc(sizeof(*p_mc_new), M_USBDEV, M_WAITOK);
int ch;
- if (p_mc_new != NULL) {
- memcpy(p_mc_new, mc, sizeof(*p_mc_new));
- p_mc_new->next = sc->sc_mixer_root;
- sc->sc_mixer_root = p_mc_new;
- sc->sc_mixer_count++;
+ memcpy(p_mc_new, mc, sizeof(*p_mc_new));
+ p_mc_new->next = sc->sc_mixer_root;
+ sc->sc_mixer_root = p_mc_new;
+ sc->sc_mixer_count++;
- /* set default value for all channels */
- for (ch = 0; ch < p_mc_new->nchan; ch++) {
- switch (p_mc_new->val_default) {
- case 1:
- /* 50% */
- p_mc_new->wData[ch] = (p_mc_new->maxval + p_mc_new->minval) / 2;
- break;
- case 2:
- /* 100% */
- p_mc_new->wData[ch] = p_mc_new->maxval;
- break;
- default:
- /* 0% */
- p_mc_new->wData[ch] = p_mc_new->minval;
- break;
- }
+ /* set default value for all channels */
+ for (ch = 0; ch < p_mc_new->nchan; ch++) {
+ switch (p_mc_new->val_default) {
+ case 1:
+ /* 50% */
+ p_mc_new->wData[ch] = (p_mc_new->maxval + p_mc_new->minval) / 2;
+ break;
+ case 2:
+ /* 100% */
+ p_mc_new->wData[ch] = p_mc_new->maxval;
+ break;
+ default:
+ /* 0% */
+ p_mc_new->wData[ch] = p_mc_new->minval;
+ break;
}
- } else {
- DPRINTF("out of memory\n");
}
}
diff --git a/sys/dev/sound/usb/uaudio.h b/sys/dev/sound/usb/uaudio.h
index 08144701efe5..54b31a4e7bd2 100644
--- a/sys/dev/sound/usb/uaudio.h
+++ b/sys/dev/sound/usb/uaudio.h
@@ -64,8 +64,4 @@ extern void uaudio_mixer_set(struct uaudio_softc *, struct snd_mixer *,
extern uint32_t uaudio_mixer_setrecsrc(struct uaudio_softc *, struct snd_mixer *,
uint32_t src);
-int uaudio_get_vendor(device_t dev);
-int uaudio_get_product(device_t dev);
-int uaudio_get_release(device_t dev);
-
#endif /* _UAUDIO_H_ */
diff --git a/sys/dev/sound/usb/uaudio_pcm.c b/sys/dev/sound/usb/uaudio_pcm.c
index 9b17cb232907..0b3da9b20440 100644
--- a/sys/dev/sound/usb/uaudio_pcm.c
+++ b/sys/dev/sound/usb/uaudio_pcm.c
@@ -32,7 +32,6 @@
#endif
#include <dev/sound/pcm/sound.h>
-#include <dev/sound/chip.h>
#include <dev/sound/usb/uaudio.h>
#include "mixer_if.h"
diff --git a/sys/dev/sound/version.h b/sys/dev/sound/version.h
deleted file mode 100644
index b816bbf964e8..000000000000
--- a/sys/dev/sound/version.h
+++ /dev/null
@@ -1,42 +0,0 @@
-/*-
- * SPDX-License-Identifier: BSD-2-Clause
- *
- * Copyright (c) 2007 Ariff Abdullah <ariff@FreeBSD.org>
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#ifndef _SND_VERSION_H_
-#define _SND_VERSION_H_
-
-/*
- * FreeBSD sound driver internal versioning, nothing to do
- * with OSS whatsoever. Dear future maintainer, please revisit
- * this _before_ Jan 1 2148
- *
- * Last 2 decimal places reserved for daily versioning, starting
- * with 0.
- */
-#define SND_DRV_VERSION 2009061500
-
-#endif /* !_SND_VERSION_H_ */