diff options
Diffstat (limited to 'sys/dev/sound')
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_ */ |