diff options
Diffstat (limited to 'sys')
92 files changed, 12467 insertions, 5785 deletions
diff --git a/sys/conf/files b/sys/conf/files index b47ee0d..54f6ed1 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -65,6 +65,21 @@ p17v-alsa%diked.h optional snd_emu10kx pci \ compile-with "CC='${CC}' AWK=${AWK} sh $S/tools/emu10k1-mkalsa.sh $S/gnu/dev/sound/pci/p17v-alsa.h p17v-alsa%diked.h" \ no-obj no-implicit-rule before-depend \ clean "p17v-alsa%diked.h" +feeder_eq_gen.h optional sound \ + dependency "$S/tools/feeder_eq_mkfilter.awk" \ + compile-with "${AWK} -f $S/tools/feeder_eq_mkfilter.awk -- ${FEEDER_EQ_PRESETS} > feeder_eq_gen.h" \ + no-obj no-implicit-rule before-depend \ + clean "feeder_eq_gen.h" +feeder_rate_gen.h optional sound \ + dependency "$S/tools/feeder_rate_mkfilter.awk" \ + compile-with "${AWK} -f $S/tools/feeder_rate_mkfilter.awk -- ${FEEDER_RATE_PRESETS} > feeder_rate_gen.h" \ + no-obj no-implicit-rule before-depend \ + clean "feeder_rate_gen.h" +snd_fxdiv_gen.h optional sound \ + dependency "$S/tools/snd_fxdiv_gen.awk" \ + compile-with "${AWK} -f $S/tools/snd_fxdiv_gen.awk -- > snd_fxdiv_gen.h" \ + no-obj no-implicit-rule before-depend \ + clean "snd_fxdiv_gen.h" miidevs.h optional miibus | mii \ dependency "$S/tools/miidevs2h.awk $S/dev/mii/miidevs" \ compile-with "${AWK} -f $S/tools/miidevs2h.awk $S/dev/mii/miidevs" \ @@ -1433,16 +1448,28 @@ dev/sound/pci/hda/hdac.c optional snd_hda pci dev/sound/pcm/ac97.c optional sound dev/sound/pcm/ac97_if.m optional sound dev/sound/pcm/ac97_patch.c optional sound -dev/sound/pcm/buffer.c optional sound +dev/sound/pcm/buffer.c optional sound \ + dependency "snd_fxdiv_gen.h" dev/sound/pcm/channel.c optional sound dev/sound/pcm/channel_if.m optional sound dev/sound/pcm/dsp.c optional sound -dev/sound/pcm/fake.c optional sound dev/sound/pcm/feeder.c optional sound -dev/sound/pcm/feeder_fmt.c optional sound +dev/sound/pcm/feeder_chain.c optional sound +dev/sound/pcm/feeder_eq.c optional sound \ + dependency "feeder_eq_gen.h" \ + dependency "snd_fxdiv_gen.h" dev/sound/pcm/feeder_if.m optional sound -dev/sound/pcm/feeder_rate.c optional sound -dev/sound/pcm/feeder_volume.c optional sound +dev/sound/pcm/feeder_format.c optional sound \ + dependency "snd_fxdiv_gen.h" +dev/sound/pcm/feeder_matrix.c optional sound \ + dependency "snd_fxdiv_gen.h" +dev/sound/pcm/feeder_mixer.c optional sound \ + dependency "snd_fxdiv_gen.h" +dev/sound/pcm/feeder_rate.c optional sound \ + dependency "feeder_rate_gen.h" \ + dependency "snd_fxdiv_gen.h" +dev/sound/pcm/feeder_volume.c optional sound \ + dependency "snd_fxdiv_gen.h" dev/sound/pcm/mixer.c optional sound dev/sound/pcm/mixer_if.m optional sound dev/sound/pcm/sndstat.c optional sound diff --git a/sys/conf/options b/sys/conf/options index c72b3d1..f959fa4 100644 --- a/sys/conf/options +++ b/sys/conf/options @@ -830,3 +830,12 @@ VIMAGE_GLOBALS opt_global.h # Common Flash Interface (CFI) options CFI_SUPPORT_STRATAFLASH opt_cfi.h CFI_ARMEDANDDANGEROUS opt_cfi.h + +# Sound options +SND_DEBUG opt_snd.h +SND_DIAGNOSTIC opt_snd.h +SND_FEEDER_MULTIFORMAT opt_snd.h +SND_FEEDER_FULL_MULTIFORMAT opt_snd.h +SND_FEEDER_RATE_HP opt_snd.h +SND_PCM_64 opt_snd.h +SND_OLDSTEREO opt_snd.h diff --git a/sys/dev/sound/clone.c b/sys/dev/sound/clone.c index 29632d9..8b6c53c 100644 --- a/sys/dev/sound/clone.c +++ b/sys/dev/sound/clone.c @@ -33,6 +33,10 @@ #include <sys/malloc.h> #include <sys/proc.h> +#ifdef HAVE_KERNEL_OPTION_HEADERS +#include "opt_snd.h" +#endif + #if defined(SND_DIAGNOSTIC) || defined(SND_DEBUG) #include <dev/sound/pcm/sound.h> #endif @@ -88,9 +92,9 @@ struct snd_clone { #define SND_CLONE_ASSERT(x, y) do { \ if (!(x)) \ panic y; \ -} while(0) +} while (0) #else -#define SND_CLONE_ASSERT(x...) KASSERT(x) +#define SND_CLONE_ASSERT(...) KASSERT(__VA_ARGS__) #endif /* diff --git a/sys/dev/sound/driver.c b/sys/dev/sound/driver.c index 1f7c3b7..470ee74 100644 --- a/sys/dev/sound/driver.c +++ b/sys/dev/sound/driver.c @@ -26,6 +26,10 @@ * $FreeBSD$ */ +#ifdef HAVE_KERNEL_OPTION_HEADERS +#include "opt_snd.h" +#endif + #include <dev/sound/pcm/sound.h> static int @@ -38,7 +42,7 @@ snd_modevent(module_t mod, int type, void *data) case MOD_UNLOAD: break; default: - return (EOPNOTSUPP); + return (ENOTSUP); break; } return 0; diff --git a/sys/dev/sound/isa/ad1816.c b/sys/dev/sound/isa/ad1816.c index 4c5ecab..f3bb89a 100644 --- a/sys/dev/sound/isa/ad1816.c +++ b/sys/dev/sound/isa/ad1816.c @@ -26,6 +26,10 @@ * SUCH DAMAGE. */ +#ifdef HAVE_KERNEL_OPTION_HEADERS +#include "opt_snd.h" +#endif + #include <dev/sound/pcm/sound.h> #include <dev/sound/isa/ad1816.h> @@ -62,14 +66,14 @@ struct ad1816_info { }; static u_int32_t ad1816_fmt[] = { - AFMT_U8, - AFMT_STEREO | AFMT_U8, - AFMT_S16_LE, - AFMT_STEREO | AFMT_S16_LE, - AFMT_MU_LAW, - AFMT_STEREO | AFMT_MU_LAW, - AFMT_A_LAW, - AFMT_STEREO | AFMT_A_LAW, + SND_FORMAT(AFMT_U8, 1, 0), + SND_FORMAT(AFMT_U8, 2, 0), + SND_FORMAT(AFMT_S16_LE, 1, 0), + SND_FORMAT(AFMT_S16_LE, 2, 0), + SND_FORMAT(AFMT_MU_LAW, 1, 0), + SND_FORMAT(AFMT_MU_LAW, 2, 0), + SND_FORMAT(AFMT_A_LAW, 1, 0), + SND_FORMAT(AFMT_A_LAW, 2, 0), 0 }; @@ -269,7 +273,7 @@ ad1816mix_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right) return left | (right << 8); } -static int +static u_int32_t ad1816mix_setrecsrc(struct snd_mixer *m, u_int32_t src) { struct ad1816_info *ad1816 = mix_getdevinfo(m); @@ -303,7 +307,7 @@ static kobj_method_t ad1816mixer_methods[] = { KOBJMETHOD(mixer_init, ad1816mix_init), KOBJMETHOD(mixer_set, ad1816mix_set), KOBJMETHOD(mixer_setrecsrc, ad1816mix_setrecsrc), - { 0, 0 } + KOBJMETHOD_END }; MIXER_DECLARE(ad1816mixer); @@ -315,23 +319,19 @@ ad1816chan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channe struct ad1816_info *ad1816 = devinfo; struct ad1816_chinfo *ch = (dir == PCMDIR_PLAY)? &ad1816->pch : &ad1816->rch; + ch->dir = dir; ch->parent = ad1816; ch->channel = c; ch->buffer = b; if (sndbuf_alloc(ch->buffer, ad1816->parent_dmat, 0, ad1816->bufsize) != 0) return NULL; - return ch; -} -static int -ad1816chan_setdir(kobj_t obj, void *data, int dir) -{ - struct ad1816_chinfo *ch = data; - struct ad1816_info *ad1816 = ch->parent; + sndbuf_dmasetup(ch->buffer, (dir == PCMDIR_PLAY) ? ad1816->drq1 : + ad1816->drq2); + if (SND_DMA(ch->buffer)) + sndbuf_dmasetdir(ch->buffer, dir); - sndbuf_dmasetup(ch->buffer, (dir == PCMDIR_PLAY)? ad1816->drq1 : ad1816->drq2); - ch->dir = dir; - return 0; + return ch; } static int @@ -351,7 +351,7 @@ ad1816chan_setformat(kobj_t obj, void *data, u_int32_t format) ad1816_write(ad1816, 10, 0x0000); ad1816_write(ad1816, 11, 0x0000); } - switch (format & ~AFMT_STEREO) { + switch (AFMT_ENCODING(format)) { case AFMT_A_LAW: fmt = AD1816_ALAW; break; @@ -372,7 +372,7 @@ ad1816chan_setformat(kobj_t obj, void *data, u_int32_t format) fmt = AD1816_U8; break; } - if (format & AFMT_STEREO) fmt |= AD1816_STEREO; + if (AFMT_CHANNEL(format) > 1) fmt |= AD1816_STEREO; io_wr(ad1816, reg, fmt); ad1816_unlock(ad1816); #if 0 @@ -382,7 +382,7 @@ ad1816chan_setformat(kobj_t obj, void *data, u_int32_t format) #endif } -static int +static u_int32_t ad1816chan_setspeed(kobj_t obj, void *data, u_int32_t speed) { struct ad1816_chinfo *ch = data; @@ -395,7 +395,7 @@ ad1816chan_setspeed(kobj_t obj, void *data, u_int32_t speed) return speed; } -static int +static u_int32_t ad1816chan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) { struct ad1816_chinfo *ch = data; @@ -456,7 +456,7 @@ ad1816chan_trigger(kobj_t obj, void *data, int go) return 0; } -static int +static u_int32_t ad1816chan_getptr(kobj_t obj, void *data) { struct ad1816_chinfo *ch = data; @@ -471,14 +471,13 @@ ad1816chan_getcaps(kobj_t obj, void *data) static kobj_method_t ad1816chan_methods[] = { KOBJMETHOD(channel_init, ad1816chan_init), - KOBJMETHOD(channel_setdir, ad1816chan_setdir), KOBJMETHOD(channel_setformat, ad1816chan_setformat), KOBJMETHOD(channel_setspeed, ad1816chan_setspeed), KOBJMETHOD(channel_setblocksize, ad1816chan_setblocksize), KOBJMETHOD(channel_trigger, ad1816chan_trigger), KOBJMETHOD(channel_getptr, ad1816chan_getptr), KOBJMETHOD(channel_getcaps, ad1816chan_getcaps), - { 0, 0 } + KOBJMETHOD_END }; CHANNEL_DECLARE(ad1816chan); diff --git a/sys/dev/sound/isa/ess.c b/sys/dev/sound/isa/ess.c index 4e59777..5fd9529 100644 --- a/sys/dev/sound/isa/ess.c +++ b/sys/dev/sound/isa/ess.c @@ -29,6 +29,10 @@ * SUCH DAMAGE. */ +#ifdef HAVE_KERNEL_OPTION_HEADERS +#include "opt_snd.h" +#endif + #include <dev/sound/pcm/sound.h> #include <dev/sound/isa/sb.h> @@ -50,28 +54,28 @@ SND_DECLARE_FILE("$FreeBSD$"); #define ESS18XX_NEWSPEED static u_int32_t ess_pfmt[] = { - AFMT_U8, - AFMT_STEREO | AFMT_U8, - AFMT_S8, - AFMT_STEREO | AFMT_S8, - AFMT_S16_LE, - AFMT_STEREO | AFMT_S16_LE, - AFMT_U16_LE, - AFMT_STEREO | AFMT_U16_LE, + SND_FORMAT(AFMT_U8, 1, 0), + SND_FORMAT(AFMT_U8, 2, 0), + SND_FORMAT(AFMT_S8, 1, 0), + SND_FORMAT(AFMT_S8, 2, 0), + SND_FORMAT(AFMT_S16_LE, 1, 0), + SND_FORMAT(AFMT_S16_LE, 2, 0), + SND_FORMAT(AFMT_U16_LE, 1, 0), + SND_FORMAT(AFMT_U16_LE, 2, 0), 0 }; static struct pcmchan_caps ess_playcaps = {6000, 48000, ess_pfmt, 0}; static u_int32_t ess_rfmt[] = { - AFMT_U8, - AFMT_STEREO | AFMT_U8, - AFMT_S8, - AFMT_STEREO | AFMT_S8, - AFMT_S16_LE, - AFMT_STEREO | AFMT_S16_LE, - AFMT_U16_LE, - AFMT_STEREO | AFMT_U16_LE, + SND_FORMAT(AFMT_U8, 1, 0), + SND_FORMAT(AFMT_U8, 2, 0), + SND_FORMAT(AFMT_S8, 1, 0), + SND_FORMAT(AFMT_S8, 2, 0), + SND_FORMAT(AFMT_S16_LE, 1, 0), + SND_FORMAT(AFMT_S16_LE, 2, 0), + SND_FORMAT(AFMT_U16_LE, 1, 0), + SND_FORMAT(AFMT_U16_LE, 2, 0), 0 }; @@ -458,7 +462,7 @@ ess_setupch(struct ess_info *sc, int ch, int dir, int spd, u_int32_t fmt, int le { int play = (dir == PCMDIR_PLAY)? 1 : 0; int b16 = (fmt & AFMT_16BIT)? 1 : 0; - int stereo = (fmt & AFMT_STEREO)? 1 : 0; + int stereo = (AFMT_CHANNEL(fmt) > 1)? 1 : 0; int unsign = (fmt == AFMT_U8 || fmt == AFMT_U16_LE)? 1 : 0; u_int8_t spdval, fmtval; @@ -583,7 +587,7 @@ esschan_setformat(kobj_t obj, void *data, u_int32_t format) return 0; } -static int +static u_int32_t esschan_setspeed(kobj_t obj, void *data, u_int32_t speed) { struct ess_chinfo *ch = data; @@ -597,7 +601,7 @@ esschan_setspeed(kobj_t obj, void *data, u_int32_t speed) return ch->spd; } -static int +static u_int32_t esschan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) { struct ess_chinfo *ch = data; @@ -630,7 +634,7 @@ esschan_trigger(kobj_t obj, void *data, int go) return 0; } -static int +static u_int32_t esschan_getptr(kobj_t obj, void *data) { struct ess_chinfo *ch = data; @@ -654,7 +658,7 @@ static kobj_method_t esschan_methods[] = { KOBJMETHOD(channel_trigger, esschan_trigger), KOBJMETHOD(channel_getptr, esschan_getptr), KOBJMETHOD(channel_getcaps, esschan_getcaps), - { 0, 0 } + KOBJMETHOD_END }; CHANNEL_DECLARE(esschan); @@ -741,7 +745,7 @@ essmix_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right) return left | (right << 8); } -static int +static u_int32_t essmix_setrecsrc(struct snd_mixer *m, u_int32_t src) { struct ess_info *sc = mix_getdevinfo(m); @@ -776,7 +780,7 @@ static kobj_method_t essmixer_methods[] = { KOBJMETHOD(mixer_init, essmix_init), KOBJMETHOD(mixer_set, essmix_set), KOBJMETHOD(mixer_setrecsrc, essmix_setrecsrc), - { 0, 0 } + KOBJMETHOD_END }; MIXER_DECLARE(essmixer); diff --git a/sys/dev/sound/isa/gusc.c b/sys/dev/sound/isa/gusc.c index 706081c..ef211c6 100644 --- a/sys/dev/sound/isa/gusc.c +++ b/sys/dev/sound/isa/gusc.c @@ -34,7 +34,11 @@ #include <machine/resource.h> #include <machine/bus.h> #include <sys/rman.h> -#include <sys/soundcard.h> + +#ifdef HAVE_KERNEL_OPTION_HEADERS +#include "opt_snd.h" +#endif + #include <dev/sound/pcm/sound.h> #include <dev/sound/chip.h> #include "bus_if.h" @@ -301,11 +305,9 @@ static int gusc_attach(device_t dev) { sc_p scp; - int unit; void *ih; scp = device_get_softc(dev); - unit = device_get_unit(dev); bzero(scp, sizeof(*scp)); @@ -580,16 +582,14 @@ alloc_resource(sc_p scp) static int release_resource(sc_p scp) { - int i, lid, flags; + int i, lid; device_t dev; - flags = 0; if (isa_get_vendorid(scp->dev)) lid = isa_get_logicalid(scp->dev); - else { + else lid = LOGICALID_NOPNP; - flags = device_get_flags(scp->dev); - } + switch(lid) { case LOGICALID_PCM: case LOGICALID_NOPNP: /* XXX Non-PnP */ diff --git a/sys/dev/sound/isa/mss.c b/sys/dev/sound/isa/mss.c index 411296b..cc62df3 100644 --- a/sys/dev/sound/isa/mss.c +++ b/sys/dev/sound/isa/mss.c @@ -27,6 +27,10 @@ * SUCH DAMAGE. */ +#ifdef HAVE_KERNEL_OPTION_HEADERS +#include "opt_snd.h" +#endif + #include <dev/sound/pcm/sound.h> SND_DECLARE_FILE("$FreeBSD$"); @@ -128,34 +132,34 @@ static int pnpmss_attach(device_t dev); static driver_intr_t opti931_intr; static u_int32_t mss_fmt[] = { - AFMT_U8, - AFMT_STEREO | AFMT_U8, - AFMT_S16_LE, - AFMT_STEREO | AFMT_S16_LE, - AFMT_MU_LAW, - AFMT_STEREO | AFMT_MU_LAW, - AFMT_A_LAW, - AFMT_STEREO | AFMT_A_LAW, + SND_FORMAT(AFMT_U8, 1, 0), + SND_FORMAT(AFMT_U8, 2, 0), + SND_FORMAT(AFMT_S16_LE, 1, 0), + SND_FORMAT(AFMT_S16_LE, 2, 0), + SND_FORMAT(AFMT_MU_LAW, 1, 0), + SND_FORMAT(AFMT_MU_LAW, 2, 0), + SND_FORMAT(AFMT_A_LAW, 1, 0), + SND_FORMAT(AFMT_A_LAW, 2, 0), 0 }; static struct pcmchan_caps mss_caps = {4000, 48000, mss_fmt, 0}; static u_int32_t guspnp_fmt[] = { - AFMT_U8, - AFMT_STEREO | AFMT_U8, - AFMT_S16_LE, - AFMT_STEREO | AFMT_S16_LE, - AFMT_A_LAW, - AFMT_STEREO | AFMT_A_LAW, + SND_FORMAT(AFMT_U8, 1, 0), + SND_FORMAT(AFMT_U8, 2, 0), + SND_FORMAT(AFMT_S16_LE, 1, 0), + SND_FORMAT(AFMT_S16_LE, 2, 0), + SND_FORMAT(AFMT_A_LAW, 1, 0), + SND_FORMAT(AFMT_A_LAW, 2, 0), 0 }; static struct pcmchan_caps guspnp_caps = {4000, 48000, guspnp_fmt, 0}; static u_int32_t opti931_fmt[] = { - AFMT_U8, - AFMT_STEREO | AFMT_U8, - AFMT_S16_LE, - AFMT_STEREO | AFMT_S16_LE, + SND_FORMAT(AFMT_U8, 1, 0), + SND_FORMAT(AFMT_U8, 2, 0), + SND_FORMAT(AFMT_S16_LE, 1, 0), + SND_FORMAT(AFMT_S16_LE, 2, 0), 0 }; static struct pcmchan_caps opti931_caps = {4000, 48000, opti931_fmt, 0}; @@ -520,7 +524,7 @@ mssmix_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right) return left | (right << 8); } -static int +static u_int32_t mssmix_setrecsrc(struct snd_mixer *m, u_int32_t src) { struct mss_info *mss = mix_getdevinfo(m); @@ -535,7 +539,7 @@ static kobj_method_t mssmix_mixer_methods[] = { KOBJMETHOD(mixer_init, mssmix_init), KOBJMETHOD(mixer_set, mssmix_set), KOBJMETHOD(mixer_setrecsrc, mssmix_setrecsrc), - { 0, 0 } + KOBJMETHOD_END }; MIXER_DECLARE(mssmix_mixer); @@ -604,7 +608,7 @@ ymmix_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right) return left | (right << 8); } -static int +static u_int32_t ymmix_setrecsrc(struct snd_mixer *m, u_int32_t src) { struct mss_info *mss = mix_getdevinfo(m); @@ -618,7 +622,7 @@ static kobj_method_t ymmix_mixer_methods[] = { KOBJMETHOD(mixer_init, ymmix_init), KOBJMETHOD(mixer_set, ymmix_set), KOBJMETHOD(mixer_setrecsrc, ymmix_setrecsrc), - { 0, 0 } + KOBJMETHOD_END }; MIXER_DECLARE(ymmix_mixer); @@ -997,7 +1001,7 @@ static int mss_format(struct mss_chinfo *ch, u_int32_t format) { struct mss_info *mss = ch->parent; - int i, arg = format & ~AFMT_STEREO; + int i, arg = AFMT_ENCODING(format); /* * The data format uses 3 bits (just 2 on the 1848). For each @@ -1014,7 +1018,7 @@ mss_format(struct mss_chinfo *ch, u_int32_t format) ch->fmt = format; for (i = 0; i < 8; i++) if (arg == fmts[i]) break; arg = i << 1; - if (format & AFMT_STEREO) arg |= 1; + if (AFMT_CHANNEL(format) > 1) arg |= 1; arg <<= 4; ad_enter_MCE(mss); ad_write(mss, 8, (ad_read(mss, 8) & 0x0f) | arg); @@ -1035,7 +1039,7 @@ mss_trigger(struct mss_chinfo *ch, int go) int retry, wr, cnt, ss; ss = 1; - ss <<= (ch->fmt & AFMT_STEREO)? 1 : 0; + ss <<= (AFMT_CHANNEL(ch->fmt) > 1)? 1 : 0; ss <<= (ch->fmt & AFMT_16BIT)? 1 : 0; wr = (ch->dir == PCMDIR_PLAY)? 1 : 0; @@ -1170,12 +1174,12 @@ msschan_setformat(kobj_t obj, void *data, u_int32_t format) return 0; } -static int +static u_int32_t msschan_setspeed(kobj_t obj, void *data, u_int32_t speed) { struct mss_chinfo *ch = data; struct mss_info *mss = ch->parent; - int r; + u_int32_t r; mss_lock(mss); r = mss_speed(ch, speed); @@ -1184,7 +1188,7 @@ msschan_setspeed(kobj_t obj, void *data, u_int32_t speed) return r; } -static int +static u_int32_t msschan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) { struct mss_chinfo *ch = data; @@ -1211,7 +1215,7 @@ msschan_trigger(kobj_t obj, void *data, int go) return 0; } -static int +static u_int32_t msschan_getptr(kobj_t obj, void *data) { struct mss_chinfo *ch = data; @@ -1247,7 +1251,7 @@ static kobj_method_t msschan_methods[] = { KOBJMETHOD(channel_trigger, msschan_trigger), KOBJMETHOD(channel_getptr, msschan_getptr), KOBJMETHOD(channel_getcaps, msschan_getcaps), - { 0, 0 } + KOBJMETHOD_END }; CHANNEL_DECLARE(msschan); diff --git a/sys/dev/sound/isa/sb16.c b/sys/dev/sound/isa/sb16.c index eb37337..3e3cbc8 100644 --- a/sys/dev/sound/isa/sb16.c +++ b/sys/dev/sound/isa/sb16.c @@ -29,6 +29,10 @@ * SUCH DAMAGE. */ +#ifdef HAVE_KERNEL_OPTION_HEADERS +#include "opt_snd.h" +#endif + #include <dev/sound/pcm/sound.h> #include <dev/sound/isa/sb.h> @@ -44,24 +48,24 @@ SND_DECLARE_FILE("$FreeBSD$"); #define PLAIN_SB16(x) ((((x)->bd_flags) & (BD_F_SB16|BD_F_SB16X)) == BD_F_SB16) static u_int32_t sb16_fmt8[] = { - AFMT_U8, - AFMT_STEREO | AFMT_U8, + SND_FORMAT(AFMT_U8, 1, 0), + SND_FORMAT(AFMT_U8, 2, 0), 0 }; static struct pcmchan_caps sb16_caps8 = {5000, 45000, sb16_fmt8, 0}; static u_int32_t sb16_fmt16[] = { - AFMT_S16_LE, - AFMT_STEREO | AFMT_S16_LE, + SND_FORMAT(AFMT_S16_LE, 1, 0), + SND_FORMAT(AFMT_S16_LE, 2, 0), 0 }; static struct pcmchan_caps sb16_caps16 = {5000, 45000, sb16_fmt16, 0}; static u_int32_t sb16x_fmt[] = { - AFMT_U8, - AFMT_STEREO | AFMT_U8, - AFMT_S16_LE, - AFMT_STEREO | AFMT_S16_LE, + SND_FORMAT(AFMT_U8, 1, 0), + SND_FORMAT(AFMT_U8, 2, 0), + SND_FORMAT(AFMT_S16_LE, 1, 0), + SND_FORMAT(AFMT_S16_LE, 2, 0), 0 }; static struct pcmchan_caps sb16x_caps = {5000, 49000, sb16x_fmt, 0}; @@ -366,7 +370,7 @@ sb16mix_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right) return left | (right << 8); } -static int +static u_int32_t sb16mix_setrecsrc(struct snd_mixer *m, u_int32_t src) { struct sb_info *sb = mix_getdevinfo(m); @@ -420,7 +424,7 @@ static kobj_method_t sb16mix_mixer_methods[] = { KOBJMETHOD(mixer_init, sb16mix_init), KOBJMETHOD(mixer_set, sb16mix_set), KOBJMETHOD(mixer_setrecsrc, sb16mix_setrecsrc), - { 0, 0 } + KOBJMETHOD_END }; MIXER_DECLARE(sb16mix_mixer); @@ -633,7 +637,7 @@ sb_setup(struct sb_info *sb) v |= (ch->fmt & AFMT_16BIT)? DSP_DMA16 : DSP_DMA8; sb_cmd(sb, v); - v = (ch->fmt & AFMT_STEREO)? DSP_F16_STEREO : 0; + v = (AFMT_CHANNEL(ch->fmt) > 1)? DSP_F16_STEREO : 0; v |= (ch->fmt & AFMT_SIGNED)? DSP_F16_SIGNED : 0; sb_cmd2(sb, v, l); sndbuf_dma(ch->buffer, PCMTRIG_START); @@ -658,7 +662,7 @@ sb_setup(struct sb_info *sb) v |= (ch->fmt & AFMT_16BIT)? DSP_DMA16 : DSP_DMA8; sb_cmd(sb, v); - v = (ch->fmt & AFMT_STEREO)? DSP_F16_STEREO : 0; + v = (AFMT_CHANNEL(ch->fmt) > 1)? DSP_F16_STEREO : 0; v |= (ch->fmt & AFMT_SIGNED)? DSP_F16_SIGNED : 0; sb_cmd2(sb, v, l); sndbuf_dma(ch->buffer, PCMTRIG_START); @@ -700,7 +704,7 @@ sb16chan_setformat(kobj_t obj, void *data, u_int32_t format) return 0; } -static int +static u_int32_t sb16chan_setspeed(kobj_t obj, void *data, u_int32_t speed) { struct sb_chinfo *ch = data; @@ -709,7 +713,7 @@ sb16chan_setspeed(kobj_t obj, void *data, u_int32_t speed) return speed; } -static int +static u_int32_t sb16chan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) { struct sb_chinfo *ch = data; @@ -737,7 +741,7 @@ sb16chan_trigger(kobj_t obj, void *data, int go) return 0; } -static int +static u_int32_t sb16chan_getptr(kobj_t obj, void *data) { struct sb_chinfo *ch = data; @@ -777,7 +781,7 @@ static kobj_method_t sb16chan_methods[] = { KOBJMETHOD(channel_trigger, sb16chan_trigger), KOBJMETHOD(channel_getptr, sb16chan_getptr), KOBJMETHOD(channel_getcaps, sb16chan_getcaps), - { 0, 0 } + KOBJMETHOD_END }; CHANNEL_DECLARE(sb16chan); diff --git a/sys/dev/sound/isa/sb8.c b/sys/dev/sound/isa/sb8.c index 3b0f295..7c39515 100644 --- a/sys/dev/sound/isa/sb8.c +++ b/sys/dev/sound/isa/sb8.c @@ -29,6 +29,10 @@ * SUCH DAMAGE. */ +#ifdef HAVE_KERNEL_OPTION_HEADERS +#include "opt_snd.h" +#endif + #include <dev/sound/pcm/sound.h> #include <dev/sound/isa/sb.h> @@ -43,7 +47,7 @@ SND_DECLARE_FILE("$FreeBSD$"); #define SB_DEFAULT_BUFSZ 4096 static u_int32_t sb_fmt[] = { - AFMT_U8, + SND_FORMAT(AFMT_U8, 1, 0), 0 }; static struct pcmchan_caps sb200_playcaps = {4000, 23000, sb_fmt, 0}; @@ -52,8 +56,8 @@ static struct pcmchan_caps sb201_playcaps = {4000, 44100, sb_fmt, 0}; static struct pcmchan_caps sb201_reccaps = {4000, 15000, sb_fmt, 0}; static u_int32_t sbpro_fmt[] = { - AFMT_U8, - AFMT_STEREO | AFMT_U8, + SND_FORMAT(AFMT_U8, 1, 0), + SND_FORMAT(AFMT_U8, 2, 0), 0 }; static struct pcmchan_caps sbpro_playcaps = {4000, 44100, sbpro_fmt, 0}; @@ -372,7 +376,7 @@ sbpromix_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right) return left | (right << 8); } -static int +static u_int32_t sbpromix_setrecsrc(struct snd_mixer *m, u_int32_t src) { struct sb_info *sb = mix_getdevinfo(m); @@ -395,7 +399,7 @@ static kobj_method_t sbpromix_mixer_methods[] = { KOBJMETHOD(mixer_init, sbpromix_init), KOBJMETHOD(mixer_set, sbpromix_set), KOBJMETHOD(mixer_setrecsrc, sbpromix_setrecsrc), - { 0, 0 } + KOBJMETHOD_END }; MIXER_DECLARE(sbpromix_mixer); @@ -453,7 +457,7 @@ sbmix_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right) return left | (left << 8); } -static int +static u_int32_t sbmix_setrecsrc(struct snd_mixer *m, u_int32_t src) { return 0; @@ -463,7 +467,7 @@ static kobj_method_t sbmix_mixer_methods[] = { KOBJMETHOD(mixer_init, sbmix_init), KOBJMETHOD(mixer_set, sbmix_set), KOBJMETHOD(mixer_setrecsrc, sbmix_setrecsrc), - { 0, 0 } + KOBJMETHOD_END }; MIXER_DECLARE(sbmix_mixer); @@ -496,7 +500,7 @@ sb_speed(struct sb_chinfo *ch) { struct sb_info *sb = ch->parent; int play = (ch->dir == PCMDIR_PLAY)? 1 : 0; - int stereo = (ch->fmt & AFMT_STEREO)? 1 : 0; + int stereo = (AFMT_CHANNEL(ch->fmt) > 1)? 1 : 0; int speed, tmp, thresh, max; u_char tconst; @@ -537,7 +541,7 @@ sb_start(struct sb_chinfo *ch) { struct sb_info *sb = ch->parent; int play = (ch->dir == PCMDIR_PLAY)? 1 : 0; - int stereo = (ch->fmt & AFMT_STEREO)? 1 : 0; + int stereo = (AFMT_CHANNEL(ch->fmt) > 1)? 1 : 0; int l = ch->blksz; u_char i; @@ -614,7 +618,7 @@ sbchan_setformat(kobj_t obj, void *data, u_int32_t format) return 0; } -static int +static u_int32_t sbchan_setspeed(kobj_t obj, void *data, u_int32_t speed) { struct sb_chinfo *ch = data; @@ -623,7 +627,7 @@ sbchan_setspeed(kobj_t obj, void *data, u_int32_t speed) return sb_speed(ch); } -static int +static u_int32_t sbchan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) { struct sb_chinfo *ch = data; @@ -648,7 +652,7 @@ sbchan_trigger(kobj_t obj, void *data, int go) return 0; } -static int +static u_int32_t sbchan_getptr(kobj_t obj, void *data) { struct sb_chinfo *ch = data; @@ -677,7 +681,7 @@ static kobj_method_t sbchan_methods[] = { KOBJMETHOD(channel_trigger, sbchan_trigger), KOBJMETHOD(channel_getptr, sbchan_getptr), KOBJMETHOD(channel_getcaps, sbchan_getcaps), - { 0, 0 } + KOBJMETHOD_END }; CHANNEL_DECLARE(sbchan); diff --git a/sys/dev/sound/isa/sbc.c b/sys/dev/sound/isa/sbc.c index 9cbe523..3df2738 100644 --- a/sys/dev/sound/isa/sbc.c +++ b/sys/dev/sound/isa/sbc.c @@ -24,6 +24,10 @@ * SUCH DAMAGE. */ +#ifdef HAVE_KERNEL_OPTION_HEADERS +#include "opt_snd.h" +#endif + #include <dev/sound/chip.h> #include <dev/sound/pcm/sound.h> #include <dev/sound/isa/sb.h> diff --git a/sys/dev/sound/isa/sndbuf_dma.c b/sys/dev/sound/isa/sndbuf_dma.c index 9e45cde..4333217 100644 --- a/sys/dev/sound/isa/sndbuf_dma.c +++ b/sys/dev/sound/isa/sndbuf_dma.c @@ -24,6 +24,10 @@ * SUCH DAMAGE. */ +#ifdef HAVE_KERNEL_OPTION_HEADERS +#include "opt_snd.h" +#endif + #include <dev/sound/pcm/sound.h> #include <isa/isavar.h> diff --git a/sys/dev/sound/macio/aoa.c b/sys/dev/sound/macio/aoa.c index 54efe4f..5c012d3 100644 --- a/sys/dev/sound/macio/aoa.c +++ b/sys/dev/sound/macio/aoa.c @@ -43,8 +43,14 @@ #include <machine/bus.h> #include <sys/rman.h> #include <dev/ofw/ofw_bus.h> + +#ifdef HAVE_KERNEL_OPTION_HEADERS +#include "opt_snd.h" +#endif + #include <dev/sound/pcm/sound.h> #include <dev/sound/macio/aoa.h> + #include "mixer_if.h" struct aoa_dma { @@ -138,7 +144,7 @@ aoa_dma_delete(struct aoa_dma *dma) free(dma, M_DEVBUF); } -static int +static u_int32_t aoa_chan_setblocksize(kobj_t obj, void *data, u_int32_t blocksz) { struct aoa_dma *dma = data; @@ -186,13 +192,13 @@ aoa_chan_setformat(kobj_t obj, void *data, u_int32_t format) { DPRINTF(("aoa_chan_setformat: format = %u\n", format)); - if (format != (AFMT_STEREO | AFMT_S16_BE)) + if (format != SND_FORMAT(AFMT_S16_BE, 2, 0)) return (EINVAL); return (0); } -static int +static u_int32_t aoa_chan_setspeed(kobj_t obj, void *data, u_int32_t speed) { DPRINTF(("aoa_chan_setspeed: speed = %u\n", speed)); @@ -200,7 +206,7 @@ aoa_chan_setspeed(kobj_t obj, void *data, u_int32_t speed) return (44100); } -static int +static u_int32_t aoa_chan_getptr(kobj_t obj, void *data) { struct aoa_dma *dma = data; @@ -332,7 +338,7 @@ aoa_interrupt(void *xsc) } static u_int32_t sc_fmt[] = { - AFMT_S16_BE | AFMT_STEREO, + SND_FORMAT(AFMT_S16_BE, 2, 0), 0 }; static struct pcmchan_caps aoa_caps = {44100, 44100, sc_fmt, 0}; @@ -352,7 +358,7 @@ static kobj_method_t aoa_chan_methods[] = { KOBJMETHOD(channel_trigger, aoa_chan_trigger), KOBJMETHOD(channel_getptr, aoa_chan_getptr), KOBJMETHOD(channel_getcaps, aoa_chan_getcaps), - { 0, 0 } + KOBJMETHOD_END }; CHANNEL_DECLARE(aoa_chan); diff --git a/sys/dev/sound/macio/davbus.c b/sys/dev/sound/macio/davbus.c index 15909f8..46c731b 100644 --- a/sys/dev/sound/macio/davbus.c +++ b/sys/dev/sound/macio/davbus.c @@ -40,7 +40,13 @@ #include <sys/rman.h> #include <dev/ofw/ofw_bus.h> + +#ifdef HAVE_KERNEL_OPTION_HEADERS +#include "opt_snd.h" +#endif + #include <dev/sound/pcm/sound.h> + #include <dev/sound/macio/aoa.h> #include <dev/sound/macio/davbusreg.h> @@ -115,7 +121,7 @@ static void burgundy_set_outputs(struct davbus_softc *d, u_int mask); static u_int burgundy_read_status(struct davbus_softc *d, u_int status); static int burgundy_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right); -static int burgundy_setrecsrc(struct snd_mixer *m, u_int32_t src); +static u_int32_t burgundy_setrecsrc(struct snd_mixer *m, u_int32_t src); static kobj_method_t burgundy_mixer_methods[] = { KOBJMETHOD(mixer_init, burgundy_init), @@ -123,7 +129,7 @@ static kobj_method_t burgundy_mixer_methods[] = { KOBJMETHOD(mixer_reinit, burgundy_reinit), KOBJMETHOD(mixer_set, burgundy_set), KOBJMETHOD(mixer_setrecsrc, burgundy_setrecsrc), - { 0, 0 } + KOBJMETHOD_END }; MIXER_DECLARE(burgundy_mixer); @@ -293,7 +299,7 @@ burgundy_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right) return (0); } -static int +static u_int32_t burgundy_setrecsrc(struct snd_mixer *m, u_int32_t src) { return (0); @@ -311,7 +317,7 @@ static void screamer_set_outputs(struct davbus_softc *d, u_int mask); static u_int screamer_read_status(struct davbus_softc *d, u_int status); static int screamer_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right); -static int screamer_setrecsrc(struct snd_mixer *m, u_int32_t src); +static u_int32_t screamer_setrecsrc(struct snd_mixer *m, u_int32_t src); static kobj_method_t screamer_mixer_methods[] = { KOBJMETHOD(mixer_init, screamer_init), @@ -319,7 +325,7 @@ static kobj_method_t screamer_mixer_methods[] = { KOBJMETHOD(mixer_reinit, screamer_reinit), KOBJMETHOD(mixer_set, screamer_set), KOBJMETHOD(mixer_setrecsrc, screamer_setrecsrc), - { 0, 0 } + KOBJMETHOD_END }; MIXER_DECLARE(screamer_mixer); @@ -479,7 +485,7 @@ screamer_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right) return (0); } -static int +static u_int32_t screamer_setrecsrc(struct snd_mixer *m, u_int32_t src) { return (0); diff --git a/sys/dev/sound/macio/i2s.c b/sys/dev/sound/macio/i2s.c index 11a0826..d1a5463 100644 --- a/sys/dev/sound/macio/i2s.c +++ b/sys/dev/sound/macio/i2s.c @@ -72,8 +72,14 @@ #include <machine/pio.h> #include <sys/rman.h> #include <dev/ofw/ofw_bus.h> + +#ifdef HAVE_KERNEL_OPTION_HEADERS +#include "opt_snd.h" +#endif + #include <dev/sound/pcm/sound.h> #include <dev/sound/macio/aoa.h> + #include <powerpc/powermac/macgpiovar.h> struct i2s_softc { @@ -530,7 +536,7 @@ i2s_setup(struct i2s_softc *sc, u_int rate, u_int wordsize, u_int sclk_fs) * to set sane defaults (44100). */ printf("i2s_setup: changing format not supported yet.\n"); - return (EOPNOTSUPP); + return (ENOTSUP); #ifdef notyet if (obio_fcr_isset(OBIO_FCR1, I2S0CLKEN)) { diff --git a/sys/dev/sound/macio/snapper.c b/sys/dev/sound/macio/snapper.c index 8acca10..6e9b6fe 100644 --- a/sys/dev/sound/macio/snapper.c +++ b/sys/dev/sound/macio/snapper.c @@ -75,7 +75,13 @@ #include <dev/iicbus/iicbus.h> #include <dev/iicbus/iiconf.h> #include <dev/ofw/ofw_bus.h> + +#ifdef HAVE_KERNEL_OPTION_HEADERS +#include "opt_snd.h" +#endif + #include <dev/sound/pcm/sound.h> + #include "mixer_if.h" extern kobj_class_t i2s_mixer_class; @@ -94,7 +100,7 @@ static void snapper_uninit(struct snd_mixer *m); static int snapper_reinit(struct snd_mixer *m); static int snapper_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right); -static int snapper_setrecsrc(struct snd_mixer *m, u_int32_t src); +static u_int32_t snapper_setrecsrc(struct snd_mixer *m, u_int32_t src); static device_method_t snapper_methods[] = { /* Device interface. */ @@ -121,7 +127,7 @@ static kobj_method_t snapper_mixer_methods[] = { KOBJMETHOD(mixer_reinit, snapper_reinit), KOBJMETHOD(mixer_set, snapper_set), KOBJMETHOD(mixer_setrecsrc, snapper_setrecsrc), - { 0, 0 } + KOBJMETHOD_END }; MIXER_DECLARE(snapper_mixer); @@ -478,7 +484,7 @@ snapper_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right) return (0); } -static int +static u_int32_t snapper_setrecsrc(struct snd_mixer *m, u_int32_t src) { return (0); diff --git a/sys/dev/sound/macio/tumbler.c b/sys/dev/sound/macio/tumbler.c index 3007ece..57b77bc 100644 --- a/sys/dev/sound/macio/tumbler.c +++ b/sys/dev/sound/macio/tumbler.c @@ -75,7 +75,13 @@ #include <dev/iicbus/iicbus.h> #include <dev/iicbus/iiconf.h> #include <dev/ofw/ofw_bus.h> + +#ifdef HAVE_KERNEL_OPTION_HEADERS +#include "opt_snd.h" +#endif + #include <dev/sound/pcm/sound.h> + #include "mixer_if.h" extern kobj_class_t i2s_mixer_class; @@ -94,7 +100,7 @@ static void tumbler_uninit(struct snd_mixer *m); static int tumbler_reinit(struct snd_mixer *m); static int tumbler_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right); -static int tumbler_setrecsrc(struct snd_mixer *m, u_int32_t src); +static u_int32_t tumbler_setrecsrc(struct snd_mixer *m, u_int32_t src); static device_method_t tumbler_methods[] = { /* Device interface. */ @@ -121,7 +127,7 @@ static kobj_method_t tumbler_mixer_methods[] = { KOBJMETHOD(mixer_reinit, tumbler_reinit), KOBJMETHOD(mixer_set, tumbler_set), KOBJMETHOD(mixer_setrecsrc, tumbler_setrecsrc), - { 0, 0 } + KOBJMETHOD_END }; MIXER_DECLARE(tumbler_mixer); @@ -424,7 +430,7 @@ tumbler_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right) return (0); } -static int +static u_int32_t tumbler_setrecsrc(struct snd_mixer *m, u_int32_t src) { return (0); diff --git a/sys/dev/sound/midi/midi.c b/sys/dev/sound/midi/midi.c index b6ac879..59ea141 100644 --- a/sys/dev/sound/midi/midi.c +++ b/sys/dev/sound/midi/midi.c @@ -58,8 +58,23 @@ __FBSDID("$FreeBSD$"); #include <sys/poll.h> #include <sys/sbuf.h> #include <sys/kobj.h> +#ifdef SND_DEBUG +#undef KOBJMETHOD +#define KOBJMETHOD(NAME, FUNC) \ + { \ + &NAME##_desc, \ + (kobjop_t) ((FUNC != (NAME##_t *)NULL) ? FUNC : NULL) \ + } +#endif +#ifndef KOBJMETHOD_END +#define KOBJMETHOD_END { NULL, NULL } +#endif #include <sys/module.h> +#ifdef HAVE_KERNEL_OPTION_HEADERS +#include "opt_snd.h" +#endif + #include <dev/sound/midi/midi.h> #include "mpu_if.h" @@ -145,7 +160,7 @@ static kobj_method_t midisynth_methods[] = { KOBJMETHOD(synth_alloc, midisynth_alloc), KOBJMETHOD(synth_controller, midisynth_controller), KOBJMETHOD(synth_bender, midisynth_bender), - {0, 0} + KOBJMETHOD_END }; DEFINE_CLASS(midisynth, midisynth_methods, 0); @@ -1367,6 +1382,7 @@ midi_destroy(struct snd_midi *m, int midiuninit) MIDI_DEBUG(3, printf("midi_destroy\n")); m->dev->si_drv1 = NULL; + mtx_unlock(&m->lock); /* XXX */ destroy_dev(m->dev); TAILQ_REMOVE(&midi_devs, m, link); if (midiuninit) @@ -1418,6 +1434,8 @@ midi_unload() goto exit1; } + mtx_unlock(&midistat_lock); /* XXX */ + destroy_dev(midistat_dev); /* * Made it here then unload is complete diff --git a/sys/dev/sound/midi/mpu401.c b/sys/dev/sound/midi/mpu401.c index dfdf969..e017b4d 100644 --- a/sys/dev/sound/midi/mpu401.c +++ b/sys/dev/sound/midi/mpu401.c @@ -37,9 +37,24 @@ __FBSDID("$FreeBSD$"); #include <sys/proc.h> #include <sys/systm.h> #include <sys/kobj.h> +#ifdef SND_DEBUG +#undef KOBJMETHOD +#define KOBJMETHOD(NAME, FUNC) \ + { \ + &NAME##_desc, \ + (kobjop_t) ((FUNC != (NAME##_t *)NULL) ? FUNC : NULL) \ + } +#endif +#ifndef KOBJMETHOD_END +#define KOBJMETHOD_END { NULL, NULL } +#endif #include <sys/malloc.h> #include <sys/bus.h> /* to get driver_intr_t */ +#ifdef HAVE_KERNEL_OPTION_HEADERS +#include "opt_snd.h" +#endif + #include <dev/sound/midi/mpu401.h> #include <dev/sound/midi/midi.h> @@ -75,14 +90,14 @@ struct mpu401 { static void mpu401_timeout(void *m); static mpu401_intr_t mpu401_intr; -static int mpu401_minit(kobj_t obj, struct mpu401 *m); -static int mpu401_muninit(kobj_t obj, struct mpu401 *m); -static int mpu401_minqsize(kobj_t obj, struct mpu401 *m); -static int mpu401_moutqsize(kobj_t obj, struct mpu401 *m); -static void mpu401_mcallback(kobj_t obj, struct mpu401 *m, int flags); -static void mpu401_mcallbackp(kobj_t obj, struct mpu401 *m, int flags); -static const char *mpu401_mdescr(kobj_t obj, struct mpu401 *m, int verbosity); -static const char *mpu401_mprovider(kobj_t obj, struct mpu401 *m); +static int mpu401_minit(struct snd_midi *, void *); +static int mpu401_muninit(struct snd_midi *, void *); +static int mpu401_minqsize(struct snd_midi *, void *); +static int mpu401_moutqsize(struct snd_midi *, void *); +static void mpu401_mcallback(struct snd_midi *, void *, int); +static void mpu401_mcallbackp(struct snd_midi *, void *, int); +static const char *mpu401_mdescr(struct snd_midi *, void *, int); +static const char *mpu401_mprovider(struct snd_midi *, void *); static kobj_method_t mpu401_methods[] = { KOBJMETHOD(mpu_init, mpu401_minit), @@ -93,7 +108,7 @@ static kobj_method_t mpu401_methods[] = { KOBJMETHOD(mpu_callbackp, mpu401_mcallbackp), KOBJMETHOD(mpu_descr, mpu401_mdescr), KOBJMETHOD(mpu_provider, mpu401_mprovider), - {0, 0} + KOBJMETHOD_END }; DEFINE_CLASS(mpu401, mpu401_methods, 0); @@ -208,8 +223,9 @@ mpu401_uninit(struct mpu401 *m) } static int -mpu401_minit(kobj_t obj, struct mpu401 *m) +mpu401_minit(struct snd_midi *sm, void *arg) { + struct mpu401 *m = arg; int i; CMD(m, MPU_RESET); @@ -232,27 +248,29 @@ mpu401_minit(kobj_t obj, struct mpu401 *m) int -mpu401_muninit(kobj_t obj, struct mpu401 *m) +mpu401_muninit(struct snd_midi *sm, void *arg) { + struct mpu401 *m = arg; return MPUFOI_UNINIT(m, m->cookie); } int -mpu401_minqsize(kobj_t obj, struct mpu401 *m) +mpu401_minqsize(struct snd_midi *sm, void *arg) { return 128; } int -mpu401_moutqsize(kobj_t obj, struct mpu401 *m) +mpu401_moutqsize(struct snd_midi *sm, void *arg) { return 128; } static void -mpu401_mcallback(kobj_t obj, struct mpu401 *m, int flags) +mpu401_mcallback(struct snd_midi *sm, void *arg, int flags) { + struct mpu401 *m = arg; #if 0 printf("mpu401_callback %s %s %s %s\n", flags & M_RX ? "M_RX" : "", @@ -267,21 +285,21 @@ mpu401_mcallback(kobj_t obj, struct mpu401 *m, int flags) } static void -mpu401_mcallbackp(kobj_t obj, struct mpu401 *m, int flags) +mpu401_mcallbackp(struct snd_midi *sm, void *arg, int flags) { /* printf("mpu401_callbackp\n"); */ - mpu401_mcallback(obj, m, flags); + mpu401_mcallback(sm, arg, flags); } static const char * -mpu401_mdescr(kobj_t obj, struct mpu401 *m, int verbosity) +mpu401_mdescr(struct snd_midi *sm, void *arg, int verbosity) { return "descr mpu401"; } static const char * -mpu401_mprovider(kobj_t obj, struct mpu401 *m) +mpu401_mprovider(struct snd_midi *m, void *arg) { return "provider mpu401"; } diff --git a/sys/dev/sound/midi/sequencer.c b/sys/dev/sound/midi/sequencer.c index 9cdc2d5..5bb030d 100644 --- a/sys/dev/sound/midi/sequencer.c +++ b/sys/dev/sound/midi/sequencer.c @@ -66,6 +66,9 @@ __FBSDID("$FreeBSD$"); #include <sys/unistd.h> #include <sys/selinfo.h> +#ifdef HAVE_KERNEL_OPTION_HEADERS +#include "opt_snd.h" +#endif #include <dev/sound/midi/midi.h> #include <dev/sound/midi/midiq.h> @@ -258,13 +261,17 @@ midi_cmdtab cmdtab_seqccmn[] = { {-1, NULL}, }; +#ifndef KOBJMETHOD_END +#define KOBJMETHOD_END { NULL, NULL } +#endif + /* * static const char *mpu401_mprovider(kobj_t obj, struct mpu401 *m); */ static kobj_method_t seq_methods[] = { /* KOBJMETHOD(mpu_provider,mpu401_mprovider), */ - {0, 0} + KOBJMETHOD_END }; DEFINE_CLASS(sequencer, seq_methods, 0); @@ -459,7 +466,12 @@ done: cv_broadcast(&scp->th_cv); mtx_unlock(&scp->seq_lock); SEQ_DEBUG(2, printf("seq_eventthread finished\n")); +#if __FreeBSD_version >= 800002 kproc_exit(0); +#else + mtx_lock(&Giant); + kthread_exit(0); +#endif } /* @@ -574,7 +586,13 @@ seq_addunit(void) * TODO: Add to list of sequencers this module provides */ - ret = kproc_create(seq_eventthread, scp, NULL, RFHIGHPID, 0, + ret = +#if __FreeBSD_version >= 800002 + kproc_create +#else + kthread_create +#endif + (seq_eventthread, scp, NULL, RFHIGHPID, 0, "sequencer %02d", scp->unit); if (ret) diff --git a/sys/dev/sound/midi/sequencer.h b/sys/dev/sound/midi/sequencer.h index 946582e..97ce47a 100644 --- a/sys/dev/sound/midi/sequencer.h +++ b/sys/dev/sound/midi/sequencer.h @@ -57,7 +57,7 @@ extern int seq_debug; if (seq_debug >= y) { \ (x); \ } \ - } while(0) + } while (0) SYSCTL_DECL(_hw_midi); diff --git a/sys/dev/sound/pci/als4000.c b/sys/dev/sound/pci/als4000.c index 6df5ffd..bde95e7 100644 --- a/sys/dev/sound/pci/als4000.c +++ b/sys/dev/sound/pci/als4000.c @@ -33,6 +33,10 @@ * SB16 register descriptions. */ +#ifdef HAVE_KERNEL_OPTION_HEADERS +#include "opt_snd.h" +#endif + #include <dev/sound/pcm/sound.h> #include <dev/sound/isa/sb.h> #include <dev/sound/pci/als4000.h> @@ -84,10 +88,10 @@ struct sc_info { /* Channel caps */ static u_int32_t als_format[] = { - AFMT_U8, - AFMT_STEREO | AFMT_U8, - AFMT_S16_LE, - AFMT_STEREO | AFMT_S16_LE, + SND_FORMAT(AFMT_U8, 1, 0), + SND_FORMAT(AFMT_U8, 2, 0), + SND_FORMAT(AFMT_S16_LE, 1, 0), + SND_FORMAT(AFMT_S16_LE, 2, 0), 0 }; @@ -216,7 +220,7 @@ alschan_init(kobj_t obj, void *devinfo, ch->parent = sc; ch->channel = c; ch->bps = 1; - ch->format = AFMT_U8; + ch->format = SND_FORMAT(AFMT_U8, 1, 0); ch->speed = DSP_DEFAULT_SPEED; ch->buffer = b; snd_mtxunlock(sc->lock); @@ -236,7 +240,7 @@ alschan_setformat(kobj_t obj, void *data, u_int32_t format) return 0; } -static int +static u_int32_t alschan_setspeed(kobj_t obj, void *data, u_int32_t speed) { struct sc_chinfo *ch = data, *other; @@ -254,7 +258,7 @@ alschan_setspeed(kobj_t obj, void *data, u_int32_t speed) return speed; } -static int +static u_int32_t alschan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) { struct sc_chinfo *ch = data; @@ -267,7 +271,7 @@ alschan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) return blocksize; } -static int +static u_int32_t alschan_getptr(kobj_t obj, void *data) { struct sc_chinfo *ch = data; @@ -316,10 +320,10 @@ struct playback_command { u_int8_t dma_prog; /* sb16 dma program */ u_int8_t dma_stop; /* sb16 stop register */ } static const playback_cmds[] = { - ALS_8BIT_CMD(AFMT_U8, DSP_MODE_U8MONO), - ALS_8BIT_CMD(AFMT_U8 | AFMT_STEREO, DSP_MODE_U8STEREO), - ALS_16BIT_CMD(AFMT_S16_LE, DSP_MODE_S16MONO), - ALS_16BIT_CMD(AFMT_S16_LE | AFMT_STEREO, DSP_MODE_S16STEREO), + ALS_8BIT_CMD(SND_FORMAT(AFMT_U8, 1, 0), DSP_MODE_U8MONO), + ALS_8BIT_CMD(SND_FORMAT(AFMT_U8, 2, 0), DSP_MODE_U8STEREO), + ALS_16BIT_CMD(SND_FORMAT(AFMT_S16_LE, 1, 0), DSP_MODE_S16MONO), + ALS_16BIT_CMD(SND_FORMAT(AFMT_S16_LE, 2, 0), DSP_MODE_S16STEREO), }; static const struct playback_command* @@ -418,7 +422,7 @@ static kobj_method_t alspchan_methods[] = { KOBJMETHOD(channel_trigger, alspchan_trigger), KOBJMETHOD(channel_getptr, alschan_getptr), KOBJMETHOD(channel_getcaps, alschan_getcaps), - { 0, 0 } + KOBJMETHOD_END }; CHANNEL_DECLARE(alspchan); @@ -429,13 +433,13 @@ static u_int8_t als_get_fifo_format(struct sc_info *sc, u_int32_t format) { switch (format) { - case AFMT_U8: + case SND_FORMAT(AFMT_U8, 1, 0): return ALS_FIFO1_8BIT; - case AFMT_U8 | AFMT_STEREO: + case SND_FORMAT(AFMT_U8, 2, 0): return ALS_FIFO1_8BIT | ALS_FIFO1_STEREO; - case AFMT_S16_LE: + case SND_FORMAT(AFMT_S16_LE, 1, 0): return ALS_FIFO1_SIGNED; - case AFMT_S16_LE | AFMT_STEREO: + case SND_FORMAT(AFMT_S16_LE, 2, 0): return ALS_FIFO1_SIGNED | ALS_FIFO1_STEREO; } device_printf(sc->dev, "format not found: 0x%08x\n", format); @@ -512,7 +516,7 @@ static kobj_method_t alsrchan_methods[] = { KOBJMETHOD(channel_trigger, alsrchan_trigger), KOBJMETHOD(channel_getptr, alschan_getptr), KOBJMETHOD(channel_getcaps, alschan_getcaps), - { 0, 0 } + KOBJMETHOD_END }; CHANNEL_DECLARE(alsrchan); @@ -594,7 +598,7 @@ alsmix_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right) return 0; } -static int +static u_int32_t alsmix_setrecsrc(struct snd_mixer *m, u_int32_t src) { struct sc_info *sc = mix_getdevinfo(m); @@ -621,7 +625,7 @@ static kobj_method_t als_mixer_methods[] = { KOBJMETHOD(mixer_init, alsmix_init), KOBJMETHOD(mixer_set, alsmix_set), KOBJMETHOD(mixer_setrecsrc, alsmix_setrecsrc), - { 0, 0 } + KOBJMETHOD_END }; MIXER_DECLARE(als_mixer); diff --git a/sys/dev/sound/pci/atiixp.c b/sys/dev/sound/pci/atiixp.c index 6ec50ba..5dd5f7a 100644 --- a/sys/dev/sound/pci/atiixp.c +++ b/sys/dev/sound/pci/atiixp.c @@ -53,6 +53,10 @@ * random ninja hackery. */ +#ifdef HAVE_KERNEL_OPTION_HEADERS +#include "opt_snd.h" +#endif + #include <dev/sound/pcm/sound.h> #include <dev/sound/pcm/ac97.h> @@ -140,13 +144,13 @@ struct atiixp_info { #define atiixp_assert(_sc) snd_mtxassert((_sc)->lock) static uint32_t atiixp_fmt_32bit[] = { - AFMT_STEREO | AFMT_S16_LE, - AFMT_STEREO | AFMT_S32_LE, + SND_FORMAT(AFMT_S16_LE, 2, 0), + SND_FORMAT(AFMT_S32_LE, 2, 0), 0 }; static uint32_t atiixp_fmt[] = { - AFMT_STEREO | AFMT_S16_LE, + SND_FORMAT(AFMT_S16_LE, 2, 0), 0 }; @@ -187,13 +191,13 @@ static int atiixp_wrcd(kobj_t, void *, int, uint32_t); static void *atiixp_chan_init(kobj_t, void *, struct snd_dbuf *, struct pcm_channel *, int); static int atiixp_chan_setformat(kobj_t, void *, uint32_t); -static int atiixp_chan_setspeed(kobj_t, void *, uint32_t); -static int atiixp_chan_setfragments(kobj_t, void *, uint32_t, uint32_t); -static int atiixp_chan_setblocksize(kobj_t, void *, uint32_t); +static uint32_t atiixp_chan_setspeed(kobj_t, void *, uint32_t); +static int atiixp_chan_setfragments(kobj_t, void *, uint32_t, uint32_t); +static uint32_t atiixp_chan_setblocksize(kobj_t, void *, uint32_t); static void atiixp_buildsgdt(struct atiixp_chinfo *); static int atiixp_chan_trigger(kobj_t, void *, int); static __inline uint32_t atiixp_dmapos(struct atiixp_chinfo *); -static int atiixp_chan_getptr(kobj_t, void *); +static uint32_t atiixp_chan_getptr(kobj_t, void *); static struct pcmchan_caps *atiixp_chan_getcaps(kobj_t, void *); static void atiixp_intr(void *); @@ -420,7 +424,7 @@ atiixp_wrcd(kobj_t obj, void *devinfo, int reg, uint32_t data) static kobj_method_t atiixp_ac97_methods[] = { KOBJMETHOD(ac97_read, atiixp_rdcd), KOBJMETHOD(ac97_write, atiixp_wrcd), - { 0, 0 } + KOBJMETHOD_END }; AC97_DECLARE(atiixp_ac97); @@ -515,7 +519,7 @@ atiixp_chan_setformat(kobj_t obj, void *data, uint32_t format) return (0); } -static int +static uint32_t atiixp_chan_setspeed(kobj_t obj, void *data, uint32_t spd) { /* XXX We're supposed to do VRA/DRA processing right here */ @@ -558,10 +562,10 @@ atiixp_chan_setfragments(kobj_t obj, void *data, ch->blksz = sndbuf_getblksz(ch->buffer); ch->blkcnt = sndbuf_getblkcnt(ch->buffer); - return (1); + return (0); } -static int +static uint32_t atiixp_chan_setblocksize(kobj_t obj, void *data, uint32_t blksz) { struct atiixp_chinfo *ch = data; @@ -735,7 +739,7 @@ atiixp_chan_trigger(kobj_t obj, void *data, int go) ch->ptr = 0; ch->prevptr = 0; pollticks = ((uint64_t)hz * ch->blksz) / - ((uint64_t)sndbuf_getbps(ch->buffer) * + ((uint64_t)sndbuf_getalign(ch->buffer) * sndbuf_getspd(ch->buffer)); pollticks >>= 2; if (pollticks > hz) @@ -777,7 +781,7 @@ atiixp_chan_trigger(kobj_t obj, void *data, int go) else ch = &sc->rch; pollticks = ((uint64_t)hz * ch->blksz) / - ((uint64_t)sndbuf_getbps(ch->buffer) * + ((uint64_t)sndbuf_getalign(ch->buffer) * sndbuf_getspd(ch->buffer)); pollticks >>= 2; if (pollticks > hz) @@ -818,7 +822,7 @@ atiixp_chan_trigger(kobj_t obj, void *data, int go) return (0); } -static int +static uint32_t atiixp_chan_getptr(kobj_t obj, void *data) { struct atiixp_chinfo *ch = data; @@ -854,7 +858,7 @@ static kobj_method_t atiixp_chan_methods[] = { KOBJMETHOD(channel_trigger, atiixp_chan_trigger), KOBJMETHOD(channel_getptr, atiixp_chan_getptr), KOBJMETHOD(channel_getcaps, atiixp_chan_getcaps), - { 0, 0 } + KOBJMETHOD_END }; CHANNEL_DECLARE(atiixp_chan); @@ -954,7 +958,6 @@ atiixp_chip_pre_init(struct atiixp_info *sc) atiixp_unlock(sc); } -#ifdef SND_DYNSYSCTL static int sysctl_atiixp_polling(SYSCTL_HANDLER_ARGS) { @@ -994,7 +997,6 @@ sysctl_atiixp_polling(SYSCTL_HANDLER_ARGS) return (err); } -#endif static void atiixp_chip_post_init(void *arg) @@ -1090,12 +1092,10 @@ atiixp_chip_post_init(void *arg) for (i = 0; i < ATI_IXP_NRCHAN; i++) pcm_addchan(sc->dev, PCMDIR_REC, &atiixp_chan_class, sc); -#ifdef SND_DYNSYSCTL SYSCTL_ADD_PROC(device_get_sysctl_ctx(sc->dev), SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev)), OID_AUTO, "polling", CTLTYPE_INT | CTLFLAG_RW, sc->dev, sizeof(sc->dev), sysctl_atiixp_polling, "I", "Enable polling mode"); -#endif snprintf(status, SND_STATUSLEN, "at memory 0x%lx irq %ld %s", rman_get_start(sc->reg), rman_get_start(sc->irq), diff --git a/sys/dev/sound/pci/aureal.c b/sys/dev/sound/pci/aureal.c index 6b2c644..37e1c45 100644 --- a/sys/dev/sound/pci/aureal.c +++ b/sys/dev/sound/pci/aureal.c @@ -24,6 +24,10 @@ * 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/pci/aureal.h> @@ -38,19 +42,19 @@ SND_DECLARE_FILE("$FreeBSD$"); /* channel interface */ static u_int32_t au_playfmt[] = { - AFMT_U8, - AFMT_STEREO | AFMT_U8, - AFMT_S16_LE, - AFMT_STEREO | AFMT_S16_LE, + SND_FORMAT(AFMT_U8, 1, 0), + SND_FORMAT(AFMT_U8, 2, 0), + SND_FORMAT(AFMT_S16_LE, 1, 0), + SND_FORMAT(AFMT_S16_LE, 2, 0), 0 }; static struct pcmchan_caps au_playcaps = {4000, 48000, au_playfmt, 0}; static u_int32_t au_recfmt[] = { - AFMT_U8, - AFMT_STEREO | AFMT_U8, - AFMT_S16_LE, - AFMT_STEREO | AFMT_S16_LE, + SND_FORMAT(AFMT_U8, 1, 0), + SND_FORMAT(AFMT_U8, 2, 0), + SND_FORMAT(AFMT_S16_LE, 1, 0), + SND_FORMAT(AFMT_S16_LE, 2, 0), 0 }; static struct pcmchan_caps au_reccaps = {4000, 48000, au_recfmt, 0}; @@ -167,7 +171,7 @@ au_wrcd(kobj_t obj, void *arg, int regno, u_int32_t data) static kobj_method_t au_ac97_methods[] = { KOBJMETHOD(ac97_read, au_rdcd), KOBJMETHOD(ac97_write, au_wrcd), - { 0, 0 } + KOBJMETHOD_END }; AC97_DECLARE(au_ac97); @@ -242,13 +246,13 @@ static void au_prepareoutput(struct au_chinfo *ch, u_int32_t format) { struct au_info *au = ch->parent; - int i, stereo = (format & AFMT_STEREO)? 1 : 0; + int i, stereo = (AFMT_CHANNEL(format) > 1)? 1 : 0; u_int32_t baseaddr = sndbuf_getbufaddr(ch->buffer); au_wr(au, 0, 0x1061c, 0, 4); au_wr(au, 0, 0x10620, 0, 4); au_wr(au, 0, 0x10624, 0, 4); - switch(format & ~AFMT_STEREO) { + switch(AFMT_ENCODING(format)) { case 1: i=0xb000; break; @@ -382,7 +386,7 @@ static kobj_method_t auchan_methods[] = { KOBJMETHOD(channel_trigger, auchan_trigger), KOBJMETHOD(channel_getptr, auchan_getptr), KOBJMETHOD(channel_getcaps, auchan_getcaps), - { 0, 0 } + KOBJMETHOD_END }; CHANNEL_DECLARE(auchan); diff --git a/sys/dev/sound/pci/cmi.c b/sys/dev/sound/pci/cmi.c index 32edd03..2b747e1 100644 --- a/sys/dev/sound/pci/cmi.c +++ b/sys/dev/sound/pci/cmi.c @@ -42,6 +42,10 @@ * those that don't. */ +#ifdef HAVE_KERNEL_OPTION_HEADERS +#include "opt_snd.h" +#endif + #include <dev/sound/pcm/sound.h> #include <dev/sound/pci/cmireg.h> #include <dev/sound/isa/sb.h> @@ -129,10 +133,10 @@ struct sc_info { /* Channel caps */ static u_int32_t cmi_fmt[] = { - AFMT_U8, - AFMT_STEREO | AFMT_U8, - AFMT_S16_LE, - AFMT_STEREO | AFMT_S16_LE, + SND_FORMAT(AFMT_U8, 1, 0), + SND_FORMAT(AFMT_U8, 2, 0), + SND_FORMAT(AFMT_S16_LE, 1, 0), + SND_FORMAT(AFMT_S16_LE, 2, 0), 0 }; @@ -348,7 +352,7 @@ cmichan_init(kobj_t obj, void *devinfo, ch->parent = sc; ch->channel = c; ch->bps = 1; - ch->fmt = AFMT_U8; + ch->fmt = SND_FORMAT(AFMT_U8, 1, 0); ch->spd = DSP_DEFAULT_SPEED; ch->buffer = b; ch->dma_active = 0; @@ -384,7 +388,7 @@ cmichan_setformat(kobj_t obj, void *data, u_int32_t format) ch->bps = 1; } - if (format & AFMT_STEREO) { + if (AFMT_CHANNEL(format) > 1) { f |= CMPCI_REG_FORMAT_STEREO; ch->bps *= 2; } else { @@ -411,7 +415,7 @@ cmichan_setformat(kobj_t obj, void *data, u_int32_t format) return 0; } -static int +static u_int32_t cmichan_setspeed(kobj_t obj, void *data, u_int32_t speed) { struct sc_chinfo *ch = data; @@ -457,7 +461,7 @@ cmichan_setspeed(kobj_t obj, void *data, u_int32_t speed) return ch->spd; } -static int +static u_int32_t cmichan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) { struct sc_chinfo *ch = data; @@ -507,7 +511,7 @@ cmichan_trigger(kobj_t obj, void *data, int go) return 0; } -static int +static u_int32_t cmichan_getptr(kobj_t obj, void *data) { struct sc_chinfo *ch = data; @@ -589,7 +593,7 @@ static kobj_method_t cmichan_methods[] = { KOBJMETHOD(channel_trigger, cmichan_trigger), KOBJMETHOD(channel_getptr, cmichan_getptr), KOBJMETHOD(channel_getcaps, cmichan_getcaps), - { 0, 0 } + KOBJMETHOD_END }; CHANNEL_DECLARE(cmichan); @@ -716,7 +720,7 @@ cmimix_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right) return 0; } -static int +static u_int32_t cmimix_setrecsrc(struct snd_mixer *m, u_int32_t src) { struct sc_info *sc = mix_getdevinfo(m); @@ -748,7 +752,6 @@ cmimix_setrecsrc(struct snd_mixer *m, u_int32_t src) static int cmi_initsys(struct sc_info* sc) { -#ifdef SND_DYNSYSCTL /* XXX: an user should be able to set this with a control tool, if not done before 7.0-RELEASE, this needs to be converted to a device specific sysctl "dev.pcm.X.yyy" via @@ -759,7 +762,7 @@ cmi_initsys(struct sc_info* sc) OID_AUTO, "spdif_enabled", CTLFLAG_RW, &sc->spdif_enabled, 0, "enable SPDIF output at 44.1 kHz and above"); -#endif /* SND_DYNSYSCTL */ + return 0; } @@ -768,7 +771,7 @@ static kobj_method_t cmi_mixer_methods[] = { KOBJMETHOD(mixer_init, cmimix_init), KOBJMETHOD(mixer_set, cmimix_set), KOBJMETHOD(mixer_setrecsrc, cmimix_setrecsrc), - { 0, 0 } + KOBJMETHOD_END }; MIXER_DECLARE(cmi_mixer); @@ -777,7 +780,7 @@ MIXER_DECLARE(cmi_mixer); */ static unsigned char -cmi_mread(void *arg, struct sc_info *sc, int reg) +cmi_mread(struct mpu401 *arg, void *sc, int reg) { unsigned int d; @@ -788,15 +791,16 @@ cmi_mread(void *arg, struct sc_info *sc, int reg) } static void -cmi_mwrite(void *arg, struct sc_info *sc, int reg, unsigned char b) +cmi_mwrite(struct mpu401 *arg, void *sc, int reg, unsigned char b) { bus_space_write_1(0,0,0x330 + reg , b); } static int -cmi_muninit(void *arg, struct sc_info *sc) +cmi_muninit(struct mpu401 *arg, void *cookie) { + struct sc_info *sc = cookie; snd_mtxlock(sc->lock); sc->mpu_intr = 0; @@ -810,7 +814,7 @@ static kobj_method_t cmi_mpu_methods[] = { KOBJMETHOD(mpufoi_read, cmi_mread), KOBJMETHOD(mpufoi_write, cmi_mwrite), KOBJMETHOD(mpufoi_uninit, cmi_muninit), - { 0, 0 } + KOBJMETHOD_END }; static DEFINE_CLASS(cmi_mpu, cmi_mpu_methods, 0); diff --git a/sys/dev/sound/pci/cs4281.c b/sys/dev/sound/pci/cs4281.c index 665d7a5..e533eee 100644 --- a/sys/dev/sound/pci/cs4281.c +++ b/sys/dev/sound/pci/cs4281.c @@ -31,6 +31,10 @@ * contributed towards power management. */ +#ifdef HAVE_KERNEL_OPTION_HEADERS +#include "opt_snd.h" +#endif + #include <dev/sound/pcm/sound.h> #include <dev/sound/pcm/ac97.h> @@ -119,18 +123,18 @@ static u_int32_t cs4281_format_to_bps(u_int32_t); /* formats (do not add formats without editing cs_fmt_tab) */ static u_int32_t cs4281_fmts[] = { - AFMT_U8, - AFMT_U8 | AFMT_STEREO, - AFMT_S8, - AFMT_S8 | AFMT_STEREO, - AFMT_S16_LE, - AFMT_S16_LE | AFMT_STEREO, - AFMT_U16_LE, - AFMT_U16_LE | AFMT_STEREO, - AFMT_S16_BE, - AFMT_S16_BE | AFMT_STEREO, - AFMT_U16_BE, - AFMT_U16_BE | AFMT_STEREO, + SND_FORMAT(AFMT_U8, 1, 0), + SND_FORMAT(AFMT_U8, 2, 0), + SND_FORMAT(AFMT_S8, 1, 0), + SND_FORMAT(AFMT_S8, 2, 0), + SND_FORMAT(AFMT_S16_LE, 1, 0), + SND_FORMAT(AFMT_S16_LE, 2, 0), + SND_FORMAT(AFMT_U16_LE, 1, 0), + SND_FORMAT(AFMT_U16_LE, 2, 0), + SND_FORMAT(AFMT_S16_BE, 1, 0), + SND_FORMAT(AFMT_S16_BE, 2, 0), + SND_FORMAT(AFMT_U16_BE, 1, 0), + SND_FORMAT(AFMT_U16_BE, 2, 0), 0 }; @@ -173,7 +177,7 @@ cs4281_waitset(struct sc_info *sc, int regno, u_int32_t mask, int tries) { u_int32_t v; - while(tries > 0) { + while (tries > 0) { DELAY(100); v = cs4281_rd(sc, regno); if ((v & mask) == mask) break; @@ -187,7 +191,7 @@ cs4281_waitclr(struct sc_info *sc, int regno, u_int32_t mask, int tries) { u_int32_t v; - while(tries > 0) { + while (tries > 0) { DELAY(100); v = ~ cs4281_rd(sc, regno); if (v & mask) break; @@ -231,7 +235,7 @@ cs4281_format_to_dmr(u_int32_t format) { u_int32_t dmr = 0; if (AFMT_8BIT & format) dmr |= CS4281PCI_DMR_SIZE8; - if (!(AFMT_STEREO & format)) dmr |= CS4281PCI_DMR_MONO; + if (AFMT_CHANNEL(format) < 2) dmr |= CS4281PCI_DMR_MONO; if (AFMT_BIGENDIAN & format) dmr |= CS4281PCI_DMR_BEND; if (!(AFMT_SIGNED & format)) dmr |= CS4281PCI_DMR_USIGN; return dmr; @@ -240,13 +244,14 @@ cs4281_format_to_dmr(u_int32_t format) static inline u_int32_t cs4281_format_to_bps(u_int32_t format) { - return ((AFMT_8BIT & format) ? 1 : 2) * ((AFMT_STEREO & format) ? 2 : 1); + return ((AFMT_8BIT & format) ? 1 : 2) * + ((AFMT_CHANNEL(format) > 1) ? 2 : 1); } /* -------------------------------------------------------------------- */ /* ac97 codec */ -static u_int32_t +static int cs4281_rdcd(kobj_t obj, void *devinfo, int regno) { struct sc_info *sc = (struct sc_info *)devinfo; @@ -268,19 +273,19 @@ cs4281_rdcd(kobj_t obj, void *devinfo, int regno) /* Wait for read to complete */ if (cs4281_waitclr(sc, CS4281PCI_ACCTL, CS4281PCI_ACCTL_DCV, 250) == 0) { device_printf(sc->dev, "cs4281_rdcd: DCV did not go\n"); - return 0xffffffff; + return -1; } /* Wait for valid status */ if (cs4281_waitset(sc, CS4281PCI_ACSTS, CS4281PCI_ACSTS_VSTS, 250) == 0) { device_printf(sc->dev,"cs4281_rdcd: VSTS did not come\n"); - return 0xffffffff; + return -1; } return cs4281_rd(sc, CS4281PCI_ACSDA); } -static void +static int cs4281_wrcd(kobj_t obj, void *devinfo, int regno, u_int32_t data) { struct sc_info *sc = (struct sc_info *)devinfo; @@ -297,12 +302,14 @@ cs4281_wrcd(kobj_t obj, void *devinfo, int regno, u_int32_t data) if (cs4281_waitclr(sc, CS4281PCI_ACCTL, CS4281PCI_ACCTL_DCV, 250) == 0) { device_printf(sc->dev,"cs4281_wrcd: DCV did not go\n"); } + + return 0; } static kobj_method_t cs4281_ac97_methods[] = { KOBJMETHOD(ac97_read, cs4281_rdcd), KOBJMETHOD(ac97_write, cs4281_wrcd), - { 0, 0 } + KOBJMETHOD_END }; AC97_DECLARE(cs4281_ac97); @@ -322,7 +329,7 @@ cs4281chan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channe ch->parent = sc; ch->channel = c; - ch->fmt = AFMT_U8; + ch->fmt = SND_FORMAT(AFMT_U8, 1, 0); ch->spd = DSP_DEFAULT_SPEED; ch->bps = 1; ch->blksz = sndbuf_getsize(ch->buffer); @@ -336,7 +343,7 @@ cs4281chan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channe return ch; } -static int +static u_int32_t cs4281chan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) { struct sc_chinfo *ch = data; @@ -358,7 +365,7 @@ cs4281chan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) return ch->blksz; } -static int +static u_int32_t cs4281chan_setspeed(kobj_t obj, void *data, u_int32_t speed) { struct sc_chinfo *ch = data; @@ -401,7 +408,7 @@ cs4281chan_setformat(kobj_t obj, void *data, u_int32_t format) return 0; } -static int +static u_int32_t cs4281chan_getptr(kobj_t obj, void *data) { struct sc_chinfo *ch = data; @@ -453,7 +460,7 @@ static kobj_method_t cs4281chan_methods[] = { KOBJMETHOD(channel_trigger, cs4281chan_trigger), KOBJMETHOD(channel_getptr, cs4281chan_getptr), KOBJMETHOD(channel_getcaps, cs4281chan_getcaps), - { 0, 0 } + KOBJMETHOD_END }; CHANNEL_DECLARE(cs4281chan); diff --git a/sys/dev/sound/pci/csa.c b/sys/dev/sound/pci/csa.c index 1499372..09efc8a 100644 --- a/sys/dev/sound/pci/csa.c +++ b/sys/dev/sound/pci/csa.c @@ -37,7 +37,11 @@ #include <machine/resource.h> #include <machine/bus.h> #include <sys/rman.h> -#include <sys/soundcard.h> + +#ifdef HAVE_KERNEL_OPTION_HEADERS +#include "opt_snd.h" +#endif + #include <dev/sound/pcm/sound.h> #include <dev/sound/chip.h> #include <dev/sound/pci/csareg.h> @@ -884,13 +888,13 @@ int csa_readcodec(csa_res *resp, u_long offset, u_int32_t *data) { int i; - u_int32_t acsda, acctl, acsts; + u_int32_t acctl, acsts; /* * Make sure that there is not data sitting around from a previous * uncompleted access. ACSDA = Status Data Register = 47Ch */ - acsda = csa_readio(resp, BA0_ACSDA); + csa_readio(resp, BA0_ACSDA); /* * Setup the AC97 control registers on the CS461x to send the diff --git a/sys/dev/sound/pci/csapcm.c b/sys/dev/sound/pci/csapcm.c index c932c1d..89ffd2b 100644 --- a/sys/dev/sound/pci/csapcm.c +++ b/sys/dev/sound/pci/csapcm.c @@ -28,7 +28,10 @@ * SUCH DAMAGE. */ -#include <sys/soundcard.h> +#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/chip.h> @@ -94,21 +97,21 @@ static void csa_ac97_suspend(struct csa_info *csa); static void csa_ac97_resume(struct csa_info *csa); static u_int32_t csa_playfmt[] = { - AFMT_U8, - AFMT_STEREO | AFMT_U8, - AFMT_S8, - AFMT_STEREO | AFMT_S8, - AFMT_S16_LE, - AFMT_STEREO | AFMT_S16_LE, - AFMT_S16_BE, - AFMT_STEREO | AFMT_S16_BE, + SND_FORMAT(AFMT_U8, 1, 0), + SND_FORMAT(AFMT_U8, 2, 0), + SND_FORMAT(AFMT_S8, 1, 0), + SND_FORMAT(AFMT_S8, 2, 0), + SND_FORMAT(AFMT_S16_LE, 1, 0), + SND_FORMAT(AFMT_S16_LE, 2, 0), + SND_FORMAT(AFMT_S16_BE, 1, 0), + SND_FORMAT(AFMT_S16_BE, 2, 0), 0 }; static struct pcmchan_caps csa_playcaps = {8000, 48000, csa_playfmt, 0}; static u_int32_t csa_recfmt[] = { - AFMT_S16_LE, - AFMT_STEREO | AFMT_S16_LE, + SND_FORMAT(AFMT_S16_LE, 1, 0), + SND_FORMAT(AFMT_S16_LE, 2, 0), 0 }; static struct pcmchan_caps csa_reccaps = {11025, 48000, csa_recfmt, 0}; @@ -163,7 +166,7 @@ csa_wrcd(kobj_t obj, void *devinfo, int regno, u_int32_t data) static kobj_method_t csa_ac97_methods[] = { KOBJMETHOD(ac97_read, csa_rdcd), KOBJMETHOD(ac97_write, csa_wrcd), - { 0, 0 } + KOBJMETHOD_END }; AC97_DECLARE(csa_ac97); @@ -489,7 +492,7 @@ csa_setupchan(struct csa_chinfo *ch) csa->pfie |= 0x8000; if (ch->fmt & AFMT_BIGENDIAN) csa->pfie |= 0x4000; - if (!(ch->fmt & AFMT_STEREO)) + if (AFMT_CHANNEL(ch->fmt) < 2) csa->pfie |= 0x2000; if (ch->fmt & AFMT_8BIT) csa->pfie |= 0x1000; @@ -498,7 +501,7 @@ csa_setupchan(struct csa_chinfo *ch) tmp = 4; if (ch->fmt & AFMT_16BIT) tmp <<= 1; - if (ch->fmt & AFMT_STEREO) + if (AFMT_CHANNEL(ch->fmt) > 1) tmp <<= 1; tmp--; @@ -548,7 +551,7 @@ csachan_setformat(kobj_t obj, void *data, u_int32_t format) return 0; } -static int +static u_int32_t csachan_setspeed(kobj_t obj, void *data, u_int32_t speed) { struct csa_chinfo *ch = data; @@ -557,7 +560,7 @@ csachan_setspeed(kobj_t obj, void *data, u_int32_t speed) return ch->spd; /* XXX calc real speed */ } -static int +static u_int32_t csachan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) { return CS461x_BUFFSIZE / 2; @@ -589,13 +592,13 @@ csachan_trigger(kobj_t obj, void *data, int go) return 0; } -static int +static u_int32_t csachan_getptr(kobj_t obj, void *data) { struct csa_chinfo *ch = data; struct csa_info *csa = ch->parent; csa_res *resp; - int ptr; + u_int32_t ptr; resp = &csa->res; @@ -627,7 +630,7 @@ static kobj_method_t csachan_methods[] = { KOBJMETHOD(channel_trigger, csachan_trigger), KOBJMETHOD(channel_getptr, csachan_getptr), KOBJMETHOD(channel_getcaps, csachan_getcaps), - { 0, 0 } + KOBJMETHOD_END }; CHANNEL_DECLARE(csachan); diff --git a/sys/dev/sound/pci/ds1.c b/sys/dev/sound/pci/ds1.c index 9ad24fd..d510717 100644 --- a/sys/dev/sound/pci/ds1.c +++ b/sys/dev/sound/pci/ds1.c @@ -24,6 +24,10 @@ * SUCH DAMAGE. */ +#ifdef HAVE_KERNEL_OPTION_HEADERS +#include "opt_snd.h" +#endif + #include <dev/sound/pcm/sound.h> #include <dev/sound/pcm/ac97.h> @@ -167,23 +171,23 @@ static void ds_wr(struct sc_info *, int, u_int32_t, int); /* -------------------------------------------------------------------- */ static u_int32_t ds_recfmt[] = { - AFMT_U8, - AFMT_STEREO | AFMT_U8, - AFMT_S8, - AFMT_STEREO | AFMT_S8, - AFMT_S16_LE, - AFMT_STEREO | AFMT_S16_LE, - AFMT_U16_LE, - AFMT_STEREO | AFMT_U16_LE, + SND_FORMAT(AFMT_U8, 1, 0), + SND_FORMAT(AFMT_U8, 2, 0), + SND_FORMAT(AFMT_S8, 1, 0), + SND_FORMAT(AFMT_S8, 2, 0), + SND_FORMAT(AFMT_S16_LE, 1, 0), + SND_FORMAT(AFMT_S16_LE, 2, 0), + SND_FORMAT(AFMT_U16_LE, 1, 0), + SND_FORMAT(AFMT_U16_LE, 2, 0), 0 }; static struct pcmchan_caps ds_reccaps = {4000, 48000, ds_recfmt, 0}; static u_int32_t ds_playfmt[] = { - AFMT_U8, - AFMT_STEREO | AFMT_U8, - /* AFMT_S16_LE, */ - AFMT_STEREO | AFMT_S16_LE, + SND_FORMAT(AFMT_U8, 1, 0), + SND_FORMAT(AFMT_U8, 2, 0), + /* SND_FORMAT(AFMT_S16_LE, 1, 0), */ + SND_FORMAT(AFMT_S16_LE, 2, 0), 0 }; static struct pcmchan_caps ds_playcaps = {4000, 96000, ds_playfmt, 0}; @@ -323,7 +327,7 @@ static kobj_method_t ds_ac97_methods[] = { KOBJMETHOD(ac97_init, ds_initcd), KOBJMETHOD(ac97_read, ds_rdcd), KOBJMETHOD(ac97_write, ds_wrcd), - { 0, 0 } + KOBJMETHOD_END }; AC97_DECLARE(ds_ac97); @@ -432,7 +436,7 @@ ds_setuppch(struct sc_pchinfo *ch) int stereo, b16, c, sz; bus_addr_t addr; - stereo = (ch->fmt & AFMT_STEREO)? 1 : 0; + stereo = (AFMT_CHANNEL(ch->fmt) > 1)? 1 : 0; b16 = (ch->fmt & AFMT_16BIT)? 1 : 0; c = stereo? 1 : 0; addr = sndbuf_getbufaddr(ch->buffer); @@ -452,7 +456,7 @@ ds_setuprch(struct sc_rchinfo *ch) u_int32_t x, y; bus_addr_t addr; - stereo = (ch->fmt & AFMT_STEREO)? 1 : 0; + stereo = (AFMT_CHANNEL(ch->fmt) > 1)? 1 : 0; b16 = (ch->fmt & AFMT_16BIT)? 1 : 0; addr = sndbuf_getbufaddr(ch->buffer); sz = sndbuf_getsize(ch->buffer); @@ -487,7 +491,7 @@ ds1pchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel ch->parent = sc; ch->channel = c; ch->dir = dir; - ch->fmt = AFMT_U8; + ch->fmt = SND_FORMAT(AFMT_U8, 1, 0); ch->spd = 8000; ch->run = 0; if (sndbuf_alloc(ch->buffer, sc->buffer_dmat, 0, sc->bufsz) != 0) @@ -512,7 +516,7 @@ ds1pchan_setformat(kobj_t obj, void *data, u_int32_t format) return 0; } -static int +static u_int32_t ds1pchan_setspeed(kobj_t obj, void *data, u_int32_t speed) { struct sc_pchinfo *ch = data; @@ -522,7 +526,7 @@ ds1pchan_setspeed(kobj_t obj, void *data, u_int32_t speed) return speed; } -static int +static u_int32_t ds1pchan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) { struct sc_pchinfo *ch = data; @@ -530,7 +534,7 @@ ds1pchan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) int drate; /* irq rate is fixed at 187.5hz */ - drate = ch->spd * sndbuf_getbps(ch->buffer); + drate = ch->spd * sndbuf_getalign(ch->buffer); blocksize = roundup2((drate << 8) / DS1_IRQHZ, 4); sndbuf_resize(ch->buffer, sc->bufsz / blocksize, blocksize); @@ -547,7 +551,7 @@ ds1pchan_trigger(kobj_t obj, void *data, int go) if (!PCMTRIG_COMMON(go)) return 0; - stereo = (ch->fmt & AFMT_STEREO)? 1 : 0; + stereo = (AFMT_CHANNEL(ch->fmt) > 1)? 1 : 0; if (go == PCMTRIG_START) { ch->run = 1; ds_setuppch(ch); @@ -566,7 +570,7 @@ ds1pchan_trigger(kobj_t obj, void *data, int go) return 0; } -static int +static u_int32_t ds1pchan_getptr(kobj_t obj, void *data) { struct sc_pchinfo *ch = data; @@ -575,7 +579,7 @@ ds1pchan_getptr(kobj_t obj, void *data) int ss; u_int32_t ptr; - ss = (ch->fmt & AFMT_STEREO)? 1 : 0; + ss = (AFMT_CHANNEL(ch->fmt) > 1)? 1 : 0; ss += (ch->fmt & AFMT_16BIT)? 1 : 0; bank = ch->lslot + sc->currbank; @@ -599,7 +603,7 @@ static kobj_method_t ds1pchan_methods[] = { KOBJMETHOD(channel_trigger, ds1pchan_trigger), KOBJMETHOD(channel_getptr, ds1pchan_getptr), KOBJMETHOD(channel_getcaps, ds1pchan_getcaps), - { 0, 0 } + KOBJMETHOD_END }; CHANNEL_DECLARE(ds1pchan); @@ -619,7 +623,7 @@ ds1rchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel ch->parent = sc; ch->channel = c; ch->dir = dir; - ch->fmt = AFMT_U8; + ch->fmt = SND_FORMAT(AFMT_U8, 1, 0); ch->spd = 8000; if (sndbuf_alloc(ch->buffer, sc->buffer_dmat, 0, sc->bufsz) != 0) return NULL; @@ -640,7 +644,7 @@ ds1rchan_setformat(kobj_t obj, void *data, u_int32_t format) return 0; } -static int +static u_int32_t ds1rchan_setspeed(kobj_t obj, void *data, u_int32_t speed) { struct sc_rchinfo *ch = data; @@ -650,7 +654,7 @@ ds1rchan_setspeed(kobj_t obj, void *data, u_int32_t speed) return speed; } -static int +static u_int32_t ds1rchan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) { struct sc_rchinfo *ch = data; @@ -658,7 +662,7 @@ ds1rchan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) int drate; /* irq rate is fixed at 187.5hz */ - drate = ch->spd * sndbuf_getbps(ch->buffer); + drate = ch->spd * sndbuf_getalign(ch->buffer); blocksize = roundup2((drate << 8) / DS1_IRQHZ, 4); sndbuf_resize(ch->buffer, sc->bufsz / blocksize, blocksize); @@ -696,7 +700,7 @@ ds1rchan_trigger(kobj_t obj, void *data, int go) return 0; } -static int +static u_int32_t ds1rchan_getptr(kobj_t obj, void *data) { struct sc_rchinfo *ch = data; @@ -719,7 +723,7 @@ static kobj_method_t ds1rchan_methods[] = { KOBJMETHOD(channel_trigger, ds1rchan_trigger), KOBJMETHOD(channel_getptr, ds1rchan_getptr), KOBJMETHOD(channel_getcaps, ds1rchan_getcaps), - { 0, 0 } + KOBJMETHOD_END }; CHANNEL_DECLARE(ds1rchan); diff --git a/sys/dev/sound/pci/emu10k1.c b/sys/dev/sound/pci/emu10k1.c index 43cc412..43b7ebe 100644 --- a/sys/dev/sound/pci/emu10k1.c +++ b/sys/dev/sound/pci/emu10k1.c @@ -26,6 +26,10 @@ * 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 "emu10k1-alsa%diked.h" @@ -168,18 +172,18 @@ static void emu_wr(struct sc_info *, int, u_int32_t, int); /* -------------------------------------------------------------------- */ static u_int32_t emu_rfmt_ac97[] = { - AFMT_S16_LE, - AFMT_STEREO | AFMT_S16_LE, + SND_FORMAT(AFMT_S16_LE, 1, 0), + SND_FORMAT(AFMT_S16_LE, 2, 0), 0 }; static u_int32_t emu_rfmt_mic[] = { - AFMT_U8, + SND_FORMAT(AFMT_U8, 1, 0), 0 }; static u_int32_t emu_rfmt_efx[] = { - AFMT_STEREO | AFMT_S16_LE, + SND_FORMAT(AFMT_S16_LE, 2, 0), 0 }; @@ -190,10 +194,10 @@ static struct pcmchan_caps emu_reccaps[3] = { }; static u_int32_t emu_pfmt[] = { - AFMT_U8, - AFMT_STEREO | AFMT_U8, - AFMT_S16_LE, - AFMT_STEREO | AFMT_S16_LE, + SND_FORMAT(AFMT_U8, 1, 0), + SND_FORMAT(AFMT_U8, 2, 0), + SND_FORMAT(AFMT_S16_LE, 1, 0), + SND_FORMAT(AFMT_S16_LE, 2, 0), 0 }; @@ -307,7 +311,7 @@ emu_wrcd(kobj_t obj, void *devinfo, int regno, u_int32_t data) static kobj_method_t emu_ac97_methods[] = { KOBJMETHOD(ac97_read, emu_rdcd), KOBJMETHOD(ac97_write, emu_wrcd), - { 0, 0 } + KOBJMETHOD_END }; AC97_DECLARE(emu_ac97); @@ -324,7 +328,7 @@ emu_settimer(struct sc_info *sc) for (i = 0; i < sc->nchans; i++) { pch = &sc->pch[i]; if (pch->buffer) { - tmp = (pch->spd * sndbuf_getbps(pch->buffer)) + tmp = (pch->spd * sndbuf_getalign(pch->buffer)) / pch->blksz; if (tmp > rate) rate = tmp; @@ -334,7 +338,7 @@ emu_settimer(struct sc_info *sc) for (i = 0; i < 3; i++) { rch = &sc->rch[i]; if (rch->buffer) { - tmp = (rch->spd * sndbuf_getbps(rch->buffer)) + tmp = (rch->spd * sndbuf_getalign(rch->buffer)) / rch->blksz; if (tmp > rate) rate = tmp; @@ -533,7 +537,7 @@ emu_vsetup(struct sc_pchinfo *ch) if (ch->fmt) { v->b16 = (ch->fmt & AFMT_16BIT) ? 1 : 0; - v->stereo = (ch->fmt & AFMT_STEREO) ? 1 : 0; + v->stereo = (AFMT_CHANNEL(ch->fmt) > 1) ? 1 : 0; if (v->slave != NULL) { v->slave->b16 = v->b16; v->slave->stereo = v->stereo; @@ -729,7 +733,7 @@ emupchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, ch->parent = sc; ch->channel = c; ch->blksz = sc->bufsz / 2; - ch->fmt = AFMT_U8; + ch->fmt = SND_FORMAT(AFMT_U8, 1, 0); ch->spd = 8000; snd_mtxlock(sc->lock); ch->master = emu_valloc(sc); @@ -764,7 +768,7 @@ emupchan_setformat(kobj_t obj, void *data, u_int32_t format) return 0; } -static int +static u_int32_t emupchan_setspeed(kobj_t obj, void *data, u_int32_t speed) { struct sc_pchinfo *ch = data; @@ -773,7 +777,7 @@ emupchan_setspeed(kobj_t obj, void *data, u_int32_t speed) return ch->spd; } -static int +static u_int32_t emupchan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) { struct sc_pchinfo *ch = data; @@ -785,7 +789,7 @@ emupchan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) emu_settimer(sc); irqrate = 48000 / sc->timerinterval; snd_mtxunlock(sc->lock); - blksz = (ch->spd * sndbuf_getbps(ch->buffer)) / irqrate; + blksz = (ch->spd * sndbuf_getalign(ch->buffer)) / irqrate; return blocksize; } @@ -819,7 +823,7 @@ emupchan_trigger(kobj_t obj, void *data, int go) return 0; } -static int +static u_int32_t emupchan_getptr(kobj_t obj, void *data) { struct sc_pchinfo *ch = data; @@ -848,7 +852,7 @@ static kobj_method_t emupchan_methods[] = { KOBJMETHOD(channel_trigger, emupchan_trigger), KOBJMETHOD(channel_getptr, emupchan_getptr), KOBJMETHOD(channel_getcaps, emupchan_getcaps), - { 0, 0 } + KOBJMETHOD_END }; CHANNEL_DECLARE(emupchan); @@ -866,7 +870,7 @@ emurchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, ch->parent = sc; ch->channel = c; ch->blksz = sc->bufsz / 2; - ch->fmt = AFMT_U8; + ch->fmt = SND_FORMAT(AFMT_U8, 1, 0); ch->spd = 8000; ch->num = sc->rnum; switch(sc->rnum) { @@ -915,7 +919,7 @@ emurchan_setformat(kobj_t obj, void *data, u_int32_t format) return 0; } -static int +static u_int32_t emurchan_setspeed(kobj_t obj, void *data, u_int32_t speed) { struct sc_rchinfo *ch = data; @@ -934,7 +938,7 @@ emurchan_setspeed(kobj_t obj, void *data, u_int32_t speed) return ch->spd; } -static int +static u_int32_t emurchan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) { struct sc_rchinfo *ch = data; @@ -946,7 +950,7 @@ emurchan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) emu_settimer(sc); irqrate = 48000 / sc->timerinterval; snd_mtxunlock(sc->lock); - blksz = (ch->spd * sndbuf_getbps(ch->buffer)) / irqrate; + blksz = (ch->spd * sndbuf_getalign(ch->buffer)) / irqrate; return blocksize; } @@ -994,12 +998,12 @@ emurchan_trigger(kobj_t obj, void *data, int go) if (ch->num == 0) { if (sc->audigy) { val = A_ADCCR_LCHANENABLE; - if (ch->fmt & AFMT_STEREO) + if (AFMT_CHANNEL(ch->fmt) > 1) val |= A_ADCCR_RCHANENABLE; val |= audigy_recval(ch->spd); } else { val = ADCCR_LCHANENABLE; - if (ch->fmt & AFMT_STEREO) + if (AFMT_CHANNEL(ch->fmt) > 1) val |= ADCCR_RCHANENABLE; val |= emu_recval(ch->spd); } @@ -1033,7 +1037,7 @@ emurchan_trigger(kobj_t obj, void *data, int go) return 0; } -static int +static u_int32_t emurchan_getptr(kobj_t obj, void *data) { struct sc_rchinfo *ch = data; @@ -1063,29 +1067,30 @@ static kobj_method_t emurchan_methods[] = { KOBJMETHOD(channel_trigger, emurchan_trigger), KOBJMETHOD(channel_getptr, emurchan_getptr), KOBJMETHOD(channel_getcaps, emurchan_getcaps), - { 0, 0 } + KOBJMETHOD_END }; CHANNEL_DECLARE(emurchan); static unsigned char -emu_mread(void *arg, struct sc_info *sc, int reg) +emu_mread(struct mpu401 *arg, void *sc, int reg) { unsigned int d; - d = emu_rd(sc, 0x18 + reg, 1); + d = emu_rd((struct sc_info *)sc, 0x18 + reg, 1); return d; } static void -emu_mwrite(void *arg, struct sc_info *sc, int reg, unsigned char b) +emu_mwrite(struct mpu401 *arg, void *sc, int reg, unsigned char b) { - emu_wr(sc, 0x18 + reg, b, 1); + emu_wr((struct sc_info *)sc, 0x18 + reg, b, 1); } static int -emu_muninit(void *arg, struct sc_info *sc) +emu_muninit(struct mpu401 *arg, void *cookie) { + struct sc_info *sc = cookie; snd_mtxlock(sc->lock); sc->mpu_intr = 0; @@ -1098,7 +1103,7 @@ static kobj_method_t emu_mpu_methods[] = { KOBJMETHOD(mpufoi_read, emu_mread), KOBJMETHOD(mpufoi_write, emu_mwrite), KOBJMETHOD(mpufoi_uninit, emu_muninit), - { 0, 0 } + KOBJMETHOD_END }; static DEFINE_CLASS(emu_mpu, emu_mpu_methods, 0); diff --git a/sys/dev/sound/pci/emu10kx-midi.c b/sys/dev/sound/pci/emu10kx-midi.c index 012796d..63abd3d 100644 --- a/sys/dev/sound/pci/emu10kx-midi.c +++ b/sys/dev/sound/pci/emu10kx-midi.c @@ -39,6 +39,10 @@ #include <sys/lock.h> #include <sys/mutex.h> +#ifdef HAVE_KERNEL_OPTION_HEADERS +#include "opt_snd.h" +#endif + #include <dev/sound/chip.h> #include <dev/sound/pcm/sound.h> @@ -65,8 +69,9 @@ static uint32_t emu_midi_card_intr(void *p, uint32_t arg); static devclass_t emu_midi_devclass; static unsigned char -emu_mread(void *arg __unused, struct emu_midi_softc *sc, int reg) +emu_mread(struct mpu401 *arg __unused, void *cookie, int reg) { + struct emu_midi_softc *sc = cookie; unsigned int d; d = 0; @@ -79,8 +84,9 @@ emu_mread(void *arg __unused, struct emu_midi_softc *sc, int reg) } static void -emu_mwrite(void *arg __unused, struct emu_midi_softc *sc, int reg, unsigned char b) +emu_mwrite(struct mpu401 *arg __unused, void *cookie, int reg, unsigned char b) { + struct emu_midi_softc *sc = cookie; if (sc->is_emu10k1) emu_wr(sc->card, 0x18 + reg, b, 1); @@ -89,8 +95,9 @@ emu_mwrite(void *arg __unused, struct emu_midi_softc *sc, int reg, unsigned char } static int -emu_muninit(void *arg __unused, struct emu_midi_softc *sc) +emu_muninit(struct mpu401 *arg __unused, void *cookie) { + struct emu_midi_softc *sc = cookie; mtx_lock(&sc->mtx); sc->mpu_intr = NULL; @@ -103,7 +110,7 @@ static kobj_method_t emu_mpu_methods[] = { KOBJMETHOD(mpufoi_read, emu_mread), KOBJMETHOD(mpufoi_write, emu_mwrite), KOBJMETHOD(mpufoi_uninit, emu_muninit), - {0, 0} + KOBJMETHOD_END }; static DEFINE_CLASS(emu_mpu, emu_mpu_methods, 0); diff --git a/sys/dev/sound/pci/emu10kx-pcm.c b/sys/dev/sound/pci/emu10kx-pcm.c index 99fefd7..7ffee84 100644 --- a/sys/dev/sound/pci/emu10kx-pcm.c +++ b/sys/dev/sound/pci/emu10kx-pcm.c @@ -39,6 +39,10 @@ #include <sys/lock.h> #include <sys/mutex.h> +#ifdef HAVE_KERNEL_OPTION_HEADERS +#include "opt_snd.h" +#endif + #include <dev/sound/chip.h> #include <dev/sound/pcm/sound.h> #include <dev/sound/pcm/ac97.h> @@ -112,8 +116,8 @@ struct emu_pcm_info { static uint32_t emu_rfmt_adc[] = { - AFMT_S16_LE, - AFMT_STEREO | AFMT_S16_LE, + SND_FORMAT(AFMT_S16_LE, 1, 0), + SND_FORMAT(AFMT_S16_LE, 2, 0), 0 }; static struct pcmchan_caps emu_reccaps_adc = { @@ -121,7 +125,7 @@ static struct pcmchan_caps emu_reccaps_adc = { }; static uint32_t emu_rfmt_efx[] = { - AFMT_S16_LE, + SND_FORMAT(AFMT_S16_LE, 1, 0), 0 }; @@ -142,15 +146,15 @@ static int emu_rates_audigy[] = { }; static uint32_t emu_pfmt[] = { - AFMT_U8, - AFMT_STEREO | AFMT_U8, - AFMT_S16_LE, - AFMT_STEREO | AFMT_S16_LE, + SND_FORMAT(AFMT_U8, 1, 0), + SND_FORMAT(AFMT_U8, 2, 0), + SND_FORMAT(AFMT_S16_LE, 1, 0), + SND_FORMAT(AFMT_S16_LE, 2, 0), 0 }; static uint32_t emu_pfmt_mono[] = { - AFMT_U8, - AFMT_S16_LE, + SND_FORMAT(AFMT_U8, 1, 0), + SND_FORMAT(AFMT_S16_LE, 1, 0), 0 }; @@ -385,7 +389,7 @@ emu_dspmixer_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned righ return (0); } -static int +static u_int32_t emu_dspmixer_setrecsrc(struct snd_mixer *m, u_int32_t src) { struct emu_pcm_info *sc; @@ -467,7 +471,7 @@ static kobj_method_t emudspmixer_methods[] = { KOBJMETHOD(mixer_uninit, emu_dspmixer_uninit), KOBJMETHOD(mixer_set, emu_dspmixer_set), KOBJMETHOD(mixer_setrecsrc, emu_dspmixer_setrecsrc), - { 0, 0 } + KOBJMETHOD_END }; MIXER_DECLARE(emudspmixer); @@ -486,7 +490,7 @@ emu_efxmixer_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned righ return (0); } -static int +static u_int32_t emu_efxmixer_setrecsrc(struct snd_mixer *m __unused, u_int32_t src __unused) { return (SOUND_MASK_MONITOR); @@ -496,7 +500,7 @@ static kobj_method_t emuefxmixer_methods[] = { KOBJMETHOD(mixer_init, emu_efxmixer_init), KOBJMETHOD(mixer_set, emu_efxmixer_set), KOBJMETHOD(mixer_setrecsrc, emu_efxmixer_setrecsrc), - { 0, 0 } + KOBJMETHOD_END }; MIXER_DECLARE(emuefxmixer); @@ -642,7 +646,7 @@ emu_ewrcd(kobj_t obj __unused, void *devinfo, int regno, uint32_t data) static kobj_method_t emu_eac97_methods[] = { KOBJMETHOD(ac97_read, emu_erdcd), KOBJMETHOD(ac97_write, emu_ewrcd), - {0, 0} + KOBJMETHOD_END }; AC97_DECLARE(emu_eac97); @@ -673,7 +677,7 @@ emu_wrcd(kobj_t obj __unused, void *devinfo, int regno, uint32_t data) static kobj_method_t emu_ac97_methods[] = { KOBJMETHOD(ac97_read, emu_rdcd), KOBJMETHOD(ac97_write, emu_wrcd), - {0, 0} + KOBJMETHOD_END }; AC97_DECLARE(emu_ac97); @@ -718,7 +722,7 @@ emupchan_init(kobj_t obj __unused, void *devinfo, struct snd_dbuf *b, struct pcm ch->pcm = sc; ch->channel = c; ch->blksz = sc->bufsz; - ch->fmt = AFMT_U8; + ch->fmt = SND_FORMAT(AFMT_U8, 1, 0); ch->spd = 8000; ch->master = emu_valloc(sc->card); /* @@ -753,7 +757,7 @@ emupchan_setformat(kobj_t obj __unused, void *c_devinfo, uint32_t format) return (0); } -static int +static uint32_t emupchan_setspeed(kobj_t obj __unused, void *c_devinfo, uint32_t speed) { struct emu_pcm_pchinfo *ch = c_devinfo; @@ -762,7 +766,7 @@ emupchan_setspeed(kobj_t obj __unused, void *c_devinfo, uint32_t speed) return (ch->spd); } -static int +static uint32_t emupchan_setblocksize(kobj_t obj __unused, void *c_devinfo, uint32_t blocksize) { struct emu_pcm_pchinfo *ch = c_devinfo; @@ -772,7 +776,7 @@ emupchan_setblocksize(kobj_t obj __unused, void *c_devinfo, uint32_t blocksize) blocksize = ch->pcm->bufsz; snd_mtxlock(sc->lock); ch->blksz = blocksize; - emu_timer_set(sc->card, ch->timer, ch->blksz / sndbuf_getbps(ch->buffer)); + emu_timer_set(sc->card, ch->timer, ch->blksz / sndbuf_getalign(ch->buffer)); snd_mtxunlock(sc->lock); return (ch->blksz); } @@ -789,12 +793,12 @@ emupchan_trigger(kobj_t obj __unused, void *c_devinfo, int go) snd_mtxlock(sc->lock); /* XXX can we trigger on parallel threads ? */ if (go == PCMTRIG_START) { emu_vsetup(ch->master, ch->fmt, ch->spd); - if ((ch->fmt & AFMT_STEREO) == AFMT_STEREO) + if (AFMT_CHANNEL(ch->fmt) > 1) emu_vroute(sc->card, &(sc->rt), ch->master); else emu_vroute(sc->card, &(sc->rt_mono), ch->master); emu_vwrite(sc->card, ch->master); - emu_timer_set(sc->card, ch->timer, ch->blksz / sndbuf_getbps(ch->buffer)); + emu_timer_set(sc->card, ch->timer, ch->blksz / sndbuf_getalign(ch->buffer)); emu_timer_enable(sc->card, ch->timer, 1); } /* PCM interrupt handler will handle PCMTRIG_STOP event */ @@ -804,7 +808,7 @@ emupchan_trigger(kobj_t obj __unused, void *c_devinfo, int go) return (0); } -static int +static uint32_t emupchan_getptr(kobj_t obj __unused, void *c_devinfo) { struct emu_pcm_pchinfo *ch = c_devinfo; @@ -848,7 +852,7 @@ static kobj_method_t emupchan_methods[] = { KOBJMETHOD(channel_trigger, emupchan_trigger), KOBJMETHOD(channel_getptr, emupchan_getptr), KOBJMETHOD(channel_getcaps, emupchan_getcaps), - {0, 0} + KOBJMETHOD_END }; CHANNEL_DECLARE(emupchan); @@ -864,7 +868,7 @@ emurchan_init(kobj_t obj __unused, void *devinfo, struct snd_dbuf *b, struct pcm ch->pcm = sc; ch->channel = c; ch->blksz = sc->bufsz / 2; /* We rise interrupt for half-full buffer */ - ch->fmt = AFMT_U8; + ch->fmt = SND_FORMAT(AFMT_U8, 1, 0); ch->spd = 8000; ch->idxreg = sc->is_emu10k1 ? ADCIDX : A_ADCIDX; ch->basereg = ADCBA; @@ -902,7 +906,7 @@ emurchan_setformat(kobj_t obj __unused, void *c_devinfo, uint32_t format) return (0); } -static int +static uint32_t emurchan_setspeed(kobj_t obj __unused, void *c_devinfo, uint32_t speed) { struct emu_pcm_rchinfo *ch = c_devinfo; @@ -916,7 +920,7 @@ emurchan_setspeed(kobj_t obj __unused, void *c_devinfo, uint32_t speed) return (ch->spd); } -static int +static uint32_t emurchan_setblocksize(kobj_t obj __unused, void *c_devinfo, uint32_t blocksize) { struct emu_pcm_rchinfo *ch = c_devinfo; @@ -929,7 +933,7 @@ emurchan_setblocksize(kobj_t obj __unused, void *c_devinfo, uint32_t blocksize) * (and use) timer interrupts. Otherwise channel will be marked dead. */ if (ch->blksz < (ch->pcm->bufsz / 2)) { - emu_timer_set(sc->card, ch->timer, ch->blksz / sndbuf_getbps(ch->buffer)); + emu_timer_set(sc->card, ch->timer, ch->blksz / sndbuf_getalign(ch->buffer)); emu_timer_enable(sc->card, ch->timer, 1); } else { emu_timer_enable(sc->card, ch->timer, 0); @@ -973,7 +977,7 @@ emurchan_trigger(kobj_t obj __unused, void *c_devinfo, int go) ch->run = 1; emu_wrptr(sc->card, 0, ch->sizereg, sz); val = sc->is_emu10k1 ? ADCCR_LCHANENABLE : A_ADCCR_LCHANENABLE; - if (ch->fmt & AFMT_STEREO) + if (AFMT_CHANNEL(ch->fmt) > 1) val |= sc->is_emu10k1 ? ADCCR_RCHANENABLE : A_ADCCR_RCHANENABLE; val |= sc->is_emu10k1 ? emu_k1_recval(ch->spd) : emu_k2_recval(ch->spd); emu_wrptr(sc->card, 0, ch->setupreg, 0); @@ -1001,7 +1005,7 @@ emurchan_trigger(kobj_t obj __unused, void *c_devinfo, int go) return (0); } -static int +static uint32_t emurchan_getptr(kobj_t obj __unused, void *c_devinfo) { struct emu_pcm_rchinfo *ch = c_devinfo; @@ -1028,7 +1032,7 @@ static kobj_method_t emurchan_methods[] = { KOBJMETHOD(channel_trigger, emurchan_trigger), KOBJMETHOD(channel_getptr, emurchan_getptr), KOBJMETHOD(channel_getcaps, emurchan_getcaps), - {0, 0} + KOBJMETHOD_END }; CHANNEL_DECLARE(emurchan); @@ -1043,7 +1047,7 @@ emufxrchan_init(kobj_t obj __unused, void *devinfo, struct snd_dbuf *b, struct p if (sc == NULL) return (NULL); ch = &(sc->rch_efx); - ch->fmt = AFMT_S16_LE; + ch->fmt = SND_FORMAT(AFMT_S16_LE, 1, 0); ch->spd = sc->is_emu10k1 ? 48000*32 : 48000 * 64; ch->idxreg = FXIDX; ch->basereg = FXBA; @@ -1067,11 +1071,11 @@ emufxrchan_init(kobj_t obj __unused, void *devinfo, struct snd_dbuf *b, struct p static int emufxrchan_setformat(kobj_t obj __unused, void *c_devinfo __unused, uint32_t format) { - if (format == AFMT_S16_LE) return (0); + if (format == SND_FORMAT(AFMT_S16_LE, 1, 0)) return (0); return (EINVAL); } -static int +static uint32_t emufxrchan_setspeed(kobj_t obj __unused, void *c_devinfo, uint32_t speed) { struct emu_pcm_rchinfo *ch = c_devinfo; @@ -1080,7 +1084,7 @@ emufxrchan_setspeed(kobj_t obj __unused, void *c_devinfo, uint32_t speed) return (ch->spd); } -static int +static uint32_t emufxrchan_setblocksize(kobj_t obj __unused, void *c_devinfo, uint32_t blocksize) { struct emu_pcm_rchinfo *ch = c_devinfo; @@ -1171,7 +1175,7 @@ emufxrchan_trigger(kobj_t obj __unused, void *c_devinfo, int go) return (0); } -static int +static uint32_t emufxrchan_getptr(kobj_t obj __unused, void *c_devinfo) { struct emu_pcm_rchinfo *ch = c_devinfo; @@ -1218,7 +1222,7 @@ static kobj_method_t emufxrchan_methods[] = { KOBJMETHOD(channel_getptr, emufxrchan_getptr), KOBJMETHOD(channel_getcaps, emufxrchan_getcaps), KOBJMETHOD(channel_getrates, emufxrchan_getrates), - {0, 0} + KOBJMETHOD_END }; CHANNEL_DECLARE(emufxrchan); diff --git a/sys/dev/sound/pci/emu10kx.c b/sys/dev/sound/pci/emu10kx.c index 3fb4d23..f1d10ed 100644 --- a/sys/dev/sound/pci/emu10kx.c +++ b/sys/dev/sound/pci/emu10kx.c @@ -45,6 +45,10 @@ #include <machine/clock.h> /* for DELAY */ +#ifdef HAVE_KERNEL_OPTION_HEADERS +#include "opt_snd.h" +#endif + #include <dev/sound/chip.h> #include <dev/sound/pcm/sound.h> #include <dev/sound/pcm/ac97.h> @@ -1325,7 +1329,7 @@ emu_vsetup(struct emu_voice *v, int fmt, int spd) { if (fmt) { v->b16 = (fmt & AFMT_16BIT) ? 1 : 0; - v->stereo = (fmt & AFMT_STEREO) ? 1 : 0; + v->stereo = (AFMT_CHANNEL(fmt) > 1) ? 1 : 0; if (v->slave != NULL) { v->slave->b16 = v->b16; v->slave->stereo = v->stereo; @@ -2313,7 +2317,7 @@ emu10kx_dev_init(struct emu_sc_info *sc) mtx_init(&sc->emu10kx_lock, device_get_nameunit(sc->dev), "kxdevlock", 0); unit = device_get_unit(sc->dev); - sc->cdev = make_dev(&emu10kx_cdevsw, unit, UID_ROOT, GID_WHEEL, 0640, "emu10kx%d", unit); + sc->cdev = make_dev(&emu10kx_cdevsw, PCMMINOR(unit), UID_ROOT, GID_WHEEL, 0640, "emu10kx%d", unit); if (sc->cdev != NULL) { sc->cdev->si_drv1 = sc; return (0); @@ -3193,7 +3197,11 @@ emu_pci_attach(device_t dev) i = 0; sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &i, RF_ACTIVE | RF_SHAREABLE); - if ((sc->irq == NULL) || bus_setup_intr(dev, sc->irq, INTR_MPSAFE | INTR_TYPE_AV, NULL, emu_intr, sc, &sc->ih)) { + if ((sc->irq == NULL) || bus_setup_intr(dev, sc->irq, INTR_MPSAFE | INTR_TYPE_AV, +#if __FreeBSD_version >= 700031 + NULL, +#endif + emu_intr, sc, &sc->ih)) { device_printf(dev, "unable to map interrupt\n"); goto bad; } diff --git a/sys/dev/sound/pci/envy24.c b/sys/dev/sound/pci/envy24.c index fc6fc8a..09c616c 100644 --- a/sys/dev/sound/pci/envy24.c +++ b/sys/dev/sound/pci/envy24.c @@ -25,6 +25,10 @@ * */ +#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/pci/spicds.h> @@ -51,7 +55,7 @@ struct sc_info; #define ENVY24_TIMEOUT 1000 -#define ENVY24_DEFAULT_FORMAT (AFMT_STEREO | AFMT_S16_LE) +#define ENVY24_DEFAULT_FORMAT SND_FORMAT(AFMT_S16_LE, 2, 0) #define ENVY24_NAMELEN 32 @@ -187,10 +191,10 @@ static void envy24_r32sl(struct sc_chinfo *); /* channel interface */ static void *envy24chan_init(kobj_t, void *, struct snd_dbuf *, struct pcm_channel *, int); static int envy24chan_setformat(kobj_t, void *, u_int32_t); -static int envy24chan_setspeed(kobj_t, void *, u_int32_t); -static int envy24chan_setblocksize(kobj_t, void *, u_int32_t); +static u_int32_t envy24chan_setspeed(kobj_t, void *, u_int32_t); +static u_int32_t envy24chan_setblocksize(kobj_t, void *, u_int32_t); static int envy24chan_trigger(kobj_t, void *, int); -static int envy24chan_getptr(kobj_t, void *); +static u_int32_t envy24chan_getptr(kobj_t, void *); static struct pcmchan_caps *envy24chan_getcaps(kobj_t, void *); /* mixer interface */ @@ -358,16 +362,16 @@ static struct cfg_info cfg_table[] = { }; static u_int32_t envy24_recfmt[] = { - AFMT_STEREO | AFMT_S16_LE, - AFMT_STEREO | AFMT_S32_LE, + SND_FORMAT(AFMT_S16_LE, 2, 0), + SND_FORMAT(AFMT_S32_LE, 2, 0), 0 }; static struct pcmchan_caps envy24_reccaps = {8000, 96000, envy24_recfmt, 0}; static u_int32_t envy24_playfmt[] = { - AFMT_STEREO | AFMT_U8, - AFMT_STEREO | AFMT_S16_LE, - AFMT_STEREO | AFMT_S32_LE, + SND_FORMAT(AFMT_U8, 2, 0), + SND_FORMAT(AFMT_S16_LE, 2, 0), + SND_FORMAT(AFMT_S32_LE, 2, 0), 0 }; @@ -380,15 +384,15 @@ struct envy24_emldma { }; static struct envy24_emldma envy24_pemltab[] = { - {AFMT_STEREO | AFMT_U8, envy24_p8u, 2}, - {AFMT_STEREO | AFMT_S16_LE, envy24_p16sl, 4}, - {AFMT_STEREO | AFMT_S32_LE, envy24_p32sl, 8}, + {SND_FORMAT(AFMT_U8, 2, 0), envy24_p8u, 2}, + {SND_FORMAT(AFMT_S16_LE, 2, 0), envy24_p16sl, 4}, + {SND_FORMAT(AFMT_S32_LE, 2, 0), envy24_p32sl, 8}, {0, NULL, 0} }; static struct envy24_emldma envy24_remltab[] = { - {AFMT_STEREO | AFMT_S16_LE, envy24_r16sl, 4}, - {AFMT_STEREO | AFMT_S32_LE, envy24_r32sl, 8}, + {SND_FORMAT(AFMT_S16_LE, 2, 0), envy24_r16sl, 4}, + {SND_FORMAT(AFMT_S32_LE, 2, 0), envy24_r32sl, 8}, {0, NULL, 0} }; @@ -730,7 +734,7 @@ envy24_wrcd(kobj_t obj, void *devinfo, int regno, u_int16_t data) static kobj_method_t envy24_ac97_methods[] = { KOBJMETHOD(ac97_read, envy24_rdcd), KOBJMETHOD(ac97_write, envy24_wrcd), - {0, 0} + KOBJMETHOD_END }; AC97_DECLARE(envy24_ac97); #endif @@ -1087,7 +1091,7 @@ static struct { {0, 0x10} }; -static int +static u_int32_t envy24_setspeed(struct sc_info *sc, u_int32_t speed) { u_int32_t code; int i = 0; @@ -1691,7 +1695,7 @@ envy24chan_setformat(kobj_t obj, void *data, u_int32_t format) start triggerd, some other channel is running, and that channel's speed isn't same with, then trigger function will fail. */ -static int +static u_int32_t envy24chan_setspeed(kobj_t obj, void *data, u_int32_t speed) { struct sc_chinfo *ch = data; @@ -1716,7 +1720,7 @@ envy24chan_setspeed(kobj_t obj, void *data, u_int32_t speed) return ch->speed; } -static int +static u_int32_t envy24chan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) { struct sc_chinfo *ch = data; @@ -1871,13 +1875,12 @@ fail: return (error); } -static int +static u_int32_t envy24chan_getptr(kobj_t obj, void *data) { struct sc_chinfo *ch = data; struct sc_info *sc = ch->parent; - u_int32_t ptr; - int rtn; + u_int32_t ptr, rtn; #if(0) device_printf(sc->dev, "envy24chan_getptr()\n"); @@ -1931,7 +1934,7 @@ static kobj_method_t envy24chan_methods[] = { KOBJMETHOD(channel_trigger, envy24chan_trigger), KOBJMETHOD(channel_getptr, envy24chan_getptr), KOBJMETHOD(channel_getcaps, envy24chan_getcaps), - { 0, 0 } + KOBJMETHOD_END }; CHANNEL_DECLARE(envy24chan); @@ -2051,7 +2054,7 @@ static kobj_method_t envy24mixer_methods[] = { KOBJMETHOD(mixer_uninit, envy24mixer_uninit), KOBJMETHOD(mixer_set, envy24mixer_set), KOBJMETHOD(mixer_setrecsrc, envy24mixer_setrecsrc), - { 0, 0 } + KOBJMETHOD_END }; MIXER_DECLARE(envy24mixer); diff --git a/sys/dev/sound/pci/envy24ht.c b/sys/dev/sound/pci/envy24ht.c index d7b0341..b049562 100644 --- a/sys/dev/sound/pci/envy24ht.c +++ b/sys/dev/sound/pci/envy24ht.c @@ -37,6 +37,10 @@ * */ +#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/pci/spicds.h> @@ -63,7 +67,7 @@ struct sc_info; #define ENVY24HT_TIMEOUT 1000 -#define ENVY24HT_DEFAULT_FORMAT (AFMT_STEREO | AFMT_S16_LE) +#define ENVY24HT_DEFAULT_FORMAT SND_FORMAT(AFMT_S16_LE, 2, 0) #define ENVY24HT_NAMELEN 32 @@ -186,10 +190,10 @@ static void envy24ht_r32sl(struct sc_chinfo *); /* channel interface */ static void *envy24htchan_init(kobj_t, void *, struct snd_dbuf *, struct pcm_channel *, int); static int envy24htchan_setformat(kobj_t, void *, u_int32_t); -static int envy24htchan_setspeed(kobj_t, void *, u_int32_t); -static int envy24htchan_setblocksize(kobj_t, void *, u_int32_t); +static u_int32_t envy24htchan_setspeed(kobj_t, void *, u_int32_t); +static u_int32_t envy24htchan_setblocksize(kobj_t, void *, u_int32_t); static int envy24htchan_trigger(kobj_t, void *, int); -static int envy24htchan_getptr(kobj_t, void *); +static u_int32_t envy24htchan_getptr(kobj_t, void *); static struct pcmchan_caps *envy24htchan_getcaps(kobj_t, void *); /* mixer interface */ @@ -411,16 +415,16 @@ static struct cfg_info cfg_table[] = { }; static u_int32_t envy24ht_recfmt[] = { - AFMT_STEREO | AFMT_S16_LE, - AFMT_STEREO | AFMT_S32_LE, + SND_FORMAT(AFMT_S16_LE, 2, 0), + SND_FORMAT(AFMT_S32_LE, 2, 0), 0 }; static struct pcmchan_caps envy24ht_reccaps = {8000, 96000, envy24ht_recfmt, 0}; static u_int32_t envy24ht_playfmt[] = { - AFMT_STEREO | AFMT_U8, - AFMT_STEREO | AFMT_S16_LE, - AFMT_STEREO | AFMT_S32_LE, + SND_FORMAT(AFMT_U8, 2, 0), + SND_FORMAT(AFMT_S16_LE, 2, 0), + SND_FORMAT(AFMT_S32_LE, 2, 0), 0 }; @@ -433,15 +437,15 @@ struct envy24ht_emldma { }; static struct envy24ht_emldma envy24ht_pemltab[] = { - {AFMT_STEREO | AFMT_U8, envy24ht_p8u, 2}, - {AFMT_STEREO | AFMT_S16_LE, envy24ht_p16sl, 4}, - {AFMT_STEREO | AFMT_S32_LE, envy24ht_p32sl, 8}, + {SND_FORMAT(AFMT_U8, 2, 0), envy24ht_p8u, 2}, + {SND_FORMAT(AFMT_S16_LE, 2, 0), envy24ht_p16sl, 4}, + {SND_FORMAT(AFMT_S32_LE, 2, 0), envy24ht_p32sl, 8}, {0, NULL, 0} }; static struct envy24ht_emldma envy24ht_remltab[] = { - {AFMT_STEREO | AFMT_S16_LE, envy24ht_r16sl, 4}, - {AFMT_STEREO | AFMT_S32_LE, envy24ht_r32sl, 8}, + {SND_FORMAT(AFMT_S16_LE, 2, 0), envy24ht_r16sl, 4}, + {SND_FORMAT(AFMT_S32_LE, 2, 0), envy24ht_r32sl, 8}, {0, NULL, 0} }; @@ -809,7 +813,7 @@ envy24ht_wrcd(kobj_t obj, void *devinfo, int regno, u_int16_t data) static kobj_method_t envy24ht_ac97_methods[] = { KOBJMETHOD(ac97_read, envy24ht_rdcd), KOBJMETHOD(ac97_write, envy24ht_wrcd), - {0, 0} + KOBJMETHOD_END }; AC97_DECLARE(envy24ht_ac97); #endif @@ -1037,7 +1041,7 @@ static struct { {0, 0x10} }; -static int +static u_int32_t envy24ht_setspeed(struct sc_info *sc, u_int32_t speed) { u_int32_t code, i2sfmt; int i = 0; @@ -1602,7 +1606,7 @@ envy24htchan_setformat(kobj_t obj, void *data, u_int32_t format) start triggerd, some other channel is running, and that channel's speed isn't same with, then trigger function will fail. */ -static int +static u_int32_t envy24htchan_setspeed(kobj_t obj, void *data, u_int32_t speed) { struct sc_chinfo *ch = data; @@ -1627,7 +1631,7 @@ envy24htchan_setspeed(kobj_t obj, void *data, u_int32_t speed) return ch->speed; } -static int +static u_int32_t envy24htchan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) { struct sc_chinfo *ch = data; @@ -1780,13 +1784,12 @@ fail: return (error); } -static int +static u_int32_t envy24htchan_getptr(kobj_t obj, void *data) { struct sc_chinfo *ch = data; struct sc_info *sc = ch->parent; - u_int32_t ptr; - int rtn; + u_int32_t ptr, rtn; #if(0) device_printf(sc->dev, "envy24htchan_getptr()\n"); @@ -1840,7 +1843,7 @@ static kobj_method_t envy24htchan_methods[] = { KOBJMETHOD(channel_trigger, envy24htchan_trigger), KOBJMETHOD(channel_getptr, envy24htchan_getptr), KOBJMETHOD(channel_getcaps, envy24htchan_getcaps), - { 0, 0 } + KOBJMETHOD_END }; CHANNEL_DECLARE(envy24htchan); @@ -1965,7 +1968,7 @@ static kobj_method_t envy24htmixer_methods[] = { KOBJMETHOD(mixer_uninit, envy24htmixer_uninit), KOBJMETHOD(mixer_set, envy24htmixer_set), KOBJMETHOD(mixer_setrecsrc, envy24htmixer_setrecsrc), - { 0, 0 } + KOBJMETHOD_END }; MIXER_DECLARE(envy24htmixer); diff --git a/sys/dev/sound/pci/es137x.c b/sys/dev/sound/pci/es137x.c index 984aac9..3a62724 100644 --- a/sys/dev/sound/pci/es137x.c +++ b/sys/dev/sound/pci/es137x.c @@ -48,6 +48,10 @@ * */ +#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/pci/es137x.h> @@ -216,10 +220,10 @@ static int es1370_init(struct es_info *); static int es1370_wrcodec(struct es_info *, unsigned char, unsigned char); static uint32_t es_fmt[] = { - AFMT_U8, - AFMT_STEREO | AFMT_U8, - AFMT_S16_LE, - AFMT_STEREO | AFMT_S16_LE, + SND_FORMAT(AFMT_U8, 1, 0), + SND_FORMAT(AFMT_U8, 2, 0), + SND_FORMAT(AFMT_S16_LE, 1, 0), + SND_FORMAT(AFMT_S16_LE, 2, 0), 0 }; static struct pcmchan_caps es_caps = {4000, 48000, es_fmt, 0}; @@ -349,7 +353,7 @@ es1370_mixset(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right) return (l | (r << 8)); } -static int +static uint32_t es1370_mixsetrecsrc(struct snd_mixer *m, uint32_t src) { struct es_info *es; @@ -380,7 +384,7 @@ static kobj_method_t es1370_mixer_methods[] = { KOBJMETHOD(mixer_init, es1370_mixinit), KOBJMETHOD(mixer_set, es1370_mixset), KOBJMETHOD(mixer_setrecsrc, es1370_mixsetrecsrc), - { 0, 0 } + KOBJMETHOD_END }; MIXER_DECLARE(es1370_mixer); @@ -513,20 +517,20 @@ eschan_setformat(kobj_t obj, void *data, uint32_t format) es->sctrl &= ~SCTRL_P1FMT; if (format & AFMT_S16_LE) es->sctrl |= SCTRL_P1SEB; - if (format & AFMT_STEREO) + if (AFMT_CHANNEL(format) > 1) es->sctrl |= SCTRL_P1SMB; } else { es->sctrl &= ~SCTRL_P2FMT; if (format & AFMT_S16_LE) es->sctrl |= SCTRL_P2SEB; - if (format & AFMT_STEREO) + if (AFMT_CHANNEL(format) > 1) es->sctrl |= SCTRL_P2SMB; } } else { es->sctrl &= ~SCTRL_R1FMT; if (format & AFMT_S16_LE) es->sctrl |= SCTRL_R1SEB; - if (format & AFMT_STEREO) + if (AFMT_CHANNEL(format) > 1) es->sctrl |= SCTRL_R1SMB; } es_wr(es, ES1370_REG_SERIAL_CONTROL, es->sctrl, 4); @@ -535,7 +539,7 @@ eschan_setformat(kobj_t obj, void *data, uint32_t format) return (0); } -static int +static uint32_t eschan1370_setspeed(kobj_t obj, void *data, uint32_t speed) { struct es_chinfo *ch = data; @@ -580,7 +584,7 @@ eschan1370_setspeed(kobj_t obj, void *data, uint32_t speed) return (speed); } -static int +static uint32_t eschan1371_setspeed(kobj_t obj, void *data, uint32_t speed) { struct es_chinfo *ch = data; @@ -636,10 +640,10 @@ eschan_setfragments(kobj_t obj, void *data, uint32_t blksz, uint32_t blkcnt) ch->blksz = sndbuf_getblksz(ch->buffer); ch->blkcnt = sndbuf_getblkcnt(ch->buffer); - return (1); + return (0); } -static int +static uint32_t eschan_setblocksize(kobj_t obj, void *data, uint32_t blksz) { struct es_chinfo *ch = data; @@ -733,10 +737,10 @@ eschan_trigger(kobj_t obj, void *data, int go) return 0; ES_LOCK(es); - cnt = (ch->blksz / sndbuf_getbps(ch->buffer)) - 1; + cnt = (ch->blksz / sndbuf_getalign(ch->buffer)) - 1; if (ch->fmt & AFMT_16BIT) b |= 0x02; - if (ch->fmt & AFMT_STEREO) + if (AFMT_CHANNEL(ch->fmt) > 1) b |= 0x01; if (ch->dir == PCMDIR_PLAY) { if (go == PCMTRIG_START) { @@ -820,7 +824,7 @@ eschan_trigger(kobj_t obj, void *data, int go) return (0); } -static int +static uint32_t eschan_getptr(kobj_t obj, void *data) { struct es_chinfo *ch = data; @@ -867,7 +871,7 @@ static kobj_method_t eschan1370_methods[] = { KOBJMETHOD(channel_trigger, eschan_trigger), KOBJMETHOD(channel_getptr, eschan_getptr), KOBJMETHOD(channel_getcaps, eschan_getcaps), - { 0, 0 } + KOBJMETHOD_END }; CHANNEL_DECLARE(eschan1370); @@ -880,7 +884,7 @@ static kobj_method_t eschan1371_methods[] = { KOBJMETHOD(channel_trigger, eschan_trigger), KOBJMETHOD(channel_getptr, eschan_getptr), KOBJMETHOD(channel_getcaps, eschan_getcaps), - { 0, 0 } + KOBJMETHOD_END }; CHANNEL_DECLARE(eschan1371); @@ -1163,7 +1167,7 @@ es1371_rdcd(kobj_t obj, void *s, int addr) static kobj_method_t es1371_ac97_methods[] = { KOBJMETHOD(ac97_read, es1371_rdcd), KOBJMETHOD(ac97_write, es1371_wrcd), - { 0, 0 } + KOBJMETHOD_END }; AC97_DECLARE(es1371_ac97); @@ -1362,7 +1366,6 @@ es_pci_probe(device_t dev) } } -#ifdef SND_DYNSYSCTL static int sysctl_es137x_spdif_enable(SYSCTL_HANDLER_ARGS) { @@ -1595,12 +1598,10 @@ sysctl_es_polling(SYSCTL_HANDLER_ARGS) return (err); } -#endif /* SND_DYNSYSCTL */ static void es_init_sysctls(device_t dev) { -#ifdef SND_DYNSYSCTL struct es_info *es; int r, devid, revid; @@ -1673,7 +1674,6 @@ es_init_sysctls(device_t dev) "polling", CTLTYPE_INT | CTLFLAG_RW, dev, sizeof(dev), sysctl_es_polling, "I", "Enable polling mode"); -#endif /* SND_DYNSYSCTL */ } static int diff --git a/sys/dev/sound/pci/fm801.c b/sys/dev/sound/pci/fm801.c index 8ee8f28..9f00652 100644 --- a/sys/dev/sound/pci/fm801.c +++ b/sys/dev/sound/pci/fm801.c @@ -24,6 +24,10 @@ * 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/pci/pcireg.h> @@ -106,10 +110,10 @@ static int fm801ch_setup(struct pcm_channel *c); */ static u_int32_t fmts[] = { - AFMT_U8, - AFMT_STEREO | AFMT_U8, - AFMT_S16_LE, - AFMT_STEREO | AFMT_S16_LE, + SND_FORMAT(AFMT_U8, 1, 0), + SND_FORMAT(AFMT_U8, 2, 0), + SND_FORMAT(AFMT_S16_LE, 1, 0), + SND_FORMAT(AFMT_S16_LE, 2, 0), 0 }; @@ -272,7 +276,7 @@ fm801_wrcd(kobj_t obj, void *devinfo, int regno, u_int32_t data) static kobj_method_t fm801_ac97_methods[] = { KOBJMETHOD(ac97_read, fm801_rdcd), KOBJMETHOD(ac97_write, fm801_wrcd), - { 0, 0 } + KOBJMETHOD_END }; AC97_DECLARE(fm801_ac97); @@ -346,19 +350,20 @@ fm801ch_setformat(kobj_t obj, void *data, u_int32_t format) struct fm801_info *fm801 = ch->parent; DPRINT("fm801ch_setformat 0x%x : %s, %s, %s, %s\n", format, - (format & AFMT_STEREO)?"stereo":"mono", - (format & (AFMT_S16_LE | AFMT_S16_BE | AFMT_U16_LE | AFMT_U16_BE)) ? "16bit":"8bit", + (AFMT_CHANNEL(format) > 1)?"stereo":"mono", + (format & AFMT_16BIT) ? "16bit":"8bit", (format & AFMT_SIGNED)? "signed":"unsigned", (format & AFMT_BIGENDIAN)?"bigendiah":"littleendian" ); if(ch->dir == PCMDIR_PLAY) { - fm801->play_fmt = (format & AFMT_STEREO)? FM_PLAY_STEREO : 0; + fm801->play_fmt = + (AFMT_CHANNEL(format) > 1)? FM_PLAY_STEREO : 0; fm801->play_fmt |= (format & AFMT_16BIT) ? FM_PLAY_16BIT : 0; return 0; } if(ch->dir == PCMDIR_REC ) { - fm801->rec_fmt = (format & AFMT_STEREO)? FM_REC_STEREO:0; + fm801->rec_fmt = (AFMT_CHANNEL(format) > 1)? FM_REC_STEREO:0; fm801->rec_fmt |= (format & AFMT_16BIT) ? FM_PLAY_16BIT : 0; return 0; } @@ -367,8 +372,8 @@ fm801ch_setformat(kobj_t obj, void *data, u_int32_t format) } struct { - int limit; - int rate; + u_int32_t limit; + u_int32_t rate; } fm801_rates[11] = { { 6600, 5500 }, { 8750, 8000 }, @@ -384,7 +389,7 @@ struct { /* anything above -> 48000 */ }; -static int +static u_int32_t fm801ch_setspeed(kobj_t obj, void *data, u_int32_t speed) { struct fm801_chinfo *ch = data; @@ -411,7 +416,7 @@ fm801ch_setspeed(kobj_t obj, void *data, u_int32_t speed) return fm801_rates[i].rate; } -static int +static u_int32_t fm801ch_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) { struct fm801_chinfo *ch = data; @@ -489,12 +494,12 @@ fm801ch_trigger(kobj_t obj, void *data, int go) } /* Almost ALSA copy */ -static int +static u_int32_t fm801ch_getptr(kobj_t obj, void *data) { struct fm801_chinfo *ch = data; struct fm801_info *fm801 = ch->parent; - int result = 0; + u_int32_t result = 0; if (ch->dir == PCMDIR_PLAY) { result = fm801_rd(fm801, @@ -525,7 +530,7 @@ static kobj_method_t fm801ch_methods[] = { KOBJMETHOD(channel_trigger, fm801ch_trigger), KOBJMETHOD(channel_getptr, fm801ch_getptr), KOBJMETHOD(channel_getcaps, fm801ch_getcaps), - { 0, 0 } + KOBJMETHOD_END }; CHANNEL_DECLARE(fm801ch); diff --git a/sys/dev/sound/pci/hda/hdac.c b/sys/dev/sound/pci/hda/hdac.c index 1b3432c..4e32ce2 100644 --- a/sys/dev/sound/pci/hda/hdac.c +++ b/sys/dev/sound/pci/hda/hdac.c @@ -69,6 +69,10 @@ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +#ifdef HAVE_KERNEL_OPTION_HEADERS +#include "opt_snd.h" +#endif + #include <dev/sound/pcm/sound.h> #include <dev/pci/pcireg.h> #include <dev/pci/pcivar.h> @@ -91,13 +95,13 @@ SND_DECLARE_FILE("$FreeBSD$"); if (bootverbose != 0 || snd_verbose > 3) { \ stmt \ } \ -} while(0) +} while (0) #define HDA_BOOTHVERBOSE(stmt) do { \ if (snd_verbose > 3) { \ stmt \ } \ -} while(0) +} while (0) #if 1 #undef HDAC_INTR_EXTRA @@ -463,7 +467,7 @@ const char *HDA_CONNS[4] = {"Jack", "None", "Fixed", "Both"}; /* Default */ static uint32_t hdac_fmt[] = { - AFMT_STEREO | AFMT_S16_LE, + SND_FORMAT(AFMT_S16_LE, 2, 0), 0 }; @@ -2905,8 +2909,7 @@ hdac_poll_reinit(struct hdac_softc *sc) continue; ch = &sc->chans[i]; pollticks = ((uint64_t)hz * ch->blksz) / - ((uint64_t)sndbuf_getbps(ch->b) * - sndbuf_getspd(ch->b)); + ((uint64_t)sndbuf_getalign(ch->b) * sndbuf_getspd(ch->b)); pollticks >>= 1; if (pollticks > hz) pollticks = hz; @@ -3370,7 +3373,7 @@ hdac_channel_setformat(kobj_t obj, void *data, uint32_t format) return (EINVAL); } -static int +static uint32_t hdac_channel_setspeed(kobj_t obj, void *data, uint32_t speed) { struct hdac_chan *ch = data; @@ -3426,11 +3429,9 @@ hdac_stream_setup(struct hdac_chan *ch) } } - if (ch->fmt & (AFMT_STEREO | AFMT_AC3)) { + totalchn = AFMT_CHANNEL(ch->fmt); + if (totalchn > 1) fmt |= 1; - totalchn = 2; - } else - totalchn = 1; HDAC_WRITE_2(&sc->mem, ch->off + HDAC_SDFMT, fmt); @@ -3515,10 +3516,10 @@ hdac_channel_setfragments(kobj_t obj, void *data, ch->blksz = sndbuf_getblksz(ch->b); ch->blkcnt = sndbuf_getblkcnt(ch->b); - return (1); + return (0); } -static int +static uint32_t hdac_channel_setblocksize(kobj_t obj, void *data, uint32_t blksz) { struct hdac_chan *ch = data; @@ -3592,7 +3593,7 @@ hdac_channel_trigger(kobj_t obj, void *data, int go) return (0); } -static int +static uint32_t hdac_channel_getptr(kobj_t obj, void *data) { struct hdac_chan *ch = data; @@ -3632,7 +3633,7 @@ static kobj_method_t hdac_channel_methods[] = { KOBJMETHOD(channel_trigger, hdac_channel_trigger), KOBJMETHOD(channel_getptr, hdac_channel_getptr), KOBJMETHOD(channel_getcaps, hdac_channel_getcaps), - { 0, 0 } + KOBJMETHOD_END }; CHANNEL_DECLARE(hdac_channel); @@ -3710,7 +3711,7 @@ hdac_audio_ctl_ossmixer_init(struct snd_mixer *m) } /* Declare soft PCM volume if needed. */ - if (pdevinfo->play >= 0 && !pdevinfo->digital) { + if (pdevinfo->play >= 0) { ctl = NULL; if ((mask & SOUND_MASK_PCM) == 0 || (devinfo->function.audio.quirks & HDA_QUIRK_SOFTPCMVOL)) { @@ -3963,7 +3964,7 @@ static kobj_method_t hdac_audio_ctl_ossmixer_methods[] = { KOBJMETHOD(mixer_init, hdac_audio_ctl_ossmixer_init), KOBJMETHOD(mixer_set, hdac_audio_ctl_ossmixer_set), KOBJMETHOD(mixer_setrecsrc, hdac_audio_ctl_ossmixer_setrecsrc), - { 0, 0 } + KOBJMETHOD_END }; MIXER_DECLARE(hdac_audio_ctl_ossmixer); @@ -6446,17 +6447,20 @@ hdac_pcmchannel_setup(struct hdac_chan *ch) else if (HDA_PARAM_SUPP_PCM_SIZE_RATE_20BIT(pcmcap)) ch->bit32 = 2; if (!(devinfo->function.audio.quirks & HDA_QUIRK_FORCESTEREO)) - ch->fmtlist[i++] = AFMT_S16_LE; - ch->fmtlist[i++] = AFMT_S16_LE | AFMT_STEREO; + ch->fmtlist[i++] = + SND_FORMAT(AFMT_S16_LE, 1, 0); + ch->fmtlist[i++] = SND_FORMAT(AFMT_S16_LE, 2, 0); if (ch->bit32 > 0) { if (!(devinfo->function.audio.quirks & HDA_QUIRK_FORCESTEREO)) - ch->fmtlist[i++] = AFMT_S32_LE; - ch->fmtlist[i++] = AFMT_S32_LE | AFMT_STEREO; + ch->fmtlist[i++] = + SND_FORMAT(AFMT_S32_LE, 1, 0); + ch->fmtlist[i++] = + SND_FORMAT(AFMT_S32_LE, 2, 0); } } if (HDA_PARAM_SUPP_STREAM_FORMATS_AC3(fmtcap)) { - ch->fmtlist[i++] = AFMT_AC3; + ch->fmtlist[i++] = SND_FORMAT(AFMT_AC3, 2, 0); } ch->fmtlist[i] = 0; i = 0; @@ -7181,7 +7185,6 @@ hdac_config_fetch(struct hdac_softc *sc, uint32_t *on, uint32_t *off) } } -#ifdef SND_DYNSYSCTL static int sysctl_hdac_polling(SYSCTL_HANDLER_ARGS) { @@ -7409,7 +7412,6 @@ sysctl_hdac_pindump(SYSCTL_HANDLER_ARGS) hdac_unlock(sc); return (0); } -#endif static void hdac_attach2(void *arg) @@ -7657,7 +7659,6 @@ hdac_attach2(void *arg) bus_generic_attach(sc->dev); -#ifdef SND_DYNSYSCTL SYSCTL_ADD_PROC(device_get_sysctl_ctx(sc->dev), SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev)), OID_AUTO, "polling", CTLTYPE_INT | CTLFLAG_RW, sc->dev, sizeof(sc->dev), @@ -7671,7 +7672,6 @@ hdac_attach2(void *arg) SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev)), OID_AUTO, "pindump", CTLTYPE_INT | CTLFLAG_RW, sc->dev, sizeof(sc->dev), sysctl_hdac_pindump, "I", "Dump pin states/data"); -#endif } /**************************************************************************** diff --git a/sys/dev/sound/pci/ich.c b/sys/dev/sound/pci/ich.c index dc234a7..6fb8809 100644 --- a/sys/dev/sound/pci/ich.c +++ b/sys/dev/sound/pci/ich.c @@ -25,6 +25,10 @@ * 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/pci/ich.h> @@ -80,7 +84,7 @@ SND_DECLARE_FILE("$FreeBSD$"); #if 0 #define ICH_DEBUG(stmt) do { \ stmt \ -} while(0) +} while (0) #else #define ICH_DEBUG(...) #endif @@ -198,7 +202,7 @@ struct sc_info { /* -------------------------------------------------------------------- */ static uint32_t ich_fmt[] = { - AFMT_STEREO | AFMT_S16_LE, + SND_FORMAT(AFMT_S16_LE, 2, 0), 0 }; static struct pcmchan_caps ich_vrcaps = {8000, 48000, ich_fmt, 0}; @@ -269,7 +273,7 @@ ich_rdcd(kobj_t obj, void *devinfo, int regno) } static int -ich_wrcd(kobj_t obj, void *devinfo, int regno, uint16_t data) +ich_wrcd(kobj_t obj, void *devinfo, int regno, uint32_t data) { struct sc_info *sc = (struct sc_info *)devinfo; @@ -283,7 +287,7 @@ ich_wrcd(kobj_t obj, void *devinfo, int regno, uint16_t data) static kobj_method_t ich_ac97_methods[] = { KOBJMETHOD(ac97_read, ich_rdcd), KOBJMETHOD(ac97_write, ich_wrcd), - { 0, 0 } + KOBJMETHOD_END }; AC97_DECLARE(ich_ac97); @@ -439,7 +443,7 @@ ichchan_setformat(kobj_t obj, void *data, uint32_t format) return (0); } -static int +static uint32_t ichchan_setspeed(kobj_t obj, void *data, uint32_t speed) { struct sc_chinfo *ch = data; @@ -473,7 +477,7 @@ ichchan_setspeed(kobj_t obj, void *data, uint32_t speed) return (ch->spd); } -static int +static uint32_t ichchan_setblocksize(kobj_t obj, void *data, uint32_t blocksize) { struct sc_chinfo *ch = data; @@ -535,7 +539,7 @@ ichchan_trigger(kobj_t obj, void *data, int go) return (0); } -static int +static uint32_t ichchan_getptr(kobj_t obj, void *data) { struct sc_chinfo *ch = data; @@ -583,7 +587,7 @@ static kobj_method_t ichchan_methods[] = { KOBJMETHOD(channel_trigger, ichchan_trigger), KOBJMETHOD(channel_getptr, ichchan_getptr), KOBJMETHOD(channel_getcaps, ichchan_getcaps), - { 0, 0 } + KOBJMETHOD_END }; CHANNEL_DECLARE(ichchan); @@ -665,7 +669,6 @@ ich_intr(void *p) static int ich_initsys(struct sc_info* sc) { -#ifdef SND_DYNSYSCTL /* XXX: this should move to a device specific sysctl "dev.pcm.X.yyy" via device_get_sysctl_*() as discussed on multimedia@ in msg-id <861wujij2q.fsf@xps.des.no> */ @@ -674,7 +677,7 @@ ich_initsys(struct sc_info* sc) OID_AUTO, "ac97rate", CTLFLAG_RW, &sc->ac97rate, 48000, "AC97 link rate (default = 48000)"); -#endif /* SND_DYNSYSCTL */ + return (0); } diff --git a/sys/dev/sound/pci/maestro.c b/sys/dev/sound/pci/maestro.c index cec690a..7c977ad 100644 --- a/sys/dev/sound/pci/maestro.c +++ b/sys/dev/sound/pci/maestro.c @@ -47,6 +47,10 @@ * John Baldwin <jhb@freebsd.org>. */ +#ifdef HAVE_KERNEL_OPTION_HEADERS +#include "opt_snd.h" +#endif + #include <dev/sound/pcm/sound.h> #include <dev/sound/pcm/ac97.h> #include <dev/pci/pcireg.h> @@ -157,9 +161,7 @@ struct agg_info { bus_dma_tag_t stat_dmat; /* FreeBSD SMPng related */ -#ifdef USING_MUTEX struct mtx lock; /* mutual exclusion */ -#endif /* FreeBSD newpcm related */ struct ac97_info *codec; @@ -278,11 +280,7 @@ agg_sleep(struct agg_info *sc, const char *wmesg, int msec) timo = msec * hz / 1000; if (timo == 0) timo = 1; -#ifdef USING_MUTEX msleep(sc, &sc->lock, PWAIT, wmesg, timo); -#else - tsleep(sc, PWAIT, wmesg, timo); -#endif } @@ -1266,7 +1264,7 @@ static kobj_method_t agg_ac97_methods[] = { KOBJMETHOD(ac97_init, agg_ac97_init), KOBJMETHOD(ac97_read, agg_ac97_read), KOBJMETHOD(ac97_write, agg_ac97_write), - { 0, 0 } + KOBJMETHOD_END }; AC97_DECLARE(agg_ac97); @@ -1370,7 +1368,7 @@ aggpch_setformat(kobj_t obj, void *data, u_int32_t format) if (format & AFMT_BIGENDIAN || format & AFMT_U16_LE) return EINVAL; ch->stereo = ch->qs16 = ch->us = 0; - if (format & AFMT_STEREO) + if (AFMT_CHANNEL(format) > 1) ch->stereo = 1; if (format & AFMT_U8 || format & AFMT_S8) { @@ -1381,13 +1379,16 @@ aggpch_setformat(kobj_t obj, void *data, u_int32_t format) return 0; } -static int +static u_int32_t aggpch_setspeed(kobj_t obj, void *data, u_int32_t speed) { - return ((struct agg_chinfo*)data)->speed = speed; + + ((struct agg_chinfo*)data)->speed = speed; + + return (speed); } -static int +static u_int32_t aggpch_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) { struct agg_chinfo *ch = data; @@ -1430,11 +1431,11 @@ aggpch_trigger(kobj_t obj, void *data, int go) return 0; } -static int +static u_int32_t aggpch_getptr(kobj_t obj, void *data) { struct agg_chinfo *ch = data; - u_int cp; + u_int32_t cp; agg_lock(ch->parent); cp = wp_rdapu(ch->parent, (ch->num << 1) | 32, APUREG_CURPTR); @@ -1449,12 +1450,12 @@ static struct pcmchan_caps * aggpch_getcaps(kobj_t obj, void *data) { static u_int32_t playfmt[] = { - AFMT_U8, - AFMT_STEREO | AFMT_U8, - AFMT_S8, - AFMT_STEREO | AFMT_S8, - AFMT_S16_LE, - AFMT_STEREO | AFMT_S16_LE, + SND_FORMAT(AFMT_U8, 1, 0), + SND_FORMAT(AFMT_U8, 2, 0), + SND_FORMAT(AFMT_S8, 1, 0), + SND_FORMAT(AFMT_S8, 2, 0), + SND_FORMAT(AFMT_S16_LE, 1, 0), + SND_FORMAT(AFMT_S16_LE, 2, 0), 0 }; static struct pcmchan_caps playcaps = {8000, 48000, playfmt, 0}; @@ -1472,7 +1473,7 @@ static kobj_method_t aggpch_methods[] = { KOBJMETHOD(channel_trigger, aggpch_trigger), KOBJMETHOD(channel_getptr, aggpch_getptr), KOBJMETHOD(channel_getcaps, aggpch_getcaps), - { 0, 0 } + KOBJMETHOD_END }; CHANNEL_DECLARE(aggpch); @@ -1519,20 +1520,23 @@ aggrch_setformat(kobj_t obj, void *data, u_int32_t format) if (!(format & AFMT_S16_LE)) return EINVAL; - if (format & AFMT_STEREO) + if (AFMT_CHANNEL(format) > 1) ch->stereo = 1; else ch->stereo = 0; return 0; } -static int +static u_int32_t aggrch_setspeed(kobj_t obj, void *data, u_int32_t speed) { - return ((struct agg_rchinfo*)data)->speed = speed; + + ((struct agg_rchinfo*)data)->speed = speed; + + return (speed); } -static int +static u_int32_t aggrch_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) { struct agg_rchinfo *ch = data; @@ -1579,7 +1583,7 @@ aggrch_trigger(kobj_t obj, void *sc, int go) return 0; } -static int +static u_int32_t aggrch_getptr(kobj_t obj, void *sc) { struct agg_rchinfo *ch = sc; @@ -1591,8 +1595,8 @@ static struct pcmchan_caps * aggrch_getcaps(kobj_t obj, void *sc) { static u_int32_t recfmt[] = { - AFMT_S16_LE, - AFMT_STEREO | AFMT_S16_LE, + SND_FORMAT(AFMT_S16_LE, 1, 0), + SND_FORMAT(AFMT_S16_LE, 2, 0), 0 }; static struct pcmchan_caps reccaps = {8000, 48000, recfmt, 0}; @@ -1609,7 +1613,7 @@ static kobj_method_t aggrch_methods[] = { KOBJMETHOD(channel_trigger, aggrch_trigger), KOBJMETHOD(channel_getptr, aggrch_getptr), KOBJMETHOD(channel_getcaps, aggrch_getcaps), - { 0, 0 } + KOBJMETHOD_END }; CHANNEL_DECLARE(aggrch); @@ -1776,7 +1780,6 @@ agg_attach(device_t dev) ess = malloc(sizeof(*ess), M_DEVBUF, M_WAITOK | M_ZERO); ess->dev = dev; -#ifdef USING_MUTEX mtx_init(&ess->lock, device_get_desc(dev), "snd_maestro softc", MTX_DEF | MTX_RECURSE); if (!mtx_initialized(&ess->lock)) { @@ -1784,7 +1787,6 @@ agg_attach(device_t dev) ret = ENOMEM; goto bad; } -#endif if (resource_int_value(device_get_name(dev), device_get_unit(dev), "dac", &dacn) == 0) { @@ -1941,10 +1943,8 @@ agg_attach(device_t dev) bus_dma_tag_destroy(ess->stat_dmat); if (ess->buf_dmat != NULL) bus_dma_tag_destroy(ess->buf_dmat); -#ifdef USING_MUTEX if (mtx_initialized(&ess->lock)) mtx_destroy(&ess->lock); -#endif free(ess, M_DEVBUF); } @@ -1985,9 +1985,7 @@ agg_detach(device_t dev) dma_free(ess->stat_dmat, ess->stat); bus_dma_tag_destroy(ess->stat_dmat); bus_dma_tag_destroy(ess->buf_dmat); -#ifdef USING_MUTEX mtx_destroy(&ess->lock); -#endif free(ess, M_DEVBUF); return 0; } @@ -1996,18 +1994,11 @@ static int agg_suspend(device_t dev) { struct agg_info *ess = pcm_getdevinfo(dev); -#ifndef USING_MUTEX - int x; - x = spltty(); -#endif AGG_WR(ess, PORT_HOSTINT_CTRL, 0, 2); agg_lock(ess); agg_power(ess, PCI_POWERSTATE_D3); agg_unlock(ess); -#ifndef USING_MUTEX - splx(x); -#endif return 0; } @@ -2017,11 +2008,7 @@ agg_resume(device_t dev) { int i; struct agg_info *ess = pcm_getdevinfo(dev); -#ifndef USING_MUTEX - int x; - x = spltty(); -#endif for (i = 0; i < ess->playchns; i++) if (ess->active & (1 << i)) aggch_start_dac(ess->pch + i); @@ -2032,9 +2019,6 @@ agg_resume(device_t dev) if (!ess->active) agg_power(ess, powerstate_init); agg_unlock(ess); -#ifndef USING_MUTEX - splx(x); -#endif if (mixer_reinit(dev)) { device_printf(dev, "unable to reinitialize the mixer\n"); diff --git a/sys/dev/sound/pci/maestro3.c b/sys/dev/sound/pci/maestro3.c index 8f1084c..8a57c8c 100644 --- a/sys/dev/sound/pci/maestro3.c +++ b/sys/dev/sound/pci/maestro3.c @@ -52,6 +52,10 @@ * http://virgo.caltech.edu/~dmoore/maestro3.pdf.gz */ +#ifdef HAVE_KERNEL_OPTION_HEADERS +#include "opt_snd.h" +#endif + #include <dev/sound/pcm/sound.h> #include <dev/sound/pcm/ac97.h> @@ -162,8 +166,8 @@ struct sc_info { static void *m3_pchan_init(kobj_t, void *, struct snd_dbuf *, struct pcm_channel *, int); static int m3_pchan_free(kobj_t, void *); static int m3_pchan_setformat(kobj_t, void *, u_int32_t); -static int m3_pchan_setspeed(kobj_t, void *, u_int32_t); -static int m3_pchan_setblocksize(kobj_t, void *, u_int32_t); +static u_int32_t m3_pchan_setspeed(kobj_t, void *, u_int32_t); +static u_int32_t m3_pchan_setblocksize(kobj_t, void *, u_int32_t); static int m3_pchan_trigger(kobj_t, void *, int); static int m3_pchan_trigger_locked(kobj_t, void *, int); static u_int32_t m3_pchan_getptr_internal(struct sc_pchinfo *); @@ -174,8 +178,8 @@ static struct pcmchan_caps *m3_pchan_getcaps(kobj_t, void *); static void *m3_rchan_init(kobj_t, void *, struct snd_dbuf *, struct pcm_channel *, int); static int m3_rchan_free(kobj_t, void *); static int m3_rchan_setformat(kobj_t, void *, u_int32_t); -static int m3_rchan_setspeed(kobj_t, void *, u_int32_t); -static int m3_rchan_setblocksize(kobj_t, void *, u_int32_t); +static u_int32_t m3_rchan_setspeed(kobj_t, void *, u_int32_t); +static u_int32_t m3_rchan_setblocksize(kobj_t, void *, u_int32_t); static int m3_rchan_trigger(kobj_t, void *, int); static int m3_rchan_trigger_locked(kobj_t, void *, int); static u_int32_t m3_rchan_getptr_internal(struct sc_rchinfo *); @@ -185,7 +189,7 @@ static struct pcmchan_caps *m3_rchan_getcaps(kobj_t, void *); static int m3_chan_active(struct sc_info *); /* talk to the codec - called from ac97.c */ -static int m3_initcd(kobj_t, void *); +static u_int32_t m3_initcd(kobj_t, void *); static int m3_rdcd(kobj_t, void *, int); static int m3_wrcd(kobj_t, void *, int, u_int32_t); @@ -206,7 +210,7 @@ static kobj_method_t m3_codec_methods[] = { KOBJMETHOD(ac97_init, m3_initcd), KOBJMETHOD(ac97_read, m3_rdcd), KOBJMETHOD(ac97_write, m3_wrcd), - { 0, 0 } + KOBJMETHOD_END }; AC97_DECLARE(m3_codec); @@ -214,10 +218,10 @@ AC97_DECLARE(m3_codec); /* channel descriptors */ static u_int32_t m3_playfmt[] = { - AFMT_U8, - AFMT_STEREO | AFMT_U8, - AFMT_S16_LE, - AFMT_STEREO | AFMT_S16_LE, + SND_FORMAT(AFMT_U8, 1, 0), + SND_FORMAT(AFMT_U8, 2, 0), + SND_FORMAT(AFMT_S16_LE, 1, 0), + SND_FORMAT(AFMT_S16_LE, 2, 0), 0 }; static struct pcmchan_caps m3_playcaps = {8000, 48000, m3_playfmt, 0}; @@ -231,15 +235,15 @@ static kobj_method_t m3_pch_methods[] = { KOBJMETHOD(channel_getptr, m3_pchan_getptr), KOBJMETHOD(channel_getcaps, m3_pchan_getcaps), KOBJMETHOD(channel_free, m3_pchan_free), - { 0, 0 } + KOBJMETHOD_END }; CHANNEL_DECLARE(m3_pch); static u_int32_t m3_recfmt[] = { - AFMT_U8, - AFMT_STEREO | AFMT_U8, - AFMT_S16_LE, - AFMT_STEREO | AFMT_S16_LE, + SND_FORMAT(AFMT_U8, 1, 0), + SND_FORMAT(AFMT_U8, 2, 0), + SND_FORMAT(AFMT_S16_LE, 1, 0), + SND_FORMAT(AFMT_S16_LE, 2, 0), 0 }; static struct pcmchan_caps m3_reccaps = {8000, 48000, m3_recfmt, 0}; @@ -253,7 +257,7 @@ static kobj_method_t m3_rch_methods[] = { KOBJMETHOD(channel_getptr, m3_rchan_getptr), KOBJMETHOD(channel_getcaps, m3_rchan_getcaps), KOBJMETHOD(channel_free, m3_rchan_free), - { 0, 0 } + KOBJMETHOD_END }; CHANNEL_DECLARE(m3_rch); @@ -309,7 +313,7 @@ m3_wait(struct sc_info *sc) /* -------------------------------------------------------------------- */ /* ac97 codec */ -static int +static u_int32_t m3_initcd(kobj_t kobj, void *devinfo) { struct sc_info *sc = (struct sc_info *)devinfo; @@ -404,7 +408,7 @@ m3_pchan_init(kobj_t kobj, void *devinfo, struct snd_dbuf *b, struct pcm_channel ch->buffer = b; ch->parent = sc; ch->channel = c; - ch->fmt = AFMT_U8; + ch->fmt = SND_FORMAT(AFMT_U8, 1, 0); ch->spd = DSP_DEFAULT_SPEED; M3_UNLOCK(sc); /* XXX */ if (sndbuf_alloc(ch->buffer, sc->parent_dmat, 0, sc->bufsz) != 0) { @@ -516,10 +520,10 @@ m3_pchan_setformat(kobj_t kobj, void *chdata, u_int32_t format) ("m3_pchan_setformat(dac=%d, format=0x%x{%s-%s})\n", ch->dac_idx, format, format & (AFMT_U8|AFMT_S8) ? "8bit":"16bit", - format & AFMT_STEREO ? "STEREO":"MONO")); + (AFMT_CHANNEL(format) > 1) ? "STEREO":"MONO")); /* mono word */ - data = (format & AFMT_STEREO) ? 0 : 1; + data = (AFMT_CHANNEL(format) > 1)? 0 : 1; m3_wr_assp_data(sc, ch->dac_data + SRC3_MODE_OFFSET, data); /* 8bit word */ @@ -532,7 +536,7 @@ m3_pchan_setformat(kobj_t kobj, void *chdata, u_int32_t format) return (0); } -static int +static u_int32_t m3_pchan_setspeed(kobj_t kobj, void *chdata, u_int32_t speed) { struct sc_pchinfo *ch = chdata; @@ -555,7 +559,7 @@ m3_pchan_setspeed(kobj_t kobj, void *chdata, u_int32_t speed) return (speed); } -static int +static u_int32_t m3_pchan_setblocksize(kobj_t kobj, void *chdata, u_int32_t blocksize) { struct sc_pchinfo *ch = chdata; @@ -756,7 +760,7 @@ m3_rchan_init(kobj_t kobj, void *devinfo, struct snd_dbuf *b, struct pcm_channel ch->buffer = b; ch->parent = sc; ch->channel = c; - ch->fmt = AFMT_U8; + ch->fmt = SND_FORMAT(AFMT_U8, 1, 0); ch->spd = DSP_DEFAULT_SPEED; M3_UNLOCK(sc); /* XXX */ if (sndbuf_alloc(ch->buffer, sc->parent_dmat, 0, sc->bufsz) != 0) { @@ -863,10 +867,10 @@ m3_rchan_setformat(kobj_t kobj, void *chdata, u_int32_t format) ("m3_rchan_setformat(dac=%d, format=0x%x{%s-%s})\n", ch->adc_idx, format, format & (AFMT_U8|AFMT_S8) ? "8bit":"16bit", - format & AFMT_STEREO ? "STEREO":"MONO")); + (AFMT_CHANNEL(format) > 1) ? "STEREO":"MONO")); /* mono word */ - data = (format & AFMT_STEREO) ? 0 : 1; + data = (AFMT_CHANNEL(format) > 1) ? 0 : 1; m3_wr_assp_data(sc, ch->adc_data + SRC3_MODE_OFFSET, data); /* 8bit word */ @@ -878,7 +882,7 @@ m3_rchan_setformat(kobj_t kobj, void *chdata, u_int32_t format) return (0); } -static int +static u_int32_t m3_rchan_setspeed(kobj_t kobj, void *chdata, u_int32_t speed) { struct sc_rchinfo *ch = chdata; @@ -901,7 +905,7 @@ m3_rchan_setspeed(kobj_t kobj, void *chdata, u_int32_t speed) return (speed); } -static int +static u_int32_t m3_rchan_setblocksize(kobj_t kobj, void *chdata, u_int32_t blocksize) { struct sc_rchinfo *ch = chdata; diff --git a/sys/dev/sound/pci/neomagic.c b/sys/dev/sound/pci/neomagic.c index 4b011bb..f46e3e1 100644 --- a/sys/dev/sound/pci/neomagic.c +++ b/sys/dev/sound/pci/neomagic.c @@ -26,6 +26,10 @@ * 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/pci/neomagic.h> @@ -113,10 +117,10 @@ static int samplerates[9] = { /* -------------------------------------------------------------------- */ static u_int32_t nm_fmt[] = { - AFMT_U8, - AFMT_STEREO | AFMT_U8, - AFMT_S16_LE, - AFMT_STEREO | AFMT_S16_LE, + SND_FORMAT(AFMT_U8, 1, 0), + SND_FORMAT(AFMT_U8, 2, 0), + SND_FORMAT(AFMT_S16_LE, 1, 0), + SND_FORMAT(AFMT_S16_LE, 2, 0), 0 }; static struct pcmchan_caps nm_caps = {4000, 48000, nm_fmt, 0}; @@ -277,7 +281,7 @@ static kobj_method_t nm_ac97_methods[] = { KOBJMETHOD(ac97_init, nm_initcd), KOBJMETHOD(ac97_read, nm_rdcd), KOBJMETHOD(ac97_write, nm_wrcd), - { 0, 0 } + KOBJMETHOD_END }; AC97_DECLARE(nm_ac97); @@ -334,7 +338,7 @@ nm_setch(struct sc_chinfo *ch) x <<= 4; x &= NM_RATE_MASK; if (ch->fmt & AFMT_16BIT) x |= NM_RATE_BITS_16; - if (ch->fmt & AFMT_STEREO) x |= NM_RATE_STEREO; + if (AFMT_CHANNEL(ch->fmt) > 1) x |= NM_RATE_STEREO; base = (ch->dir == PCMDIR_PLAY)? NM_PLAYBACK_REG_OFFSET : NM_RECORD_REG_OFFSET; nm_wr(sc, base + NM_RATE_REG_OFFSET, x, 1); @@ -380,7 +384,7 @@ nmchan_setformat(kobj_t obj, void *data, u_int32_t format) return nm_setch(ch); } -static int +static u_int32_t nmchan_setspeed(kobj_t obj, void *data, u_int32_t speed) { struct sc_chinfo *ch = data; @@ -389,7 +393,7 @@ nmchan_setspeed(kobj_t obj, void *data, u_int32_t speed) return nm_setch(ch)? 0 : ch->spd; } -static int +static u_int32_t nmchan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) { struct sc_chinfo *ch = data; @@ -410,7 +414,7 @@ nmchan_trigger(kobj_t obj, void *data, int go) return 0; ssz = (ch->fmt & AFMT_16BIT)? 2 : 1; - if (ch->fmt & AFMT_STEREO) + if (AFMT_CHANNEL(ch->fmt) > 1) ssz <<= 1; if (ch->dir == PCMDIR_PLAY) { @@ -447,7 +451,7 @@ nmchan_trigger(kobj_t obj, void *data, int go) return 0; } -static int +static u_int32_t nmchan_getptr(kobj_t obj, void *data) { struct sc_chinfo *ch = data; @@ -474,7 +478,7 @@ static kobj_method_t nmchan_methods[] = { KOBJMETHOD(channel_trigger, nmchan_trigger), KOBJMETHOD(channel_getptr, nmchan_getptr), KOBJMETHOD(channel_getcaps, nmchan_getcaps), - { 0, 0 } + KOBJMETHOD_END }; CHANNEL_DECLARE(nmchan); diff --git a/sys/dev/sound/pci/solo.c b/sys/dev/sound/pci/solo.c index 5cf776d..ba92020 100644 --- a/sys/dev/sound/pci/solo.c +++ b/sys/dev/sound/pci/solo.c @@ -23,6 +23,10 @@ * SUCH DAMAGE. */ +#ifdef HAVE_KERNEL_OPTION_HEADERS +#include "opt_snd.h" +#endif + #include <dev/sound/pcm/sound.h> #include <dev/pci/pcireg.h> @@ -48,14 +52,14 @@ SND_DECLARE_FILE("$FreeBSD$"); #define ESS18XX_MPSAFE 1 static u_int32_t ess_playfmt[] = { - AFMT_U8, - AFMT_STEREO | AFMT_U8, - AFMT_S8, - AFMT_STEREO | AFMT_S8, - AFMT_S16_LE, - AFMT_STEREO | AFMT_S16_LE, - AFMT_U16_LE, - AFMT_STEREO | AFMT_U16_LE, + SND_FORMAT(AFMT_U8, 1, 0), + SND_FORMAT(AFMT_U8, 2, 0), + SND_FORMAT(AFMT_S8, 1, 0), + SND_FORMAT(AFMT_S8, 2, 0), + SND_FORMAT(AFMT_S16_LE, 1, 0), + SND_FORMAT(AFMT_S16_LE, 2, 0), + SND_FORMAT(AFMT_U16_LE, 1, 0), + SND_FORMAT(AFMT_U16_LE, 2, 0), 0 }; static struct pcmchan_caps ess_playcaps = {6000, 48000, ess_playfmt, 0}; @@ -64,14 +68,14 @@ static struct pcmchan_caps ess_playcaps = {6000, 48000, ess_playfmt, 0}; * Recording output is byte-swapped */ static u_int32_t ess_recfmt[] = { - AFMT_U8, - AFMT_STEREO | AFMT_U8, - AFMT_S8, - AFMT_STEREO | AFMT_S8, - AFMT_S16_BE, - AFMT_STEREO | AFMT_S16_BE, - AFMT_U16_BE, - AFMT_STEREO | AFMT_U16_BE, + SND_FORMAT(AFMT_U8, 1, 0), + SND_FORMAT(AFMT_U8, 2, 0), + SND_FORMAT(AFMT_S8, 1, 0), + SND_FORMAT(AFMT_S8, 2, 0), + SND_FORMAT(AFMT_S16_BE, 1, 0), + SND_FORMAT(AFMT_S16_BE, 2, 0), + SND_FORMAT(AFMT_U16_BE, 1, 0), + SND_FORMAT(AFMT_U16_BE, 2, 0), 0 }; static struct pcmchan_caps ess_reccaps = {6000, 48000, ess_recfmt, 0}; @@ -424,8 +428,8 @@ ess_setupch(struct ess_info *sc, int ch, int dir, int spd, u_int32_t fmt, int le { int play = (dir == PCMDIR_PLAY)? 1 : 0; int b16 = (fmt & AFMT_16BIT)? 1 : 0; - int stereo = (fmt & AFMT_STEREO)? 1 : 0; - int unsign = (fmt == AFMT_U8 || fmt == AFMT_U16_LE || fmt == AFMT_U16_BE)? 1 : 0; + int stereo = (AFMT_CHANNEL(fmt) > 1)? 1 : 0; + int unsign = (!(fmt & AFMT_SIGNED))? 1 : 0; u_int8_t spdval, fmtval; DEB(printf("ess_setupch\n")); @@ -556,7 +560,7 @@ esschan_setformat(kobj_t obj, void *data, u_int32_t format) return 0; } -static int +static u_int32_t esschan_setspeed(kobj_t obj, void *data, u_int32_t speed) { struct ess_chinfo *ch = data; @@ -570,7 +574,7 @@ esschan_setspeed(kobj_t obj, void *data, u_int32_t speed) return ch->spd; } -static int +static u_int32_t esschan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) { struct ess_chinfo *ch = data; @@ -608,12 +612,12 @@ esschan_trigger(kobj_t obj, void *data, int go) return 0; } -static int +static u_int32_t esschan_getptr(kobj_t obj, void *data) { struct ess_chinfo *ch = data; struct ess_info *sc = ch->parent; - int ret; + u_int32_t ret; ess_lock(sc); ret = ess_dmapos(sc, ch->hwch); @@ -637,7 +641,7 @@ static kobj_method_t esschan_methods[] = { KOBJMETHOD(channel_trigger, esschan_trigger), KOBJMETHOD(channel_getptr, esschan_getptr), KOBJMETHOD(channel_getcaps, esschan_getcaps), - { 0, 0 } + KOBJMETHOD_END }; CHANNEL_DECLARE(esschan); @@ -720,7 +724,7 @@ essmix_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right) return left | (right << 8); } -static int +static u_int32_t essmix_setrecsrc(struct snd_mixer *m, u_int32_t src) { struct ess_info *sc = mix_getdevinfo(m); @@ -755,7 +759,7 @@ static kobj_method_t solomixer_methods[] = { KOBJMETHOD(mixer_init, essmix_init), KOBJMETHOD(mixer_set, essmix_set), KOBJMETHOD(mixer_setrecsrc, essmix_setrecsrc), - { 0, 0 } + KOBJMETHOD_END }; MIXER_DECLARE(solomixer); diff --git a/sys/dev/sound/pci/spicds.c b/sys/dev/sound/pci/spicds.c index af61b04..09fdc11 100644 --- a/sys/dev/sound/pci/spicds.c +++ b/sys/dev/sound/pci/spicds.c @@ -27,6 +27,10 @@ * $FreeBSD$ */ +#ifdef HAVE_KERNEL_OPTION_HEADERS +#include "opt_snd.h" +#endif + #include <dev/sound/pcm/sound.h> #include <dev/sound/pci/spicds.h> diff --git a/sys/dev/sound/pci/t4dwave.c b/sys/dev/sound/pci/t4dwave.c index 39ce02e..086c4fe 100644 --- a/sys/dev/sound/pci/t4dwave.c +++ b/sys/dev/sound/pci/t4dwave.c @@ -24,6 +24,10 @@ * 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/pci/t4dwave.h> @@ -103,27 +107,27 @@ struct tr_info { /* -------------------------------------------------------------------- */ static u_int32_t tr_recfmt[] = { - AFMT_U8, - AFMT_STEREO | AFMT_U8, - AFMT_S8, - AFMT_STEREO | AFMT_S8, - AFMT_S16_LE, - AFMT_STEREO | AFMT_S16_LE, - AFMT_U16_LE, - AFMT_STEREO | AFMT_U16_LE, + SND_FORMAT(AFMT_U8, 1, 0), + SND_FORMAT(AFMT_U8, 2, 0), + SND_FORMAT(AFMT_S8, 1, 0), + SND_FORMAT(AFMT_S8, 2, 0), + SND_FORMAT(AFMT_S16_LE, 1, 0), + SND_FORMAT(AFMT_S16_LE, 2, 0), + SND_FORMAT(AFMT_U16_LE, 1, 0), + SND_FORMAT(AFMT_U16_LE, 2, 0), 0 }; static struct pcmchan_caps tr_reccaps = {4000, 48000, tr_recfmt, 0}; static u_int32_t tr_playfmt[] = { - AFMT_U8, - AFMT_STEREO | AFMT_U8, - AFMT_S8, - AFMT_STEREO | AFMT_S8, - AFMT_S16_LE, - AFMT_STEREO | AFMT_S16_LE, - AFMT_U16_LE, - AFMT_STEREO | AFMT_U16_LE, + SND_FORMAT(AFMT_U8, 1, 0), + SND_FORMAT(AFMT_U8, 2, 0), + SND_FORMAT(AFMT_S8, 1, 0), + SND_FORMAT(AFMT_S8, 2, 0), + SND_FORMAT(AFMT_S16_LE, 1, 0), + SND_FORMAT(AFMT_S16_LE, 2, 0), + SND_FORMAT(AFMT_U16_LE, 1, 0), + SND_FORMAT(AFMT_U16_LE, 2, 0), 0 }; static struct pcmchan_caps tr_playcaps = {4000, 48000, tr_playfmt, 0}; @@ -289,7 +293,7 @@ tr_wrcd(kobj_t obj, void *devinfo, int regno, u_int32_t data) static kobj_method_t tr_ac97_methods[] = { KOBJMETHOD(ac97_read, tr_rdcd), KOBJMETHOD(ac97_write, tr_wrcd), - { 0, 0 } + KOBJMETHOD_END }; AC97_DECLARE(tr_ac97); @@ -473,7 +477,7 @@ tr_fmttobits(u_int32_t fmt) bits = 0; bits |= (fmt & AFMT_SIGNED)? 0x2 : 0; - bits |= (fmt & AFMT_STEREO)? 0x4 : 0; + bits |= (AFMT_CHANNEL(fmt) > 1)? 0x4 : 0; bits |= (fmt & AFMT_16BIT)? 0x8 : 0; return bits; @@ -510,7 +514,7 @@ trpchan_setformat(kobj_t obj, void *data, u_int32_t format) return 0; } -static int +static u_int32_t trpchan_setspeed(kobj_t obj, void *data, u_int32_t speed) { struct tr_chinfo *ch = data; @@ -519,7 +523,7 @@ trpchan_setspeed(kobj_t obj, void *data, u_int32_t speed) return (ch->delta * 48000) >> 12; } -static int +static u_int32_t trpchan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) { struct tr_chinfo *ch = data; @@ -543,7 +547,7 @@ trpchan_trigger(kobj_t obj, void *data, int go) ch->alpha = 0; ch->lba = sndbuf_getbufaddr(ch->buffer); ch->cso = 0; - ch->eso = (sndbuf_getsize(ch->buffer) / sndbuf_getbps(ch->buffer)) - 1; + ch->eso = (sndbuf_getsize(ch->buffer) / sndbuf_getalign(ch->buffer)) - 1; ch->rvol = ch->cvol = 0x7f; ch->gvsel = 0; ch->pan = 0; @@ -561,13 +565,13 @@ trpchan_trigger(kobj_t obj, void *data, int go) return 0; } -static int +static u_int32_t trpchan_getptr(kobj_t obj, void *data) { struct tr_chinfo *ch = data; tr_rdch(ch); - return ch->cso * sndbuf_getbps(ch->buffer); + return ch->cso * sndbuf_getalign(ch->buffer); } static struct pcmchan_caps * @@ -584,7 +588,7 @@ static kobj_method_t trpchan_methods[] = { KOBJMETHOD(channel_trigger, trpchan_trigger), KOBJMETHOD(channel_getptr, trpchan_getptr), KOBJMETHOD(channel_getcaps, trpchan_getcaps), - { 0, 0 } + KOBJMETHOD_END }; CHANNEL_DECLARE(trpchan); @@ -627,7 +631,7 @@ trrchan_setformat(kobj_t obj, void *data, u_int32_t format) } -static int +static u_int32_t trrchan_setspeed(kobj_t obj, void *data, u_int32_t speed) { struct tr_rchinfo *ch = data; @@ -641,7 +645,7 @@ trrchan_setspeed(kobj_t obj, void *data, u_int32_t speed) return (48000 << 12) / ch->delta; } -static int +static u_int32_t trrchan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) { struct tr_rchinfo *ch = data; @@ -683,7 +687,7 @@ trrchan_trigger(kobj_t obj, void *data, int go) return 0; } -static int +static u_int32_t trrchan_getptr(kobj_t obj, void *data) { struct tr_rchinfo *ch = data; @@ -707,7 +711,7 @@ static kobj_method_t trrchan_methods[] = { KOBJMETHOD(channel_trigger, trrchan_trigger), KOBJMETHOD(channel_getptr, trrchan_getptr), KOBJMETHOD(channel_getcaps, trrchan_getcaps), - { 0, 0 } + KOBJMETHOD_END }; CHANNEL_DECLARE(trrchan); diff --git a/sys/dev/sound/pci/via8233.c b/sys/dev/sound/pci/via8233.c index 68de81b..1f5f482 100644 --- a/sys/dev/sound/pci/via8233.c +++ b/sys/dev/sound/pci/via8233.c @@ -35,6 +35,10 @@ * ordering. */ +#ifdef HAVE_KERNEL_OPTION_HEADERS +#include "opt_snd.h" +#endif + #include <dev/sound/pcm/sound.h> #include <dev/sound/pcm/ac97.h> @@ -123,10 +127,10 @@ struct via_info { }; static uint32_t via_fmt[] = { - AFMT_U8, - AFMT_STEREO | AFMT_U8, - AFMT_S16_LE, - AFMT_STEREO | AFMT_S16_LE, + SND_FORMAT(AFMT_U8, 1, 0), + SND_FORMAT(AFMT_U8, 2, 0), + SND_FORMAT(AFMT_S16_LE, 1, 0), + SND_FORMAT(AFMT_S16_LE, 2, 0), 0 }; @@ -150,7 +154,6 @@ via_chan_active(struct via_info *via) return (ret); } -#ifdef SND_DYNSYSCTL static int sysctl_via8233_spdif_enable(SYSCTL_HANDLER_ARGS) { @@ -243,12 +246,10 @@ sysctl_via_polling(SYSCTL_HANDLER_ARGS) return (err); } -#endif /* SND_DYNSYSCTL */ static void via_init_sysctls(device_t dev) { -#ifdef SND_DYNSYSCTL /* XXX: an user should be able to set this with a control tool, if not done before 7.0-RELEASE, this needs to be converted to a device specific sysctl "dev.pcm.X.yyy" via device_get_sysctl_*() @@ -268,7 +269,6 @@ via_init_sysctls(device_t dev) "polling", CTLTYPE_INT | CTLFLAG_RW, dev, sizeof(dev), sysctl_via_polling, "I", "Enable polling mode"); -#endif } static __inline uint32_t @@ -374,7 +374,7 @@ via_read_codec(kobj_t obj, void *addr, int reg) static kobj_method_t via_ac97_methods[] = { KOBJMETHOD(ac97_read, via_read_codec), KOBJMETHOD(ac97_write, via_write_codec), - { 0, 0 } + KOBJMETHOD_END }; AC97_DECLARE(via_ac97); @@ -408,7 +408,7 @@ via8233wr_setformat(kobj_t obj, void *data, uint32_t format) uint32_t f = WR_FORMAT_STOP_INDEX; - if (format & AFMT_STEREO) + if (AFMT_CHANNEL(format) > 1) f |= WR_FORMAT_STEREO; if (format & AFMT_S16_LE) f |= WR_FORMAT_16BIT; @@ -431,7 +431,7 @@ via8233dxs_setformat(kobj_t obj, void *data, uint32_t format) v = via_rd(via, r, 4); v &= ~(VIA8233_DXS_RATEFMT_STEREO | VIA8233_DXS_RATEFMT_16BIT); - if (format & AFMT_STEREO) + if (AFMT_CHANNEL(format) > 1) v |= VIA8233_DXS_RATEFMT_STEREO; if (format & AFMT_16BIT) v |= VIA8233_DXS_RATEFMT_16BIT; @@ -450,7 +450,7 @@ via8233msgd_setformat(kobj_t obj, void *data, uint32_t format) uint32_t s = 0xff000000; uint8_t v = (format & AFMT_S16_LE) ? MC_SGD_16BIT : MC_SGD_8BIT; - if (format & AFMT_STEREO) { + if (AFMT_CHANNEL(format) > 1) { v |= MC_SGD_CHANNELS(2); s |= SLOT3(1) | SLOT4(2); } else { @@ -469,7 +469,7 @@ via8233msgd_setformat(kobj_t obj, void *data, uint32_t format) /* -------------------------------------------------------------------- */ /* Speed setting functions */ -static int +static uint32_t via8233wr_setspeed(kobj_t obj, void *data, uint32_t speed) { struct via_chinfo *ch = data; @@ -481,7 +481,7 @@ via8233wr_setspeed(kobj_t obj, void *data, uint32_t speed) return (48000); } -static int +static uint32_t via8233dxs_setspeed(kobj_t obj, void *data, uint32_t speed) { struct via_chinfo *ch = data; @@ -501,7 +501,7 @@ via8233dxs_setspeed(kobj_t obj, void *data, uint32_t speed) return (speed); } -static int +static uint32_t via8233msgd_setspeed(kobj_t obj, void *data, uint32_t speed) { struct via_chinfo *ch = data; @@ -596,10 +596,10 @@ via8233chan_setfragments(kobj_t obj, void *data, ch->blksz = sndbuf_getblksz(ch->buffer); ch->blkcnt = sndbuf_getblkcnt(ch->buffer); - return (1); + return (0); } -static int +static uint32_t via8233chan_setblocksize(kobj_t obj, void *data, uint32_t blksz) { struct via_chinfo *ch = data; @@ -610,13 +610,12 @@ via8233chan_setblocksize(kobj_t obj, void *data, uint32_t blksz) return (ch->blksz); } -static int +static uint32_t via8233chan_getptr(kobj_t obj, void *data) { struct via_chinfo *ch = data; struct via_info *via = ch->parent; - uint32_t v, index, count; - int ptr; + uint32_t v, index, count, ptr; snd_mtxlock(via->lock); if (via->polling != 0) { @@ -852,7 +851,7 @@ via_poll_ticks(struct via_info *via) if (ch->channel == NULL || ch->active == 0) continue; pollticks = ((uint64_t)hz * ch->blksz) / - ((uint64_t)sndbuf_getbps(ch->buffer) * + ((uint64_t)sndbuf_getalign(ch->buffer) * sndbuf_getspd(ch->buffer)); pollticks >>= 2; if (pollticks > hz) @@ -868,7 +867,7 @@ via_poll_ticks(struct via_info *via) if (ch->channel == NULL || ch->active == 0) continue; pollticks = ((uint64_t)hz * ch->blksz) / - ((uint64_t)sndbuf_getbps(ch->buffer) * + ((uint64_t)sndbuf_getalign(ch->buffer) * sndbuf_getspd(ch->buffer)); pollticks >>= 2; if (pollticks > hz) @@ -902,7 +901,7 @@ via8233chan_trigger(kobj_t obj, void* data, int go) ch->ptr = 0; ch->prevptr = 0; pollticks = ((uint64_t)hz * ch->blksz) / - ((uint64_t)sndbuf_getbps(ch->buffer) * + ((uint64_t)sndbuf_getalign(ch->buffer) * sndbuf_getspd(ch->buffer)); pollticks >>= 2; if (pollticks > hz) @@ -974,7 +973,7 @@ static kobj_method_t via8233wr_methods[] = { KOBJMETHOD(channel_setfragments, via8233chan_setfragments), KOBJMETHOD(channel_trigger, via8233chan_trigger), KOBJMETHOD(channel_getptr, via8233chan_getptr), - { 0, 0 } + KOBJMETHOD_END }; CHANNEL_DECLARE(via8233wr); @@ -987,7 +986,7 @@ static kobj_method_t via8233dxs_methods[] = { KOBJMETHOD(channel_setfragments, via8233chan_setfragments), KOBJMETHOD(channel_trigger, via8233chan_trigger), KOBJMETHOD(channel_getptr, via8233chan_getptr), - { 0, 0 } + KOBJMETHOD_END }; CHANNEL_DECLARE(via8233dxs); @@ -1000,7 +999,7 @@ static kobj_method_t via8233msgd_methods[] = { KOBJMETHOD(channel_setfragments, via8233chan_setfragments), KOBJMETHOD(channel_trigger, via8233chan_trigger), KOBJMETHOD(channel_getptr, via8233chan_getptr), - { 0, 0 } + KOBJMETHOD_END }; CHANNEL_DECLARE(via8233msgd); diff --git a/sys/dev/sound/pci/via82c686.c b/sys/dev/sound/pci/via82c686.c index e04e480..4f336bf 100644 --- a/sys/dev/sound/pci/via82c686.c +++ b/sys/dev/sound/pci/via82c686.c @@ -24,6 +24,10 @@ * SUCH DAMAGE. */ +#ifdef HAVE_KERNEL_OPTION_HEADERS +#include "opt_snd.h" +#endif + #include <dev/sound/pcm/sound.h> #include <dev/sound/pcm/ac97.h> @@ -90,10 +94,10 @@ struct via_info { }; static u_int32_t via_fmt[] = { - AFMT_U8, - AFMT_STEREO | AFMT_U8, - AFMT_S16_LE, - AFMT_STEREO | AFMT_S16_LE, + SND_FORMAT(AFMT_U8, 1, 0), + SND_FORMAT(AFMT_U8, 2, 0), + SND_FORMAT(AFMT_S16_LE, 1, 0), + SND_FORMAT(AFMT_S16_LE, 2, 0), 0 }; static struct pcmchan_caps via_vracaps = {4000, 48000, via_fmt, 0}; @@ -207,7 +211,7 @@ via_read_codec(kobj_t obj, void *addr, int reg) static kobj_method_t via_ac97_methods[] = { KOBJMETHOD(ac97_read, via_read_codec), KOBJMETHOD(ac97_write, via_write_codec), - { 0, 0 } + KOBJMETHOD_END }; AC97_DECLARE(via_ac97); @@ -284,7 +288,7 @@ viachan_setformat(kobj_t obj, void *data, u_int32_t format) int mode, mode_set; mode_set = 0; - if (format & AFMT_STEREO) + if (AFMT_CHANNEL(format) > 1) mode_set |= VIA_RPMODE_STEREO; if (format & AFMT_S16_LE) mode_set |= VIA_RPMODE_16BIT; @@ -300,7 +304,7 @@ viachan_setformat(kobj_t obj, void *data, u_int32_t format) return 0; } -static int +static u_int32_t viachan_setspeed(kobj_t obj, void *data, u_int32_t speed) { struct via_chinfo *ch = data; @@ -323,7 +327,7 @@ viachan_setspeed(kobj_t obj, void *data, u_int32_t speed) return 48000; } -static int +static u_int32_t viachan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) { struct via_chinfo *ch = data; @@ -361,14 +365,14 @@ viachan_trigger(kobj_t obj, void *data, int go) return 0; } -static int +static u_int32_t viachan_getptr(kobj_t obj, void *data) { struct via_chinfo *ch = data; struct via_info *via = ch->parent; struct via_dma_op *ado; bus_addr_t sgd_addr = ch->sgd_addr; - int ptr, base, base1, len, seg; + u_int32_t ptr, base, base1, len, seg; ado = ch->sgd_table; snd_mtxlock(via->lock); @@ -396,7 +400,7 @@ viachan_getptr(kobj_t obj, void *data) ptr = ptr & ~0x1f; } - DEB(printf("return ptr=%d\n", ptr)); + DEB(printf("return ptr=%u\n", ptr)); return ptr; } @@ -417,7 +421,7 @@ static kobj_method_t viachan_methods[] = { KOBJMETHOD(channel_trigger, viachan_trigger), KOBJMETHOD(channel_getptr, viachan_getptr), KOBJMETHOD(channel_getcaps, viachan_getcaps), - { 0, 0 } + KOBJMETHOD_END }; CHANNEL_DECLARE(viachan); diff --git a/sys/dev/sound/pci/vibes.c b/sys/dev/sound/pci/vibes.c index 17d0d07..d391575 100644 --- a/sys/dev/sound/pci/vibes.c +++ b/sys/dev/sound/pci/vibes.c @@ -30,6 +30,10 @@ * muting. */ +#ifdef HAVE_KERNEL_OPTION_HEADERS +#include "opt_snd.h" +#endif + #include <dev/sound/pcm/sound.h> #include <dev/sound/pci/vibes.h> @@ -99,10 +103,10 @@ struct sc_info { }; static u_int32_t sc_fmt[] = { - AFMT_U8, - AFMT_U8 | AFMT_STEREO, - AFMT_S16_LE, - AFMT_S16_LE | AFMT_STEREO, + SND_FORMAT(AFMT_U8, 1, 0), + SND_FORMAT(AFMT_U8, 2, 0), + SND_FORMAT(AFMT_S16_LE, 1, 0), + SND_FORMAT(AFMT_S16_LE, 2, 0), 0 }; @@ -199,7 +203,7 @@ svchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c return NULL; } ch->buffer = b; - ch->fmt = AFMT_U8; + ch->fmt = SND_FORMAT(AFMT_U8, 1, 0); ch->spd = DSP_DEFAULT_SPEED; ch->dma_active = ch->dma_was_active = 0; @@ -212,7 +216,7 @@ svchan_getcaps(kobj_t obj, void *data) return &sc_caps; } -static int +static u_int32_t svchan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) { struct sc_chinfo *ch = data; @@ -231,12 +235,12 @@ svchan_setformat(kobj_t obj, void *data, u_int32_t format) struct sc_chinfo *ch = data; /* NB Just note format here as setting format register * generates noise if dma channel is inactive. */ - ch->fmt = (format & AFMT_STEREO) ? SV_AFMT_STEREO : SV_AFMT_MONO; + ch->fmt = (AFMT_CHANNEL(format) > 1) ? SV_AFMT_STEREO : SV_AFMT_MONO; ch->fmt |= (format & AFMT_16BIT) ? SV_AFMT_S16 : SV_AFMT_U8; return 0; } -static int +static u_int32_t svchan_setspeed(kobj_t obj, void *data, u_int32_t speed) { struct sc_chinfo *ch = data; @@ -349,7 +353,7 @@ svrchan_trigger(kobj_t obj, void *data, int go) return 0; } -static int +static u_int32_t svrchan_getptr(kobj_t obj, void *data) { struct sc_chinfo *ch = data; @@ -370,7 +374,7 @@ static kobj_method_t svrchan_methods[] = { KOBJMETHOD(channel_trigger, svrchan_trigger), KOBJMETHOD(channel_getptr, svrchan_getptr), KOBJMETHOD(channel_getcaps, svchan_getcaps), - { 0, 0 } + KOBJMETHOD_END }; CHANNEL_DECLARE(svrchan); @@ -426,7 +430,7 @@ svpchan_trigger(kobj_t obj, void *data, int go) return 0; } -static int +static u_int32_t svpchan_getptr(kobj_t obj, void *data) { struct sc_chinfo *ch = data; @@ -447,7 +451,7 @@ static kobj_method_t svpchan_methods[] = { KOBJMETHOD(channel_trigger, svpchan_trigger), KOBJMETHOD(channel_getptr, svpchan_getptr), KOBJMETHOD(channel_getcaps, svchan_getcaps), - { 0, 0 } + KOBJMETHOD_END }; CHANNEL_DECLARE(svpchan); @@ -538,7 +542,7 @@ sv_mix_set(struct snd_mixer *m, u_int32_t dev, u_int32_t left, u_int32_t right) return sv_gain(sc, dev, left, right); } -static int +static u_int32_t sv_mix_setrecsrc(struct snd_mixer *m, u_int32_t mask) { struct sc_info *sc = mix_getdevinfo(m); @@ -559,7 +563,7 @@ static kobj_method_t sv_mixer_methods[] = { KOBJMETHOD(mixer_init, sv_mix_init), KOBJMETHOD(mixer_set, sv_mix_set), KOBJMETHOD(mixer_setrecsrc, sv_mix_setrecsrc), - { 0, 0 } + KOBJMETHOD_END }; MIXER_DECLARE(sv_mixer); diff --git a/sys/dev/sound/pcm/ac97.c b/sys/dev/sound/pcm/ac97.c index 22c14e8..ad164d6 100644 --- a/sys/dev/sound/pcm/ac97.c +++ b/sys/dev/sound/pcm/ac97.c @@ -24,6 +24,10 @@ * 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> @@ -822,7 +826,7 @@ struct ac97_info * ac97_create(device_t dev, void *devinfo, kobj_class_t cls) { struct ac97_info *codec; - int eapdinv; + int i; codec = malloc(sizeof(*codec), M_AC97, M_WAITOK | M_ZERO); snprintf(codec->name, sizeof(codec->name), "%s:ac97", @@ -832,11 +836,15 @@ ac97_create(device_t dev, void *devinfo, kobj_class_t cls) codec->dev = dev; codec->devinfo = devinfo; codec->flags = 0; + if (resource_int_value(device_get_name(dev), device_get_unit(dev), - "eapdinv", &eapdinv) == 0) { - if (eapdinv != 0) - codec->flags |= AC97_F_EAPD_INV; - } + "eapdinv", &i) == 0 && i != 0) + codec->flags |= AC97_F_EAPD_INV; + + if (resource_int_value(device_get_name(dev), device_get_unit(dev), + "softpcmvol", &i) == 0 && i != 0) + pcm_setflags(dev, pcm_getflags(dev) | SD_F_SOFTPCMVOL); + return codec; } @@ -864,7 +872,6 @@ ac97_getflags(struct ac97_info *codec) /* -------------------------------------------------------------------- */ -#ifdef SND_DYNSYSCTL static int sysctl_hw_snd_ac97_eapd(SYSCTL_HANDLER_ARGS) { @@ -892,12 +899,10 @@ sysctl_hw_snd_ac97_eapd(SYSCTL_HANDLER_ARGS) } return err; } -#endif static void ac97_init_sysctl(struct ac97_info *codec) { -#ifdef SND_DYNSYSCTL u_int16_t orig, val; if (codec == NULL || codec->dev == NULL) @@ -915,7 +920,6 @@ ac97_init_sysctl(struct ac97_info *codec) OID_AUTO, "eapd", CTLTYPE_INT | CTLFLAG_RW, codec, sizeof(codec), sysctl_hw_snd_ac97_eapd, "I", "AC97 External Amplifier"); -#endif } static int @@ -984,7 +988,6 @@ ac97mix_init(struct snd_mixer *m) case 0x434d4978: /* CMI9761 */ case 0x434d4982: /* CMI9761 */ case 0x434d4983: /* CMI9761 */ - ac97_wrcd(codec, AC97_MIX_PCM, 0); bzero(&codec->mix[SOUND_MIXER_PCM], sizeof(codec->mix[SOUND_MIXER_PCM])); pcm_setflags(codec->dev, pcm_getflags(codec->dev) | @@ -995,6 +998,8 @@ ac97mix_init(struct snd_mixer *m) break; } + 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, @@ -1053,7 +1058,7 @@ ac97mix_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right) return ac97_setmixer(codec, dev, left, right); } -static int +static u_int32_t ac97mix_setrecsrc(struct snd_mixer *m, u_int32_t src) { int i; @@ -1064,7 +1069,7 @@ ac97mix_setrecsrc(struct snd_mixer *m, u_int32_t src) for (i = 0; i < AC97_MIXER_SIZE; i++) if ((src & (1 << i)) != 0) break; - return (ac97_setrecsrc(codec, i) == 0)? 1 << i : -1; + return (ac97_setrecsrc(codec, i) == 0)? 1U << i : 0xffffffffU; } static kobj_method_t ac97mixer_methods[] = { @@ -1073,7 +1078,7 @@ static kobj_method_t ac97mixer_methods[] = { KOBJMETHOD(mixer_reinit, ac97mix_reinit), KOBJMETHOD(mixer_set, ac97mix_set), KOBJMETHOD(mixer_setrecsrc, ac97mix_setrecsrc), - { 0, 0 } + KOBJMETHOD_END }; MIXER_DECLARE(ac97mixer); diff --git a/sys/dev/sound/pcm/ac97_patch.c b/sys/dev/sound/pcm/ac97_patch.c index da6fb1b..e5ae55c 100644 --- a/sys/dev/sound/pcm/ac97_patch.c +++ b/sys/dev/sound/pcm/ac97_patch.c @@ -24,6 +24,10 @@ * 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> diff --git a/sys/dev/sound/pcm/buffer.c b/sys/dev/sound/pcm/buffer.c index 98fb60e..6e82592 100644 --- a/sys/dev/sound/pcm/buffer.c +++ b/sys/dev/sound/pcm/buffer.c @@ -1,5 +1,7 @@ /*- - * Copyright (c) 1999 Cameron Grant <cg@freebsd.org> + * Copyright (c) 2005-2009 Ariff Abdullah <ariff@FreeBSD.org> + * Portions Copyright (c) Ryan Beasley <ryan.beasley@gmail.com> - GSoC 2006 + * Copyright (c) 1999 Cameron Grant <cg@FreeBSD.org> * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,10 +26,17 @@ * SUCH DAMAGE. */ +#ifdef HAVE_KERNEL_OPTION_HEADERS +#include "opt_snd.h" +#endif + #include <dev/sound/pcm/sound.h> #include "feeder_if.h" +#define SND_USE_FXDIV +#include "snd_fxdiv_gen.h" + SND_DECLARE_FILE("$FreeBSD$"); struct snd_dbuf * @@ -154,7 +163,7 @@ sndbuf_resize(struct snd_dbuf *b, unsigned int blkcnt, unsigned int blksz) unsigned int bufsize, allocsize; u_int8_t *tmpbuf; - chn_lock(b->channel); + CHN_LOCK(b->channel); if (b->maxsize == 0) goto out; if (blkcnt == 0) @@ -162,7 +171,7 @@ sndbuf_resize(struct snd_dbuf *b, unsigned int blkcnt, unsigned int blksz) if (blksz == 0) blksz = b->blksz; if (blkcnt < 2 || blksz < 16 || (blkcnt * blksz) > b->maxsize) { - chn_unlock(b->channel); + CHN_UNLOCK(b->channel); return EINVAL; } if (blkcnt == b->blkcnt && blksz == b->blksz) @@ -173,9 +182,9 @@ sndbuf_resize(struct snd_dbuf *b, unsigned int blkcnt, unsigned int blksz) if (bufsize > b->allocsize || bufsize < (b->allocsize >> SNDBUF_CACHE_SHIFT)) { allocsize = round_page(bufsize); - chn_unlock(b->channel); + CHN_UNLOCK(b->channel); tmpbuf = malloc(allocsize, M_DEVBUF, M_WAITOK); - chn_lock(b->channel); + CHN_LOCK(b->channel); if (snd_verbose > 3) printf("%s(): b=%p %p -> %p [%d -> %d : %d]\n", __func__, b, b->tmpbuf, tmpbuf, @@ -194,7 +203,7 @@ sndbuf_resize(struct snd_dbuf *b, unsigned int blkcnt, unsigned int blksz) sndbuf_reset(b); out: - chn_unlock(b->channel); + CHN_UNLOCK(b->channel); return 0; } @@ -212,11 +221,11 @@ sndbuf_remalloc(struct snd_dbuf *b, unsigned int blkcnt, unsigned int blksz) if (bufsize > b->allocsize || bufsize < (b->allocsize >> SNDBUF_CACHE_SHIFT)) { allocsize = round_page(bufsize); - chn_unlock(b->channel); + CHN_UNLOCK(b->channel); buf = malloc(allocsize, M_DEVBUF, M_WAITOK); tmpbuf = malloc(allocsize, M_DEVBUF, M_WAITOK); shadbuf = malloc(allocsize, M_DEVBUF, M_WAITOK); - chn_lock(b->channel); + CHN_LOCK(b->channel); if (b->buf != NULL) free(b->buf, M_DEVBUF); b->buf = buf; @@ -334,14 +343,17 @@ int sndbuf_setfmt(struct snd_dbuf *b, u_int32_t fmt) { b->fmt = fmt; - b->bps = 1; - b->bps <<= (b->fmt & AFMT_STEREO)? 1 : 0; + 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; } @@ -360,9 +372,7 @@ sndbuf_setspd(struct snd_dbuf *b, unsigned int spd) unsigned int sndbuf_getalign(struct snd_dbuf *b) { - static int align[] = {0, 1, 1, 2, 2, 2, 2, 3}; - - return align[b->bps - 1]; + return (b->align); } unsigned int @@ -515,7 +525,7 @@ sndbuf_getfreeptr(struct snd_dbuf *b) return (b->rp + b->rl) % b->bufsize; } -unsigned int +u_int64_t sndbuf_getblocks(struct snd_dbuf *b) { SNDBUF_LOCKASSERT(b); @@ -523,7 +533,7 @@ sndbuf_getblocks(struct snd_dbuf *b) return b->total / b->blksz; } -unsigned int +u_int64_t sndbuf_getprevblocks(struct snd_dbuf *b) { SNDBUF_LOCKASSERT(b); @@ -531,7 +541,7 @@ sndbuf_getprevblocks(struct snd_dbuf *b) return b->prev_total / b->blksz; } -unsigned int +u_int64_t sndbuf_gettotal(struct snd_dbuf *b) { SNDBUF_LOCKASSERT(b); @@ -539,6 +549,14 @@ sndbuf_gettotal(struct snd_dbuf *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) { @@ -577,14 +595,14 @@ sndbuf_xbytes(unsigned int v, struct snd_dbuf *from, struct snd_dbuf *to) if (from == NULL || to == NULL || v == 0) return 0; - return snd_xbytes(v, sndbuf_getbps(from) * sndbuf_getspd(from), - sndbuf_getbps(to) * sndbuf_getspd(to)); + return snd_xbytes(v, sndbuf_getalign(from) * sndbuf_getspd(from), + sndbuf_getalign(to) * sndbuf_getspd(to)); } u_int8_t sndbuf_zerodata(u_int32_t fmt) { - if (fmt & AFMT_SIGNED) + if (fmt & (AFMT_SIGNED | AFMT_PASSTHROUGH)) return (0x00); else if (fmt & AFMT_MU_LAW) return (0x7f); @@ -670,26 +688,55 @@ sndbuf_dispose(struct snd_dbuf *b, u_int8_t *to, unsigned int count) return 0; } +#ifdef SND_DIAGNOSTIC +static uint32_t snd_feeder_maxfeed = 0; +SYSCTL_INT(_hw_snd, OID_AUTO, feeder_maxfeed, CTLFLAG_RD, + &snd_feeder_maxfeed, 0, "maximum feeder count request"); + +static uint32_t snd_feeder_maxcycle = 0; +SYSCTL_INT(_hw_snd, OID_AUTO, feeder_maxcycle, CTLFLAG_RD, + &snd_feeder_maxcycle, 0, "maximum feeder cycle"); +#endif + /* count is number of bytes we want added to destination buffer */ int sndbuf_feed(struct snd_dbuf *from, struct snd_dbuf *to, struct pcm_channel *channel, struct pcm_feeder *feeder, unsigned int count) { - unsigned int cnt; + unsigned int cnt, maxfeed; +#ifdef SND_DIAGNOSTIC + unsigned int cycle; + + if (count > snd_feeder_maxfeed) + snd_feeder_maxfeed = count; + + cycle = 0; +#endif KASSERT(count > 0, ("can't feed 0 bytes")); if (sndbuf_getfree(to) < count) - return EINVAL; + return (EINVAL); + + maxfeed = SND_FXROUND(SND_FXDIV_MAX, sndbuf_getalign(to)); do { - cnt = FEEDER_FEED(feeder, channel, to->tmpbuf, count, from); - if (cnt) { - sndbuf_acquire(to, to->tmpbuf, cnt); - count -= cnt; - } - } while (count && cnt); + cnt = FEEDER_FEED(feeder, channel, to->tmpbuf, + min(count, maxfeed), from); + if (cnt == 0) + break; + sndbuf_acquire(to, to->tmpbuf, cnt); + count -= cnt; +#ifdef SND_DIAGNOSTIC + cycle++; +#endif + } while (count != 0); - return 0; +#ifdef SND_DIAGNOSTIC + if (cycle > snd_feeder_maxcycle) + snd_feeder_maxcycle = cycle; +#endif + + return (0); } /************************************************************/ @@ -703,7 +750,7 @@ sndbuf_dump(struct snd_dbuf *b, char *s, u_int32_t what) if (what & 0x02) printf(" dl: %d, rp: %d, rl: %d, hp: %d", b->dl, b->rp, b->rl, b->hp); if (what & 0x04) - printf(" total: %d, prev_total: %d, xrun: %d", b->total, b->prev_total, b->xrun); + printf(" total: %ju, prev_total: %ju, xrun: %d", (uintmax_t)b->total, (uintmax_t)b->prev_total, b->xrun); if (what & 0x08) printf(" fmt: 0x%x, spd: %d", b->fmt, b->spd); if (what & 0x10) diff --git a/sys/dev/sound/pcm/buffer.h b/sys/dev/sound/pcm/buffer.h index a21c8ec..91a63af 100644 --- a/sys/dev/sound/pcm/buffer.h +++ b/sys/dev/sound/pcm/buffer.h @@ -46,9 +46,9 @@ struct snd_dbuf { volatile int rp; /* pointers to the ready area */ volatile int rl; /* length of ready area */ volatile int hp; - volatile u_int32_t total, prev_total; + volatile u_int64_t total, prev_total; int dmachan, dir; /* dma channel */ - u_int32_t fmt, spd, bps; + u_int32_t fmt, spd, bps, align; unsigned int blksz, blkcnt; int xrun; u_int32_t flags; @@ -107,9 +107,10 @@ unsigned int sndbuf_getfree(struct snd_dbuf *b); unsigned int sndbuf_getfreeptr(struct snd_dbuf *b); unsigned int sndbuf_getready(struct snd_dbuf *b); unsigned int sndbuf_getreadyptr(struct snd_dbuf *b); -unsigned int sndbuf_getblocks(struct snd_dbuf *b); -unsigned int sndbuf_getprevblocks(struct snd_dbuf *b); -unsigned int sndbuf_gettotal(struct snd_dbuf *b); +u_int64_t sndbuf_getblocks(struct snd_dbuf *b); +u_int64_t sndbuf_getprevblocks(struct snd_dbuf *b); +u_int64_t sndbuf_gettotal(struct snd_dbuf *b); +u_int64_t sndbuf_getprevtotal(struct snd_dbuf *b); unsigned int snd_xbytes(unsigned int v, unsigned int from, unsigned int to); unsigned int sndbuf_xbytes(unsigned int v, struct snd_dbuf *from, struct snd_dbuf *to); u_int8_t sndbuf_zerodata(u_int32_t fmt); diff --git a/sys/dev/sound/pcm/channel.c b/sys/dev/sound/pcm/channel.c index 464330a..42ea86f 100644 --- a/sys/dev/sound/pcm/channel.c +++ b/sys/dev/sound/pcm/channel.c @@ -1,6 +1,8 @@ /*- - * Copyright (c) 1999 Cameron Grant <cg@freebsd.org> - * Portions Copyright by Luigi Rizzo - 1997-99 + * Copyright (c) 2005-2009 Ariff Abdullah <ariff@FreeBSD.org> + * Portions Copyright (c) Ryan Beasley <ryan.beasley@gmail.com> - GSoC 2006 + * Copyright (c) 1999 Cameron Grant <cg@FreeBSD.org> + * Portions Copyright (c) Luigi Rizzo <luigi@FreeBSD.org> - 1997-99 * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -27,7 +29,12 @@ #include "opt_isa.h" +#ifdef HAVE_KERNEL_OPTION_HEADERS +#include "opt_snd.h" +#endif + #include <dev/sound/pcm/sound.h> +#include <dev/sound/pcm/vchan.h> #include "feeder_if.h" @@ -37,6 +44,10 @@ int report_soft_formats = 1; SYSCTL_INT(_hw_snd, OID_AUTO, report_soft_formats, CTLFLAG_RW, &report_soft_formats, 1, "report software-emulated formats"); +int report_soft_matrix = 1; +SYSCTL_INT(_hw_snd, OID_AUTO, report_soft_matrix, CTLFLAG_RW, + &report_soft_matrix, 1, "report software-emulated channel matrixing"); + int chn_latency = CHN_LATENCY_DEFAULT; TUNABLE_INT("hw.snd.latency", &chn_latency); @@ -107,6 +118,80 @@ SYSCTL_PROC(_hw_snd, OID_AUTO, timeout, CTLTYPE_INT | CTLFLAG_RW, "interrupt timeout (1 - 10) seconds"); #endif +static int chn_vpc_autoreset = 1; +TUNABLE_INT("hw.snd.vpc_autoreset", &chn_vpc_autoreset); +SYSCTL_INT(_hw_snd, OID_AUTO, vpc_autoreset, CTLFLAG_RW, + &chn_vpc_autoreset, 0, "automatically reset channels volume to 0db"); + +static int chn_vol_0db_pcm = SND_VOL_0DB_PCM; + +static void +chn_vpc_proc(int reset, int db) +{ + struct snddev_info *d; + struct pcm_channel *c; + int i; + + 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_LOCK(d); + PCM_WAIT(d); + PCM_ACQUIRE(d); + CHN_FOREACH(c, d, channels.pcm) { + CHN_LOCK(c); + CHN_SETVOLUME(c, SND_VOL_C_PCM, SND_CHN_T_VOL_0DB, db); + if (reset != 0) + chn_vpc_reset(c, SND_VOL_C_PCM, 1); + CHN_UNLOCK(c); + } + PCM_RELEASE(d); + PCM_UNLOCK(d); + } +} + +static int +sysctl_hw_snd_vpc_0db(SYSCTL_HANDLER_ARGS) +{ + int err, val; + + val = chn_vol_0db_pcm; + err = sysctl_handle_int(oidp, &val, 0, req); + if (err != 0 || req->newptr == NULL) + return (err); + if (val < SND_VOL_0DB_MIN || val > SND_VOL_0DB_MAX) + return (EINVAL); + + chn_vol_0db_pcm = val; + chn_vpc_proc(0, val); + + return (0); +} +SYSCTL_PROC(_hw_snd, OID_AUTO, vpc_0db, CTLTYPE_INT | CTLFLAG_RW, + 0, sizeof(int), sysctl_hw_snd_vpc_0db, "I", + "0db relative level"); + +static int +sysctl_hw_snd_vpc_reset(SYSCTL_HANDLER_ARGS) +{ + int err, val; + + val = 0; + err = sysctl_handle_int(oidp, &val, 0, req); + if (err != 0 || req->newptr == NULL || val == 0) + return (err); + + chn_vol_0db_pcm = SND_VOL_0DB_PCM; + chn_vpc_proc(1, SND_VOL_0DB_PCM); + + return (0); +} +SYSCTL_PROC(_hw_snd, OID_AUTO, vpc_reset, CTLTYPE_INT | CTLFLAG_RW, + 0, sizeof(int), sysctl_hw_snd_vpc_reset, "I", + "reset volume on all channels"); + static int chn_usefrags = 0; TUNABLE_INT("hw.snd.usefrags", &chn_usefrags); static int chn_syncdelay = -1; @@ -137,8 +222,6 @@ MTX_SYSINIT(pcm_syncgroup, &snd_pcm_syncgroups_mtx, "PCM channel sync group lock */ struct pcm_synclist snd_pcm_syncgroups = SLIST_HEAD_INITIALIZER(head); -static int chn_buildfeeder(struct pcm_channel *c); - static void chn_lockinit(struct pcm_channel *c, int dir) { @@ -159,9 +242,8 @@ chn_lockinit(struct pcm_channel *c, int dir) c->lock = snd_mtxcreate(c->name, "pcm virtual record channel"); cv_init(&c->intr_cv, "pcmrdv"); break; - case 0: - c->lock = snd_mtxcreate(c->name, "pcm fake channel"); - cv_init(&c->intr_cv, "pcmfk"); + default: + panic("%s(): Invalid direction=%d", __func__, dir); break; } @@ -192,33 +274,31 @@ static int chn_polltrigger(struct pcm_channel *c) { struct snd_dbuf *bs = c->bufsoft; - unsigned amt, lim; + u_int delta; CHN_LOCKASSERT(c); - if (c->flags & CHN_F_MAPPED) { - if (sndbuf_getprevblocks(bs) == 0) - return 1; + + if (c->flags & CHN_F_MMAP) { + if (sndbuf_getprevtotal(bs) < c->lw) + delta = c->lw; else - return (sndbuf_getblocks(bs) > sndbuf_getprevblocks(bs))? 1 : 0; + delta = sndbuf_gettotal(bs) - sndbuf_getprevtotal(bs); } else { - amt = (c->direction == PCMDIR_PLAY)? sndbuf_getfree(bs) : sndbuf_getready(bs); -#if 0 - lim = (c->flags & CHN_F_HAS_SIZE)? sndbuf_getblksz(bs) : 1; -#endif - lim = c->lw; - return (amt >= lim) ? 1 : 0; + if (c->direction == PCMDIR_PLAY) + delta = sndbuf_getfree(bs); + else + delta = sndbuf_getready(bs); } - return 0; + + return ((delta < c->lw) ? 0 : 1); } -static int +static void chn_pollreset(struct pcm_channel *c) { - struct snd_dbuf *bs = c->bufsoft; CHN_LOCKASSERT(c); - sndbuf_updateprevtotal(bs); - return 1; + sndbuf_updateprevtotal(c->bufsoft); } static void @@ -289,12 +369,12 @@ chn_dmaupdate(struct pcm_channel *c) if (c->direction == PCMDIR_PLAY) { amt = min(delta, sndbuf_getready(b)); - amt -= amt % sndbuf_getbps(b); + amt -= amt % sndbuf_getalign(b); if (amt > 0) sndbuf_dispose(b, NULL, amt); } else { amt = min(delta, sndbuf_getfree(b)); - amt -= amt % sndbuf_getbps(b); + amt -= amt % sndbuf_getalign(b); if (amt > 0) sndbuf_acquire(b, NULL, amt); } @@ -309,40 +389,22 @@ chn_dmaupdate(struct pcm_channel *c) return delta; } -void -chn_wrupdate(struct pcm_channel *c) -{ - int ret; - - CHN_LOCKASSERT(c); - KASSERT(c->direction == PCMDIR_PLAY, ("chn_wrupdate on bad channel")); - - if ((c->flags & (CHN_F_MAPPED | CHN_F_VIRTUAL)) || CHN_STOPPED(c)) - return; - chn_dmaupdate(c); - ret = chn_wrfeed(c); - /* tell the driver we've updated the primary buffer */ - chn_trigger(c, PCMTRIG_EMLDMAWR); - DEB(if (ret) - printf("chn_wrupdate: chn_wrfeed returned %d\n", ret);) - -} - -int +static void chn_wrfeed(struct pcm_channel *c) { struct snd_dbuf *b = c->bufhard; struct snd_dbuf *bs = c->bufsoft; - unsigned int ret, amt; + unsigned int amt; CHN_LOCKASSERT(c); - if ((c->flags & CHN_F_MAPPED) && !(c->flags & CHN_F_CLOSING)) + if ((c->flags & CHN_F_MMAP) && !(c->flags & CHN_F_CLOSING)) sndbuf_acquire(bs, NULL, sndbuf_getfree(bs)); amt = sndbuf_getfree(b); + if (amt > 0) + sndbuf_feed(bs, b, c, c->feeder, amt); - ret = (amt > 0) ? sndbuf_feed(bs, b, c, c->feeder, amt) : ENOSPC; /* * Possible xruns. There should be no empty space left in buffer. */ @@ -351,24 +413,36 @@ chn_wrfeed(struct pcm_channel *c) if (sndbuf_getfree(b) < amt) chn_wakeup(c); +} - return ret; +#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) { - int ret; CHN_LOCKASSERT(c); /* update pointers in primary buffer */ chn_dmaupdate(c); /* ...and feed from secondary to primary */ - ret = chn_wrfeed(c); + chn_wrfeed(c); /* tell the driver we've updated the primary buffer */ chn_trigger(c, PCMTRIG_EMLDMAWR); - DEB(if (ret) - printf("chn_wrintr: chn_wrfeed returned %d\n", ret);) } /* @@ -431,8 +505,9 @@ chn_write(struct pcm_channel *c, struct uio *buf) if (ret == EAGAIN) { ret = EINVAL; c->flags |= CHN_F_DEAD; - printf("%s: play interrupt timeout, " - "channel dead\n", c->name); + device_printf(c->dev, "%s(): %s: " + "play interrupt timeout, channel dead\n", + __func__, c->name); } else if (ret == ERESTART || ret == EINTR) c->flags |= CHN_F_ABORTING; } @@ -444,20 +519,21 @@ chn_write(struct pcm_channel *c, struct uio *buf) /* * Feed new data from the read buffer. Can be called in the bottom half. */ -int +static void chn_rdfeed(struct pcm_channel *c) { struct snd_dbuf *b = c->bufhard; struct snd_dbuf *bs = c->bufsoft; - unsigned int ret, amt; + unsigned int amt; CHN_LOCKASSERT(c); - if (c->flags & CHN_F_MAPPED) + if (c->flags & CHN_F_MMAP) sndbuf_dispose(bs, NULL, sndbuf_getready(bs)); amt = sndbuf_getfree(bs); - ret = (amt > 0) ? sndbuf_feed(b, bs, c, c->feeder, amt) : ENOSPC; + if (amt > 0) + sndbuf_feed(b, bs, c, c->feeder, amt); amt = sndbuf_getready(b); if (amt > 0) { @@ -467,33 +543,28 @@ chn_rdfeed(struct pcm_channel *c) if (sndbuf_getready(bs) > 0) chn_wakeup(c); - - return ret; } -void +#if 0 +static void chn_rdupdate(struct pcm_channel *c) { - int ret; CHN_LOCKASSERT(c); KASSERT(c->direction == PCMDIR_REC, ("chn_rdupdate on bad channel")); - if ((c->flags & (CHN_F_MAPPED | CHN_F_VIRTUAL)) || CHN_STOPPED(c)) + if ((c->flags & (CHN_F_MMAP | CHN_F_VIRTUAL)) || CHN_STOPPED(c)) return; chn_trigger(c, PCMTRIG_EMLDMARD); chn_dmaupdate(c); - ret = chn_rdfeed(c); - DEB(if (ret) - printf("chn_rdfeed: %d\n", ret);) - + chn_rdfeed(c); } +#endif /* read interrupt routine. Must be called with interrupts blocked. */ static void chn_rdintr(struct pcm_channel *c) { - int ret; CHN_LOCKASSERT(c); /* tell the driver to update the primary buffer if non-dma */ @@ -501,7 +572,7 @@ chn_rdintr(struct pcm_channel *c) /* update pointers in primary buffer */ chn_dmaupdate(c); /* ...and feed from primary to secondary */ - ret = chn_rdfeed(c); + chn_rdfeed(c); } /* @@ -557,8 +628,9 @@ chn_read(struct pcm_channel *c, struct uio *buf) if (ret == EAGAIN) { ret = EINVAL; c->flags |= CHN_F_DEAD; - printf("%s: record interrupt timeout, " - "channel dead\n", c->name); + device_printf(c->dev, "%s(): %s: " + "record interrupt timeout, channel dead\n", + __func__, c->name); } else if (ret == ERESTART || ret == EINTR) c->flags |= CHN_F_ABORTING; } @@ -568,28 +640,31 @@ chn_read(struct pcm_channel *c, struct uio *buf) } void -chn_intr(struct pcm_channel *c) +chn_intr_locked(struct pcm_channel *c) { - uint8_t do_unlock; - if (CHN_LOCK_OWNED(c)) { - /* - * Allow sound drivers to call this function with - * "CHN_LOCK()" locked: - */ - do_unlock = 0; - } else { - do_unlock = 1; - CHN_LOCK(c); - } + + CHN_LOCKASSERT(c); + c->interrupts++; + if (c->direction == PCMDIR_PLAY) chn_wrintr(c); else chn_rdintr(c); - if (do_unlock) { - CHN_UNLOCK(c); +} + +void +chn_intr(struct pcm_channel *c) +{ + + if (CHN_LOCKOWNED(c)) { + chn_intr_locked(c); + return; } - return; + + CHN_LOCK(c); + chn_intr_locked(c); + CHN_UNLOCK(c); } u_int32_t @@ -623,37 +698,43 @@ chn_start(struct pcm_channel *c, int force) pb = CHN_BUF_PARENT(c, b); i = sndbuf_xbytes(sndbuf_getready(bs), bs, pb); - j = sndbuf_getbps(pb); + j = sndbuf_getalign(pb); } } if (snd_verbose > 3 && CHN_EMPTY(c, children)) - printf("%s: %s (%s) threshold i=%d j=%d\n", - __func__, CHN_DIRSTR(c), - (c->flags & CHN_F_VIRTUAL) ? "virtual" : "hardware", - i, j); + device_printf(c->dev, "%s(): %s (%s) threshold " + "i=%d j=%d\n", __func__, CHN_DIRSTR(c), + (c->flags & CHN_F_VIRTUAL) ? "virtual" : + "hardware", i, j); } if (i >= j) { c->flags |= CHN_F_TRIGGERED; sndbuf_setrun(b, 1); - c->feedcount = (c->flags & CHN_F_CLOSING) ? 2 : 0; - c->interrupts = 0; - c->xruns = 0; - if (c->direction == PCMDIR_PLAY && c->parentchannel == NULL) { - sndbuf_fillsilence(b); + if (c->flags & CHN_F_CLOSING) + c->feedcount = 2; + else { + c->feedcount = 0; + c->interrupts = 0; + c->xruns = 0; + } + if (c->parentchannel == NULL) { + if (c->direction == PCMDIR_PLAY) + sndbuf_fillsilence(b); if (snd_verbose > 3) - printf("%s: %s starting! (%s) (ready=%d " - "force=%d i=%d j=%d intrtimeout=%u " - "latency=%dms)\n", + device_printf(c->dev, + "%s(): %s starting! (%s/%s) " + "(ready=%d force=%d i=%d j=%d " + "intrtimeout=%u latency=%dms)\n", __func__, (c->flags & CHN_F_HAS_VCHAN) ? - "VCHAN" : "HW", + "VCHAN PARENT" : "HW", CHN_DIRSTR(c), (c->flags & CHN_F_CLOSING) ? "closing" : "running", sndbuf_getready(b), force, i, j, c->timeout, (sndbuf_getsize(b) * 1000) / - (sndbuf_getbps(b) * sndbuf_getspd(b))); + (sndbuf_getalign(b) * sndbuf_getspd(b))); } err = chn_trigger(c, PCMTRIG_START); } @@ -720,10 +801,10 @@ chn_sync(struct pcm_channel *c, int threshold) * to avoid audible truncation. */ if (syncdelay > 0) - minflush += (sndbuf_getbps(bs) * sndbuf_getspd(bs) * + minflush += (sndbuf_getalign(bs) * sndbuf_getspd(bs) * ((syncdelay > 1000) ? 1000 : syncdelay)) / 1000; - minflush -= minflush % sndbuf_getbps(bs); + minflush -= minflush % sndbuf_getalign(bs); if (minflush > 0) { threshold = min(minflush, sndbuf_getfree(bs)); @@ -736,7 +817,8 @@ chn_sync(struct pcm_channel *c, int threshold) residp = resid; blksz = sndbuf_getblksz(b); if (blksz < 1) { - printf("%s: WARNING: blksz < 1 ! maxsize=%d [%d/%d/%d]\n", + device_printf(c->dev, + "%s(): WARNING: blksz < 1 ! maxsize=%d [%d/%d/%d]\n", __func__, sndbuf_getmaxsize(b), sndbuf_getsize(b), sndbuf_getblksz(b), sndbuf_getblkcnt(b)); if (sndbuf_getblkcnt(b) > 0) @@ -749,7 +831,7 @@ chn_sync(struct pcm_channel *c, int threshold) ret = 0; if (snd_verbose > 3) - printf("%s: [begin] timeout=%d count=%d " + device_printf(c->dev, "%s(): [begin] timeout=%d count=%d " "minflush=%d resid=%d\n", __func__, c->timeout, count, minflush, resid); @@ -765,7 +847,8 @@ chn_sync(struct pcm_channel *c, int threshold) if (resid == residp) { --count; if (snd_verbose > 3) - printf("%s: [stalled] timeout=%d " + device_printf(c->dev, + "%s(): [stalled] timeout=%d " "count=%d hcount=%d " "resid=%d minflush=%d\n", __func__, c->timeout, count, @@ -773,7 +856,8 @@ chn_sync(struct pcm_channel *c, int threshold) } else if (resid < residp && count < hcount) { ++count; if (snd_verbose > 3) - printf("%s: [resume] timeout=%d " + device_printf(c->dev, + "%s((): [resume] timeout=%d " "count=%d hcount=%d " "resid=%d minflush=%d\n", __func__, c->timeout, count, @@ -795,7 +879,8 @@ chn_sync(struct pcm_channel *c, int threshold) c->flags |= cflag; if (snd_verbose > 3) - printf("%s: timeout=%d count=%d hcount=%d resid=%d residp=%d " + device_printf(c->dev, + "%s(): timeout=%d count=%d hcount=%d resid=%d residp=%d " "minflush=%d ret=%d\n", __func__, c->timeout, count, hcount, resid, residp, minflush, ret); @@ -811,16 +896,20 @@ chn_poll(struct pcm_channel *c, int ev, struct thread *td) int ret; CHN_LOCKASSERT(c); - if (!(c->flags & (CHN_F_MAPPED | CHN_F_TRIGGERED))) { + + if (!(c->flags & (CHN_F_MMAP | CHN_F_TRIGGERED))) { ret = chn_start(c, 1); if (ret != 0) return (0); } + ret = 0; - if (chn_polltrigger(c) && chn_pollreset(c)) + if (chn_polltrigger(c)) { + chn_pollreset(c); ret = ev; - else + } else selrecord(td, sndbuf_getsel(bs)); + return (ret); } @@ -886,145 +975,179 @@ chn_flush(struct pcm_channel *c) } int -fmtvalid(u_int32_t fmt, u_int32_t *fmtlist) +snd_fmtvalid(uint32_t fmt, uint32_t *fmtlist) { int i; - for (i = 0; fmtlist[i]; i++) - if (fmt == fmtlist[i]) - return 1; - return 0; + for (i = 0; fmtlist[i] != 0; i++) { + if (fmt == fmtlist[i] || + ((fmt & AFMT_PASSTHROUGH) && + (AFMT_ENCODING(fmt) & fmtlist[i]))) + return (1); + } + + return (0); } -static struct afmtstr_table default_afmtstr_table[] = { - { "alaw", AFMT_A_LAW }, { "mulaw", AFMT_MU_LAW }, - { "u8", AFMT_U8 }, { "s8", AFMT_S8 }, - { "s16le", AFMT_S16_LE }, { "s16be", AFMT_S16_BE }, - { "u16le", AFMT_U16_LE }, { "u16be", AFMT_U16_BE }, - { "s24le", AFMT_S24_LE }, { "s24be", AFMT_S24_BE }, - { "u24le", AFMT_U24_LE }, { "u24be", AFMT_U24_BE }, - { "s32le", AFMT_S32_LE }, { "s32be", AFMT_S32_BE }, - { "u32le", AFMT_U32_LE }, { "u32be", AFMT_U32_BE }, - { NULL, 0 }, +static const struct { + char *name, *alias1, *alias2; + uint32_t afmt; +} afmt_tab[] = { + { "alaw", NULL, NULL, AFMT_A_LAW }, + { "mulaw", NULL, NULL, AFMT_MU_LAW }, + { "u8", "8", NULL, AFMT_U8 }, + { "s8", NULL, NULL, AFMT_S8 }, +#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 }, + { "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 }, +#else + { "s32le", NULL, NULL, AFMT_S32_LE }, + { "s32be", "s32", "32", AFMT_S32_BE }, +#endif + { "u32le", NULL, NULL, AFMT_U32_LE }, + { "u32be", NULL, NULL, AFMT_U32_BE }, + { "ac3", NULL, NULL, AFMT_AC3 }, + { NULL, NULL, NULL, 0 } }; -int -afmtstr_swap_sign(char *s) -{ - if (s == NULL || strlen(s) < 2) /* full length of "s8" */ - return 0; - if (*s == 's') - *s = 'u'; - else if (*s == 'u') - *s = 's'; - else - return 0; - return 1; -} +static const struct { + char *name, *alias1, *alias2; + int matrix_id; +} matrix_id_tab[] = { + { "1.0", "1", "mono", SND_CHN_MATRIX_1_0 }, + { "2.0", "2", "stereo", SND_CHN_MATRIX_2_0 }, + { "2.1", NULL, NULL, SND_CHN_MATRIX_2_1 }, + { "3.0", "3", NULL, SND_CHN_MATRIX_3_0 }, + { "4.0", "4", "quad", SND_CHN_MATRIX_4_0 }, + { "4.1", NULL, NULL, SND_CHN_MATRIX_4_1 }, + { "5.0", "5", NULL, SND_CHN_MATRIX_5_0 }, + { "5.1", "6", NULL, SND_CHN_MATRIX_5_1 }, + { "6.0", NULL, NULL, SND_CHN_MATRIX_6_0 }, + { "6.1", "7", NULL, SND_CHN_MATRIX_6_1 }, + { "7.1", "8", NULL, SND_CHN_MATRIX_7_1 }, + { NULL, NULL, NULL, SND_CHN_MATRIX_UNKNOWN } +}; -int -afmtstr_swap_endian(char *s) +uint32_t +snd_str2afmt(const char *req) { - if (s == NULL || strlen(s) < 5) /* full length of "s16le" */ - return 0; - if (s[3] == 'l') - s[3] = 'b'; - else if (s[3] == 'b') - s[3] = 'l'; - else - return 0; - return 1; -} + uint32_t i, afmt; + int matrix_id; + char b1[8], b2[8]; -u_int32_t -afmtstr2afmt(struct afmtstr_table *tbl, const char *s, int stereo) -{ - size_t fsz, sz; + i = sscanf(req, "%5[^:]:%6s", b1, b2); - sz = (s == NULL) ? 0 : strlen(s); + if (i == 1) { + if (strlen(req) != strlen(b1)) + return (0); + strlcpy(b2, "2.0", sizeof(b2)); + } else if (i == 2) { + if (strlen(req) != (strlen(b1) + 1 + strlen(b2))) + return (0); + } else + return (0); - if (sz > 1) { + afmt = 0; + matrix_id = SND_CHN_MATRIX_UNKNOWN; + + for (i = 0; afmt == 0 && afmt_tab[i].name != NULL; i++) { + if (strcasecmp(afmt_tab[i].name, b1) == 0 || + (afmt_tab[i].alias1 != NULL && + strcasecmp(afmt_tab[i].alias1, b1) == 0) || + (afmt_tab[i].alias2 != NULL && + strcasecmp(afmt_tab[i].alias2, b1) == 0)) { + afmt = afmt_tab[i].afmt; + strlcpy(b1, afmt_tab[i].name, sizeof(b1)); + } + } - if (tbl == NULL) - tbl = default_afmtstr_table; + if (afmt == 0) + return (0); - for (; tbl->fmtstr != NULL; tbl++) { - fsz = strlen(tbl->fmtstr); - if (sz < fsz) - continue; - if (strncmp(s, tbl->fmtstr, fsz) != 0) - continue; - if (fsz == sz) - return tbl->format | - ((stereo) ? AFMT_STEREO : 0); - if ((sz - fsz) < 2 || s[fsz] != ':') - break; - /* - * For now, just handle mono/stereo. - */ - if ((s[fsz + 2] == '\0' && (s[fsz + 1] == 'm' || - s[fsz + 1] == '1')) || - strcmp(s + fsz + 1, "mono") == 0) - return tbl->format; - if ((s[fsz + 2] == '\0' && (s[fsz + 1] == 's' || - s[fsz + 1] == '2')) || - strcmp(s + fsz + 1, "stereo") == 0) - return tbl->format | AFMT_STEREO; - break; + for (i = 0; matrix_id == SND_CHN_MATRIX_UNKNOWN && + matrix_id_tab[i].name != NULL; i++) { + if (strcmp(matrix_id_tab[i].name, b2) == 0 || + (matrix_id_tab[i].alias1 != NULL && + strcmp(matrix_id_tab[i].alias1, b2) == 0) || + (matrix_id_tab[i].alias2 != NULL && + strcasecmp(matrix_id_tab[i].alias2, b2) == 0)) { + matrix_id = matrix_id_tab[i].matrix_id; + strlcpy(b2, matrix_id_tab[i].name, sizeof(b2)); } } - return 0; + if (matrix_id == SND_CHN_MATRIX_UNKNOWN) + return (0); + +#ifndef _KERNEL + printf("Parse OK: '%s' -> '%s:%s' %d\n", req, b1, b2, + (int)(b2[0]) - '0' + (int)(b2[2]) - '0'); +#endif + + return (SND_FORMAT(afmt, b2[0] - '0' + b2[2] - '0', b2[2] - '0')); } -u_int32_t -afmt2afmtstr(struct afmtstr_table *tbl, u_int32_t afmt, char *dst, - size_t len, int type, int stereo) +uint32_t +snd_afmt2str(uint32_t afmt, char *buf, size_t len) { - u_int32_t fmt = 0; - char *fmtstr = NULL, *tag = ""; + uint32_t i, enc, ch, ext; + char tmp[AFMTSTR_LEN]; + + if (buf == NULL || len < AFMTSTR_LEN) + return (0); + + + bzero(tmp, sizeof(tmp)); - if (tbl == NULL) - tbl = default_afmtstr_table; + enc = AFMT_ENCODING(afmt); + ch = AFMT_CHANNEL(afmt); + ext = AFMT_EXTCHANNEL(afmt); - for (; tbl->format != 0; tbl++) { - if (tbl->format == 0) + for (i = 0; afmt_tab[i].name != NULL; i++) { + if (enc == afmt_tab[i].afmt) { + strlcpy(tmp, afmt_tab[i].name, sizeof(tmp)); + strlcat(tmp, ":", sizeof(tmp)); break; - if ((afmt & ~AFMT_STEREO) != tbl->format) - continue; - fmt = afmt; - fmtstr = tbl->fmtstr; - break; + } } - if (fmt != 0 && fmtstr != NULL && dst != NULL && len > 0) { - strlcpy(dst, fmtstr, len); - switch (type) { - case AFMTSTR_SIMPLE: - tag = (fmt & AFMT_STEREO) ? ":s" : ":m"; - break; - case AFMTSTR_NUM: - tag = (fmt & AFMT_STEREO) ? ":2" : ":1"; - break; - case AFMTSTR_FULL: - tag = (fmt & AFMT_STEREO) ? ":stereo" : ":mono"; - break; - case AFMTSTR_NONE: - default: + if (strlen(tmp) == 0) + return (0); + + for (i = 0; matrix_id_tab[i].name != NULL; i++) { + if (ch == (matrix_id_tab[i].name[0] - '0' + + matrix_id_tab[i].name[2] - '0') && + ext == (matrix_id_tab[i].name[2] - '0')) { + strlcat(tmp, matrix_id_tab[i].name, sizeof(tmp)); break; } - if (strlen(tag) > 0 && ((stereo && !(fmt & AFMT_STEREO)) || \ - (!stereo && (fmt & AFMT_STEREO)))) - strlcat(dst, tag, len); } - return fmt; + if (strlen(tmp) == 0) + return (0); + + strlcpy(buf, tmp, len); + + return (snd_str2afmt(buf)); } int -chn_reset(struct pcm_channel *c, u_int32_t fmt) +chn_reset(struct pcm_channel *c, uint32_t fmt, uint32_t spd) { - int hwspd, r; + int r; CHN_LOCKASSERT(c); c->feedcount = 0; @@ -1033,27 +1156,19 @@ chn_reset(struct pcm_channel *c, u_int32_t fmt) c->timeout = 1; c->xruns = 0; - r = CHANNEL_RESET(c->methods, c->devinfo); - if (fmt != 0) { -#if 0 - hwspd = DSP_DEFAULT_SPEED; - /* only do this on a record channel until feederbuilder works */ - if (c->direction == PCMDIR_REC) - RANGE(hwspd, chn_getcaps(c)->minspeed, chn_getcaps(c)->maxspeed); - c->speed = hwspd; -#endif - hwspd = chn_getcaps(c)->minspeed; - c->speed = hwspd; + c->flags |= (pcm_getflags(c->dev) & SD_F_BITPERFECT) ? + CHN_F_BITPERFECT : 0; - if (r == 0) - r = chn_setformat(c, fmt); - if (r == 0) - r = chn_setspeed(c, hwspd); -#if 0 - if (r == 0) - r = chn_setvolume(c, 100, 100); -#endif + r = CHANNEL_RESET(c->methods, c->devinfo); + if (r == 0 && fmt != 0 && spd != 0) { + r = chn_setparam(c, fmt, spd); + fmt = 0; + spd = 0; } + if (r == 0 && fmt != 0) + r = chn_setformat(c, fmt); + if (r == 0 && spd != 0) + r = chn_setspeed(c, spd); if (r == 0) r = chn_setlatency(c, chn_latency); if (r == 0) { @@ -1068,7 +1183,7 @@ chn_init(struct pcm_channel *c, void *devinfo, int dir, int direction) { struct feeder_class *fc; struct snd_dbuf *b, *bs; - int ret; + int i, ret; if (chn_timeout < CHN_TIMEOUT_MIN || chn_timeout > CHN_TIMEOUT_MAX) chn_timeout = CHN_TIMEOUT; @@ -1114,6 +1229,20 @@ chn_init(struct pcm_channel *c, void *devinfo, int dir, int direction) c->flags = 0; c->feederflags = 0; c->sm = NULL; + c->format = SND_FORMAT(AFMT_U8, 1, 0); + c->speed = DSP_DEFAULT_SPEED; + + 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++) { + 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; + + chn_vpc_reset(c, SND_VOL_C_PCM, 1); ret = ENODEV; CHN_UNLOCK(c); /* XXX - Unlock for CHANNEL_INIT() malloc() call */ @@ -1126,17 +1255,13 @@ chn_init(struct pcm_channel *c, void *devinfo, int dir, int direction) if ((sndbuf_getsize(b) == 0) && ((c->flags & CHN_F_VIRTUAL) == 0)) goto out; - ret = chn_setdir(c, direction); - if (ret) - goto out; - - ret = sndbuf_setfmt(b, AFMT_U8); - if (ret) - goto out; + ret = 0; + c->direction = direction; - ret = sndbuf_setfmt(bs, AFMT_U8); - if (ret) - goto out; + sndbuf_setfmt(b, c->format); + sndbuf_setspd(b, c->speed); + sndbuf_setfmt(bs, c->format); + sndbuf_setspd(bs, c->speed); /** * @todo Should this be moved somewhere else? The primary buffer @@ -1197,39 +1322,226 @@ chn_kill(struct pcm_channel *c) return (0); } +/* XXX Obsolete. Use *_matrix() variant instead. */ int -chn_setdir(struct pcm_channel *c, int dir) +chn_setvolume(struct pcm_channel *c, int left, int right) { -#ifdef DEV_ISA - struct snd_dbuf *b = c->bufhard; -#endif - int r; + int ret; + ret = chn_setvolume_matrix(c, SND_VOL_C_MASTER, SND_CHN_T_FL, left); + ret |= chn_setvolume_matrix(c, SND_VOL_C_MASTER, SND_CHN_T_FR, + right) << 8; + + return (ret); +} + +int +chn_setvolume_multi(struct pcm_channel *c, int vc, int left, int right, + int center) +{ + int i, ret; + + ret = 0; + + for (i = 0; i < SND_CHN_T_MAX; i++) { + if ((1 << i) & SND_CHN_LEFT_MASK) + ret |= chn_setvolume_matrix(c, vc, i, left); + else if ((1 << i) & SND_CHN_RIGHT_MASK) + ret |= chn_setvolume_matrix(c, vc, i, right) << 8; + else + ret |= chn_setvolume_matrix(c, vc, i, center) << 16; + } + + return (ret); +} + +int +chn_setvolume_matrix(struct pcm_channel *c, int vc, int vt, int val) +{ + int i; + + KASSERT(c != NULL && vc >= SND_VOL_C_MASTER && vc < SND_VOL_C_MAX && + (vc == SND_VOL_C_MASTER || (vc & 1)) && + (vt == SND_CHN_T_VOL_0DB || (vt >= SND_CHN_T_BEGIN && + vt <= SND_CHN_T_END)) && (vt != SND_CHN_T_VOL_0DB || + (val >= SND_VOL_0DB_MIN && val <= SND_VOL_0DB_MAX)), + ("%s(): invalid volume matrix c=%p vc=%d vt=%d val=%d", + __func__, c, vc, vt, val)); CHN_LOCKASSERT(c); - c->direction = dir; - r = CHANNEL_SETDIR(c->methods, c->devinfo, c->direction); -#ifdef DEV_ISA - if (!r && SND_DMA(b)) - sndbuf_dmasetdir(b, c->direction); -#endif - return r; + + if (val < 0) + val = 0; + if (val > 100) + val = 100; + + c->volume[vc][vt] = val; + + /* + * Do relative calculation here and store it into class + 1 + * to ease the job of feeder_volume. + */ + if (vc == SND_VOL_C_MASTER) { + for (vc = SND_VOL_C_BEGIN; vc <= SND_VOL_C_END; + vc += SND_VOL_C_STEP) + c->volume[SND_VOL_C_VAL(vc)][vt] = + SND_VOL_CALC_VAL(c->volume, vc, vt); + } else if (vc & 1) { + if (vt == SND_CHN_T_VOL_0DB) + for (i = SND_CHN_T_BEGIN; i <= SND_CHN_T_END; + i += SND_CHN_T_STEP) { + c->volume[SND_VOL_C_VAL(vc)][i] = + SND_VOL_CALC_VAL(c->volume, vc, i); + } + else + c->volume[SND_VOL_C_VAL(vc)][vt] = + SND_VOL_CALC_VAL(c->volume, vc, vt); + } + + return (val); } int -chn_setvolume(struct pcm_channel *c, int left, int right) +chn_getvolume_matrix(struct pcm_channel *c, int vc, int vt) { + KASSERT(c != NULL && vc >= SND_VOL_C_MASTER && vc < SND_VOL_C_MAX && + (vt == SND_CHN_T_VOL_0DB || + (vt >= SND_CHN_T_BEGIN && vt <= SND_CHN_T_END)), + ("%s(): invalid volume matrix c=%p vc=%d vt=%d", + __func__, c, vc, vt)); CHN_LOCKASSERT(c); - /* should add a feeder for volume changing if channel returns -1 */ - if (left > 100) - left = 100; - if (left < 0) - left = 0; - if (right > 100) - right = 100; - if (right < 0) - right = 0; - c->volume = left | (right << 8); - return 0; + + return (c->volume[vc][vt]); +} + +struct pcmchan_matrix * +chn_getmatrix(struct pcm_channel *c) +{ + + KASSERT(c != NULL, ("%s(): NULL channel", __func__)); + CHN_LOCKASSERT(c); + + if (!(c->format & AFMT_CONVERTIBLE)) + return (NULL); + + return (&c->matrix); +} + +int +chn_setmatrix(struct pcm_channel *c, struct pcmchan_matrix *m) +{ + + KASSERT(c != NULL && m != NULL, + ("%s(): NULL channel or matrix", __func__)); + CHN_LOCKASSERT(c); + + if (!(c->format & AFMT_CONVERTIBLE)) + return (EINVAL); + + c->matrix = *m; + c->matrix.id = SND_CHN_MATRIX_PCMCHANNEL; + + return (chn_setformat(c, SND_FORMAT(c->format, m->channels, m->ext))); +} + +/* + * XXX chn_oss_* exists for the sake of compatibility. + */ +int +chn_oss_getorder(struct pcm_channel *c, unsigned long long *map) +{ + + KASSERT(c != NULL && map != NULL, + ("%s(): NULL channel or map", __func__)); + CHN_LOCKASSERT(c); + + if (!(c->format & AFMT_CONVERTIBLE)) + return (EINVAL); + + return (feeder_matrix_oss_get_channel_order(&c->matrix, map)); +} + +int +chn_oss_setorder(struct pcm_channel *c, unsigned long long *map) +{ + struct pcmchan_matrix m; + int ret; + + KASSERT(c != NULL && map != NULL, + ("%s(): NULL channel or map", __func__)); + CHN_LOCKASSERT(c); + + if (!(c->format & AFMT_CONVERTIBLE)) + return (EINVAL); + + m = c->matrix; + ret = feeder_matrix_oss_set_channel_order(&m, map); + if (ret != 0) + return (ret); + + return (chn_setmatrix(c, &m)); +} + +#define SND_CHN_OSS_FRONT (SND_CHN_T_MASK_FL | SND_CHN_T_MASK_FR) +#define SND_CHN_OSS_SURR (SND_CHN_T_MASK_SL | SND_CHN_T_MASK_SR) +#define SND_CHN_OSS_CENTER_LFE (SND_CHN_T_MASK_FC | SND_CHN_T_MASK_LF) +#define SND_CHN_OSS_REAR (SND_CHN_T_MASK_BL | SND_CHN_T_MASK_BR) + +int +chn_oss_getmask(struct pcm_channel *c, uint32_t *retmask) +{ + struct pcmchan_matrix *m; + struct pcmchan_caps *caps; + uint32_t i, format; + + KASSERT(c != NULL && retmask != NULL, + ("%s(): NULL channel or retmask", __func__)); + CHN_LOCKASSERT(c); + + caps = chn_getcaps(c); + if (caps == NULL || caps->fmtlist == NULL) + return (ENODEV); + + for (i = 0; caps->fmtlist[i] != 0; i++) { + format = caps->fmtlist[i]; + if (!(format & AFMT_CONVERTIBLE)) { + *retmask |= DSP_BIND_SPDIF; + continue; + } + m = CHANNEL_GETMATRIX(c->methods, c->devinfo, format); + if (m == NULL) + continue; + if (m->mask & SND_CHN_OSS_FRONT) + *retmask |= DSP_BIND_FRONT; + if (m->mask & SND_CHN_OSS_SURR) + *retmask |= DSP_BIND_SURR; + if (m->mask & SND_CHN_OSS_CENTER_LFE) + *retmask |= DSP_BIND_CENTER_LFE; + if (m->mask & SND_CHN_OSS_REAR) + *retmask |= DSP_BIND_REAR; + } + + /* report software-supported binding mask */ + if (!CHN_BITPERFECT(c) && report_soft_matrix) + *retmask |= DSP_BIND_FRONT | DSP_BIND_SURR | + DSP_BIND_CENTER_LFE | DSP_BIND_REAR; + + return (0); +} + +void +chn_vpc_reset(struct pcm_channel *c, int vc, int force) +{ + int i; + + KASSERT(c != NULL && vc >= SND_VOL_C_BEGIN && vc <= SND_VOL_C_END, + ("%s(): invalid reset c=%p vc=%d", __func__, c, vc)); + CHN_LOCKASSERT(c); + + if (force == 0 && chn_vpc_autoreset == 0) + return; + + for (i = SND_CHN_T_BEGIN; i <= SND_CHN_T_END; i += SND_CHN_T_STEP) + CHN_SETVOLUME(c, vc, i, c->volume[vc][SND_CHN_T_VOL_0DB]); } static u_int32_t @@ -1380,7 +1692,7 @@ chn_calclatency(int dir, int latency, int bps, u_int32_t datarate, *rblksz = CHN_2NDBUFMAXSIZE >> 1; if (rblkcnt != NULL) *rblkcnt = 2; - printf("%s: FAILED dir=%d latency=%d bps=%d " + printf("%s(): FAILED dir=%d latency=%d bps=%d " "datarate=%u max=%u\n", __func__, dir, latency, bps, datarate, max); return CHN_2NDBUFMAXSIZE; @@ -1420,7 +1732,7 @@ chn_resizebuf(struct pcm_channel *c, int latency, CHN_LOCKASSERT(c); - if ((c->flags & (CHN_F_MAPPED | CHN_F_TRIGGERED)) || + if ((c->flags & (CHN_F_MMAP | CHN_F_TRIGGERED)) || !(c->direction == PCMDIR_PLAY || c->direction == PCMDIR_REC)) return EINVAL; @@ -1442,12 +1754,12 @@ chn_resizebuf(struct pcm_channel *c, int latency, b = c->bufhard; if (!(blksz == 0 || blkcnt == -1) && - (blksz < 16 || blksz < sndbuf_getbps(bs) || blkcnt < 2 || + (blksz < 16 || blksz < sndbuf_getalign(bs) || blkcnt < 2 || (blksz * blkcnt) > CHN_2NDBUFMAXSIZE)) return EINVAL; - chn_calclatency(c->direction, latency, sndbuf_getbps(bs), - sndbuf_getbps(bs) * sndbuf_getspd(bs), CHN_2NDBUFMAXSIZE, + chn_calclatency(c->direction, latency, sndbuf_getalign(bs), + sndbuf_getalign(bs) * sndbuf_getspd(bs), CHN_2NDBUFMAXSIZE, &sblksz, &sblkcnt); if (blksz == 0 || blkcnt == -1) { @@ -1468,7 +1780,7 @@ chn_resizebuf(struct pcm_channel *c, int latency, * defeat the purpose of having custom control. The least * we can do is round it to the nearest ^2 and align it. */ - sblksz = round_blksz(blksz, sndbuf_getbps(bs)); + sblksz = round_blksz(blksz, sndbuf_getalign(bs)); sblkcnt = round_pow2(blkcnt); limit = 0; } @@ -1487,17 +1799,17 @@ chn_resizebuf(struct pcm_channel *c, int latency, hblkcnt = 2; if (c->flags & CHN_F_HAS_SIZE) { hblksz = round_blksz(sndbuf_xbytes(sblksz, bs, b), - sndbuf_getbps(b)); + sndbuf_getalign(b)); hblkcnt = round_pow2(sndbuf_getblkcnt(bs)); } else chn_calclatency(c->direction, latency, - sndbuf_getbps(b), - sndbuf_getbps(b) * sndbuf_getspd(b), + sndbuf_getalign(b), + sndbuf_getalign(b) * sndbuf_getspd(b), CHN_2NDBUFMAXSIZE, &hblksz, &hblkcnt); if ((hblksz << 1) > sndbuf_getmaxsize(b)) hblksz = round_blksz(sndbuf_getmaxsize(b) >> 1, - sndbuf_getbps(b)); + sndbuf_getalign(b)); while ((hblksz * hblkcnt) > sndbuf_getmaxsize(b)) { if (hblkcnt < 4) @@ -1506,18 +1818,18 @@ chn_resizebuf(struct pcm_channel *c, int latency, hblkcnt >>= 1; } - hblksz -= hblksz % sndbuf_getbps(b); + hblksz -= hblksz % sndbuf_getalign(b); #if 0 hblksz = sndbuf_getmaxsize(b) >> 1; - hblksz -= hblksz % sndbuf_getbps(b); + hblksz -= hblksz % sndbuf_getalign(b); hblkcnt = 2; #endif CHN_UNLOCK(c); if (chn_usefrags == 0 || CHANNEL_SETFRAGMENTS(c->methods, c->devinfo, - hblksz, hblkcnt) < 1) + hblksz, hblkcnt) != 0) sndbuf_setblksz(b, CHANNEL_SETBLOCKSIZE(c->methods, c->devinfo, hblksz)); CHN_LOCK(c); @@ -1525,7 +1837,7 @@ chn_resizebuf(struct pcm_channel *c, int latency, if (!CHN_EMPTY(c, children)) { sblksz = round_blksz( sndbuf_xbytes(sndbuf_getsize(b) >> 1, b, bs), - sndbuf_getbps(bs)); + sndbuf_getalign(bs)); sblkcnt = 2; limit = 0; } else if (limit != 0) @@ -1535,7 +1847,7 @@ chn_resizebuf(struct pcm_channel *c, int latency, * Interrupt timeout */ c->timeout = ((u_int64_t)hz * sndbuf_getsize(b)) / - ((u_int64_t)sndbuf_getspd(b) * sndbuf_getbps(b)); + ((u_int64_t)sndbuf_getspd(b) * sndbuf_getalign(b)); if (c->timeout < 1) c->timeout = 1; } @@ -1561,14 +1873,14 @@ chn_resizebuf(struct pcm_channel *c, int latency, sblkcnt >>= 1; } - sblksz -= sblksz % sndbuf_getbps(bs); + sblksz -= sblksz % sndbuf_getalign(bs); if (sndbuf_getblkcnt(bs) != sblkcnt || sndbuf_getblksz(bs) != sblksz || sndbuf_getsize(bs) != (sblkcnt * sblksz)) { ret = sndbuf_remalloc(bs, sblkcnt, sblksz); if (ret != 0) { - printf("%s: Failed: %d %d\n", __func__, - sblkcnt, sblksz); + device_printf(c->dev, "%s(): Failed: %d %d\n", + __func__, sblkcnt, sblksz); return ret; } } @@ -1581,7 +1893,7 @@ chn_resizebuf(struct pcm_channel *c, int latency, chn_resetbuf(c); if (snd_verbose > 3) - printf("%s: %s (%s) timeout=%u " + device_printf(c->dev, "%s(): %s (%s) timeout=%u " "b[%d/%d/%d] bs[%d/%d/%d] limit=%d\n", __func__, CHN_DIRSTR(c), (c->flags & CHN_F_VIRTUAL) ? "virtual" : "hardware", @@ -1610,132 +1922,204 @@ chn_setblocksize(struct pcm_channel *c, int blkcnt, int blksz) return chn_resizebuf(c, -1, blkcnt, blksz); } -static int -chn_tryspeed(struct pcm_channel *c, int speed) +int +chn_setparam(struct pcm_channel *c, uint32_t format, uint32_t speed) { - struct pcm_feeder *f; - struct snd_dbuf *b = c->bufhard; - struct snd_dbuf *bs = c->bufsoft; - struct snd_dbuf *x; - int r, delta; + struct pcmchan_caps *caps; + uint32_t hwspeed, delta; + int ret; CHN_LOCKASSERT(c); - DEB(printf("setspeed, channel %s\n", c->name)); - DEB(printf("want speed %d, ", speed)); - if (speed <= 0) - return EINVAL; - if (CHN_STOPPED(c)) { - r = 0; - c->speed = speed; - sndbuf_setspd(bs, speed); - RANGE(speed, chn_getcaps(c)->minspeed, chn_getcaps(c)->maxspeed); - DEB(printf("try speed %d, ", speed)); - sndbuf_setspd(b, CHANNEL_SETSPEED(c->methods, c->devinfo, speed)); - DEB(printf("got speed %d\n", sndbuf_getspd(b))); - - delta = sndbuf_getspd(b) - sndbuf_getspd(bs); - if (delta < 0) - delta = -delta; - - c->feederflags &= ~(1 << FEEDER_RATE); - /* - * Used to be 500. It was too big! - */ - if (delta > feeder_rate_round) - c->feederflags |= 1 << FEEDER_RATE; - else - sndbuf_setspd(bs, sndbuf_getspd(b)); - r = chn_buildfeeder(c); - DEB(printf("r = %d\n", r)); - if (r) - goto out; + if (speed < 1 || format == 0 || CHN_STARTED(c)) + return (EINVAL); - if (!(c->feederflags & (1 << FEEDER_RATE))) - goto out; + c->format = format; + c->speed = speed; - r = EINVAL; - f = chn_findfeeder(c, FEEDER_RATE); - DEB(printf("feedrate = %p\n", f)); - if (f == NULL) - goto out; + caps = chn_getcaps(c); - x = (c->direction == PCMDIR_REC)? b : bs; - r = FEEDER_SET(f, FEEDRATE_SRC, sndbuf_getspd(x)); - DEB(printf("feeder_set(FEEDRATE_SRC, %d) = %d\n", sndbuf_getspd(x), r)); - if (r) - goto out; + hwspeed = speed; + RANGE(hwspeed, caps->minspeed, caps->maxspeed); - x = (c->direction == PCMDIR_REC)? bs : b; - r = FEEDER_SET(f, FEEDRATE_DST, sndbuf_getspd(x)); - DEB(printf("feeder_set(FEEDRATE_DST, %d) = %d\n", sndbuf_getspd(x), r)); -out: - if (!r) - r = CHANNEL_SETFORMAT(c->methods, c->devinfo, - sndbuf_getfmt(b)); - if (!r) - sndbuf_setfmt(bs, c->format); - if (!r) - r = chn_resizebuf(c, -2, 0, 0); - DEB(printf("setspeed done, r = %d\n", r)); - return r; - } else - return EINVAL; + sndbuf_setspd(c->bufhard, CHANNEL_SETSPEED(c->methods, c->devinfo, + hwspeed)); + hwspeed = sndbuf_getspd(c->bufhard); + + delta = (hwspeed > speed) ? (hwspeed - speed) : (speed - hwspeed); + + if (delta <= feeder_rate_round) + c->speed = hwspeed; + + ret = feeder_chain(c); + + if (ret == 0) + ret = CHANNEL_SETFORMAT(c->methods, c->devinfo, + sndbuf_getfmt(c->bufhard)); + + if (ret == 0) + ret = chn_resizebuf(c, -2, 0, 0); + + return (ret); } int -chn_setspeed(struct pcm_channel *c, int speed) +chn_setspeed(struct pcm_channel *c, uint32_t speed) { - int r, oldspeed = c->speed; + uint32_t oldformat, oldspeed, format; + int ret; + +#if 0 + /* XXX force 48k */ + if (c->format & AFMT_PASSTHROUGH) + speed = AFMT_PASSTHROUGH_RATE; +#endif - r = chn_tryspeed(c, speed); - if (r) { + oldformat = c->format; + oldspeed = c->speed; + format = oldformat; + + ret = chn_setparam(c, format, speed); + if (ret != 0) { if (snd_verbose > 3) - printf("Failed to set speed %d falling back to %d\n", - speed, oldspeed); - r = chn_tryspeed(c, oldspeed); + device_printf(c->dev, + "%s(): Setting speed %d failed, " + "falling back to %d\n", + __func__, speed, oldspeed); + chn_setparam(c, c->format, oldspeed); } - return r; + + return (ret); } -static int -chn_tryformat(struct pcm_channel *c, u_int32_t fmt) +int +chn_setformat(struct pcm_channel *c, uint32_t format) { - struct snd_dbuf *b = c->bufhard; - struct snd_dbuf *bs = c->bufsoft; - int r; + uint32_t oldformat, oldspeed, speed; + int ret; - CHN_LOCKASSERT(c); - if (CHN_STOPPED(c)) { - DEB(printf("want format %d\n", fmt)); - c->format = fmt; - r = chn_buildfeeder(c); - if (r == 0) { - sndbuf_setfmt(bs, c->format); - chn_resetbuf(c); - r = CHANNEL_SETFORMAT(c->methods, c->devinfo, sndbuf_getfmt(b)); - if (r == 0) - r = chn_tryspeed(c, c->speed); - } - return r; - } else - return EINVAL; + /* XXX force stereo */ + if (format & AFMT_PASSTHROUGH) + format = SND_FORMAT(format, AFMT_PASSTHROUGH_CHANNEL, + AFMT_PASSTHROUGH_EXTCHANNEL); + + oldformat = c->format; + oldspeed = c->speed; + speed = oldspeed; + + ret = chn_setparam(c, format, speed); + if (ret != 0) { + if (snd_verbose > 3) + device_printf(c->dev, + "%s(): Format change 0x%08x failed, " + "falling back to 0x%08x\n", + __func__, format, oldformat); + chn_setparam(c, oldformat, oldspeed); + } + + return (ret); } -int -chn_setformat(struct pcm_channel *c, u_int32_t fmt) +void +chn_syncstate(struct pcm_channel *c) { - u_int32_t oldfmt = c->format; - int r; + struct snddev_info *d; + struct snd_mixer *m; - r = chn_tryformat(c, fmt); - if (r) { - if (snd_verbose > 3) - printf("Format change 0x%08x failed, reverting to 0x%08x\n", - fmt, oldfmt); - chn_tryformat(c, oldfmt); + d = (c != NULL) ? c->parentsnddev : NULL; + m = (d != NULL && d->mixer_dev != NULL) ? d->mixer_dev->si_drv1 : + NULL; + + if (d == NULL || m == NULL) + return; + + CHN_LOCKASSERT(c); + + if (c->feederflags & (1 << FEEDER_VOLUME)) { + uint32_t parent; + int vol, pvol, left, right, center; + + if (c->direction == PCMDIR_PLAY && + (d->flags & SD_F_SOFTPCMVOL)) { + /* CHN_UNLOCK(c); */ + vol = mix_get(m, SOUND_MIXER_PCM); + parent = mix_getparent(m, SOUND_MIXER_PCM); + if (parent != SOUND_MIXER_NONE) + pvol = mix_get(m, parent); + else + pvol = 100 | (100 << 8); + /* CHN_LOCK(c); */ + } else { + vol = 100 | (100 << 8); + pvol = vol; + } + + if (vol == -1) { + device_printf(c->dev, + "Soft PCM Volume: Failed to read pcm " + "default value\n"); + vol = 100 | (100 << 8); + } + + if (pvol == -1) { + device_printf(c->dev, + "Soft PCM Volume: Failed to read parent " + "default value\n"); + pvol = 100 | (100 << 8); + } + + left = ((vol & 0x7f) * (pvol & 0x7f)) / 100; + right = (((vol >> 8) & 0x7f) * ((pvol >> 8) & 0x7f)) / 100; + center = (left + right) >> 1; + + chn_setvolume_multi(c, SND_VOL_C_MASTER, left, right, center); + } + + if (c->feederflags & (1 << FEEDER_EQ)) { + struct pcm_feeder *f; + int treble, bass, state; + + /* CHN_UNLOCK(c); */ + treble = mix_get(m, SOUND_MIXER_TREBLE); + bass = mix_get(m, SOUND_MIXER_BASS); + /* CHN_LOCK(c); */ + + if (treble == -1) + treble = 50; + else + treble = ((treble & 0x7f) + + ((treble >> 8) & 0x7f)) >> 1; + + if (bass == -1) + bass = 50; + else + bass = ((bass & 0x7f) + ((bass >> 8) & 0x7f)) >> 1; + + f = chn_findfeeder(c, FEEDER_EQ); + if (f != NULL) { + if (FEEDER_SET(f, FEEDEQ_TREBLE, treble) != 0) + device_printf(c->dev, + "EQ: Failed to set treble -- %d\n", + treble); + if (FEEDER_SET(f, FEEDEQ_BASS, bass) != 0) + device_printf(c->dev, + "EQ: Failed to set bass -- %d\n", + bass); + if (FEEDER_SET(f, FEEDEQ_PREAMP, d->eqpreamp) != 0) + device_printf(c->dev, + "EQ: Failed to set preamp -- %d\n", + d->eqpreamp); + if (d->flags & SD_F_EQ_BYPASSED) + state = FEEDEQ_BYPASS; + else if (d->flags & SD_F_EQ_ENABLED) + state = FEEDEQ_ENABLE; + else + state = FEEDEQ_DISABLE; + if (FEEDER_SET(f, FEEDEQ_STATE, state) != 0) + device_printf(c->dev, + "EQ: Failed to set state -- %d\n", state); + } } - return r; } int @@ -1772,10 +2156,11 @@ chn_trigger(struct pcm_channel *c, int go) if (c->trigger != PCMTRIG_START) { c->trigger = go; CHN_UNLOCK(c); - pcm_lock(d); + PCM_LOCK(d); CHN_INSERT_HEAD(d, c, channels.pcm.busy); - pcm_unlock(d); + PCM_UNLOCK(d); CHN_LOCK(c); + chn_syncstate(c); } break; case PCMTRIG_STOP: @@ -1788,9 +2173,9 @@ chn_trigger(struct pcm_channel *c, int go) if (c->trigger == PCMTRIG_START) { c->trigger = go; CHN_UNLOCK(c); - pcm_lock(d); + PCM_LOCK(d); CHN_REMOVE(d, c, channels.pcm.busy); - pcm_unlock(d); + PCM_UNLOCK(d); CHN_LOCK(c); } break; @@ -1819,7 +2204,7 @@ chn_getptr(struct pcm_channel *c) CHN_LOCKASSERT(c); hwptr = (CHN_STARTED(c)) ? CHANNEL_GETPTR(c->methods, c->devinfo) : 0; - return (hwptr - (hwptr % sndbuf_getbps(c->bufhard))); + return (hwptr - (hwptr % sndbuf_getalign(c->bufhard))); } struct pcmchan_caps * @@ -1841,238 +2226,20 @@ chn_getformats(struct pcm_channel *c) fmts |= fmtlist[i]; /* report software-supported formats */ - if (report_soft_formats) - fmts |= AFMT_MU_LAW|AFMT_A_LAW|AFMT_U32_LE|AFMT_U32_BE| - AFMT_S32_LE|AFMT_S32_BE|AFMT_U24_LE|AFMT_U24_BE| - AFMT_S24_LE|AFMT_S24_BE|AFMT_U16_LE|AFMT_U16_BE| - AFMT_S16_LE|AFMT_S16_BE|AFMT_U8|AFMT_S8; + if (!CHN_BITPERFECT(c) && report_soft_formats) + fmts |= AFMT_CONVERTIBLE; - return fmts; -} - -static int -chn_buildfeeder(struct pcm_channel *c) -{ - struct feeder_class *fc; - struct pcm_feederdesc desc; - struct snd_mixer *m; - u_int32_t tmp[2], type, flags, hwfmt, *fmtlist; - int err; - char fmtstr[AFMTSTR_MAXSZ]; - - CHN_LOCKASSERT(c); - while (chn_removefeeder(c) == 0) - ; - KASSERT((c->feeder == NULL), ("feeder chain not empty")); - - c->align = sndbuf_getalign(c->bufsoft); - - if (CHN_EMPTY(c, children) || c->direction == PCMDIR_REC) { - /* - * Virtual rec need this. - */ - fc = feeder_getclass(NULL); - KASSERT(fc != NULL, ("can't find root feeder")); - - err = chn_addfeeder(c, fc, NULL); - if (err) { - DEB(printf("can't add root feeder, err %d\n", err)); - - return err; - } - c->feeder->desc->out = c->format; - } else if (c->direction == PCMDIR_PLAY) { - if (c->flags & CHN_F_HAS_VCHAN) { - desc.type = FEEDER_MIXER; - desc.in = c->format; - } else { - DEB(printf("can't decide which feeder type to use!\n")); - return EOPNOTSUPP; - } - desc.out = c->format; - desc.flags = 0; - fc = feeder_getclass(&desc); - if (fc == NULL) { - DEB(printf("can't find vchan feeder\n")); - - return EOPNOTSUPP; - } - - err = chn_addfeeder(c, fc, &desc); - if (err) { - DEB(printf("can't add vchan feeder, err %d\n", err)); - - return err; - } - } else - return EOPNOTSUPP; - - /* XXX These are too much.. */ - if (c->parentsnddev != NULL && c->parentsnddev->mixer_dev != NULL && - c->parentsnddev->mixer_dev->si_drv1 != NULL) - m = c->parentsnddev->mixer_dev->si_drv1; - else - m = NULL; - - c->feederflags &= ~(1 << FEEDER_VOLUME); - if (c->direction == PCMDIR_PLAY && !(c->flags & CHN_F_VIRTUAL) && m && - (c->parentsnddev->flags & SD_F_SOFTPCMVOL)) - c->feederflags |= 1 << FEEDER_VOLUME; - - if (!(c->flags & CHN_F_VIRTUAL) && c->parentsnddev && - ((c->direction == PCMDIR_PLAY && - (c->parentsnddev->flags & SD_F_PSWAPLR)) || - (c->direction == PCMDIR_REC && - (c->parentsnddev->flags & SD_F_RSWAPLR)))) - c->feederflags |= 1 << FEEDER_SWAPLR; - - flags = c->feederflags; - fmtlist = chn_getcaps(c)->fmtlist; - - DEB(printf("feederflags %x\n", flags)); - - for (type = FEEDER_RATE; type < FEEDER_LAST; type++) { - if (flags & (1 << type)) { - desc.type = type; - desc.in = 0; - desc.out = 0; - desc.flags = 0; - DEB(printf("find feeder type %d, ", type)); - if (type == FEEDER_VOLUME || type == FEEDER_RATE) { - if (c->feeder->desc->out & AFMT_32BIT) - strlcpy(fmtstr,"s32le", sizeof(fmtstr)); - else if (c->feeder->desc->out & AFMT_24BIT) - strlcpy(fmtstr, "s24le", sizeof(fmtstr)); - else { - /* - * 8bit doesn't provide enough headroom - * for proper processing without - * creating too much noises. Force to - * 16bit instead. - */ - strlcpy(fmtstr, "s16le", sizeof(fmtstr)); - } - if (!(c->feeder->desc->out & AFMT_8BIT) && - c->feeder->desc->out & AFMT_BIGENDIAN) - afmtstr_swap_endian(fmtstr); - if (!(c->feeder->desc->out & (AFMT_A_LAW | AFMT_MU_LAW)) && - !(c->feeder->desc->out & AFMT_SIGNED)) - afmtstr_swap_sign(fmtstr); - desc.in = afmtstr2afmt(NULL, fmtstr, AFMTSTR_MONO_RETURN); - if (desc.in == 0) - desc.in = AFMT_S16_LE; - /* feeder_volume need stereo processing */ - if (type == FEEDER_VOLUME || - c->feeder->desc->out & AFMT_STEREO) - desc.in |= AFMT_STEREO; - desc.out = desc.in; - } else if (type == FEEDER_SWAPLR) { - desc.in = c->feeder->desc->out; - desc.in |= AFMT_STEREO; - desc.out = desc.in; - } - - fc = feeder_getclass(&desc); - DEB(printf("got %p\n", fc)); - if (fc == NULL) { - DEB(printf("can't find required feeder type %d\n", type)); - - return EOPNOTSUPP; - } - - if (desc.in == 0 || desc.out == 0) - desc = *fc->desc; - - DEB(printf("build fmtchain from 0x%08x to 0x%08x: ", c->feeder->desc->out, fc->desc->in)); - tmp[0] = desc.in; - tmp[1] = 0; - if (chn_fmtchain(c, tmp) == 0) { - DEB(printf("failed\n")); - - return ENODEV; - } - DEB(printf("ok\n")); - - err = chn_addfeeder(c, fc, &desc); - if (err) { - DEB(printf("can't add feeder %p, output 0x%x, err %d\n", fc, fc->desc->out, err)); - - return err; - } - DEB(printf("added feeder %p, output 0x%x\n", fc, c->feeder->desc->out)); - } - } - - if (c->direction == PCMDIR_REC) { - tmp[0] = c->format; - tmp[1] = 0; - hwfmt = chn_fmtchain(c, tmp); - } else - hwfmt = chn_fmtchain(c, fmtlist); - - if (hwfmt == 0 || !fmtvalid(hwfmt, fmtlist)) { - DEB(printf("Invalid hardware format: 0x%08x\n", hwfmt)); - return ENODEV; - } else if (c->direction == PCMDIR_REC && !CHN_EMPTY(c, children)) { - /* - * Kind of awkward. This whole "MIXER" concept need a - * rethinking, I guess :) . Recording is the inverse - * of Playback, which is why we push mixer vchan down here. - */ - if (c->flags & CHN_F_HAS_VCHAN) { - desc.type = FEEDER_MIXER; - desc.in = c->format; - } else - return EOPNOTSUPP; - desc.out = c->format; - desc.flags = 0; - fc = feeder_getclass(&desc); - if (fc == NULL) - return EOPNOTSUPP; - - err = chn_addfeeder(c, fc, &desc); - if (err != 0) - return err; - } - - sndbuf_setfmt(c->bufhard, hwfmt); - - if ((flags & (1 << FEEDER_VOLUME))) { - u_int32_t parent; - int vol, left, right; - - CHN_UNLOCK(c); - vol = mix_get(m, SOUND_MIXER_PCM); - if (vol == -1) { - device_printf(c->dev, - "Soft PCM Volume: Failed to read default value\n"); - vol = 100 | (100 << 8); - } - left = vol & 0x7f; - right = (vol >> 8) & 0x7f; - parent = mix_getparent(m, SOUND_MIXER_PCM); - if (parent != SOUND_MIXER_NONE) { - vol = mix_get(m, parent); - if (vol == -1) { - device_printf(c->dev, - "Soft Volume: Failed to read parent " - "default value\n"); - vol = 100 | (100 << 8); - } - left = (left * (vol & 0x7f)) / 100; - right = (right * ((vol >> 8) & 0x7f)) / 100; - } - CHN_LOCK(c); - chn_setvolume(c, left, right); - } - - return 0; + return (AFMT_ENCODING(fmts)); } int chn_notify(struct pcm_channel *c, u_int32_t flags) { - int err, run, nrun; + struct pcm_channel *ch; + struct pcmchan_caps *caps; + uint32_t bestformat, bestspeed, besthwformat, *vchanformat, *vchanrate; + uint32_t vpflags; + int dirty, err, run, nrun; CHN_LOCKASSERT(c); @@ -2090,26 +2257,188 @@ chn_notify(struct pcm_channel *c, u_int32_t flags) flags &= CHN_N_VOLUME | CHN_N_TRIGGER; if (flags & CHN_N_RATE) { - /* XXX I'll make good use of this someday. */ + /* + * XXX I'll make good use of this someday. + * However this is currently being superseded by + * the availability of CHN_F_VCHAN_DYNAMIC. + */ } + if (flags & CHN_N_FORMAT) { - /* XXX I'll make good use of this someday. */ + /* + * XXX I'll make good use of this someday. + * However this is currently being superseded by + * the availability of CHN_F_VCHAN_DYNAMIC. + */ } + if (flags & CHN_N_VOLUME) { - /* XXX I'll make good use of this someday. */ + /* + * XXX I'll make good use of this someday, though + * soft volume control is currently pretty much + * integrated. + */ } + if (flags & CHN_N_BLOCKSIZE) { /* * Set to default latency profile */ chn_setlatency(c, chn_latency); } - if (flags & CHN_N_TRIGGER) { + + if ((flags & CHN_N_TRIGGER) && !(c->flags & CHN_F_VCHAN_DYNAMIC)) { nrun = CHN_EMPTY(c, children.busy) ? 0 : 1; if (nrun && !run) err = chn_start(c, 1); if (!nrun && run) chn_abort(c); + flags &= ~CHN_N_TRIGGER; + } + + if (flags & CHN_N_TRIGGER) { + if (c->direction == PCMDIR_PLAY) { + vchanformat = &c->parentsnddev->pvchanformat; + vchanrate = &c->parentsnddev->pvchanrate; + } else { + vchanformat = &c->parentsnddev->rvchanformat; + vchanrate = &c->parentsnddev->rvchanrate; + } + + /* Dynamic Virtual Channel */ + if (!(c->flags & CHN_F_VCHAN_ADAPTIVE)) { + bestformat = *vchanformat; + bestspeed = *vchanrate; + } else { + bestformat = 0; + bestspeed = 0; + } + + besthwformat = 0; + nrun = 0; + caps = chn_getcaps(c); + dirty = 0; + vpflags = 0; + + CHN_FOREACH(ch, c, children.busy) { + CHN_LOCK(ch); + if ((ch->format & AFMT_PASSTHROUGH) && + snd_fmtvalid(ch->format, caps->fmtlist)) { + bestformat = ch->format; + bestspeed = ch->speed; + CHN_UNLOCK(ch); + vpflags = CHN_F_PASSTHROUGH; + nrun++; + break; + } + if ((ch->flags & CHN_F_EXCLUSIVE) && vpflags == 0) { + if (c->flags & CHN_F_VCHAN_ADAPTIVE) { + bestspeed = ch->speed; + RANGE(bestspeed, caps->minspeed, + caps->maxspeed); + besthwformat = snd_fmtbest(ch->format, + caps->fmtlist); + if (besthwformat != 0) + bestformat = besthwformat; + } + CHN_UNLOCK(ch); + vpflags = CHN_F_EXCLUSIVE; + nrun++; + continue; + } + if (!(c->flags & CHN_F_VCHAN_ADAPTIVE) || + vpflags != 0) { + CHN_UNLOCK(ch); + nrun++; + continue; + } + if (ch->speed > bestspeed) { + bestspeed = ch->speed; + RANGE(bestspeed, caps->minspeed, + caps->maxspeed); + } + besthwformat = snd_fmtbest(ch->format, caps->fmtlist); + if (!(besthwformat & AFMT_VCHAN)) { + CHN_UNLOCK(ch); + nrun++; + continue; + } + if (AFMT_CHANNEL(besthwformat) > + AFMT_CHANNEL(bestformat)) + bestformat = besthwformat; + else if (AFMT_CHANNEL(besthwformat) == + AFMT_CHANNEL(bestformat) && + AFMT_BIT(besthwformat) > AFMT_BIT(bestformat)) + bestformat = besthwformat; + CHN_UNLOCK(ch); + nrun++; + } + + if (bestformat == 0) + bestformat = c->format; + if (bestspeed == 0) + bestspeed = c->speed; + + if (bestformat != c->format || bestspeed != c->speed) + dirty = 1; + + c->flags &= ~(CHN_F_PASSTHROUGH | CHN_F_EXCLUSIVE); + c->flags |= vpflags; + + if (nrun && !run) { + if (dirty) { + bestspeed = CHANNEL_SETSPEED(c->methods, + c->devinfo, bestspeed); + err = chn_reset(c, bestformat, bestspeed); + } + if (err == 0 && dirty) { + CHN_FOREACH(ch, c, children.busy) { + CHN_LOCK(ch); + if (VCHAN_SYNC_REQUIRED(ch)) + vchan_sync(ch); + CHN_UNLOCK(ch); + } + } + if (err == 0) { + if (dirty) + c->flags |= CHN_F_DIRTY; + err = chn_start(c, 1); + } + } + + if (nrun && run && dirty) { + chn_abort(c); + bestspeed = CHANNEL_SETSPEED(c->methods, c->devinfo, + bestspeed); + err = chn_reset(c, bestformat, bestspeed); + if (err == 0) { + CHN_FOREACH(ch, c, children.busy) { + CHN_LOCK(ch); + if (VCHAN_SYNC_REQUIRED(ch)) + vchan_sync(ch); + CHN_UNLOCK(ch); + } + } + if (err == 0) { + c->flags |= CHN_F_DIRTY; + err = chn_start(c, 1); + } + } + + if (err == 0 && !(bestformat & AFMT_PASSTHROUGH) && + (bestformat & AFMT_VCHAN)) { + *vchanformat = bestformat; + *vchanrate = bestspeed; + } + + if (!nrun && run) { + c->flags &= ~(CHN_F_PASSTHROUGH | CHN_F_EXCLUSIVE); + bestformat = *vchanformat; + bestspeed = *vchanrate; + chn_abort(c); + if (c->format != bestformat || c->speed != bestspeed) + chn_reset(c, bestformat, bestspeed); + } } return (err); @@ -2186,18 +2515,6 @@ chn_syncdestroy(struct pcm_channel *c) return sg_id; } -void -chn_lock(struct pcm_channel *c) -{ - CHN_LOCK(c); -} - -void -chn_unlock(struct pcm_channel *c) -{ - CHN_UNLOCK(c); -} - #ifdef OSSV4_EXPERIMENT int chn_getpeaks(struct pcm_channel *c, int *lpeak, int *rpeak) diff --git a/sys/dev/sound/pcm/channel.h b/sys/dev/sound/pcm/channel.h index 8f9f8f6..6effec3 100644 --- a/sys/dev/sound/pcm/channel.h +++ b/sys/dev/sound/pcm/channel.h @@ -1,5 +1,7 @@ /*- - * Copyright (c) 1999 Cameron Grant <cg@freebsd.org> + * Copyright (c) 2005-2009 Ariff Abdullah <ariff@FreeBSD.org> + * Portions Copyright (c) Ryan Beasley <ryan.beasley@gmail.com> - GSoC 2006 + * Copyright (c) 1999 Cameron Grant <cg@FreeBSD.org> * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -32,6 +34,17 @@ struct pcmchan_caps { u_int32_t caps; }; +struct pcmchan_matrix { + int id; + uint8_t channels, ext; + struct { + int type; + uint32_t members; + } map[SND_CHN_T_MAX + 1]; + uint32_t mask; + int8_t offset[SND_CHN_T_MAX]; +}; + /* Forward declarations */ struct pcm_channel; struct pcmchan_syncgroup; @@ -63,7 +76,10 @@ struct pcmchan_syncmember { struct pcm_channel *ch; }; -#define CHN_NAMELEN 32 +#define CHN_NAMELEN 32 +#define CHN_COMM_UNUSED "<UNUSED>" +#define CHN_COMM_UNKNOWN "<UNKNOWN>" + struct pcm_channel { kobj_t methods; @@ -72,13 +88,12 @@ struct pcm_channel { struct pcm_feeder *feeder; u_int32_t align; - int volume; int latency; u_int32_t speed; u_int32_t format; u_int32_t flags; u_int32_t feederflags; - u_int32_t blocks; + u_int64_t blocks; int direction; unsigned int interrupts, xruns, feedcount; @@ -90,6 +105,7 @@ struct pcm_channel { device_t dev; int unit; char name[CHN_NAMELEN]; + char comm[MAXCOMLEN + 1]; struct mtx *lock; int trigger; /** @@ -139,9 +155,16 @@ struct pcm_channel { struct { SLIST_ENTRY(pcm_channel) link; } busy; + struct { + SLIST_ENTRY(pcm_channel) link; + } opened; } pcm; } channels; + struct pcmchan_matrix matrix; + + int volume[SND_VOL_C_MAX][SND_CHN_T_VOL_MAX]; + void *data1, *data2; }; @@ -172,10 +195,9 @@ struct pcm_channel { if (t == y) \ break; \ } \ - if (t != y) { \ + if (t != y) \ CHN_INSERT_HEAD(x, y, z); \ - } \ -} while(0) +} while (0) #define CHN_INSERT_AFTER_SAFE(w, x, y, z) do { \ struct pcm_channel *t = NULL; \ @@ -183,10 +205,9 @@ struct pcm_channel { if (t == y) \ break; \ } \ - if (t != y) { \ + if (t != y) \ CHN_INSERT_AFTER(x, y, z); \ - } \ -} while(0) +} while (0) #define CHN_REMOVE_SAFE(x, y, z) do { \ struct pcm_channel *t = NULL; \ @@ -194,10 +215,26 @@ struct pcm_channel { if (t == y) \ break; \ } \ - if (t == y) { \ + if (t == y) \ CHN_REMOVE(x, y, z); \ +} while (0) + +#define CHN_INSERT_SORT(w, x, y, z) do { \ + struct pcm_channel *t, *a = NULL; \ + CHN_FOREACH(t, x, z) { \ + if ((y)->unit w t->unit) \ + a = t; \ + else \ + break; \ } \ -} while(0) + if (a != NULL) \ + CHN_INSERT_AFTER(a, y, z); \ + else \ + 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_DESCEND(x, y, z) CHN_INSERT_SORT(<, x, y, z) #define CHN_UNIT(x) (snd_unit2u((x)->unit)) #define CHN_DEV(x) (snd_unit2d((x)->unit)) @@ -211,7 +248,7 @@ struct pcm_channel { #define CHN_BROADCAST(x) do { \ if ((x)->cv_waiters != 0) \ cv_broadcastpri(x, PRIBIO); \ -} while(0) +} while (0) #include "channel_if.h" @@ -225,79 +262,72 @@ 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); int chn_kill(struct pcm_channel *c); -int chn_setdir(struct pcm_channel *c, int dir); -int chn_reset(struct pcm_channel *c, u_int32_t fmt); +int chn_reset(struct pcm_channel *c, u_int32_t fmt, u_int32_t spd); int chn_setvolume(struct pcm_channel *c, int left, int right); -int chn_setspeed(struct pcm_channel *c, int speed); -int chn_setformat(struct pcm_channel *c, u_int32_t fmt); +int chn_setvolume_multi(struct pcm_channel *c, int vc, int left, int right, + int center); +int chn_setvolume_matrix(struct pcm_channel *c, int vc, int vt, int val); +int chn_getvolume_matrix(struct pcm_channel *c, int vc, int vt); +void chn_vpc_reset(struct pcm_channel *c, int vc, int force); +int chn_setparam(struct pcm_channel *c, uint32_t format, uint32_t speed); +int chn_setspeed(struct pcm_channel *c, uint32_t speed); +int chn_setformat(struct pcm_channel *c, uint32_t format); int chn_setblocksize(struct pcm_channel *c, int blkcnt, int blksz); int chn_setlatency(struct pcm_channel *c, int latency); +void chn_syncstate(struct pcm_channel *c); int chn_trigger(struct pcm_channel *c, int go); int chn_getptr(struct pcm_channel *c); struct pcmchan_caps *chn_getcaps(struct pcm_channel *c); u_int32_t chn_getformats(struct pcm_channel *c); +struct pcmchan_matrix *chn_getmatrix(struct pcm_channel *); +int chn_setmatrix(struct pcm_channel *, struct pcmchan_matrix *); + +int chn_oss_getorder(struct pcm_channel *, unsigned long long *); +int chn_oss_setorder(struct pcm_channel *, unsigned long long *); +int chn_oss_getmask(struct pcm_channel *, uint32_t *); + void chn_resetbuf(struct pcm_channel *c); +void chn_intr_locked(struct pcm_channel *c); void chn_intr(struct pcm_channel *c); -int chn_wrfeed(struct pcm_channel *c); -int chn_rdfeed(struct pcm_channel *c); int chn_abort(struct pcm_channel *c); -void chn_wrupdate(struct pcm_channel *c); -void chn_rdupdate(struct pcm_channel *c); - int chn_notify(struct pcm_channel *c, u_int32_t flags); -void chn_lock(struct pcm_channel *c); -void chn_unlock(struct pcm_channel *c); int chn_getrates(struct pcm_channel *c, int **rates); int chn_syncdestroy(struct pcm_channel *c); -#ifdef OSSV4_EXPERIMENT -int chn_getpeaks(struct pcm_channel *c, int *lpeak, int *rpeak); +#define CHN_SETVOLUME(...) chn_setvolume_matrix(__VA_ARGS__) +#if defined(SND_DIAGNOSTIC) || defined(INVARIANTS) +#define CHN_GETVOLUME(...) chn_getvolume_matrix(__VA_ARGS__) +#else +#define CHN_GETVOLUME(x, y, z) ((x)->volume[y][z]) #endif -#ifdef USING_MUTEX -#define CHN_LOCK_OWNED(c) mtx_owned((struct mtx *)((c)->lock)) -#define CHN_LOCK(c) mtx_lock((struct mtx *)((c)->lock)) -#define CHN_UNLOCK(c) mtx_unlock((struct mtx *)((c)->lock)) -#define CHN_TRYLOCK(c) mtx_trylock((struct mtx *)((c)->lock)) -#define CHN_LOCKASSERT(c) mtx_assert((struct mtx *)((c)->lock), MA_OWNED) -#else -#define CHN_LOCK_OWNED(c) 0 -#define CHN_LOCK(c) -#define CHN_UNLOCK(c) -#define CHN_TRYLOCK(c) -#define CHN_LOCKASSERT(c) +#ifdef OSSV4_EXPERIMENT +int chn_getpeaks(struct pcm_channel *c, int *lpeak, int *rpeak); #endif -int fmtvalid(u_int32_t fmt, u_int32_t *fmtlist); +#define CHN_LOCKOWNED(c) mtx_owned((c)->lock) +#define CHN_LOCK(c) mtx_lock((c)->lock) +#define CHN_UNLOCK(c) mtx_unlock((c)->lock) +#define CHN_TRYLOCK(c) mtx_trylock((c)->lock) +#define CHN_LOCKASSERT(c) mtx_assert((c)->lock, MA_OWNED) +#define CHN_UNLOCKASSERT(c) mtx_assert((c)->lock, MA_NOTOWNED) -#define AFMTSTR_NONE 0 /* "s16le" */ -#define AFMTSTR_SIMPLE 1 /* "s16le:s" */ -#define AFMTSTR_NUM 2 /* "s16le:2" */ -#define AFMTSTR_FULL 3 /* "s16le:stereo" */ +int snd_fmtvalid(uint32_t fmt, uint32_t *fmtlist); -#define AFMTSTR_MAXSZ 13 /* include null terminator */ +uint32_t snd_str2afmt(const char *); +uint32_t snd_afmt2str(uint32_t, char *, size_t); -#define AFMTSTR_MONO_RETURN 0 -#define AFMTSTR_STEREO_RETURN 1 +#define AFMTSTR_LEN 16 -struct afmtstr_table { - char *fmtstr; - u_int32_t format; -}; - -int afmtstr_swap_sign(char *); -int afmtstr_swap_endian(char *); -u_int32_t afmtstr2afmt(struct afmtstr_table *, const char *, int); -u_int32_t afmt2afmtstr(struct afmtstr_table *, u_int32_t, char *, size_t, int, int); extern int chn_latency; extern int chn_latency_profile; extern int report_soft_formats; +extern int report_soft_matrix; -#define PCMDIR_FAKE 0 #define PCMDIR_PLAY 1 #define PCMDIR_PLAY_VIRTUAL 2 #define PCMDIR_REC -1 @@ -313,26 +343,60 @@ extern int report_soft_formats; (x) == PCMTRIG_STOP || \ (x) == PCMTRIG_ABORT) -#define CHN_F_CLOSING 0x00000004 /* a pending close */ -#define CHN_F_ABORTING 0x00000008 /* a pending abort */ -#define CHN_F_RUNNING 0x00000010 /* dma is running */ -#define CHN_F_TRIGGERED 0x00000020 -#define CHN_F_NOTRIGGER 0x00000040 -#define CHN_F_SLEEPING 0x00000080 - -#define CHN_F_BUSY 0x00001000 /* has been opened */ -#define CHN_F_HAS_SIZE 0x00002000 /* user set block size */ -#define CHN_F_NBIO 0x00004000 /* do non-blocking i/o */ -#define CHN_F_MAPPED 0x00010000 /* has been mmap()ed */ -#define CHN_F_DEAD 0x00020000 -#define CHN_F_BADSETTING 0x00040000 -#define CHN_F_SETBLOCKSIZE 0x00080000 -#define CHN_F_HAS_VCHAN 0x00100000 +#define CHN_F_CLOSING 0x00000001 /* a pending close */ +#define CHN_F_ABORTING 0x00000002 /* a pending abort */ +#define CHN_F_RUNNING 0x00000004 /* dma is running */ +#define CHN_F_TRIGGERED 0x00000008 +#define CHN_F_NOTRIGGER 0x00000010 +#define CHN_F_SLEEPING 0x00000020 + +#define CHN_F_NBIO 0x00000040 /* do non-blocking i/o */ +#define CHN_F_MMAP 0x00000080 /* has been mmap()ed */ + +#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 */ + +#define CHN_F_HAS_SIZE 0x00001000 /* user set block size */ +#define CHN_F_HAS_VCHAN 0x00002000 /* vchan master */ + +#define CHN_F_VCHAN_PASSTHROUGH 0x00004000 /* digital ac3/dts passthrough */ +#define CHN_F_VCHAN_ADAPTIVE 0x00008000 /* adaptive format/rate selection */ +#define CHN_F_VCHAN_DYNAMIC (CHN_F_VCHAN_PASSTHROUGH | CHN_F_VCHAN_ADAPTIVE) #define CHN_F_VIRTUAL 0x10000000 /* not backed by hardware */ +#define CHN_F_BITPERFECT 0x20000000 /* un-cooked, Heh.. */ +#define CHN_F_PASSTHROUGH 0x40000000 /* passthrough re-config */ +#define CHN_F_EXCLUSIVE 0x80000000 /* exclusive access */ + +#define CHN_F_BITS "\020" \ + "\001CLOSING" \ + "\002ABORTING" \ + "\003RUNNING" \ + "\004TRIGGERED" \ + "\005NOTRIGGER" \ + "\006SLEEPING" \ + "\007NBIO" \ + "\010MMAP" \ + "\011BUSY" \ + "\012DIRTY" \ + "\013DEAD" \ + "\014SILENCE" \ + "\015HAS_SIZE" \ + "\016HAS_VCHAN" \ + "\017VCHAN_PASSTHROUGH" \ + "\020VCHAN_ADAPTIVE" \ + "\035VIRTUAL" \ + "\036BITPERFECT" \ + "\037PASSTHROUGH" \ + "\040EXCLUSIVE" + #define CHN_F_RESET (CHN_F_BUSY | CHN_F_DEAD | \ - CHN_F_HAS_VCHAN | CHN_F_VIRTUAL) + CHN_F_VIRTUAL | CHN_F_HAS_VCHAN | \ + CHN_F_VCHAN_DYNAMIC | \ + CHN_F_PASSTHROUGH | CHN_F_EXCLUSIVE) #define CHN_F_MMAP_INVALID (CHN_F_DEAD | CHN_F_RUNNING) @@ -359,6 +423,8 @@ extern int report_soft_formats; #define CHN_STOPPED(c) (!CHN_STARTED(c)) #define CHN_DIRSTR(c) (((c)->direction == PCMDIR_PLAY) ? \ "PCMDIR_PLAY" : "PCMDIR_REC") +#define CHN_BITPERFECT(c) ((c)->flags & CHN_F_BITPERFECT) +#define CHN_PASSTHROUGH(c) ((c)->flags & CHN_F_PASSTHROUGH) #define CHN_TIMEOUT 5 #define CHN_TIMEOUT_MIN 1 @@ -375,4 +441,15 @@ extern int report_soft_formats; /* The size of a whole secondary bufhard. */ #define CHN_2NDBUFMAXSIZE (131072) +#ifdef SND_DEBUG +#define CHANNEL_DECLARE(channel) \ + static struct kobj_class channel##_class = { \ + .name = #channel, \ + .methods = channel##_methods, \ + .size = sizeof(struct kobj), \ + .baseclasses = NULL, \ + .refs = 0 \ + } +#else #define CHANNEL_DECLARE(name) static DEFINE_CLASS(name, name ## _methods, sizeof(struct kobj)) +#endif diff --git a/sys/dev/sound/pcm/channel_if.m b/sys/dev/sound/pcm/channel_if.m index 3429a33..c5c4fff 100644 --- a/sys/dev/sound/pcm/channel_if.m +++ b/sys/dev/sound/pcm/channel_if.m @@ -1,7 +1,9 @@ #- # KOBJ # -# Copyright (c) 2000 Cameron Grant <cg@freebsd.org> +# Copyright (c) 2005-2009 Ariff Abdullah <ariff@FreeBSD.org> +# Portions Copyright (c) Ryan Beasley <ryan.beasley@gmail.com> - GSoC 2006 +# Copyright (c) 2000 Cameron Grant <cg@FreeBSD.org> # All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -35,12 +37,6 @@ INTERFACE channel; CODE { static int - channel_nosetdir(kobj_t obj, void *data, int dir) - { - return 0; - } - - static int channel_noreset(kobj_t obj, void *data) { return 0; @@ -86,9 +82,21 @@ CODE { static int channel_nosetfragments(kobj_t obj, void *data, u_int32_t blocksize, u_int32_t blockcount) { - return 0; + return ENOTSUP; } + static struct pcmchan_matrix * + channel_nogetmatrix(kobj_t obj, void *data, u_int32_t format) + { + format = feeder_matrix_default_format(format); + return (feeder_matrix_format_map(format)); + } + + static int + channel_nosetmatrix(kobj_t obj, void *data, struct pcmchan_matrix *m) + { + return ENOTSUP; + } }; METHOD void* init { @@ -114,13 +122,7 @@ METHOD int resetdone { void *data; } DEFAULT channel_noresetdone; -METHOD int setdir { - kobj_t obj; - void *data; - int dir; -} DEFAULT channel_nosetdir; - -METHOD u_int32_t setformat { +METHOD int setformat { kobj_t obj; void *data; u_int32_t format; @@ -217,3 +219,15 @@ METHOD int getrates { void *data; int **rates; } DEFAULT channel_nogetrates; + +METHOD struct pcmchan_matrix * getmatrix { + kobj_t obj; + void *data; + u_int32_t format; +} DEFAULT channel_nogetmatrix; + +METHOD int setmatrix { + kobj_t obj; + void *data; + struct pcmchan_matrix *m; +} DEFAULT channel_nosetmatrix; diff --git a/sys/dev/sound/pcm/dsp.c b/sys/dev/sound/pcm/dsp.c index 56c1aaf..66faef1 100644 --- a/sys/dev/sound/pcm/dsp.c +++ b/sys/dev/sound/pcm/dsp.c @@ -1,5 +1,7 @@ /*- - * Copyright (c) 1999 Cameron Grant <cg@freebsd.org> + * Copyright (c) 2005-2009 Ariff Abdullah <ariff@FreeBSD.org> + * Portions Copyright (c) Ryan Beasley <ryan.beasley@gmail.com> - GSoC 2006 + * Copyright (c) 1999 Cameron Grant <cg@FreeBSD.org> * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,23 +26,31 @@ * SUCH DAMAGE. */ +#ifdef HAVE_KERNEL_OPTION_HEADERS +#include "opt_snd.h" +#endif + #include <dev/sound/pcm/sound.h> #include <sys/ctype.h> +#include <sys/sysent.h> SND_DECLARE_FILE("$FreeBSD$"); static int dsp_mmap_allow_prot_exec = 0; SYSCTL_INT(_hw_snd, OID_AUTO, compat_linux_mmap, CTLFLAG_RW, - &dsp_mmap_allow_prot_exec, 0, "linux mmap compatibility"); + &dsp_mmap_allow_prot_exec, 0, + "linux mmap compatibility (-1=force disable 0=auto 1=force enable)"); struct dsp_cdevinfo { struct pcm_channel *rdch, *wrch; + struct pcm_channel *volch; int busy, simplex; TAILQ_ENTRY(dsp_cdevinfo) link; }; #define PCM_RDCH(x) (((struct dsp_cdevinfo *)(x)->si_drv1)->rdch) #define PCM_WRCH(x) (((struct dsp_cdevinfo *)(x)->si_drv1)->wrch) +#define PCM_VOLCH(x) (((struct dsp_cdevinfo *)(x)->si_drv1)->volch) #define PCM_SIMPLEX(x) (((struct dsp_cdevinfo *)(x)->si_drv1)->simplex) #define DSP_CDEVINFO_CACHESIZE 8 @@ -70,19 +80,18 @@ struct cdevsw dsp_cdevsw = { .d_name = "dsp", }; -#ifdef USING_DEVFS static eventhandler_tag dsp_ehtag = NULL; static int dsp_umax = -1; static int dsp_cmax = -1; -#endif static int dsp_oss_syncgroup(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_syncgroup *group); static int dsp_oss_syncstart(int sg_id); static int dsp_oss_policy(struct pcm_channel *wrch, struct pcm_channel *rdch, int policy); -#ifdef OSSV4_EXPERIMENT static int dsp_oss_cookedmode(struct pcm_channel *wrch, struct pcm_channel *rdch, int enabled); static int dsp_oss_getchnorder(struct pcm_channel *wrch, struct pcm_channel *rdch, unsigned long long *map); static int dsp_oss_setchnorder(struct pcm_channel *wrch, struct pcm_channel *rdch, unsigned long long *map); +static int dsp_oss_getchannelmask(struct pcm_channel *wrch, struct pcm_channel *rdch, int *mask); +#ifdef OSSV4_EXPERIMENT static int dsp_oss_getlabel(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_label_t *label); static int dsp_oss_setlabel(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_label_t *label); static int dsp_oss_getsong(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_longname_t *song); @@ -133,7 +142,7 @@ getchns(struct cdev *dev, struct pcm_channel **rdch, struct pcm_channel **wrch, d = dsp_get_info(dev); if (!PCM_REGISTERED(d)) return (ENXIO); - pcm_lock(d); + PCM_LOCK(d); PCM_WAIT(d); PCM_ACQUIRE(d); /* @@ -173,7 +182,7 @@ getchns(struct cdev *dev, struct pcm_channel **rdch, struct pcm_channel **wrch, pcm_chnrelease(ch); } PCM_RELEASE(d); - pcm_unlock(d); + PCM_UNLOCK(d); } *rdch = PCM_RDCH(dev); @@ -200,7 +209,8 @@ relchns(struct cdev *dev, struct pcm_channel *rdch, struct pcm_channel *wrch, static void dsp_cdevinfo_alloc(struct cdev *dev, - struct pcm_channel *rdch, struct pcm_channel *wrch) + struct pcm_channel *rdch, struct pcm_channel *wrch, + struct pcm_channel *volch) { struct snddev_info *d; struct dsp_cdevinfo *cdi; @@ -209,10 +219,10 @@ dsp_cdevinfo_alloc(struct cdev *dev, d = dsp_get_info(dev); KASSERT(PCM_REGISTERED(d) && dev != NULL && dev->si_drv1 == NULL && - rdch != wrch, + ((rdch == NULL && wrch == NULL) || rdch != wrch), ("bogus %s(), what are you trying to accomplish here?", __func__)); PCM_BUSYASSERT(d); - mtx_assert(d->lock, MA_OWNED); + PCM_LOCKASSERT(d); simplex = (dsp_get_flags(dev) & SD_F_SIMPLEX) ? 1 : 0; @@ -225,6 +235,7 @@ dsp_cdevinfo_alloc(struct cdev *dev, break; cdi->rdch = rdch; cdi->wrch = wrch; + cdi->volch = volch; cdi->simplex = simplex; cdi->busy = 1; TAILQ_REMOVE(&d->dsp_cdevinfo_pool, cdi, link); @@ -232,11 +243,12 @@ dsp_cdevinfo_alloc(struct cdev *dev, dev->si_drv1 = cdi; return; } - pcm_unlock(d); + PCM_UNLOCK(d); cdi = malloc(sizeof(*cdi), M_DEVBUF, M_WAITOK | M_ZERO); - pcm_lock(d); + PCM_LOCK(d); cdi->rdch = rdch; cdi->wrch = wrch; + cdi->volch = volch; cdi->simplex = simplex; cdi->busy = 1; TAILQ_INSERT_TAIL(&d->dsp_cdevinfo_pool, cdi, link); @@ -254,15 +266,17 @@ dsp_cdevinfo_free(struct cdev *dev) d = dsp_get_info(dev); KASSERT(PCM_REGISTERED(d) && dev != NULL && dev->si_drv1 != NULL && - PCM_RDCH(dev) == NULL && PCM_WRCH(dev) == NULL, + PCM_RDCH(dev) == NULL && PCM_WRCH(dev) == NULL && + PCM_VOLCH(dev) == NULL, ("bogus %s(), what are you trying to accomplish here?", __func__)); PCM_BUSYASSERT(d); - mtx_assert(d->lock, MA_OWNED); + PCM_LOCKASSERT(d); cdi = dev->si_drv1; dev->si_drv1 = NULL; cdi->rdch = NULL; cdi->wrch = NULL; + cdi->volch = NULL; cdi->simplex = 0; cdi->busy = 0; @@ -306,7 +320,7 @@ dsp_cdevinfo_init(struct snddev_info *d) KASSERT(d != NULL, ("NULL snddev_info")); PCM_BUSYASSERT(d); - mtx_assert(d->lock, MA_NOTOWNED); + PCM_UNLOCKASSERT(d); TAILQ_INIT(&d->dsp_cdevinfo_pool); for (i = 0; i < DSP_CDEVINFO_CACHESIZE; i++) { @@ -322,7 +336,7 @@ dsp_cdevinfo_flush(struct snddev_info *d) KASSERT(d != NULL, ("NULL snddev_info")); PCM_BUSYASSERT(d); - mtx_assert(d->lock, MA_NOTOWNED); + PCM_UNLOCKASSERT(d); cdi = TAILQ_FIRST(&d->dsp_cdevinfo_pool); while (cdi != NULL) { @@ -337,7 +351,13 @@ dsp_cdevinfo_flush(struct snddev_info *d) 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 */ + DSP_CDEV_TYPE_RDWR /* duplex read, write, or both */ +}; + +enum { + DSP_CDEV_VOLCTL_NONE, + DSP_CDEV_VOLCTL_READ, + DSP_CDEV_VOLCTL_WRITE }; #define DSP_F_VALID(x) ((x) & (FREAD | FWRITE)) @@ -350,30 +370,49 @@ 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", ".", 0, 0, 0, - AFMT_U8, DSP_DEFAULT_SPEED, DSP_CDEV_TYPE_RDWR }, - { SND_DEV_AUDIO, "audio", ".", 0, 0, 0, - AFMT_MU_LAW, DSP_DEFAULT_SPEED, DSP_CDEV_TYPE_RDWR }, - { SND_DEV_DSP16, "dspW", ".", 0, 0, 0, - AFMT_S16_LE, DSP_DEFAULT_SPEED, DSP_CDEV_TYPE_RDWR }, - { SND_DEV_DSPHW_PLAY, "dsp", ".p", 1, 1, SND_MAXHWCHAN, - AFMT_S16_LE | AFMT_STEREO, 48000, DSP_CDEV_TYPE_WRONLY }, - { SND_DEV_DSPHW_VPLAY, "dsp", ".vp", 1, 1, SND_MAXVCHANS, - AFMT_S16_LE | AFMT_STEREO, 48000, DSP_CDEV_TYPE_WRONLY }, - { SND_DEV_DSPHW_REC, "dsp", ".r", 1, 1, SND_MAXHWCHAN, - AFMT_S16_LE | AFMT_STEREO, 48000, DSP_CDEV_TYPE_RDONLY }, - { SND_DEV_DSPHW_VREC, "dsp", ".vr", 1, 1, SND_MAXVCHANS, - AFMT_S16_LE | AFMT_STEREO, 48000, DSP_CDEV_TYPE_RDONLY }, - { SND_DEV_DSPHW_CD, "dspcd", ".", 0, 0, 0, - AFMT_S16_LE | AFMT_STEREO, 44100, DSP_CDEV_TYPE_RDWR }, - { SND_DEV_DSP_MMAP, "dsp_mmap", ".", 0, 0, 0, - AFMT_S16_LE | AFMT_STEREO, 48000, DSP_CDEV_TYPE_RDWR }, + { 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 }, }; #define DSP_FIXUP_ERROR() do { \ @@ -390,14 +429,14 @@ static const struct { error = EBUSY; \ else if (DSP_REGISTERED(d, i_dev)) \ error = EBUSY; \ -} while(0) +} while (0) static int dsp_open(struct cdev *i_dev, int flags, int mode, struct thread *td) { struct pcm_channel *rdch, *wrch; struct snddev_info *d; - uint32_t fmt, spd, prio; + uint32_t fmt, spd, prio, volctl; int i, error, rderror, wrerror, devtype, wdevunit, rdevunit; /* Kind of impossible.. */ @@ -411,7 +450,7 @@ dsp_open(struct cdev *i_dev, int flags, int mode, struct thread *td) PCM_GIANT_ENTER(d); /* Lock snddev so nobody else can monkey with it. */ - pcm_lock(d); + PCM_LOCK(d); PCM_WAIT(d); /* @@ -421,7 +460,7 @@ dsp_open(struct cdev *i_dev, int flags, int mode, struct thread *td) error = snd_clone_acquire(i_dev); if (!(error == 0 || error == ENODEV)) { DSP_FIXUP_ERROR(); - pcm_unlock(d); + PCM_UNLOCK(d); PCM_GIANT_EXIT(d); return (error); } @@ -431,7 +470,7 @@ dsp_open(struct cdev *i_dev, int flags, int mode, struct thread *td) if (error != 0) { (void)snd_clone_release(i_dev); - pcm_unlock(d); + PCM_UNLOCK(d); PCM_GIANT_EXIT(d); return (error); } @@ -442,34 +481,53 @@ dsp_open(struct cdev *i_dev, int flags, int mode, struct thread *td) * everything. */ PCM_ACQUIRE(d); - pcm_unlock(d); + PCM_UNLOCK(d); devtype = PCMDEV(i_dev); wdevunit = -1; rdevunit = -1; fmt = 0; spd = 0; + volctl = DSP_CDEV_VOLCTL_NONE; for (i = 0; i < (sizeof(dsp_cdevs) / sizeof(dsp_cdevs[0])); i++) { - if (devtype != dsp_cdevs[i].type) + if (devtype != dsp_cdevs[i].type || dsp_cdevs[i].alias != NULL) continue; - if (DSP_F_SIMPLEX(flags) && - ((dsp_cdevs[i].query == DSP_CDEV_TYPE_WRONLY && - DSP_F_READ(flags)) || - (dsp_cdevs[i].query == DSP_CDEV_TYPE_RDONLY && - DSP_F_WRITE(flags)))) { - /* - * simplex, opposite direction? Please be gone.. - */ - (void)snd_clone_release(i_dev); - PCM_RELEASE_QUICK(d); - PCM_GIANT_EXIT(d); - return (ENOTSUP); - } - if (dsp_cdevs[i].query == DSP_CDEV_TYPE_WRONLY) + /* + * Volume control only valid for DSPHW devices, + * and it must be opened in opposite direction be it + * simplex or duplex. Anything else will be handled + * as usual. + */ + if (dsp_cdevs[i].query == DSP_CDEV_TYPE_WRONLY) { + if (dsp_cdevs[i].volctl != 0 && + DSP_F_READ(flags)) { + volctl = DSP_CDEV_VOLCTL_WRITE; + flags &= ~FREAD; + flags |= FWRITE; + } + if (DSP_F_READ(flags)) { + (void)snd_clone_release(i_dev); + PCM_RELEASE_QUICK(d); + PCM_GIANT_EXIT(d); + return (ENOTSUP); + } wdevunit = dev2unit(i_dev); - else if (dsp_cdevs[i].query == DSP_CDEV_TYPE_RDONLY) + } else if (dsp_cdevs[i].query == DSP_CDEV_TYPE_RDONLY) { + if (dsp_cdevs[i].volctl != 0 && + DSP_F_WRITE(flags)) { + volctl = DSP_CDEV_VOLCTL_READ; + flags &= ~FWRITE; + flags |= FREAD; + } + if (DSP_F_WRITE(flags)) { + (void)snd_clone_release(i_dev); + PCM_RELEASE_QUICK(d); + PCM_GIANT_EXIT(d); + return (ENOTSUP); + } rdevunit = dev2unit(i_dev); + } fmt = dsp_cdevs[i].fmt; spd = dsp_cdevs[i].spd; break; @@ -493,12 +551,14 @@ dsp_open(struct cdev *i_dev, int flags, int mode, struct thread *td) if (DSP_F_READ(flags)) { /* open for read */ rderror = pcm_chnalloc(d, &rdch, PCMDIR_REC, - td->td_proc->p_pid, rdevunit); + td->td_proc->p_pid, td->td_proc->p_comm, rdevunit); - if (rderror == 0 && (chn_reset(rdch, fmt) != 0 || - (chn_setspeed(rdch, spd) != 0))) + if (rderror == 0 && chn_reset(rdch, fmt, spd) != 0) rderror = ENXIO; + if (volctl == DSP_CDEV_VOLCTL_READ) + rderror = 0; + if (rderror != 0) { if (rdch != NULL) pcm_chnrelease(rdch); @@ -509,10 +569,19 @@ dsp_open(struct cdev *i_dev, int flags, int mode, struct thread *td) return (rderror); } rdch = NULL; + } else if (volctl == DSP_CDEV_VOLCTL_READ) { + if (rdch != NULL) { + pcm_chnref(rdch, 1); + pcm_chnrelease(rdch); + } } else { if (flags & O_NONBLOCK) rdch->flags |= CHN_F_NBIO; + if (flags & O_EXCL) + rdch->flags |= CHN_F_EXCLUSIVE; pcm_chnref(rdch, 1); + if (volctl == DSP_CDEV_VOLCTL_NONE) + chn_vpc_reset(rdch, SND_VOL_C_PCM, 0); CHN_UNLOCK(rdch); } } @@ -520,12 +589,14 @@ dsp_open(struct cdev *i_dev, int flags, int mode, struct thread *td) if (DSP_F_WRITE(flags)) { /* open for write */ wrerror = pcm_chnalloc(d, &wrch, PCMDIR_PLAY, - td->td_proc->p_pid, wdevunit); + td->td_proc->p_pid, td->td_proc->p_comm, wdevunit); - if (wrerror == 0 && (chn_reset(wrch, fmt) != 0 || - (chn_setspeed(wrch, spd) != 0))) + if (wrerror == 0 && chn_reset(wrch, fmt, spd) != 0) wrerror = ENXIO; + if (volctl == DSP_CDEV_VOLCTL_WRITE) + wrerror = 0; + if (wrerror != 0) { if (wrch != NULL) pcm_chnrelease(wrch); @@ -545,27 +616,58 @@ dsp_open(struct cdev *i_dev, int flags, int mode, struct thread *td) return (wrerror); } wrch = NULL; + } else if (volctl == DSP_CDEV_VOLCTL_WRITE) { + if (wrch != NULL) { + pcm_chnref(wrch, 1); + pcm_chnrelease(wrch); + } } else { if (flags & O_NONBLOCK) wrch->flags |= CHN_F_NBIO; + if (flags & O_EXCL) + wrch->flags |= CHN_F_EXCLUSIVE; pcm_chnref(wrch, 1); + if (volctl == DSP_CDEV_VOLCTL_NONE) + chn_vpc_reset(wrch, SND_VOL_C_PCM, 0); CHN_UNLOCK(wrch); } } - if (rdch == NULL && wrch == NULL) { - (void)snd_clone_release(i_dev); - PCM_RELEASE_QUICK(d); - PCM_GIANT_EXIT(d); - return ((wrerror != 0) ? wrerror : rderror); - } - pcm_lock(d); + PCM_LOCK(d); /* * We're done. Allocate channels information for this cdev. */ - dsp_cdevinfo_alloc(i_dev, rdch, wrch); + switch (volctl) { + case DSP_CDEV_VOLCTL_READ: + KASSERT(wrch == NULL, ("wrch=%p not null!", wrch)); + dsp_cdevinfo_alloc(i_dev, NULL, NULL, rdch); + break; + case DSP_CDEV_VOLCTL_WRITE: + KASSERT(rdch == NULL, ("rdch=%p not null!", rdch)); + dsp_cdevinfo_alloc(i_dev, NULL, NULL, wrch); + break; + case DSP_CDEV_VOLCTL_NONE: + default: + if (wrch == NULL && rdch == NULL) { + (void)snd_clone_release(i_dev); + PCM_RELEASE(d); + PCM_UNLOCK(d); + PCM_GIANT_EXIT(d); + if (wrerror != 0) + return (wrerror); + if (rderror != 0) + return (rderror); + return (EINVAL); + } + dsp_cdevinfo_alloc(i_dev, rdch, wrch, NULL); + if (rdch != NULL) + CHN_INSERT_HEAD(d, rdch, channels.pcm.opened); + if (wrch != NULL) + CHN_INSERT_HEAD(d, wrch, channels.pcm.opened); + break; + } /* * Increase clone refcount for its automatic garbage collector. @@ -573,7 +675,7 @@ dsp_open(struct cdev *i_dev, int flags, int mode, struct thread *td) (void)snd_clone_ref(i_dev); PCM_RELEASE(d); - pcm_unlock(d); + PCM_UNLOCK(d); PCM_GIANT_LEAVE(d); @@ -583,9 +685,9 @@ dsp_open(struct cdev *i_dev, int flags, int mode, struct thread *td) static int dsp_close(struct cdev *i_dev, int flags, int mode, struct thread *td) { - struct pcm_channel *rdch, *wrch; + struct pcm_channel *rdch, *wrch, *volch; struct snddev_info *d; - int sg_ids, refs; + int sg_ids, rdref, wdref; d = dsp_get_info(i_dev); if (!DSP_REGISTERED(d, i_dev)) @@ -593,26 +695,51 @@ dsp_close(struct cdev *i_dev, int flags, int mode, struct thread *td) PCM_GIANT_ENTER(d); - pcm_lock(d); + PCM_LOCK(d); PCM_WAIT(d); + PCM_ACQUIRE(d); rdch = PCM_RDCH(i_dev); wrch = PCM_WRCH(i_dev); + volch = PCM_VOLCH(i_dev); - if (rdch || wrch) { - PCM_ACQUIRE(d); - pcm_unlock(d); + PCM_RDCH(i_dev) = NULL; + PCM_WRCH(i_dev) = NULL; + PCM_VOLCH(i_dev) = NULL; - refs = 0; - if (rdch) { + rdref = -1; + wdref = -1; + + if (volch != NULL) { + if (volch == rdch) + rdref--; + else if (volch == wrch) + wdref--; + else { + CHN_LOCK(volch); + pcm_chnref(volch, -1); + CHN_UNLOCK(volch); + } + } + + if (rdch != NULL) + CHN_REMOVE(d, rdch, channels.pcm.opened); + if (wrch != NULL) + CHN_REMOVE(d, wrch, channels.pcm.opened); + + if (rdch != NULL || wrch != NULL) { + PCM_UNLOCK(d); + if (rdch != NULL) { /* * The channel itself need not be locked because: - * a) Adding a channel to a syncgroup happens only in dsp_ioctl(), - * which cannot run concurrently to dsp_close(). - * b) The syncmember pointer (sm) is protected by the global - * syncgroup list lock. - * c) A channel can't just disappear, invalidating pointers, - * unless it's closed/dereferenced first. + * a) Adding a channel to a syncgroup happens only + * in dsp_ioctl(), which cannot run concurrently + * to dsp_close(). + * b) The syncmember pointer (sm) is protected by + * the global syncgroup list lock. + * c) A channel can't just disappear, invalidating + * pointers, unless it's closed/dereferenced + * first. */ PCM_SG_LOCK(); sg_ids = chn_syncdestroy(rdch); @@ -621,14 +748,14 @@ dsp_close(struct cdev *i_dev, int flags, int mode, struct thread *td) free_unr(pcmsg_unrhdr, sg_ids); CHN_LOCK(rdch); - refs += pcm_chnref(rdch, -1); + pcm_chnref(rdch, rdref); chn_abort(rdch); /* won't sleep */ - rdch->flags &= ~(CHN_F_RUNNING | CHN_F_MAPPED | CHN_F_DEAD); - chn_reset(rdch, 0); + rdch->flags &= ~(CHN_F_RUNNING | CHN_F_MMAP | + CHN_F_DEAD | CHN_F_EXCLUSIVE); + chn_reset(rdch, 0, 0); pcm_chnrelease(rdch); - PCM_RDCH(i_dev) = NULL; } - if (wrch) { + if (wrch != NULL) { /* * Please see block above. */ @@ -639,41 +766,34 @@ dsp_close(struct cdev *i_dev, int flags, int mode, struct thread *td) free_unr(pcmsg_unrhdr, sg_ids); CHN_LOCK(wrch); - refs += pcm_chnref(wrch, -1); + pcm_chnref(wrch, wdref); chn_flush(wrch); /* may sleep */ - wrch->flags &= ~(CHN_F_RUNNING | CHN_F_MAPPED | CHN_F_DEAD); - chn_reset(wrch, 0); + wrch->flags &= ~(CHN_F_RUNNING | CHN_F_MMAP | + CHN_F_DEAD | CHN_F_EXCLUSIVE); + chn_reset(wrch, 0, 0); pcm_chnrelease(wrch); - PCM_WRCH(i_dev) = NULL; } + PCM_LOCK(d); + } - pcm_lock(d); - /* - * If there are no more references, release the channels. - */ - if (refs == 0 && PCM_RDCH(i_dev) == NULL && - PCM_WRCH(i_dev) == NULL) { - dsp_cdevinfo_free(i_dev); - /* - * Release clone busy state and unref it - * so the automatic garbage collector will - * get the hint and do the remaining cleanup - * process. - */ - (void)snd_clone_release(i_dev); + dsp_cdevinfo_free(i_dev); + /* + * Release clone busy state and unref it so the automatic + * garbage collector will get the hint and do the remaining + * cleanup process. + */ + (void)snd_clone_release(i_dev); - /* - * destroy_dev() might sleep, so release pcm lock - * here and rely on pcm cv serialization. - */ - pcm_unlock(d); - (void)snd_clone_unref(i_dev); - pcm_lock(d); - } - PCM_RELEASE(d); - } + /* + * destroy_dev() might sleep, so release pcm lock + * here and rely on pcm cv serialization. + */ + PCM_UNLOCK(d); + (void)snd_clone_unref(i_dev); + PCM_LOCK(d); - pcm_unlock(d); + PCM_RELEASE(d); + PCM_UNLOCK(d); PCM_GIANT_LEAVE(d); @@ -726,7 +846,7 @@ dsp_io_ops(struct cdev *i_dev, struct uio *buf) return (EBADF); } - if (((*ch)->flags & (CHN_F_MAPPED | CHN_F_DEAD)) || + if (((*ch)->flags & (CHN_F_MMAP | CHN_F_DEAD)) || (((*ch)->flags & CHN_F_RUNNING) && (*ch)->pid != runpid)) { relchns(i_dev, rdch, wrch, prio); PCM_GIANT_EXIT(d); @@ -767,11 +887,175 @@ dsp_write(struct cdev *i_dev, struct uio *buf, int flag) } static int -dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread *td) +dsp_get_volume_channel(struct cdev *dev, struct pcm_channel **volch) +{ + struct snddev_info *d; + struct pcm_channel *c; + int unit; + + KASSERT(dev != NULL && volch != NULL, + ("%s(): NULL query dev=%p volch=%p", __func__, dev, volch)); + + d = dsp_get_info(dev); + if (!PCM_REGISTERED(d)) { + *volch = NULL; + return (EINVAL); + } + + PCM_UNLOCKASSERT(d); + + *volch = NULL; + + c = PCM_VOLCH(dev); + if (c != NULL) { + if (!(c->feederflags & (1 << FEEDER_VOLUME))) + return (-1); + *volch = c; + return (0); + } + + PCM_LOCK(d); + PCM_WAIT(d); + PCM_ACQUIRE(d); + + unit = dev2unit(dev); + + CHN_FOREACH(c, d, channels.pcm) { + CHN_LOCK(c); + if (c->unit != unit) { + CHN_UNLOCK(c); + continue; + } + *volch = c; + pcm_chnref(c, 1); + PCM_VOLCH(dev) = c; + CHN_UNLOCK(c); + PCM_RELEASE(d); + PCM_UNLOCK(d); + return ((c->feederflags & (1 << FEEDER_VOLUME)) ? 0 : -1); + } + + PCM_RELEASE(d); + PCM_UNLOCK(d); + + return (EINVAL); +} + +static int +dsp_ioctl_channel(struct cdev *dev, struct pcm_channel *volch, u_long cmd, + caddr_t arg) +{ + struct snddev_info *d; + struct pcm_channel *rdch, *wrch; + int j, devtype, ret; + + d = dsp_get_info(dev); + if (!PCM_REGISTERED(d) || !(dsp_get_flags(dev) & SD_F_VPC)) + return (-1); + + PCM_UNLOCKASSERT(d); + + j = cmd & 0xff; + + rdch = PCM_RDCH(dev); + wrch = PCM_WRCH(dev); + + /* No specific channel, look into cache */ + if (volch == NULL) + volch = PCM_VOLCH(dev); + + /* Look harder */ + if (volch == NULL) { + if (j == SOUND_MIXER_RECLEV && rdch != NULL) + volch = rdch; + else if (j == SOUND_MIXER_PCM && wrch != NULL) + volch = wrch; + } + + devtype = PCMDEV(dev); + + /* Look super harder */ + if (volch == NULL && + (devtype == SND_DEV_DSPHW_PLAY || devtype == SND_DEV_DSPHW_VPLAY || + devtype == SND_DEV_DSPHW_REC || devtype == SND_DEV_DSPHW_VREC)) { + ret = dsp_get_volume_channel(dev, &volch); + if (ret != 0) + return (ret); + if (volch == NULL) + return (EINVAL); + } + + /* Final validation */ + if (volch != NULL) { + CHN_LOCK(volch); + if (!(volch->feederflags & (1 << FEEDER_VOLUME))) { + CHN_UNLOCK(volch); + return (-1); + } + if (volch->direction == PCMDIR_PLAY) + wrch = volch; + else + rdch = volch; + } + + ret = EINVAL; + + if (volch != NULL && + ((j == SOUND_MIXER_PCM && volch->direction == PCMDIR_PLAY) || + (j == SOUND_MIXER_RECLEV && volch->direction == PCMDIR_REC))) { + if ((cmd & MIXER_WRITE(0)) == MIXER_WRITE(0)) { + int left, right, center; + + left = *(int *)arg & 0x7f; + right = ((*(int *)arg) >> 8) & 0x7f; + center = (left + right) >> 1; + chn_setvolume_multi(volch, SND_VOL_C_PCM, left, right, + center); + } else if ((cmd & MIXER_READ(0)) == MIXER_READ(0)) { + *(int *)arg = CHN_GETVOLUME(volch, + SND_VOL_C_PCM, SND_CHN_T_FL); + *(int *)arg |= CHN_GETVOLUME(volch, + SND_VOL_C_PCM, SND_CHN_T_FR) << 8; + } + ret = 0; + } else if (rdch != NULL || wrch != NULL) { + switch (j) { + case SOUND_MIXER_DEVMASK: + case SOUND_MIXER_CAPS: + case SOUND_MIXER_STEREODEVS: + if ((cmd & MIXER_READ(0)) == MIXER_READ(0)) { + *(int *)arg = 0; + if (rdch != NULL) + *(int *)arg |= SOUND_MASK_RECLEV; + if (wrch != NULL) + *(int *)arg |= SOUND_MASK_PCM; + } + ret = 0; + break; + case SOUND_MIXER_RECMASK: + case SOUND_MIXER_RECSRC: + if ((cmd & MIXER_READ(0)) == MIXER_READ(0)) + *(int *)arg = 0; + ret = 0; + break; + default: + break; + } + } + + if (volch != NULL) + CHN_UNLOCK(volch); + + return (ret); +} + +static int +dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, + struct thread *td) { struct pcm_channel *chn, *rdch, *wrch; struct snddev_info *d; - int *arg_i, ret, kill, tmp, xcmd; + int *arg_i, ret, tmp, xcmd; d = dsp_get_info(i_dev); if (!DSP_REGISTERED(d, i_dev)) @@ -782,15 +1066,15 @@ dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread * arg_i = (int *)arg; ret = 0; xcmd = 0; + chn = NULL; - /* - * this is an evil hack to allow broken apps to perform mixer ioctls - * on dsp devices. - */ if (IOCGROUP(cmd) == 'M') { - /* - * This is at least, a bug to bug compatible with OSS. - */ + ret = dsp_ioctl_channel(i_dev, PCM_VOLCH(i_dev), cmd, arg); + if (ret != -1) { + PCM_GIANT_EXIT(d); + return (ret); + } + if (d->mixer_dev != NULL) { PCM_ACQUIRE_QUICK(d); ret = mixer_ioctl_cmd(d->mixer_dev, cmd, arg, -1, td, @@ -835,23 +1119,12 @@ dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread * getchns(i_dev, &rdch, &wrch, 0); - kill = 0; - if (wrch && (wrch->flags & CHN_F_DEAD)) - kill |= 1; - if (rdch && (rdch->flags & CHN_F_DEAD)) - kill |= 2; - if (kill == 3) { - relchns(i_dev, rdch, wrch, 0); - PCM_GIANT_EXIT(d); - return (EINVAL); - } - if (kill & 1) + if (wrch != NULL && (wrch->flags & CHN_F_DEAD)) wrch = NULL; - if (kill & 2) + if (rdch != NULL && (rdch->flags & CHN_F_DEAD)) rdch = NULL; if (wrch == NULL && rdch == NULL) { - relchns(i_dev, rdch, wrch, 0); PCM_GIANT_EXIT(d); return (EINVAL); } @@ -930,11 +1203,14 @@ dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread * if (wrch) { CHN_LOCK(wrch); if (cmd == AIOSFMT && p->play_format != 0) { - chn_setformat(wrch, p->play_format); + chn_setformat(wrch, + SND_FORMAT(p->play_format, + AFMT_CHANNEL(wrch->format), + AFMT_EXTCHANNEL(wrch->format))); chn_setspeed(wrch, p->play_rate); } p->play_rate = wrch->speed; - p->play_format = wrch->format; + p->play_format = AFMT_ENCODING(wrch->format); CHN_UNLOCK(wrch); } else { p->play_rate = 0; @@ -943,11 +1219,14 @@ dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread * if (rdch) { CHN_LOCK(rdch); if (cmd == AIOSFMT && p->rec_format != 0) { - chn_setformat(rdch, p->rec_format); + chn_setformat(rdch, + SND_FORMAT(p->rec_format, + AFMT_CHANNEL(rdch->format), + AFMT_EXTCHANNEL(rdch->format))); chn_setspeed(rdch, p->rec_rate); } p->rec_rate = rdch->speed; - p->rec_format = rdch->format; + p->rec_format = AFMT_ENCODING(rdch->format); CHN_UNLOCK(rdch); } else { p->rec_rate = 0; @@ -963,7 +1242,7 @@ dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread * struct pcmchan_caps *pcaps = NULL, *rcaps = NULL; struct cdev *pdev; - pcm_lock(d); + PCM_LOCK(d); if (rdch) { CHN_LOCK(rdch); rcaps = chn_getcaps(rdch); @@ -991,7 +1270,7 @@ dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread * CHN_UNLOCK(wrch); if (rdch) CHN_UNLOCK(rdch); - pcm_unlock(d); + PCM_UNLOCK(d); } break; @@ -1150,19 +1429,21 @@ dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread * case SNDCTL_DSP_STEREO: tmp = -1; - *arg_i = (*arg_i)? AFMT_STEREO : 0; + *arg_i = (*arg_i)? 2 : 1; PCM_ACQUIRE_QUICK(d); if (wrch) { CHN_LOCK(wrch); - ret = chn_setformat(wrch, (wrch->format & ~AFMT_STEREO) | *arg_i); - tmp = (wrch->format & AFMT_STEREO)? 1 : 0; + ret = chn_setformat(wrch, + SND_FORMAT(wrch->format, *arg_i, 0)); + tmp = (AFMT_CHANNEL(wrch->format) > 1)? 1 : 0; CHN_UNLOCK(wrch); } if (rdch && ret == 0) { CHN_LOCK(rdch); - ret = chn_setformat(rdch, (rdch->format & ~AFMT_STEREO) | *arg_i); + ret = chn_setformat(rdch, + SND_FORMAT(rdch->format, *arg_i, 0)); if (tmp == -1) - tmp = (rdch->format & AFMT_STEREO)? 1 : 0; + tmp = (AFMT_CHANNEL(rdch->format) > 1)? 1 : 0; CHN_UNLOCK(rdch); } PCM_RELEASE_QUICK(d); @@ -1171,21 +1452,39 @@ dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread * case SOUND_PCM_WRITE_CHANNELS: /* case SNDCTL_DSP_CHANNELS: ( == SOUND_PCM_WRITE_CHANNELS) */ + if (*arg_i < 0) { + *arg_i = 0; + ret = EINVAL; + break; + } if (*arg_i != 0) { + struct pcmchan_matrix *m; + uint32_t ext; + tmp = 0; - *arg_i = (*arg_i != 1)? AFMT_STEREO : 0; + if (*arg_i > SND_CHN_MAX) + *arg_i = SND_CHN_MAX; + + m = feeder_matrix_default_channel_map(*arg_i); + if (m != NULL) + ext = m->ext; + else + ext = 0; + PCM_ACQUIRE_QUICK(d); if (wrch) { CHN_LOCK(wrch); - ret = chn_setformat(wrch, (wrch->format & ~AFMT_STEREO) | *arg_i); - tmp = (wrch->format & AFMT_STEREO)? 2 : 1; + ret = chn_setformat(wrch, + SND_FORMAT(wrch->format, *arg_i, ext)); + tmp = AFMT_CHANNEL(wrch->format); CHN_UNLOCK(wrch); } if (rdch && ret == 0) { CHN_LOCK(rdch); - ret = chn_setformat(rdch, (rdch->format & ~AFMT_STEREO) | *arg_i); + ret = chn_setformat(rdch, + SND_FORMAT(rdch->format, *arg_i, ext)); if (tmp == 0) - tmp = (rdch->format & AFMT_STEREO)? 2 : 1; + tmp = AFMT_CHANNEL(rdch->format); CHN_UNLOCK(rdch); } PCM_RELEASE_QUICK(d); @@ -1193,7 +1492,7 @@ dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread * } else { chn = wrch ? wrch : rdch; CHN_LOCK(chn); - *arg_i = (chn->format & AFMT_STEREO) ? 2 : 1; + *arg_i = AFMT_CHANNEL(chn->format); CHN_UNLOCK(chn); } break; @@ -1202,7 +1501,7 @@ dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread * chn = wrch ? wrch : rdch; if (chn) { CHN_LOCK(chn); - *arg_i = (chn->format & AFMT_STEREO) ? 2 : 1; + *arg_i = AFMT_CHANNEL(chn->format); CHN_UNLOCK(chn); } else { *arg_i = 0; @@ -1223,28 +1522,32 @@ dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread * break; case SNDCTL_DSP_SETFMT: /* sets _one_ format */ - if ((*arg_i != AFMT_QUERY)) { + if (*arg_i != AFMT_QUERY) { tmp = 0; PCM_ACQUIRE_QUICK(d); if (wrch) { CHN_LOCK(wrch); - ret = chn_setformat(wrch, (*arg_i) | (wrch->format & AFMT_STEREO)); - tmp = wrch->format & ~AFMT_STEREO; + ret = chn_setformat(wrch, SND_FORMAT(*arg_i, + AFMT_CHANNEL(wrch->format), + AFMT_EXTCHANNEL(wrch->format))); + tmp = wrch->format; CHN_UNLOCK(wrch); } if (rdch && ret == 0) { CHN_LOCK(rdch); - ret = chn_setformat(rdch, (*arg_i) | (rdch->format & AFMT_STEREO)); + ret = chn_setformat(rdch, SND_FORMAT(*arg_i, + AFMT_CHANNEL(rdch->format), + AFMT_EXTCHANNEL(rdch->format))); if (tmp == 0) - tmp = rdch->format & ~AFMT_STEREO; + tmp = rdch->format; CHN_UNLOCK(rdch); } PCM_RELEASE_QUICK(d); - *arg_i = tmp; + *arg_i = AFMT_ENCODING(tmp); } else { chn = wrch ? wrch : rdch; CHN_LOCK(chn); - *arg_i = chn->format & ~AFMT_STEREO; + *arg_i = AFMT_ENCODING(chn->format); CHN_UNLOCK(chn); } break; @@ -1374,11 +1677,11 @@ dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread * break; case SNDCTL_DSP_GETCAPS: - pcm_lock(d); + PCM_LOCK(d); *arg_i = PCM_CAP_REALTIME | PCM_CAP_MMAP | PCM_CAP_TRIGGER; if (rdch && wrch && !(dsp_get_flags(i_dev) & SD_F_SIMPLEX)) *arg_i |= PCM_CAP_DUPLEX; - pcm_unlock(d); + PCM_UNLOCK(d); break; case SOUND_PCM_READ_BITS: @@ -1471,10 +1774,10 @@ dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread * * switch to full-duplex mode if card is in half-duplex * mode and is able to work in full-duplex mode */ - pcm_lock(d); + PCM_LOCK(d); if (rdch && wrch && (dsp_get_flags(i_dev) & SD_F_SIMPLEX)) dsp_set_flags(i_dev, dsp_get_flags(i_dev)^SD_F_SIMPLEX); - pcm_unlock(d); + PCM_UNLOCK(d); break; /* @@ -1483,20 +1786,34 @@ dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread * * command". */ case SNDCTL_DSP_GETRECVOL: - if (xcmd == 0) + if (xcmd == 0) { xcmd = SOUND_MIXER_READ_RECLEV; + chn = rdch; + } /* FALLTHROUGH */ case SNDCTL_DSP_SETRECVOL: - if (xcmd == 0) + if (xcmd == 0) { xcmd = SOUND_MIXER_WRITE_RECLEV; + chn = rdch; + } /* FALLTHROUGH */ case SNDCTL_DSP_GETPLAYVOL: - if (xcmd == 0) + if (xcmd == 0) { xcmd = SOUND_MIXER_READ_PCM; + chn = wrch; + } /* FALLTHROUGH */ case SNDCTL_DSP_SETPLAYVOL: - if (xcmd == 0) + if (xcmd == 0) { xcmd = SOUND_MIXER_WRITE_PCM; + chn = wrch; + } + + ret = dsp_ioctl_channel(i_dev, chn, xcmd, arg); + if (ret != -1) { + PCM_GIANT_EXIT(d); + return (ret); + } if (d->mixer_dev != NULL) { PCM_ACQUIRE_QUICK(d); @@ -1505,6 +1822,7 @@ dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread * PCM_RELEASE_QUICK(d); } else ret = ENOTSUP; + break; case SNDCTL_DSP_GET_RECSRC_NAMES: @@ -1634,11 +1952,11 @@ dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread * 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_getbps(b); - oc->fifo_samples = (sndbuf_getready(b) - tmp) / sndbuf_getbps(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_getbps(bs); - oc->fifo_samples = sndbuf_getready(bs) / sndbuf_getbps(bs); + oc->samples = sndbuf_gettotal(bs) / sndbuf_getalign(bs); + oc->fifo_samples = sndbuf_getready(bs) / sndbuf_getalign(bs); #endif CHN_UNLOCK(chn); } @@ -1719,6 +2037,30 @@ dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread * PCM_RELEASE_QUICK(d); break; + case SNDCTL_DSP_COOKEDMODE: + PCM_ACQUIRE_QUICK(d); + if (!(dsp_get_flags(i_dev) & SD_F_BITPERFECT)) + ret = dsp_oss_cookedmode(wrch, rdch, *arg_i); + PCM_RELEASE_QUICK(d); + break; + case SNDCTL_DSP_GET_CHNORDER: + PCM_ACQUIRE_QUICK(d); + ret = dsp_oss_getchnorder(wrch, rdch, (unsigned long long *)arg); + PCM_RELEASE_QUICK(d); + break; + case SNDCTL_DSP_SET_CHNORDER: + PCM_ACQUIRE_QUICK(d); + ret = dsp_oss_setchnorder(wrch, rdch, (unsigned long long *)arg); + PCM_RELEASE_QUICK(d); + break; + case SNDCTL_DSP_GETCHANNELMASK: /* XXX vlc */ + PCM_ACQUIRE_QUICK(d); + ret = dsp_oss_getchannelmask(wrch, rdch, (int *)arg); + PCM_RELEASE_QUICK(d); + break; + case SNDCTL_DSP_BIND_CHANNEL: /* XXX what?!? */ + ret = EINVAL; + break; #ifdef OSSV4_EXPERIMENT /* * XXX The following ioctls are not yet supported and just return @@ -1749,15 +2091,6 @@ dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread * * XXX Once implemented, revisit this for proper cv protection * (if necessary). */ - case SNDCTL_DSP_COOKEDMODE: - ret = dsp_oss_cookedmode(wrch, rdch, *arg_i); - break; - case SNDCTL_DSP_GET_CHNORDER: - ret = dsp_oss_getchnorder(wrch, rdch, (unsigned long long *)arg); - break; - case SNDCTL_DSP_SET_CHNORDER: - ret = dsp_oss_setchnorder(wrch, rdch, (unsigned long long *)arg); - break; case SNDCTL_GETLABEL: ret = dsp_oss_getlabel(wrch, rdch, (oss_label_t *)arg); break; @@ -1802,8 +2135,6 @@ dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread * break; } - relchns(i_dev, rdch, wrch, 0); - PCM_GIANT_LEAVE(d); return (ret); @@ -1861,7 +2192,13 @@ dsp_mmap(struct cdev *i_dev, vm_offset_t offset, vm_paddr_t *paddr, int nprot) * http://lists.freebsd.org/pipermail/freebsd-emulation/2007-June/003698.html * */ - if ((nprot & PROT_EXEC) && dsp_mmap_allow_prot_exec == 0) +#ifdef SV_ABI_LINUX + if ((nprot & PROT_EXEC) && (dsp_mmap_allow_prot_exec < 0 || + (dsp_mmap_allow_prot_exec == 0 && + SV_CURPROC_ABI() != SV_ABI_LINUX))) +#else + if ((nprot & PROT_EXEC) && dsp_mmap_allow_prot_exec < 1) +#endif return (-1); d = dsp_get_info(i_dev); @@ -1898,9 +2235,9 @@ dsp_mmap(struct cdev *i_dev, vm_offset_t offset, vm_paddr_t *paddr, int nprot) /* XXX full-duplex quack. */ if (wrch != NULL) - wrch->flags |= CHN_F_MAPPED; + wrch->flags |= CHN_F_MMAP; if (rdch != NULL) - rdch->flags |= CHN_F_MAPPED; + rdch->flags |= CHN_F_MMAP; *paddr = vtophys(sndbuf_getbufofs(c->bufsoft, offset)); relchns(i_dev, rdch, wrch, SD_F_PRIO_RD | SD_F_PRIO_WR); @@ -1910,8 +2247,6 @@ dsp_mmap(struct cdev *i_dev, vm_offset_t offset, vm_paddr_t *paddr, int nprot) return (0); } -#ifdef USING_DEVFS - /* So much for dev_stdclone() */ static int dsp_stdclone(char *name, char *namep, char *sep, int use_sep, int *u, int *c) @@ -1975,7 +2310,7 @@ dsp_clone(void *arg, struct snd_clone_entry *ce; struct pcm_channel *c; int i, unit, udcmask, cunit, devtype, devhw, devcmax, tumax; - char *devname, *devsep; + char *devname, *devcmp, *devsep; KASSERT(dsp_umax >= 0 && dsp_cmax >= 0, ("Uninitialized unit!")); @@ -1994,13 +2329,16 @@ dsp_clone(void *arg, for (i = 0; unit == -1 && i < (sizeof(dsp_cdevs) / sizeof(dsp_cdevs[0])); i++) { devtype = dsp_cdevs[i].type; - devname = dsp_cdevs[i].name; + devcmp = dsp_cdevs[i].name; devsep = dsp_cdevs[i].sep; + devname = dsp_cdevs[i].alias; + if (devname == NULL) + devname = devcmp; devhw = dsp_cdevs[i].hw; devcmax = dsp_cdevs[i].max - 1; - if (strcmp(name, devname) == 0) + if (strcmp(name, devcmp) == 0) unit = snd_unit; - else if (dsp_stdclone(name, devname, devsep, + else if (dsp_stdclone(name, devcmp, devsep, dsp_cdevs[i].use_sep, &unit, &cunit) != 0) { unit = -1; cunit = -1; @@ -2013,15 +2351,15 @@ dsp_clone(void *arg, /* XXX Need Giant magic entry ??? */ - pcm_lock(d); + PCM_LOCK(d); if (snd_clone_disabled(d->clones)) { - pcm_unlock(d); + PCM_UNLOCK(d); return; } PCM_WAIT(d); PCM_ACQUIRE(d); - pcm_unlock(d); + PCM_UNLOCK(d); udcmask = snd_u2unit(unit) | snd_d2unit(devtype); @@ -2089,7 +2427,7 @@ dsp_clone_alloc: snd_clone_setmaxunit(d->clones, tumax); if (ce != NULL) { udcmask |= snd_c2unit(cunit); - *dev = make_dev(&dsp_cdevsw, udcmask, + *dev = make_dev(&dsp_cdevsw, PCMMINOR(udcmask), UID_ROOT, GID_WHEEL, 0666, "%s%d%s%d", devname, unit, devsep, cunit); snd_clone_register(ce, *dev); @@ -2124,7 +2462,6 @@ 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); -#endif char * dsp_unit2name(char *buf, size_t len, int unit) @@ -2137,7 +2474,7 @@ dsp_unit2name(char *buf, size_t len, int unit) dtype = snd_unit2d(unit); for (i = 0; i < (sizeof(dsp_cdevs) / sizeof(dsp_cdevs[0])); i++) { - if (dtype != dsp_cdevs[i].type) + if (dtype != dsp_cdevs[i].type || dsp_cdevs[i].alias != NULL) continue; snprintf(buf, len, "%s%d%s%d", dsp_cdevs[i].name, snd_unit2u(unit), dsp_cdevs[i].sep, snd_unit2c(unit)); @@ -2215,11 +2552,11 @@ dsp_oss_audioinfo(struct cdev *i_dev, oss_audioinfo *ai) /* XXX Need Giant magic entry ??? */ /* See the note in function docblock */ - mtx_assert(d->lock, MA_NOTOWNED); - pcm_lock(d); + PCM_UNLOCKASSERT(d); + PCM_LOCK(d); CHN_FOREACH(ch, d, channels.pcm) { - mtx_assert(ch->lock, MA_NOTOWNED); + CHN_UNLOCKASSERT(ch); CHN_LOCK(ch); if (ai->dev == -1) { if (DSP_REGISTERED(d, i_dev) && @@ -2299,7 +2636,7 @@ dsp_oss_audioinfo(struct cdev *i_dev, oss_audioinfo *ai) fmts = 0; for (i = 0; caps->fmtlist[i]; i++) { fmts |= caps->fmtlist[i]; - if (caps->fmtlist[i] & AFMT_STEREO) { + if (AFMT_CHANNEL(caps->fmtlist[i]) > 1) { minch = (minch == 0) ? 2 : minch; maxch = 2; } else { @@ -2371,7 +2708,7 @@ dsp_oss_audioinfo(struct cdev *i_dev, oss_audioinfo *ai) CHN_UNLOCK(ch); } - pcm_unlock(d); + PCM_UNLOCK(d); if (devname != NULL) return (0); @@ -2629,7 +2966,7 @@ dsp_oss_syncstart(int sg_id) /* Proceed only if no errors encountered. */ if (ret == 0) { /* Launch channels */ - while((sm = SLIST_FIRST(&sg->members)) != NULL) { + while ((sm = SLIST_FIRST(&sg->members)) != NULL) { SLIST_REMOVE_HEAD(&sg->members, link); c = sm->ch; @@ -2716,7 +3053,6 @@ dsp_oss_policy(struct pcm_channel *wrch, struct pcm_channel *rdch, int policy) return (ret); } -#ifdef OSSV4_EXPERIMENT /** * @brief Enable or disable "cooked" mode * @@ -2733,10 +3069,6 @@ dsp_oss_policy(struct pcm_channel *wrch, struct pcm_channel *rdch, int policy) * See @c http://manuals.opensound.com/developer/SNDCTL_DSP_COOKEDMODE.html * for more details. * - * @note Currently, this function is just a stub that always returns EINVAL. - * - * @todo Figure out how to and actually implement this. - * * @param wrch playback channel (optional; may be NULL) * @param rdch recording channel (optional; may be NULL) * @param enabled 0 = raw mode, 1 = cooked mode @@ -2746,7 +3078,38 @@ dsp_oss_policy(struct pcm_channel *wrch, struct pcm_channel *rdch, int policy) static int dsp_oss_cookedmode(struct pcm_channel *wrch, struct pcm_channel *rdch, int enabled) { - return (EINVAL); + + /* + * XXX I just don't get it. Why don't they call it + * "BITPERFECT" ~ SNDCTL_DSP_BITPERFECT !?!?. + * This is just plain so confusing, incoherent, + * <insert any non-printable characters here>. + */ + if (!(enabled == 1 || enabled == 0)) + return (EINVAL); + + /* + * I won't give in. I'm inverting its logic here and now. + * Brag all you want, but "BITPERFECT" should be the better + * term here. + */ + enabled ^= 0x00000001; + + if (wrch != NULL) { + CHN_LOCK(wrch); + wrch->flags &= ~CHN_F_BITPERFECT; + wrch->flags |= (enabled != 0) ? CHN_F_BITPERFECT : 0x00000000; + CHN_UNLOCK(wrch); + } + + if (rdch != NULL) { + CHN_LOCK(rdch); + rdch->flags &= ~CHN_F_BITPERFECT; + rdch->flags |= (enabled != 0) ? CHN_F_BITPERFECT : 0x00000000; + CHN_UNLOCK(rdch); + } + + return (0); } /** @@ -2769,7 +3132,18 @@ dsp_oss_cookedmode(struct pcm_channel *wrch, struct pcm_channel *rdch, int enabl static int dsp_oss_getchnorder(struct pcm_channel *wrch, struct pcm_channel *rdch, unsigned long long *map) { - return (EINVAL); + struct pcm_channel *ch; + int ret; + + ch = (wrch != NULL) ? wrch : rdch; + if (ch != NULL) { + CHN_LOCK(ch); + ret = chn_oss_getorder(ch, map); + CHN_UNLOCK(ch); + } else + ret = EINVAL; + + return (ret); } /** @@ -2789,9 +3163,50 @@ dsp_oss_getchnorder(struct pcm_channel *wrch, struct pcm_channel *rdch, unsigned static int dsp_oss_setchnorder(struct pcm_channel *wrch, struct pcm_channel *rdch, unsigned long long *map) { - return (EINVAL); + int ret; + + ret = 0; + + if (wrch != NULL) { + CHN_LOCK(wrch); + ret = chn_oss_setorder(wrch, map); + CHN_UNLOCK(wrch); + } + + if (ret == 0 && rdch != NULL) { + CHN_LOCK(rdch); + ret = chn_oss_setorder(rdch, map); + CHN_UNLOCK(rdch); + } + + return (ret); +} + +static int +dsp_oss_getchannelmask(struct pcm_channel *wrch, struct pcm_channel *rdch, + int *mask) +{ + struct pcm_channel *ch; + uint32_t chnmask; + int ret; + + chnmask = 0; + ch = (wrch != NULL) ? wrch : rdch; + + if (ch != NULL) { + CHN_LOCK(ch); + ret = chn_oss_getmask(ch, &chnmask); + CHN_UNLOCK(ch); + } else + ret = EINVAL; + + if (ret == 0) + *mask = chnmask; + + return (ret); } +#ifdef OSSV4_EXPERIMENT /** * @brief Retrieve an audio device's label * diff --git a/sys/dev/sound/pcm/dsp.h b/sys/dev/sound/pcm/dsp.h index b02085c..5a0880a 100644 --- a/sys/dev/sound/pcm/dsp.h +++ b/sys/dev/sound/pcm/dsp.h @@ -1,5 +1,7 @@ /*- - * Copyright (c) 1999 Cameron Grant <cg@freebsd.org> + * Copyright (c) 2005-2009 Ariff Abdullah <ariff@FreeBSD.org> + * Portions Copyright (c) Ryan Beasley <ryan.beasley@gmail.com> - GSoC 2006 + * Copyright (c) 1999 Cameron Grant <cg@FreeBSD.org> * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/sys/dev/sound/pcm/fake.c b/sys/dev/sound/pcm/fake.c deleted file mode 100644 index 9cd17e1..0000000 --- a/sys/dev/sound/pcm/fake.c +++ /dev/null @@ -1,164 +0,0 @@ -/*- - * Copyright (c) 1999 Cameron Grant <cg@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. - */ - -#include <dev/sound/pcm/sound.h> - -SND_DECLARE_FILE("$FreeBSD$"); - -static u_int32_t fk_fmt[] = { - AFMT_MU_LAW, - AFMT_STEREO | AFMT_MU_LAW, - AFMT_A_LAW, - AFMT_STEREO | AFMT_A_LAW, - AFMT_U8, - AFMT_STEREO | AFMT_U8, - AFMT_S8, - AFMT_STEREO | AFMT_S8, - AFMT_S16_LE, - AFMT_STEREO | AFMT_S16_LE, - AFMT_U16_LE, - AFMT_STEREO | AFMT_U16_LE, - AFMT_S16_BE, - AFMT_STEREO | AFMT_S16_BE, - AFMT_U16_BE, - AFMT_STEREO | AFMT_U16_BE, - AFMT_S24_LE, - AFMT_STEREO | AFMT_S24_LE, - AFMT_U24_LE, - AFMT_STEREO | AFMT_U24_LE, - AFMT_S24_BE, - AFMT_STEREO | AFMT_S24_BE, - AFMT_U24_BE, - AFMT_STEREO | AFMT_U24_BE, - AFMT_S32_LE, - AFMT_STEREO | AFMT_S32_LE, - AFMT_U32_LE, - AFMT_STEREO | AFMT_U32_LE, - AFMT_S32_BE, - AFMT_STEREO | AFMT_S32_BE, - AFMT_U32_BE, - AFMT_STEREO | AFMT_U32_BE, - 0 -}; -static struct pcmchan_caps fk_caps = {0, 1000000, fk_fmt, 0}; - -#define FKBUFSZ 4096 -static char fakebuf[FKBUFSZ]; - -/* channel interface */ -static void * -fkchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir) -{ - sndbuf_setup(b, fakebuf, FKBUFSZ); - return (void *)0xbabef00d; -} - -static int -fkchan_free(kobj_t obj, void *data) -{ - return 0; -} - -static int -fkchan_setformat(kobj_t obj, void *data, u_int32_t format) -{ - return 0; -} - -static int -fkchan_setspeed(kobj_t obj, void *data, u_int32_t speed) -{ - return speed; -} - -static int -fkchan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) -{ - return blocksize; -} - -static int -fkchan_trigger(kobj_t obj, void *data, int go) -{ - return 0; -} - -static int -fkchan_getptr(kobj_t obj, void *data) -{ - return 0; -} - -static struct pcmchan_caps * -fkchan_getcaps(kobj_t obj, void *data) -{ - return &fk_caps; -} - -static kobj_method_t fkchan_methods[] = { - KOBJMETHOD(channel_init, fkchan_init), - KOBJMETHOD(channel_free, fkchan_free), - KOBJMETHOD(channel_setformat, fkchan_setformat), - KOBJMETHOD(channel_setspeed, fkchan_setspeed), - KOBJMETHOD(channel_setblocksize, fkchan_setblocksize), - KOBJMETHOD(channel_trigger, fkchan_trigger), - KOBJMETHOD(channel_getptr, fkchan_getptr), - KOBJMETHOD(channel_getcaps, fkchan_getcaps), - { 0, 0 } -}; -CHANNEL_DECLARE(fkchan); - -struct pcm_channel * -fkchan_setup(device_t dev) -{ - struct snddev_info *d = device_get_softc(dev); - struct pcm_channel *c; - - c = malloc(sizeof(*c), M_DEVBUF, M_WAITOK | M_ZERO); - c->methods = kobj_create(&fkchan_class, M_DEVBUF, M_WAITOK); - c->parentsnddev = d; - /* - * Fake channel is such a blessing in disguise. Using this, - * we can keep track prefered virtual channel speed / format without - * querying kernel hint repetitively (see vchan_create / vchan.c). - */ - c->speed = 0; - c->format = 0; - snprintf(c->name, CHN_NAMELEN, "%s:fake", device_get_nameunit(dev)); - - return c; -} - -int -fkchan_kill(struct pcm_channel *c) -{ - kobj_delete(c->methods, M_DEVBUF); - c->methods = NULL; - free(c, M_DEVBUF); - return 0; -} - - diff --git a/sys/dev/sound/pcm/feeder.c b/sys/dev/sound/pcm/feeder.c index 18e509b..916b2d9 100644 --- a/sys/dev/sound/pcm/feeder.c +++ b/sys/dev/sound/pcm/feeder.c @@ -1,5 +1,6 @@ /*- - * Copyright (c) 1999 Cameron Grant <cg@freebsd.org> + * Copyright (c) 2005-2009 Ariff Abdullah <ariff@FreeBSD.org> + * Copyright (c) 1999 Cameron Grant <cg@FreeBSD.org> * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,6 +25,10 @@ * SUCH DAMAGE. */ +#ifdef HAVE_KERNEL_OPTION_HEADERS +#include "opt_snd.h" +#endif + #include <dev/sound/pcm/sound.h> #include "feeder_if.h" @@ -35,43 +40,6 @@ MALLOC_DEFINE(M_FEEDER, "feeder", "pcm feeder"); #define MAXFEEDERS 256 #undef FEEDER_DEBUG -int feeder_buffersize = FEEDBUFSZ; -TUNABLE_INT("hw.snd.feeder_buffersize", &feeder_buffersize); - -#ifdef SND_DEBUG -static int -sysctl_hw_snd_feeder_buffersize(SYSCTL_HANDLER_ARGS) -{ - int i, err, val; - - val = feeder_buffersize; - err = sysctl_handle_int(oidp, &val, 0, req); - - if (err != 0 || req->newptr == NULL) - return err; - - if (val < FEEDBUFSZ_MIN || val > FEEDBUFSZ_MAX) - return EINVAL; - - i = 0; - while (val >> i) - i++; - i = 1 << i; - if (i > val && (i >> 1) > 0 && (i >> 1) >= ((val * 3) >> 2)) - i >>= 1; - - feeder_buffersize = i; - - return err; -} -SYSCTL_PROC(_hw_snd, OID_AUTO, feeder_buffersize, CTLTYPE_INT | CTLFLAG_RW, - 0, sizeof(int), sysctl_hw_snd_feeder_buffersize, "I", - "feeder buffer size"); -#else -SYSCTL_INT(_hw_snd, OID_AUTO, feeder_buffersize, CTLFLAG_RD, - &feeder_buffersize, FEEDBUFSZ, "feeder buffer size"); -#endif - struct feedertab_entry { SLIST_ENTRY(feedertab_entry) link; struct feeder_class *feederclass; @@ -131,10 +99,6 @@ feeder_register(void *p) chn_latency_profile > CHN_LATENCY_PROFILE_MAX) chn_latency_profile = CHN_LATENCY_PROFILE_DEFAULT; - if (feeder_buffersize < FEEDBUFSZ_MIN || - feeder_buffersize > FEEDBUFSZ_MAX) - feeder_buffersize = FEEDBUFSZ; - if (feeder_rate_min < FEEDRATE_MIN || feeder_rate_max < FEEDRATE_MIN || feeder_rate_min > FEEDRATE_MAX || @@ -150,11 +114,11 @@ feeder_register(void *p) if (bootverbose) printf("%s: snd_unit=%d snd_maxautovchans=%d " - "latency=%d feeder_buffersize=%d " + "latency=%d " "feeder_rate_min=%d feeder_rate_max=%d " "feeder_rate_round=%d\n", __func__, snd_unit, snd_maxautovchans, - chn_latency, feeder_buffersize, + chn_latency, feeder_rate_min, feeder_rate_max, feeder_rate_round); @@ -227,7 +191,6 @@ feeder_create(struct feeder_class *fc, struct pcm_feederdesc *desc) if (f == NULL) return NULL; - f->align = fc->align; f->data = fc->data; f->source = NULL; f->parent = NULL; @@ -280,11 +243,6 @@ chn_addfeeder(struct pcm_channel *c, struct feeder_class *fc, struct pcm_feederd nf->source = c->feeder; - /* XXX we should use the lowest common denominator for align */ - if (nf->align > 0) - c->align += nf->align; - else if (nf->align < 0 && c->align < -nf->align) - c->align = -nf->align; if (c->feeder != NULL) c->feeder->parent = nf; c->feeder = nf; @@ -321,39 +279,6 @@ chn_findfeeder(struct pcm_channel *c, u_int32_t type) return NULL; } -static int -chainok(struct pcm_feeder *test, struct pcm_feeder *stop) -{ - u_int32_t visited[MAXFEEDERS / 32]; - u_int32_t idx, mask; - - bzero(visited, sizeof(visited)); - while (test && (test != stop)) { - idx = test->desc->idx; - if (idx < 0) - panic("bad idx %d", idx); - if (idx >= MAXFEEDERS) - panic("bad idx %d", idx); - mask = 1 << (idx & 31); - idx >>= 5; - if (visited[idx] & mask) - return 0; - visited[idx] |= mask; - test = test->source; - } - - return 1; -} - -/* - * See feeder_fmtchain() for the mumbo-jumbo ridiculous explanation - * of what the heck is this FMT_Q_* - */ -#define FMT_Q_UP 1 -#define FMT_Q_DOWN 2 -#define FMT_Q_EQ 3 -#define FMT_Q_MULTI 4 - /* * 14bit format scoring * -------------------- @@ -384,11 +309,13 @@ chainok(struct pcm_feeder *test, struct pcm_feeder *stop) #define score_signeq(s1, s2) (((s1) & 0x1) == ((s2) & 0x1)) #define score_endianeq(s1, s2) (((s1) & 0x2) == ((s2) & 0x2)) #define score_cheq(s1, s2) (((s1) & 0xfc) == ((s2) & 0xfc)) +#define score_chgt(s1, s2) (((s1) & 0xfc) > ((s2) & 0xfc)) +#define score_chlt(s1, s2) (((s1) & 0xfc) < ((s2) & 0xfc)) #define score_val(s1) ((s1) & 0x3f00) #define score_cse(s1) ((s1) & 0x7f) u_int32_t -chn_fmtscore(u_int32_t fmt) +snd_fmtscore(u_int32_t fmt) { u_int32_t ret; @@ -397,10 +324,11 @@ chn_fmtscore(u_int32_t fmt) ret |= 1 << 0; if (fmt & AFMT_BIGENDIAN) ret |= 1 << 1; - if (fmt & AFMT_STEREO) + /*if (fmt & AFMT_STEREO) ret |= (2 & 0x3f) << 2; else - ret |= (1 & 0x3f) << 2; + ret |= (1 & 0x3f) << 2;*/ + ret |= (AFMT_CHANNEL(fmt) & 0x3f) << 2; if (fmt & AFMT_A_LAW) ret |= 1 << 8; else if (fmt & AFMT_MU_LAW) @@ -418,7 +346,7 @@ chn_fmtscore(u_int32_t fmt) } static u_int32_t -chn_fmtbestfunc(u_int32_t fmt, u_int32_t *fmts, int cheq) +snd_fmtbestfunc(u_int32_t fmt, u_int32_t *fmts, int cheq) { u_int32_t best, score, score2, oldscore; int i; @@ -426,16 +354,18 @@ chn_fmtbestfunc(u_int32_t fmt, u_int32_t *fmts, int cheq) if (fmt == 0 || fmts == NULL || fmts[0] == 0) return 0; - if (fmtvalid(fmt, fmts)) + if (snd_fmtvalid(fmt, fmts)) return fmt; best = 0; - score = chn_fmtscore(fmt); + score = snd_fmtscore(fmt); oldscore = 0; for (i = 0; fmts[i] != 0; i++) { - score2 = chn_fmtscore(fmts[i]); - if (cheq && !score_cheq(score, score2)) - continue; + score2 = snd_fmtscore(fmts[i]); + if (cheq && !score_cheq(score, score2) && + (score_chlt(score2, score) || + (oldscore != 0 && score_chgt(score2, oldscore)))) + continue; if (oldscore == 0 || (score_val(score2) == score_val(score)) || (score_val(score2) == score_val(oldscore)) || @@ -461,36 +391,37 @@ chn_fmtbestfunc(u_int32_t fmt, u_int32_t *fmts, int cheq) } u_int32_t -chn_fmtbestbit(u_int32_t fmt, u_int32_t *fmts) +snd_fmtbestbit(u_int32_t fmt, u_int32_t *fmts) { - return chn_fmtbestfunc(fmt, fmts, 0); + return snd_fmtbestfunc(fmt, fmts, 0); } u_int32_t -chn_fmtbeststereo(u_int32_t fmt, u_int32_t *fmts) +snd_fmtbestchannel(u_int32_t fmt, u_int32_t *fmts) { - return chn_fmtbestfunc(fmt, fmts, 1); + return snd_fmtbestfunc(fmt, fmts, 1); } u_int32_t -chn_fmtbest(u_int32_t fmt, u_int32_t *fmts) +snd_fmtbest(u_int32_t fmt, u_int32_t *fmts) { u_int32_t best1, best2; u_int32_t score, score1, score2; - if (fmtvalid(fmt, fmts)) + if (snd_fmtvalid(fmt, fmts)) return fmt; - best1 = chn_fmtbeststereo(fmt, fmts); - best2 = chn_fmtbestbit(fmt, fmts); + best1 = snd_fmtbestchannel(fmt, fmts); + best2 = snd_fmtbestbit(fmt, fmts); if (best1 != 0 && best2 != 0 && best1 != best2) { - if (fmt & AFMT_STEREO) + /*if (fmt & AFMT_STEREO)*/ + if (AFMT_CHANNEL(fmt) > 1) return best1; else { - score = score_val(chn_fmtscore(fmt)); - score1 = score_val(chn_fmtscore(best1)); - score2 = score_val(chn_fmtscore(best2)); + score = score_val(snd_fmtscore(fmt)); + score1 = score_val(snd_fmtscore(best1)); + score2 = score_val(snd_fmtscore(best2)); if (score1 == score2 || score1 == score) return best1; else if (score2 == score) @@ -505,309 +436,6 @@ chn_fmtbest(u_int32_t fmt, u_int32_t *fmts) return best2; } -static struct pcm_feeder * -feeder_fmtchain(u_int32_t *to, struct pcm_feeder *source, struct pcm_feeder *stop, int maxdepth) -{ - struct feedertab_entry *fte, *ftebest; - struct pcm_feeder *try, *ret; - uint32_t fl, qout, qsrc, qdst; - int qtype; - - if (to == NULL || to[0] == 0) - return NULL; - - DEB(printf("trying %s (0x%08x -> 0x%08x)...\n", source->class->name, source->desc->in, source->desc->out)); - if (fmtvalid(source->desc->out, to)) { - DEB(printf("got it\n")); - return source; - } - - if (maxdepth < 0) - return NULL; - - /* - * WARNING: THIS IS _NOT_ FOR THE FAINT HEART - * Disclaimer: I don't expect anybody could understand this - * without deep logical and mathematical analysis - * involving various unnamed probability theorem. - * - * This "Best Fit Random Chain Selection" (BLEHBLEHWHATEVER) algorithm - * is **extremely** difficult to digest especially when applied to - * large sets / numbers of random chains (feeders), each with - * unique characteristic providing different sets of in/out format. - * - * Basically, our FEEDER_FMT (see feeder_fmt.c) chains characteristic: - * 1) Format chains - * 1.1 "8bit to any, not to 8bit" - * 1.1.1 sign can remain consistent, e.g: u8 -> u16[le|be] - * 1.1.2 sign can be changed, e.g: u8 -> s16[le|be] - * 1.1.3 endian can be changed, e.g: u8 -> u16[le|be] - * 1.1.4 both can be changed, e.g: u8 -> [u|s]16[le|be] - * 1.2 "Any to 8bit, not from 8bit" - * 1.2.1 sign can remain consistent, e.g: s16le -> s8 - * 1.2.2 sign can be changed, e.g: s16le -> u8 - * 1.2.3 source endian can be anything e.g: s16[le|be] -> s8 - * 1.2.4 source endian / sign can be anything e.g: [u|s]16[le|be] -> u8 - * 1.3 "Any to any where BOTH input and output either 8bit or non-8bit" - * 1.3.1 endian MUST remain consistent - * 1.3.2 sign CAN be changed - * 1.4 "Long jump" is allowed, e.g: from 16bit to 32bit, excluding - * 16bit to 24bit . - * 2) Channel chains (mono <-> stereo) - * 2.1 Both endian and sign MUST remain consistent - * 3) Endian chains (big endian <-> little endian) - * 3.1 Channels and sign MUST remain consistent - * 4) Sign chains (signed <-> unsigned) - * 4.1 Channels and endian MUST remain consistent - * - * .. and the mother of all chaining rules: - * - * Rules 0: Source and destination MUST not contain multiple selections. - * (qtype != FMT_Q_MULTI) - * - * First of all, our caller ( chn_fmtchain() ) will reduce the possible - * multiple from/to formats to a single best format using chn_fmtbest(). - * Then, using chn_fmtscore(), we determine the chaining characteristic. - * Our main goal is to narrow it down until it reach FMT_Q_EQ chaining - * type while still adhering above chaining rules. - * - * The need for this complicated chaining procedures is inevitable, - * since currently we have more than 200 different types of FEEDER_FMT - * doing various unique format conversion. Without this (the old way), - * it is possible to generate broken chain since it doesn't do any - * sanity checking to ensure that the output format is "properly aligned" - * with the direction of conversion (quality up/down/equal). - * - * Conversion: s24le to s32le - * Possible chain: 1) s24le -> s32le (correct, optimized) - * 2) s24le -> s16le -> s32le - * (since we have feeder_24to16 and feeder_16to32) - * +-- obviously broken! - * - * Using scoring mechanisme, this will ensure that the chaining - * process do the right thing, or at least, give the best chain - * possible without causing quality (the 'Q') degradation. - */ - - qdst = chn_fmtscore(to[0]); - qsrc = chn_fmtscore(source->desc->out); - -#define score_q(s1) score_val(s1) -#define score_8bit(s1) ((s1) & 0x700) -#define score_non8bit(s1) (!score_8bit(s1)) -#define score_across8bit(s1, s2) ((score_8bit(s1) && score_non8bit(s2)) || \ - (score_8bit(s2) && score_non8bit(s1))) - -#define FMT_CHAIN_Q_UP(s1, s2) (score_q(s1) < score_q(s2)) -#define FMT_CHAIN_Q_DOWN(s1, s2) (score_q(s1) > score_q(s2)) -#define FMT_CHAIN_Q_EQ(s1, s2) (score_q(s1) == score_q(s2)) -#define FMT_Q_DOWN_FLAGS(s1, s2) (0x1 | (score_across8bit(s1, s2) ? \ - 0x2 : 0x0)) -#define FMT_Q_UP_FLAGS(s1, s2) FMT_Q_DOWN_FLAGS(s1, s2) -#define FMT_Q_EQ_FLAGS(s1, s2) (0x3ffc | \ - ((score_cheq(s1, s2) && \ - score_endianeq(s1, s2)) ? \ - 0x1 : 0x0) | \ - ((score_cheq(s1, s2) && \ - score_signeq(s1, s2)) ? \ - 0x2 : 0x0)) - - /* Determine chaining direction and set matching flag */ - fl = 0x3fff; - if (to[1] != 0) { - qtype = FMT_Q_MULTI; - printf("%s: WARNING: FMT_Q_MULTI chaining. Expect the unexpected.\n", __func__); - } else if (FMT_CHAIN_Q_DOWN(qsrc, qdst)) { - qtype = FMT_Q_DOWN; - fl = FMT_Q_DOWN_FLAGS(qsrc, qdst); - } else if (FMT_CHAIN_Q_UP(qsrc, qdst)) { - qtype = FMT_Q_UP; - fl = FMT_Q_UP_FLAGS(qsrc, qdst); - } else { - qtype = FMT_Q_EQ; - fl = FMT_Q_EQ_FLAGS(qsrc, qdst); - } - - ftebest = NULL; - - SLIST_FOREACH(fte, &feedertab, link) { - if (fte->desc == NULL) - continue; - if (fte->desc->type != FEEDER_FMT) - continue; - qout = chn_fmtscore(fte->desc->out); -#define FMT_Q_MULTI_VALIDATE(qt) ((qt) == FMT_Q_MULTI) -#define FMT_Q_FL_MATCH(qfl, s1, s2) (((s1) & (qfl)) == ((s2) & (qfl))) -#define FMT_Q_UP_VALIDATE(qt, s1, s2, s3) ((qt) == FMT_Q_UP && \ - score_q(s3) >= score_q(s1) && \ - score_q(s3) <= score_q(s2)) -#define FMT_Q_DOWN_VALIDATE(qt, s1, s2, s3) ((qt) == FMT_Q_DOWN && \ - score_q(s3) <= score_q(s1) && \ - score_q(s3) >= score_q(s2)) -#define FMT_Q_EQ_VALIDATE(qt, s1, s2) ((qt) == FMT_Q_EQ && \ - score_q(s1) == score_q(s2)) - if (fte->desc->in == source->desc->out && - (FMT_Q_MULTI_VALIDATE(qtype) || - (FMT_Q_FL_MATCH(fl, qout, qdst) && - (FMT_Q_UP_VALIDATE(qtype, qsrc, qdst, qout) || - FMT_Q_DOWN_VALIDATE(qtype, qsrc, qdst, qout) || - FMT_Q_EQ_VALIDATE(qtype, qdst, qout))))) { - try = feeder_create(fte->feederclass, fte->desc); - if (try) { - try->source = source; - ret = chainok(try, stop) ? feeder_fmtchain(to, try, stop, maxdepth - 1) : NULL; - if (ret != NULL) - return ret; - feeder_destroy(try); - } - } else if (fte->desc->in == source->desc->out) { - /* XXX quality must be considered! */ - if (ftebest == NULL) - ftebest = fte; - } - } - - if (ftebest != NULL) { - try = feeder_create(ftebest->feederclass, ftebest->desc); - if (try) { - try->source = source; - ret = chainok(try, stop) ? feeder_fmtchain(to, try, stop, maxdepth - 1) : NULL; - if (ret != NULL) - return ret; - feeder_destroy(try); - } - } - - /* printf("giving up %s...\n", source->class->name); */ - - return NULL; -} - -u_int32_t -chn_fmtchain(struct pcm_channel *c, u_int32_t *to) -{ - struct pcm_feeder *try, *del, *stop; - u_int32_t tmpfrom[2], tmpto[2], best, *from; - int i, max, bestmax; - - KASSERT(c != NULL, ("c == NULL")); - KASSERT(c->feeder != NULL, ("c->feeder == NULL")); - KASSERT(to != NULL, ("to == NULL")); - KASSERT(to[0] != 0, ("to[0] == 0")); - - if (c == NULL || c->feeder == NULL || to == NULL || to[0] == 0) - return 0; - - stop = c->feeder; - best = 0; - - if (c->direction == PCMDIR_REC && c->feeder->desc->type == FEEDER_ROOT) { - from = chn_getcaps(c)->fmtlist; - if (from[1] != 0) { - best = chn_fmtbest(to[0], from); - if (best != 0) { - tmpfrom[0] = best; - tmpfrom[1] = 0; - from = tmpfrom; - } - } - } else { - tmpfrom[0] = c->feeder->desc->out; - tmpfrom[1] = 0; - from = tmpfrom; - if (to[1] != 0) { - best = chn_fmtbest(from[0], to); - if (best != 0) { - tmpto[0] = best; - tmpto[1] = 0; - to = tmpto; - } - } - } - -#define FEEDER_FMTCHAIN_MAXDEPTH 8 - - try = NULL; - - if (to[0] != 0 && from[0] != 0 && - to[1] == 0 && from[1] == 0) { - max = 0; - best = from[0]; - c->feeder->desc->out = best; - do { - try = feeder_fmtchain(to, c->feeder, stop, max); - DEB(if (try != NULL) { - printf("%s: 0x%08x -> 0x%08x (maxdepth: %d)\n", - __func__, from[0], to[0], max); - }); - } while (try == NULL && max++ < FEEDER_FMTCHAIN_MAXDEPTH); - } else { - printf("%s: Using the old-way format chaining!\n", __func__); - i = 0; - best = 0; - bestmax = 100; - while (from[i] != 0) { - c->feeder->desc->out = from[i]; - try = NULL; - max = 0; - do { - try = feeder_fmtchain(to, c->feeder, stop, max); - } while (try == NULL && max++ < FEEDER_FMTCHAIN_MAXDEPTH); - if (try != NULL && max < bestmax) { - bestmax = max; - best = from[i]; - } - while (try != NULL && try != stop) { - del = try; - try = try->source; - feeder_destroy(del); - } - i++; - } - if (best == 0) - return 0; - - c->feeder->desc->out = best; - try = feeder_fmtchain(to, c->feeder, stop, bestmax); - } - if (try == NULL) - return 0; - - c->feeder = try; - c->align = 0; -#ifdef FEEDER_DEBUG - printf("\n\nchain: "); -#endif - while (try && (try != stop)) { -#ifdef FEEDER_DEBUG - printf("%s [%d]", try->class->name, try->desc->idx); - if (try->source) - printf(" -> "); -#endif - if (try->source) - try->source->parent = try; - if (try->align > 0) - c->align += try->align; - else if (try->align < 0 && c->align < -try->align) - c->align = -try->align; - try = try->source; - } -#ifdef FEEDER_DEBUG - printf("%s [%d]\n", try->class->name, try->desc->idx); -#endif - - if (c->direction == PCMDIR_REC) { - try = c->feeder; - while (try != NULL) { - if (try->desc->type == FEEDER_ROOT) - return try->desc->out; - try = try->source; - } - return best; - } else - return c->feeder->desc->out; -} - void feeder_printchain(struct pcm_feeder *head) { @@ -831,8 +459,6 @@ feed_root(struct pcm_feeder *feeder, struct pcm_channel *ch, u_int8_t *buffer, u int l, offset; KASSERT(count > 0, ("feed_root: count == 0")); - /* count &= ~((1 << ch->align) - 1); */ - KASSERT(count > 0, ("feed_root: aligned count == 0 (align = %d)", ch->align)); if (++ch->feedcount == 0) ch->feedcount = 2; @@ -882,13 +508,12 @@ feed_root(struct pcm_feeder *feeder, struct pcm_channel *ch, u_int8_t *buffer, u static kobj_method_t feeder_root_methods[] = { KOBJMETHOD(feeder_feed, feed_root), - { 0, 0 } + KOBJMETHOD_END }; static struct feeder_class feeder_root_class = { .name = "feeder_root", .methods = feeder_root_methods, .size = sizeof(struct pcm_feeder), - .align = 0, .desc = NULL, .data = NULL, }; diff --git a/sys/dev/sound/pcm/feeder.h b/sys/dev/sound/pcm/feeder.h index 6bb0741..88e35d2 100644 --- a/sys/dev/sound/pcm/feeder.h +++ b/sys/dev/sound/pcm/feeder.h @@ -1,5 +1,6 @@ /*- - * Copyright (c) 1999 Cameron Grant <cg@freebsd.org> + * Copyright (c) 2005-2009 Ariff Abdullah <ariff@FreeBSD.org> + * Copyright (c) 1999 Cameron Grant <cg@FreeBSD.org> * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -35,7 +36,6 @@ struct pcm_feederdesc { struct feeder_class { KOBJ_CLASS_FIELDS; - int align; struct pcm_feederdesc *desc; void *data; }; @@ -53,60 +53,160 @@ struct pcm_feeder { void feeder_register(void *p); struct feeder_class *feeder_getclass(struct pcm_feederdesc *desc); -u_int32_t chn_fmtscore(u_int32_t fmt); -u_int32_t chn_fmtbestbit(u_int32_t fmt, u_int32_t *fmts); -u_int32_t chn_fmtbeststereo(u_int32_t fmt, u_int32_t *fmts); -u_int32_t chn_fmtbest(u_int32_t fmt, u_int32_t *fmts); -u_int32_t chn_fmtchain(struct pcm_channel *c, u_int32_t *to); -int chn_addfeeder(struct pcm_channel *c, struct feeder_class *fc, struct pcm_feederdesc *desc); +u_int32_t snd_fmtscore(u_int32_t fmt); +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, + 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_printchain(struct pcm_feeder *head); +int feeder_chain(struct pcm_channel *); -#define FEEDER_DECLARE(feeder, palign, pdata) \ -static struct feeder_class feeder ## _class = { \ - .name = #feeder, \ - .methods = feeder ## _methods, \ - .size = sizeof(struct pcm_feeder), \ - .align = palign, \ - .desc = feeder ## _desc, \ - .data = pdata, \ -}; \ -SYSINIT(feeder, SI_SUB_DRIVERS, SI_ORDER_ANY, feeder_register, &feeder ## _class); - -#define FEEDER_ROOT 0 -#define FEEDER_FMT 1 -#define FEEDER_MIXER 2 -#define FEEDER_RATE 3 -#define FEEDER_FILTER 4 -#define FEEDER_VOLUME 5 -#define FEEDER_SWAPLR 6 -#define FEEDER_LAST 32 - -#define FEEDRATE_SRC 1 -#define FEEDRATE_DST 2 +#define FEEDER_DECLARE(feeder, pdata) \ +static struct feeder_class feeder ## _class = { \ + .name = #feeder, \ + .methods = feeder ## _methods, \ + .size = sizeof(struct pcm_feeder), \ + .desc = feeder ## _desc, \ + .data = pdata, \ +}; \ +SYSINIT(feeder, SI_SUB_DRIVERS, SI_ORDER_ANY, feeder_register, \ + &feeder ## _class) -#define FEEDRATE_RATEMIN 1 -#define FEEDRATE_RATEMAX 2016000 /* 48000 * 42 */ +enum { + FEEDER_ROOT, + FEEDER_FORMAT, + FEEDER_MIXER, + FEEDER_RATE, + FEEDER_EQ, + FEEDER_VOLUME, + FEEDER_MATRIX, + FEEDER_LAST, +}; + +/* feeder_format */ +enum { + FEEDFORMAT_CHANNELS +}; + +/* feeder_mixer */ +enum { + FEEDMIXER_CHANNELS +}; + +/* feeder_rate */ +enum { + FEEDRATE_SRC, + FEEDRATE_DST, + FEEDRATE_QUALITY, + FEEDRATE_CHANNELS +}; +#define FEEDRATE_RATEMIN 1 +#define FEEDRATE_RATEMAX 2016000 /* 48000 * 42 */ #define FEEDRATE_MIN 1 #define FEEDRATE_MAX 0x7fffff /* sign 24bit ~ 8ghz ! */ - #define FEEDRATE_ROUNDHZ 25 #define FEEDRATE_ROUNDHZ_MIN 0 #define FEEDRATE_ROUNDHZ_MAX 500 -/* - * Default buffer size for feeder processing. - * - * Big = less sndbuf_feed(), more memory usage. - * Small = aggresive sndbuf_feed() (perhaps too much), less memory usage. - */ -#define FEEDBUFSZ 16384 -#define FEEDBUFSZ_MIN 2048 -#define FEEDBUFSZ_MAX 131072 - extern int feeder_rate_min; extern int feeder_rate_max; extern int feeder_rate_round; -extern int feeder_buffersize; +extern int feeder_rate_quality; + +/* feeder_eq */ +enum { + FEEDEQ_CHANNELS, + FEEDEQ_RATE, + FEEDEQ_TREBLE, + FEEDEQ_BASS, + FEEDEQ_PREAMP, + FEEDEQ_STATE, + FEEDEQ_DISABLE, + FEEDEQ_ENABLE, + FEEDEQ_BYPASS, + FEEDEQ_UNKNOWN +}; + +int feeder_eq_validrate(uint32_t); +void feeder_eq_initsys(device_t); + +/* feeder_volume */ +enum { + FEEDVOLUME_CLASS, + FEEDVOLUME_CHANNELS, + FEEDVOLUME_STATE, + FEEDVOLUME_ENABLE, + FEEDVOLUME_BYPASS +}; + +int feeder_volume_apply_matrix(struct pcm_feeder *, struct pcmchan_matrix *); + +/* feeder_matrix */ +int feeder_matrix_default_id(uint32_t); +struct pcmchan_matrix *feeder_matrix_default_channel_map(uint32_t); + +uint32_t feeder_matrix_default_format(uint32_t); + +int feeder_matrix_format_id(uint32_t); +struct pcmchan_matrix *feeder_matrix_format_map(uint32_t); + +struct pcmchan_matrix *feeder_matrix_id_map(int); + +int feeder_matrix_setup(struct pcm_feeder *, struct pcmchan_matrix *, + struct pcmchan_matrix *); +int feeder_matrix_compare(struct pcmchan_matrix *, struct pcmchan_matrix *); + +/* 4Front OSS stuffs */ +int feeder_matrix_oss_get_channel_order(struct pcmchan_matrix *, + unsigned long long *); +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 + * is too noisy due to limited dynamic range, while 24bit is quite slow due to + * unnatural per-byte read/write. However, for debugging purposes, ensuring + * implementation correctness and torture test, the following can be defined: + * + * SND_FEEDER_MULTIFORMAT - Compile all type of converters, but force + * 8bit samples to be converted to 16bit + * native-endian for better dynamic range. + * Process 24bit samples natively. + * SND_FEEDER_FULL_MULTIFORMAT - Ditto, but process 8bit samples natively. + */ +#ifdef SND_FEEDER_FULL_MULTIFORMAT +#undef SND_FEEDER_MULTIFORMAT +#define SND_FEEDER_MULTIFORMAT 1 +#endif diff --git a/sys/dev/sound/pcm/feeder_chain.c b/sys/dev/sound/pcm/feeder_chain.c new file mode 100644 index 0000000..92a1cf5 --- /dev/null +++ b/sys/dev/sound/pcm/feeder_chain.c @@ -0,0 +1,843 @@ +/*- + * 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. + */ + +#ifdef HAVE_KERNEL_OPTION_HEADERS +#include "opt_snd.h" +#endif + +#include <dev/sound/pcm/sound.h> + +#include "feeder_if.h" + +SND_DECLARE_FILE("$FreeBSD$"); + +/* chain state */ +struct feeder_chain_state { + uint32_t afmt; /* audio format */ + uint32_t rate; /* sampling rate */ + struct pcmchan_matrix *matrix; /* matrix map */ +}; + +/* + * chain descriptor that will be passed around from the beginning until the + * end of chain process. + */ +struct feeder_chain_desc { + struct feeder_chain_state origin; /* original state */ + struct feeder_chain_state current; /* current state */ + struct feeder_chain_state target; /* target state */ + struct pcm_feederdesc desc; /* feeder descriptor */ + uint32_t afmt_ne; /* prefered native endian */ + int mode; /* chain mode */ + int use_eq; /* need EQ? */ + int use_matrix; /* need channel matrixing? */ + int use_volume; /* need softpcmvol? */ + int dummy; /* dummy passthrough */ + int expensive; /* possibly expensive */ +}; + +#define FEEDER_CHAIN_LEAN 0 +#define FEEDER_CHAIN_16 1 +#define FEEDER_CHAIN_32 2 +#define FEEDER_CHAIN_MULTI 3 +#define FEEDER_CHAIN_FULLMULTI 4 +#define FEEDER_CHAIN_LAST 5 + +#if defined(SND_FEEDER_FULL_MULTIFORMAT) +#define FEEDER_CHAIN_DEFAULT FEEDER_CHAIN_FULLMULTI +#elif defined(SND_FEEDER_MULTIFORMAT) +#define FEEDER_CHAIN_DEFAULT FEEDER_CHAIN_MULTI +#else +#define FEEDER_CHAIN_DEFAULT FEEDER_CHAIN_LEAN +#endif + +/* + * List of prefered formats that might be required during + * processing. It will be decided through snd_fmtbest(). + */ + +/* 'Lean' mode, signed 16 or 32 bit native endian. */ +static uint32_t feeder_chain_formats_lean[] = { + AFMT_S16_NE, AFMT_S32_NE, + 0 +}; + +/* Force everything to signed 16 bit native endian. */ +static uint32_t feeder_chain_formats_16[] = { + AFMT_S16_NE, + 0 +}; + +/* Force everything to signed 32 bit native endian. */ +static uint32_t feeder_chain_formats_32[] = { + AFMT_S32_NE, + 0 +}; + +/* Multiple choices, all except 8 bit. */ +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, + 0 +}; + +/* Everything that is convertible. */ +static uint32_t feeder_chain_formats_fullmulti[] = { + AFMT_S8, AFMT_U8, + 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, + 0 +}; + +static uint32_t *feeder_chain_formats[FEEDER_CHAIN_LAST] = { + [FEEDER_CHAIN_LEAN] = feeder_chain_formats_lean, + [FEEDER_CHAIN_16] = feeder_chain_formats_16, + [FEEDER_CHAIN_32] = feeder_chain_formats_32, + [FEEDER_CHAIN_MULTI] = feeder_chain_formats_multi, + [FEEDER_CHAIN_FULLMULTI] = feeder_chain_formats_fullmulti +}; + +static int feeder_chain_mode = FEEDER_CHAIN_DEFAULT; + +#if defined(_KERNEL) && defined(SND_DEBUG) && defined(SND_FEEDER_FULL_MULTIFORMAT) +TUNABLE_INT("hw.snd.feeder_chain_mode", &feeder_chain_mode); +SYSCTL_INT(_hw_snd, OID_AUTO, feeder_chain_mode, CTLFLAG_RW, + &feeder_chain_mode, 0, + "feeder chain mode " + "(0=lean, 1=16bit, 2=32bit, 3=multiformat, 4=fullmultiformat)"); +#endif + +/* + * feeder_build_format(): Chain any format converter. + */ +static int +feeder_build_format(struct pcm_channel *c, struct feeder_chain_desc *cdesc) +{ + struct feeder_class *fc; + struct pcm_feederdesc *desc; + int ret; + + desc = &(cdesc->desc); + desc->type = FEEDER_FORMAT; + desc->in = 0; + desc->out = 0; + desc->flags = 0; + + fc = feeder_getclass(desc); + if (fc == NULL) { + device_printf(c->dev, + "%s(): can't find feeder_format\n", __func__); + return (ENOTSUP); + } + + desc->in = cdesc->current.afmt; + desc->out = cdesc->target.afmt; + + ret = chn_addfeeder(c, fc, desc); + if (ret != 0) { + device_printf(c->dev, + "%s(): can't add feeder_format\n", __func__); + return (ret); + } + + c->feederflags |= 1 << FEEDER_FORMAT; + + cdesc->current.afmt = cdesc->target.afmt; + + return (0); +} + +/* + * feeder_build_formatne(): Chain format converter that suite best for native + * endian format. + */ +static int +feeder_build_formatne(struct pcm_channel *c, struct feeder_chain_desc *cdesc) +{ + struct feeder_chain_state otarget; + int ret; + + if (cdesc->afmt_ne == 0 || + AFMT_ENCODING(cdesc->current.afmt) == cdesc->afmt_ne) + return (0); + + otarget = cdesc->target; + cdesc->target = cdesc->current; + cdesc->target.afmt = SND_FORMAT(cdesc->afmt_ne, + cdesc->current.matrix->channels, cdesc->current.matrix->ext); + + ret = feeder_build_format(c, cdesc); + if (ret != 0) + return (ret); + + cdesc->target = otarget; + + return (0); +} + +/* + * feeder_build_rate(): Chain sample rate converter. + */ +static int +feeder_build_rate(struct pcm_channel *c, struct feeder_chain_desc *cdesc) +{ + struct feeder_class *fc; + struct pcm_feeder *f; + struct pcm_feederdesc *desc; + int ret; + + ret = feeder_build_formatne(c, cdesc); + if (ret != 0) + return (ret); + + desc = &(cdesc->desc); + desc->type = FEEDER_RATE; + desc->in = 0; + desc->out = 0; + desc->flags = 0; + + fc = feeder_getclass(desc); + if (fc == NULL) { + device_printf(c->dev, + "%s(): can't find feeder_rate\n", __func__); + return (ENOTSUP); + } + + desc->in = cdesc->current.afmt; + desc->out = desc->in; + + ret = chn_addfeeder(c, fc, desc); + if (ret != 0) { + device_printf(c->dev, + "%s(): can't add feeder_rate\n", __func__); + return (ret); + } + + f = c->feeder; + + /* + * If in 'dummy' mode (possibly due to passthrough mode), set the + * conversion quality to the lowest possible (should be fastest) since + * listener won't be hearing anything. Theoretically we can just + * disable it, but that will cause weird runtime behaviour: + * application appear to play something that is either too fast or too + * slow. + */ + if (cdesc->dummy != 0) { + ret = FEEDER_SET(f, FEEDRATE_QUALITY, 0); + if (ret != 0) { + device_printf(c->dev, + "%s(): can't set resampling quality\n", __func__); + return (ret); + } + } + + ret = FEEDER_SET(f, FEEDRATE_SRC, cdesc->current.rate); + if (ret != 0) { + device_printf(c->dev, + "%s(): can't set source rate\n", __func__); + return (ret); + } + + ret = FEEDER_SET(f, FEEDRATE_DST, cdesc->target.rate); + if (ret != 0) { + device_printf(c->dev, + "%s(): can't set destination rate\n", __func__); + return (ret); + } + + c->feederflags |= 1 << FEEDER_RATE; + + cdesc->current.rate = cdesc->target.rate; + + return (0); +} + +/* + * feeder_build_matrix(): Chain channel matrixing converter. + */ +static int +feeder_build_matrix(struct pcm_channel *c, struct feeder_chain_desc *cdesc) +{ + struct feeder_class *fc; + struct pcm_feeder *f; + struct pcm_feederdesc *desc; + int ret; + + ret = feeder_build_formatne(c, cdesc); + if (ret != 0) + return (ret); + + desc = &(cdesc->desc); + desc->type = FEEDER_MATRIX; + desc->in = 0; + desc->out = 0; + desc->flags = 0; + + fc = feeder_getclass(desc); + if (fc == NULL) { + device_printf(c->dev, + "%s(): can't find feeder_matrix\n", __func__); + return (ENOTSUP); + } + + desc->in = cdesc->current.afmt; + desc->out = SND_FORMAT(cdesc->current.afmt, + cdesc->target.matrix->channels, cdesc->target.matrix->ext); + + ret = chn_addfeeder(c, fc, desc); + if (ret != 0) { + device_printf(c->dev, + "%s(): can't add feeder_matrix\n", __func__); + return (ret); + } + + f = c->feeder; + ret = feeder_matrix_setup(f, cdesc->current.matrix, + cdesc->target.matrix); + if (ret != 0) { + device_printf(c->dev, + "%s(): feeder_matrix_setup() failed\n", __func__); + return (ret); + } + + c->feederflags |= 1 << FEEDER_MATRIX; + + cdesc->current.afmt = desc->out; + cdesc->current.matrix = cdesc->target.matrix; + cdesc->use_matrix = 0; + + return (0); +} + +/* + * feeder_build_volume(): Chain soft volume. + */ +static int +feeder_build_volume(struct pcm_channel *c, struct feeder_chain_desc *cdesc) +{ + struct feeder_class *fc; + struct pcm_feeder *f; + struct pcm_feederdesc *desc; + int ret; + + ret = feeder_build_formatne(c, cdesc); + if (ret != 0) + return (ret); + + desc = &(cdesc->desc); + desc->type = FEEDER_VOLUME; + desc->in = 0; + desc->out = 0; + desc->flags = 0; + + fc = feeder_getclass(desc); + if (fc == NULL) { + device_printf(c->dev, + "%s(): can't find feeder_volume\n", __func__); + return (ENOTSUP); + } + + desc->in = cdesc->current.afmt; + desc->out = desc->in; + + ret = chn_addfeeder(c, fc, desc); + if (ret != 0) { + device_printf(c->dev, + "%s(): can't add feeder_volume\n", __func__); + return (ret); + } + + f = c->feeder; + + /* + * If in 'dummy' mode (possibly due to passthrough mode), set BYPASS + * mode since listener won't be hearing anything. Theoretically we can + * just disable it, but that will confuse volume per channel mixer. + */ + if (cdesc->dummy != 0) { + ret = FEEDER_SET(f, FEEDVOLUME_STATE, FEEDVOLUME_BYPASS); + if (ret != 0) { + device_printf(c->dev, + "%s(): can't set volume bypass\n", __func__); + return (ret); + } + } + + ret = feeder_volume_apply_matrix(f, cdesc->current.matrix); + if (ret != 0) { + device_printf(c->dev, + "%s(): feeder_volume_apply_matrix() failed\n", __func__); + return (ret); + } + + c->feederflags |= 1 << FEEDER_VOLUME; + + cdesc->use_volume = 0; + + return (0); +} + +/* + * feeder_build_eq(): Chain parametric software equalizer. + */ +static int +feeder_build_eq(struct pcm_channel *c, struct feeder_chain_desc *cdesc) +{ + struct feeder_class *fc; + struct pcm_feeder *f; + struct pcm_feederdesc *desc; + int ret; + + ret = feeder_build_formatne(c, cdesc); + if (ret != 0) + return (ret); + + desc = &(cdesc->desc); + desc->type = FEEDER_EQ; + desc->in = 0; + desc->out = 0; + desc->flags = 0; + + fc = feeder_getclass(desc); + if (fc == NULL) { + device_printf(c->dev, + "%s(): can't find feeder_eq\n", __func__); + return (ENOTSUP); + } + + desc->in = cdesc->current.afmt; + desc->out = desc->in; + + ret = chn_addfeeder(c, fc, desc); + if (ret != 0) { + device_printf(c->dev, + "%s(): can't add feeder_eq\n", __func__); + return (ret); + } + + f = c->feeder; + + ret = FEEDER_SET(f, FEEDEQ_RATE, cdesc->current.rate); + if (ret != 0) { + device_printf(c->dev, + "%s(): can't set rate on feeder_eq\n", __func__); + return (ret); + } + + c->feederflags |= 1 << FEEDER_EQ; + + cdesc->use_eq = 0; + + return (0); +} + +/* + * feeder_build_root(): Chain root feeder, the top, father of all. + */ +static int +feeder_build_root(struct pcm_channel *c, struct feeder_chain_desc *cdesc) +{ + struct feeder_class *fc; + int ret; + + fc = feeder_getclass(NULL); + if (fc == NULL) { + device_printf(c->dev, + "%s(): can't find feeder_root\n", __func__); + return (ENOTSUP); + } + + ret = chn_addfeeder(c, fc, NULL); + if (ret != 0) { + device_printf(c->dev, + "%s(): can't add feeder_root\n", __func__); + return (ret); + } + + c->feederflags |= 1 << FEEDER_ROOT; + + c->feeder->desc->in = cdesc->current.afmt; + c->feeder->desc->out = cdesc->current.afmt; + + return (0); +} + +/* + * feeder_build_mixer(): Chain software mixer for virtual channels. + */ +static int +feeder_build_mixer(struct pcm_channel *c, struct feeder_chain_desc *cdesc) +{ + struct feeder_class *fc; + struct pcm_feederdesc *desc; + int ret; + + desc = &(cdesc->desc); + desc->type = FEEDER_MIXER; + desc->in = 0; + desc->out = 0; + desc->flags = 0; + + fc = feeder_getclass(desc); + if (fc == NULL) { + device_printf(c->dev, + "%s(): can't find feeder_mixer\n", __func__); + return (ENOTSUP); + } + + desc->in = cdesc->current.afmt; + desc->out = desc->in; + + ret = chn_addfeeder(c, fc, desc); + if (ret != 0) { + device_printf(c->dev, + "%s(): can't add feeder_mixer\n", __func__); + return (ret); + } + + c->feederflags |= 1 << FEEDER_MIXER; + + return (0); +} + +/* Macrosses to ease our job doing stuffs later. */ +#define FEEDER_BW(c, t) ((c)->t.matrix->channels * (c)->t.rate) + +#define FEEDRATE_UP(c) ((c)->target.rate > (c)->current.rate) +#define FEEDRATE_DOWN(c) ((c)->target.rate < (c)->current.rate) +#define FEEDRATE_REQUIRED(c) (FEEDRATE_UP(c) || FEEDRATE_DOWN(c)) + +#define FEEDMATRIX_UP(c) ((c)->target.matrix->channels > \ + (c)->current.matrix->channels) +#define FEEDMATRIX_DOWN(c) ((c)->target.matrix->channels < \ + (c)->current.matrix->channels) +#define FEEDMATRIX_REQUIRED(c) (FEEDMATRIX_UP(c) || \ + FEEDMATRIX_DOWN(c) || (c)->use_matrix != 0) + +#define FEEDFORMAT_REQUIRED(c) (AFMT_ENCODING((c)->current.afmt) != \ + AFMT_ENCODING((c)->target.afmt)) + +#define FEEDVOLUME_REQUIRED(c) ((c)->use_volume != 0) + +#define FEEDEQ_VALIDRATE(c, t) (feeder_eq_validrate((c)->t.rate) != 0) +#define FEEDEQ_ECONOMY(c) (FEEDER_BW(c, current) < FEEDER_BW(c, target)) +#define FEEDEQ_REQUIRED(c) ((c)->use_eq != 0 && \ + FEEDEQ_VALIDRATE(c, current)) + +#define FEEDFORMAT_NE_REQUIRED(c) \ + ((c)->afmt_ne != AFMT_S32_NE && \ + (((c)->mode == FEEDER_CHAIN_16 && \ + AFMT_ENCODING((c)->current.afmt) != AFMT_S16_NE) || \ + ((c)->mode == FEEDER_CHAIN_32 && \ + AFMT_ENCODING((c)->current.afmt) != AFMT_S32_NE) || \ + (c)->mode == FEEDER_CHAIN_FULLMULTI || \ + ((c)->mode == FEEDER_CHAIN_MULTI && \ + ((c)->current.afmt & AFMT_8BIT)) || \ + ((c)->mode == FEEDER_CHAIN_LEAN && \ + !((c)->current.afmt & (AFMT_S16_NE | AFMT_S32_NE))))) + +int +feeder_chain(struct pcm_channel *c) +{ + struct snddev_info *d; + struct pcmchan_caps *caps; + struct feeder_chain_desc cdesc; + struct pcmchan_matrix *hwmatrix, *softmatrix; + uint32_t hwfmt, softfmt; + int ret; + + CHN_LOCKASSERT(c); + + /* Remove everything first. */ + while (chn_removefeeder(c) == 0) + ; + + KASSERT(c->feeder == NULL, ("feeder chain not empty")); + + /* clear and populate chain descriptor. */ + bzero(&cdesc, sizeof(cdesc)); + + switch (feeder_chain_mode) { + case FEEDER_CHAIN_LEAN: + case FEEDER_CHAIN_16: + case FEEDER_CHAIN_32: +#if defined(SND_FEEDER_MULTIFORMAT) || defined(SND_FEEDER_FULL_MULTIFORMAT) + case FEEDER_CHAIN_MULTI: +#endif +#if defined(SND_FEEDER_FULL_MULTIFORMAT) + case FEEDER_CHAIN_FULLMULTI: +#endif + break; + default: + feeder_chain_mode = FEEDER_CHAIN_DEFAULT; + break; + } + + cdesc.mode = feeder_chain_mode; + cdesc.expensive = 1; /* XXX faster.. */ + +#define VCHAN_PASSTHROUGH(c) (((c)->flags & (CHN_F_VIRTUAL | \ + CHN_F_PASSTHROUGH)) == \ + (CHN_F_VIRTUAL | CHN_F_PASSTHROUGH)) + + /* Get the best possible hardware format. */ + if (VCHAN_PASSTHROUGH(c)) + hwfmt = c->parentchannel->format; + else { + caps = chn_getcaps(c); + if (caps == NULL || caps->fmtlist == NULL) { + device_printf(c->dev, + "%s(): failed to get channel caps\n", __func__); + return (ENODEV); + } + + if ((c->format & AFMT_PASSTHROUGH) && + !snd_fmtvalid(c->format, caps->fmtlist)) + return (ENODEV); + + hwfmt = snd_fmtbest(c->format, caps->fmtlist); + if (hwfmt == 0 || !snd_fmtvalid(hwfmt, caps->fmtlist)) { + device_printf(c->dev, + "%s(): invalid hardware format 0x%08x\n", + __func__, hwfmt); + { + int i; + for (i = 0; caps->fmtlist[i] != 0; i++) + printf("0x%08x\n", caps->fmtlist[i]); + printf("Req: 0x%08x\n", c->format); + } + return (ENODEV); + } + } + + /* + * The 'hardware' possibly have different intepretation of channel + * matrixing, so get it first ..... + */ + hwmatrix = CHANNEL_GETMATRIX(c->methods, c->devinfo, hwfmt); + if (hwmatrix == NULL) { + device_printf(c->dev, + "%s(): failed to acquire hw matrix [0x%08x]\n", + __func__, hwfmt); + return (ENODEV); + } + /* ..... and rebuild hwfmt. */ + hwfmt = SND_FORMAT(hwfmt, hwmatrix->channels, hwmatrix->ext); + + /* Reset and rebuild default channel format/matrix map. */ + softfmt = c->format; + softmatrix = &c->matrix; + if (softmatrix->channels != AFMT_CHANNEL(softfmt) || + softmatrix->ext != AFMT_EXTCHANNEL(softfmt)) { + softmatrix = feeder_matrix_format_map(softfmt); + if (softmatrix == NULL) { + device_printf(c->dev, + "%s(): failed to acquire soft matrix [0x%08x]\n", + __func__, softfmt); + return (ENODEV); + } + c->matrix = *softmatrix; + c->matrix.id = SND_CHN_MATRIX_PCMCHANNEL; + } + softfmt = SND_FORMAT(softfmt, softmatrix->channels, softmatrix->ext); + if (softfmt != c->format) + device_printf(c->dev, + "%s(): WARNING: %s Soft format 0x%08x -> 0x%08x\n", + __func__, CHN_DIRSTR(c), c->format, softfmt); + + /* + * PLAY and REC are opposite. + */ + if (c->direction == PCMDIR_PLAY) { + cdesc.origin.afmt = softfmt; + cdesc.origin.matrix = softmatrix; + cdesc.origin.rate = c->speed; + cdesc.target.afmt = hwfmt; + cdesc.target.matrix = hwmatrix; + cdesc.target.rate = sndbuf_getspd(c->bufhard); + } else { + cdesc.origin.afmt = hwfmt; + cdesc.origin.matrix = hwmatrix; + cdesc.origin.rate = sndbuf_getspd(c->bufhard); + cdesc.target.afmt = softfmt; + cdesc.target.matrix = softmatrix; + cdesc.target.rate = c->speed; + } + + d = c->parentsnddev; + + /* + * If channel is in bitperfect or passthrough mode, make it appear + * that 'origin' and 'target' identical, skipping mostly chain + * procedures. + */ + if (CHN_BITPERFECT(c) || (c->format & AFMT_PASSTHROUGH)) { + if (c->direction == PCMDIR_PLAY) + cdesc.origin = cdesc.target; + else + cdesc.target = cdesc.origin; + c->format = cdesc.target.afmt; + c->speed = cdesc.target.rate; + } else { + /* hwfmt is not convertible, so 'dummy' it. */ + if (hwfmt & AFMT_PASSTHROUGH) + cdesc.dummy = 1; + + if ((softfmt & AFMT_CONVERTIBLE) && + (((d->flags & SD_F_VPC) && !(c->flags & CHN_F_HAS_VCHAN)) || + (!(d->flags & SD_F_VPC) && (d->flags & SD_F_SOFTPCMVOL) && + !(c->flags & CHN_F_VIRTUAL)))) + cdesc.use_volume = 1; + + if (feeder_matrix_compare(cdesc.origin.matrix, + cdesc.target.matrix) != 0) + cdesc.use_matrix = 1; + + /* Soft EQ only applicable for PLAY. */ + if (cdesc.dummy == 0 && + c->direction == PCMDIR_PLAY && (d->flags & SD_F_EQ) && + (((d->flags & SD_F_EQ_PC) && + !(c->flags & CHN_F_HAS_VCHAN)) || + (!(d->flags & SD_F_EQ_PC) && !(c->flags & CHN_F_VIRTUAL)))) + cdesc.use_eq = 1; + + if (FEEDFORMAT_NE_REQUIRED(&cdesc)) { + cdesc.afmt_ne = + (cdesc.dummy != 0) ? + snd_fmtbest(AFMT_ENCODING(softfmt), + feeder_chain_formats[cdesc.mode]) : + snd_fmtbest(AFMT_ENCODING(cdesc.target.afmt), + feeder_chain_formats[cdesc.mode]); + if (cdesc.afmt_ne == 0) { + device_printf(c->dev, + "%s(): snd_fmtbest failed!\n", __func__); + cdesc.afmt_ne = + (((cdesc.dummy != 0) ? softfmt : + cdesc.target.afmt) & + (AFMT_24BIT | AFMT_32BIT)) ? + AFMT_S32_NE : AFMT_S16_NE; + } + } + } + + cdesc.current = cdesc.origin; + + /* Build everything. */ + + c->feederflags = 0; + +#define FEEDER_BUILD(t) do { \ + ret = feeder_build_##t(c, &cdesc); \ + if (ret != 0) \ + return (ret); \ + } while (0) + + if (!(c->flags & CHN_F_HAS_VCHAN) || c->direction == PCMDIR_REC) + FEEDER_BUILD(root); + else if (c->direction == PCMDIR_PLAY && (c->flags & CHN_F_HAS_VCHAN)) + FEEDER_BUILD(mixer); + else + return (ENOTSUP); + + /* + * The basic idea is: The smaller the bandwidth, the cheaper the + * conversion process, with following constraints:- + * + * 1) Almost all feeders work best in 16/32 native endian. + * 2) Try to avoid 8bit feeders due to poor dynamic range. + * 3) Avoid volume, format, matrix and rate in BITPERFECT or + * PASSTHROUGH mode. + * 4) Try putting volume before EQ or rate. Should help to + * avoid/reduce possible clipping. + * 5) EQ require specific, valid rate, unless it allow sloppy + * conversion. + */ + if (FEEDMATRIX_UP(&cdesc)) { + if (FEEDEQ_REQUIRED(&cdesc) && + (!FEEDEQ_VALIDRATE(&cdesc, target) || + (cdesc.expensive == 0 && FEEDEQ_ECONOMY(&cdesc)))) + FEEDER_BUILD(eq); + if (FEEDRATE_REQUIRED(&cdesc)) + FEEDER_BUILD(rate); + FEEDER_BUILD(matrix); + if (FEEDVOLUME_REQUIRED(&cdesc)) + FEEDER_BUILD(volume); + if (FEEDEQ_REQUIRED(&cdesc)) + FEEDER_BUILD(eq); + } else if (FEEDMATRIX_DOWN(&cdesc)) { + FEEDER_BUILD(matrix); + if (FEEDVOLUME_REQUIRED(&cdesc)) + FEEDER_BUILD(volume); + if (FEEDEQ_REQUIRED(&cdesc) && + (!FEEDEQ_VALIDRATE(&cdesc, target) || + FEEDEQ_ECONOMY(&cdesc))) + FEEDER_BUILD(eq); + if (FEEDRATE_REQUIRED(&cdesc)) + FEEDER_BUILD(rate); + if (FEEDEQ_REQUIRED(&cdesc)) + FEEDER_BUILD(eq); + } else { + if (FEEDRATE_DOWN(&cdesc)) { + if (FEEDEQ_REQUIRED(&cdesc) && + !FEEDEQ_VALIDRATE(&cdesc, target)) { + if (FEEDVOLUME_REQUIRED(&cdesc)) + FEEDER_BUILD(volume); + FEEDER_BUILD(eq); + } + FEEDER_BUILD(rate); + } + if (FEEDMATRIX_REQUIRED(&cdesc)) + FEEDER_BUILD(matrix); + if (FEEDVOLUME_REQUIRED(&cdesc)) + FEEDER_BUILD(volume); + if (FEEDRATE_UP(&cdesc)) { + if (FEEDEQ_REQUIRED(&cdesc) && + !FEEDEQ_VALIDRATE(&cdesc, target)) + FEEDER_BUILD(eq); + FEEDER_BUILD(rate); + } + if (FEEDEQ_REQUIRED(&cdesc)) + FEEDER_BUILD(eq); + } + + if (FEEDFORMAT_REQUIRED(&cdesc)) + FEEDER_BUILD(format); + + if (c->direction == PCMDIR_REC && (c->flags & CHN_F_HAS_VCHAN)) + FEEDER_BUILD(mixer); + + sndbuf_setfmt(c->bufsoft, c->format); + sndbuf_setspd(c->bufsoft, c->speed); + + sndbuf_setfmt(c->bufhard, hwfmt); + + chn_syncstate(c); + + return (0); +} diff --git a/sys/dev/sound/pcm/feeder_eq.c b/sys/dev/sound/pcm/feeder_eq.c new file mode 100644 index 0000000..3a51b2c --- /dev/null +++ b/sys/dev/sound/pcm/feeder_eq.c @@ -0,0 +1,703 @@ +/*- + * 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. + */ + +/* + * feeder_eq: Parametric (compile time) Software Equalizer. Though accidental, + * it proves good enough for educational and general consumption. + * + * "Cookbook formulae for audio EQ biquad filter coefficients" + * by Robert Bristow-Johnson <rbj@audioimagination.com> + * - http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt + */ + +#ifdef _KERNEL +#ifdef HAVE_KERNEL_OPTION_HEADERS +#include "opt_snd.h" +#endif +#include <dev/sound/pcm/sound.h> +#include <dev/sound/pcm/pcm.h> +#include "feeder_if.h" + +#define SND_USE_FXDIV +#include "snd_fxdiv_gen.h" + +SND_DECLARE_FILE("$FreeBSD$"); +#endif + +#include "feeder_eq_gen.h" + +#define FEEDEQ_LEVELS \ + (((FEEDEQ_GAIN_MAX - FEEDEQ_GAIN_MIN) * \ + (FEEDEQ_GAIN_DIV / FEEDEQ_GAIN_STEP)) + 1) + +#define FEEDEQ_L2GAIN(v) \ + ((int)min(((v) * FEEDEQ_LEVELS) / 100, FEEDEQ_LEVELS - 1)) + +#define FEEDEQ_PREAMP_IPART(x) (abs(x) >> FEEDEQ_GAIN_SHIFT) +#define FEEDEQ_PREAMP_FPART(x) (abs(x) & FEEDEQ_GAIN_FMASK) +#define FEEDEQ_PREAMP_SIGNVAL(x) ((x) < 0 ? -1 : 1) +#define FEEDEQ_PREAMP_SIGNMARK(x) (((x) < 0) ? '-' : '+') + +#define FEEDEQ_PREAMP_IMIN -192 +#define FEEDEQ_PREAMP_IMAX 192 +#define FEEDEQ_PREAMP_FMIN 0 +#define FEEDEQ_PREAMP_FMAX 9 + +#define FEEDEQ_PREAMP_INVALID INT_MAX + +#define FEEDEQ_IF2PREAMP(i, f) \ + ((abs(i) << FEEDEQ_GAIN_SHIFT) | \ + (((abs(f) / FEEDEQ_GAIN_STEP) * FEEDEQ_GAIN_STEP) & \ + FEEDEQ_GAIN_FMASK)) + +#define FEEDEQ_PREAMP_MIN \ + (FEEDEQ_PREAMP_SIGNVAL(FEEDEQ_GAIN_MIN) * \ + FEEDEQ_IF2PREAMP(FEEDEQ_GAIN_MIN, 0)) + +#define FEEDEQ_PREAMP_MAX \ + (FEEDEQ_PREAMP_SIGNVAL(FEEDEQ_GAIN_MAX) * \ + FEEDEQ_IF2PREAMP(FEEDEQ_GAIN_MAX, 0)) + +#define FEEDEQ_PREAMP_DEFAULT FEEDEQ_IF2PREAMP(0, 0) + +#define FEEDEQ_PREAMP2IDX(v) \ + ((int32_t)((FEEDEQ_GAIN_MAX * (FEEDEQ_GAIN_DIV / \ + FEEDEQ_GAIN_STEP)) + (FEEDEQ_PREAMP_SIGNVAL(v) * \ + FEEDEQ_PREAMP_IPART(v) * (FEEDEQ_GAIN_DIV / \ + FEEDEQ_GAIN_STEP)) + (FEEDEQ_PREAMP_SIGNVAL(v) * \ + (FEEDEQ_PREAMP_FPART(v) / FEEDEQ_GAIN_STEP)))) + +static int feeder_eq_exact_rate = 0; + +#ifdef _KERNEL +static const char feeder_eq_presets[] = FEEDER_EQ_PRESETS; +SYSCTL_STRING(_hw_snd, OID_AUTO, feeder_eq_presets, CTLFLAG_RD, + &feeder_eq_presets, 0, "compile-time eq presets"); + +TUNABLE_INT("hw.snd.feeder_eq_exact_rate", &feeder_eq_exact_rate); +SYSCTL_INT(_hw_snd, OID_AUTO, feeder_eq_exact_rate, CTLFLAG_RW, + &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]; + intpcm_t i1[SND_CHN_MAX]; + intpcm_t i2[SND_CHN_MAX]; + int gain; +}; + +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 channels; + uint32_t rate; + uint32_t align; + int32_t preamp; + int state; +}; + +#if !defined(_KERNEL) && defined(FEEDEQ_ERR_CLIP) +#define FEEDEQ_ERR_CLIP_CHECK(t, v) do { \ + if ((v) < PCM_S32_MIN || (v) > PCM_S32_MAX) \ + errx(1, "\n\n%s(): ["#t"] Sample clipping: %jd\n", \ + __func__, (intmax_t)(v)); \ +} while (0) +#else +#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 \ + } + + +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 +}; + +#define FEEDEQ_BIQUAD_TAB_SIZE \ + ((int32_t)(sizeof(feed_eq_biquad_tab) / sizeof(feed_eq_biquad_tab[0]))) + +static struct feed_eq_coeff * +feed_eq_coeff_rate(uint32_t rate) +{ + uint32_t spd, threshold; + int i; + + if (rate < FEEDEQ_RATE_MIN || rate > FEEDEQ_RATE_MAX) + return (NULL); + + /* + * Not all rates are supported. Choose the best rate that we can to + * allow 'sloppy' conversion. Good enough for naive listeners. + */ + for (i = 0; i < FEEDEQ_TAB_SIZE; i++) { + spd = feed_eq_tab[i].rate; + threshold = spd + ((i < (FEEDEQ_TAB_SIZE - 1) && + feed_eq_tab[i + 1].rate > spd) ? + ((feed_eq_tab[i + 1].rate - spd) >> 1) : 0); + if (rate == spd || + (feeder_eq_exact_rate == 0 && rate <= threshold)) + return (feed_eq_tab[i].coeff); + } + + return (NULL); +} + +int +feeder_eq_validrate(uint32_t rate) +{ + + if (feed_eq_coeff_rate(rate) != NULL) + return (1); + + return (0); +} + +static void +feed_eq_reset(struct feed_eq_info *info) +{ + uint32_t i; + + for (i = 0; i < info->channels; i++) { + info->treble.i1[i] = 0; + info->treble.i2[i] = 0; + info->treble.o1[i] = 0; + info->treble.o2[i] = 0; + info->bass.i1[i] = 0; + info->bass.i2[i] = 0; + info->bass.o1[i] = 0; + info->bass.o2[i] = 0; + } +} + +static int +feed_eq_setup(struct feed_eq_info *info) +{ + + info->coeff = feed_eq_coeff_rate(info->rate); + if (info->coeff == NULL) + return (EINVAL); + + feed_eq_reset(info); + + return (0); +} + +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->channels = AFMT_CHANNEL(f->desc->in); + info->align = info->channels * AFMT_BPS(f->desc->in); + + info->rate = FEEDEQ_RATE_MIN; + info->treble.gain = FEEDEQ_L2GAIN(50); + info->bass.gain = FEEDEQ_L2GAIN(50); + info->preamp = FEEDEQ_PREAMP2IDX(FEEDEQ_PREAMP_DEFAULT); + info->state = FEEDEQ_UNKNOWN; + + info->biquad = biquad_op; + + f->data = info; + + return (feed_eq_setup(info)); +} + +static int +feed_eq_set(struct pcm_feeder *f, int what, int value) +{ + struct feed_eq_info *info; + + info = f->data; + + switch (what) { + case FEEDEQ_CHANNELS: + if (value < SND_CHN_MIN || value > SND_CHN_MAX) + return (EINVAL); + info->channels = (uint32_t)value; + info->align = info->channels * AFMT_BPS(f->desc->in); + feed_eq_reset(info); + break; + case FEEDEQ_RATE: + if (feeder_eq_validrate(value) == 0) + return (EINVAL); + info->rate = (uint32_t)value; + if (info->state == FEEDEQ_UNKNOWN) + info->state = FEEDEQ_ENABLE; + return (feed_eq_setup(info)); + break; + case FEEDEQ_TREBLE: + case FEEDEQ_BASS: + if (value < 0 || value > 100) + return (EINVAL); + if (what == FEEDEQ_TREBLE) + info->treble.gain = FEEDEQ_L2GAIN(value); + else + info->bass.gain = FEEDEQ_L2GAIN(value); + break; + case FEEDEQ_PREAMP: + if (value < FEEDEQ_PREAMP_MIN || value > FEEDEQ_PREAMP_MAX) + return (EINVAL); + info->preamp = FEEDEQ_PREAMP2IDX(value); + break; + case FEEDEQ_STATE: + if (!(value == FEEDEQ_BYPASS || value == FEEDEQ_ENABLE || + value == FEEDEQ_DISABLE)) + return (EINVAL); + info->state = value; + feed_eq_reset(info); + break; + default: + return (EINVAL); + break; + } + + return (0); +} + +static int +feed_eq_free(struct pcm_feeder *f) +{ + struct feed_eq_info *info; + + info = f->data; + if (info != NULL) + free(info, M_DEVBUF); + + f->data = NULL; + + return (0); +} + +static int +feed_eq_feed(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, + uint32_t count, void *source) +{ + struct feed_eq_info *info; + uint32_t j; + uint8_t *dst; + + info = f->data; + + /* + * 3 major states: + * FEEDEQ_BYPASS - Bypass entirely, nothing happened. + * FEEDEQ_ENABLE - Preamp+biquad filtering. + * FEEDEQ_DISABLE - Preamp only. + */ + if (info->state == FEEDEQ_BYPASS) + return (FEEDER_FEED(f->source, c, b, count, source)); + + dst = b; + count = SND_FXROUND(count, info->align); + + do { + if (count < info->align) + break; + + j = SND_FXDIV(FEEDER_FEED(f->source, c, dst, count, source), + info->align); + if (j == 0) + break; + + info->biquad(info, dst, j); + + j *= info->align; + dst += j; + count -= j; + + } while (count != 0); + + return (dst - b); +} + +static struct pcm_feederdesc feeder_eq_desc[] = { + { FEEDER_EQ, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0 } +}; + +static kobj_method_t feeder_eq_methods[] = { + KOBJMETHOD(feeder_init, feed_eq_init), + KOBJMETHOD(feeder_free, feed_eq_free), + KOBJMETHOD(feeder_set, feed_eq_set), + KOBJMETHOD(feeder_feed, feed_eq_feed), + KOBJMETHOD_END +}; + +FEEDER_DECLARE(feeder_eq, NULL); + +static int32_t +feed_eq_scan_preamp_arg(const char *s) +{ + int r, i, f; + size_t len; + char buf[32]; + + bzero(buf, sizeof(buf)); + + /* XXX kind of ugly, but works for now.. */ + + r = sscanf(s, "%d.%d", &i, &f); + + if (r == 1 && !(i < FEEDEQ_PREAMP_IMIN || i > FEEDEQ_PREAMP_IMAX)) { + snprintf(buf, sizeof(buf), "%c%d", + FEEDEQ_PREAMP_SIGNMARK(i), abs(i)); + f = 0; + } else if (r == 2 && + !(i < FEEDEQ_PREAMP_IMIN || i > FEEDEQ_PREAMP_IMAX || + f < FEEDEQ_PREAMP_FMIN || f > FEEDEQ_PREAMP_FMAX)) + snprintf(buf, sizeof(buf), "%c%d.%d", + FEEDEQ_PREAMP_SIGNMARK(i), abs(i), f); + else + return (FEEDEQ_PREAMP_INVALID); + + len = strlen(s); + if (len > 2 && strcasecmp(s + len - 2, "dB") == 0) + strlcat(buf, "dB", sizeof(buf)); + + if (i == 0 && *s == '-') + *buf = '-'; + + if (strcasecmp(buf + ((*s >= '0' && *s <= '9') ? 1 : 0), s) != 0) + return (FEEDEQ_PREAMP_INVALID); + + while ((f / FEEDEQ_GAIN_DIV) > 0) + f /= FEEDEQ_GAIN_DIV; + + return (((i < 0 || *buf == '-') ? -1 : 1) * FEEDEQ_IF2PREAMP(i, f)); +} + +#ifdef _KERNEL +static int +sysctl_dev_pcm_eq(SYSCTL_HANDLER_ARGS) +{ + struct snddev_info *d; + struct pcm_channel *c; + struct pcm_feeder *f; + int err, val, oval; + + d = oidp->oid_arg1; + if (!PCM_REGISTERED(d)) + return (ENODEV); + + PCM_LOCK(d); + PCM_WAIT(d); + if (d->flags & SD_F_EQ_BYPASSED) + val = 2; + else if (d->flags & SD_F_EQ_ENABLED) + val = 1; + else + val = 0; + PCM_ACQUIRE(d); + PCM_UNLOCK(d); + + oval = val; + err = sysctl_handle_int(oidp, &val, 0, req); + + if (err == 0 && req->newptr != NULL && val != oval) { + if (!(val == 0 || val == 1 || val == 2)) { + PCM_RELEASE_QUICK(d); + return (EINVAL); + } + + PCM_LOCK(d); + + d->flags &= ~(SD_F_EQ_ENABLED | SD_F_EQ_BYPASSED); + if (val == 2) { + val = FEEDEQ_BYPASS; + d->flags |= SD_F_EQ_BYPASSED; + } else if (val == 1) { + val = FEEDEQ_ENABLE; + d->flags |= SD_F_EQ_ENABLED; + } else + val = FEEDEQ_DISABLE; + + CHN_FOREACH(c, d, channels.pcm.busy) { + CHN_LOCK(c); + f = chn_findfeeder(c, FEEDER_EQ); + if (f != NULL) + (void)FEEDER_SET(f, FEEDEQ_STATE, val); + CHN_UNLOCK(c); + } + + PCM_RELEASE(d); + PCM_UNLOCK(d); + } else + PCM_RELEASE_QUICK(d); + + return (err); +} + +static int +sysctl_dev_pcm_eq_preamp(SYSCTL_HANDLER_ARGS) +{ + struct snddev_info *d; + struct pcm_channel *c; + struct pcm_feeder *f; + int err, val, oval; + char buf[32]; + + d = oidp->oid_arg1; + if (!PCM_REGISTERED(d)) + return (ENODEV); + + PCM_LOCK(d); + PCM_WAIT(d); + val = d->eqpreamp; + bzero(buf, sizeof(buf)); + (void)snprintf(buf, sizeof(buf), "%c%d.%ddB", + FEEDEQ_PREAMP_SIGNMARK(val), FEEDEQ_PREAMP_IPART(val), + FEEDEQ_PREAMP_FPART(val)); + PCM_ACQUIRE(d); + PCM_UNLOCK(d); + + oval = val; + err = sysctl_handle_string(oidp, buf, sizeof(buf), req); + + if (err == 0 && req->newptr != NULL) { + val = feed_eq_scan_preamp_arg(buf); + if (val == FEEDEQ_PREAMP_INVALID) { + PCM_RELEASE_QUICK(d); + return (EINVAL); + } + + PCM_LOCK(d); + + if (val != oval) { + if (val < FEEDEQ_PREAMP_MIN) + val = FEEDEQ_PREAMP_MIN; + else if (val > FEEDEQ_PREAMP_MAX) + val = FEEDEQ_PREAMP_MAX; + + d->eqpreamp = val; + + CHN_FOREACH(c, d, channels.pcm.busy) { + CHN_LOCK(c); + f = chn_findfeeder(c, FEEDER_EQ); + if (f != NULL) + (void)FEEDER_SET(f, FEEDEQ_PREAMP, val); + CHN_UNLOCK(c); + } + + } + + PCM_RELEASE(d); + PCM_UNLOCK(d); + } else + PCM_RELEASE_QUICK(d); + + return (err); +} + +void +feeder_eq_initsys(device_t dev) +{ + struct snddev_info *d; + const char *preamp; + char buf[64]; + + d = device_get_softc(dev); + + if (!(resource_string_value(device_get_name(dev), device_get_unit(dev), + "eq_preamp", &preamp) == 0 && + (d->eqpreamp = feed_eq_scan_preamp_arg(preamp)) != + FEEDEQ_PREAMP_INVALID)) + d->eqpreamp = FEEDEQ_PREAMP_DEFAULT; + + if (d->eqpreamp < FEEDEQ_PREAMP_MIN) + d->eqpreamp = FEEDEQ_PREAMP_MIN; + else if (d->eqpreamp > FEEDEQ_PREAMP_MAX) + d->eqpreamp = FEEDEQ_PREAMP_MAX; + + SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), + SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, + "eq", CTLTYPE_INT | CTLFLAG_RW, d, sizeof(d), + sysctl_dev_pcm_eq, "I", + "Bass/Treble Equalizer (0=disable, 1=enable, 2=bypass)"); + + bzero(buf, sizeof(buf)); + + (void)snprintf(buf, sizeof(buf), "Bass/Treble Equalizer Preamp " + "(-/+ %d.0dB , %d.%ddB step)", + FEEDEQ_GAIN_MAX, FEEDEQ_GAIN_STEP / FEEDEQ_GAIN_DIV, + FEEDEQ_GAIN_STEP - ((FEEDEQ_GAIN_STEP / FEEDEQ_GAIN_DIV) * + FEEDEQ_GAIN_DIV)); + + SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), + SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, + "eq_preamp", CTLTYPE_STRING | CTLFLAG_RW, d, sizeof(d), + sysctl_dev_pcm_eq_preamp, "A", buf); +} +#endif diff --git a/sys/dev/sound/pcm/feeder_fmt.c b/sys/dev/sound/pcm/feeder_fmt.c deleted file mode 100644 index 9fcf260..0000000 --- a/sys/dev/sound/pcm/feeder_fmt.c +++ /dev/null @@ -1,1435 +0,0 @@ -/*- - * Copyright (c) 1999 Cameron Grant <cg@FreeBSD.org> - * Copyright (c) 2005 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. - */ - -/* - * *New* and rewritten soft format converter, supporting 24/32bit pcm data, - * simplified and optimized. - * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * * - * This new implementation is fully dedicated in memory of Cameron Grant, * - * the creator of the magnificent, highly addictive feeder infrastructure. * - * * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * - */ - -#include <dev/sound/pcm/sound.h> -#include "feeder_if.h" - -SND_DECLARE_FILE("$FreeBSD$"); - -static int feeder_fmt_stereodownmix = 0; -TUNABLE_INT("hw.snd.feeder_fmt_stereodownmix", &feeder_fmt_stereodownmix); -#ifdef SND_DEBUG -SYSCTL_INT(_hw_snd, OID_AUTO, feeder_fmt_stereodownmix, CTLFLAG_RW, - &feeder_fmt_stereodownmix, 1, "averaging stereo downmix"); -#endif - -#define FEEDFMT_RESERVOIR 8 /* 32bit stereo */ - -static uint8_t ulaw_to_u8_tbl[] = { - 3, 7, 11, 15, 19, 23, 27, 31, - 35, 39, 43, 47, 51, 55, 59, 63, - 66, 68, 70, 72, 74, 76, 78, 80, - 82, 84, 86, 88, 90, 92, 94, 96, - 98, 99, 100, 101, 102, 103, 104, 105, - 106, 107, 108, 109, 110, 111, 112, 113, - 113, 114, 114, 115, 115, 116, 116, 117, - 117, 118, 118, 119, 119, 120, 120, 121, - 121, 121, 122, 122, 122, 122, 123, 123, - 123, 123, 124, 124, 124, 124, 125, 125, - 125, 125, 125, 125, 126, 126, 126, 126, - 126, 126, 126, 126, 127, 127, 127, 127, - 127, 127, 127, 127, 127, 127, 127, 127, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 253, 249, 245, 241, 237, 233, 229, 225, - 221, 217, 213, 209, 205, 201, 197, 193, - 190, 188, 186, 184, 182, 180, 178, 176, - 174, 172, 170, 168, 166, 164, 162, 160, - 158, 157, 156, 155, 154, 153, 152, 151, - 150, 149, 148, 147, 146, 145, 144, 143, - 143, 142, 142, 141, 141, 140, 140, 139, - 139, 138, 138, 137, 137, 136, 136, 135, - 135, 135, 134, 134, 134, 134, 133, 133, - 133, 133, 132, 132, 132, 132, 131, 131, - 131, 131, 131, 131, 130, 130, 130, 130, - 130, 130, 130, 130, 129, 129, 129, 129, - 129, 129, 129, 129, 129, 129, 129, 129, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, -}; - -static uint8_t alaw_to_u8_tbl[] = { - 108, 109, 106, 107, 112, 113, 110, 111, - 100, 101, 98, 99, 104, 105, 102, 103, - 118, 118, 117, 117, 120, 120, 119, 119, - 114, 114, 113, 113, 116, 116, 115, 115, - 43, 47, 35, 39, 59, 63, 51, 55, - 11, 15, 3, 7, 27, 31, 19, 23, - 86, 88, 82, 84, 94, 96, 90, 92, - 70, 72, 66, 68, 78, 80, 74, 76, - 127, 127, 127, 127, 127, 127, 127, 127, - 127, 127, 127, 127, 127, 127, 127, 127, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 123, 123, 123, 123, 124, 124, 124, 124, - 121, 121, 121, 121, 122, 122, 122, 122, - 126, 126, 126, 126, 126, 126, 126, 126, - 125, 125, 125, 125, 125, 125, 125, 125, - 148, 147, 150, 149, 144, 143, 146, 145, - 156, 155, 158, 157, 152, 151, 154, 153, - 138, 138, 139, 139, 136, 136, 137, 137, - 142, 142, 143, 143, 140, 140, 141, 141, - 213, 209, 221, 217, 197, 193, 205, 201, - 245, 241, 253, 249, 229, 225, 237, 233, - 170, 168, 174, 172, 162, 160, 166, 164, - 186, 184, 190, 188, 178, 176, 182, 180, - 129, 129, 129, 129, 129, 129, 129, 129, - 129, 129, 129, 129, 129, 129, 129, 129, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 133, 133, 133, 133, 132, 132, 132, 132, - 135, 135, 135, 135, 134, 134, 134, 134, - 130, 130, 130, 130, 130, 130, 130, 130, - 131, 131, 131, 131, 131, 131, 131, 131, -}; - -static uint8_t u8_to_ulaw_tbl[] = { - 0, 0, 0, 0, 0, 1, 1, 1, - 1, 2, 2, 2, 2, 3, 3, 3, - 3, 4, 4, 4, 4, 5, 5, 5, - 5, 6, 6, 6, 6, 7, 7, 7, - 7, 8, 8, 8, 8, 9, 9, 9, - 9, 10, 10, 10, 10, 11, 11, 11, - 11, 12, 12, 12, 12, 13, 13, 13, - 13, 14, 14, 14, 14, 15, 15, 15, - 15, 16, 16, 17, 17, 18, 18, 19, - 19, 20, 20, 21, 21, 22, 22, 23, - 23, 24, 24, 25, 25, 26, 26, 27, - 27, 28, 28, 29, 29, 30, 30, 31, - 31, 32, 33, 34, 35, 36, 37, 38, - 39, 40, 41, 42, 43, 44, 45, 46, - 47, 49, 51, 53, 55, 57, 59, 61, - 63, 66, 70, 74, 78, 84, 92, 104, - 254, 231, 219, 211, 205, 201, 197, 193, - 190, 188, 186, 184, 182, 180, 178, 176, - 175, 174, 173, 172, 171, 170, 169, 168, - 167, 166, 165, 164, 163, 162, 161, 160, - 159, 159, 158, 158, 157, 157, 156, 156, - 155, 155, 154, 154, 153, 153, 152, 152, - 151, 151, 150, 150, 149, 149, 148, 148, - 147, 147, 146, 146, 145, 145, 144, 144, - 143, 143, 143, 143, 142, 142, 142, 142, - 141, 141, 141, 141, 140, 140, 140, 140, - 139, 139, 139, 139, 138, 138, 138, 138, - 137, 137, 137, 137, 136, 136, 136, 136, - 135, 135, 135, 135, 134, 134, 134, 134, - 133, 133, 133, 133, 132, 132, 132, 132, - 131, 131, 131, 131, 130, 130, 130, 130, - 129, 129, 129, 129, 128, 128, 128, 128, -}; - -static uint8_t u8_to_alaw_tbl[] = { - 42, 42, 42, 42, 42, 43, 43, 43, - 43, 40, 40, 40, 40, 41, 41, 41, - 41, 46, 46, 46, 46, 47, 47, 47, - 47, 44, 44, 44, 44, 45, 45, 45, - 45, 34, 34, 34, 34, 35, 35, 35, - 35, 32, 32, 32, 32, 33, 33, 33, - 33, 38, 38, 38, 38, 39, 39, 39, - 39, 36, 36, 36, 36, 37, 37, 37, - 37, 58, 58, 59, 59, 56, 56, 57, - 57, 62, 62, 63, 63, 60, 60, 61, - 61, 50, 50, 51, 51, 48, 48, 49, - 49, 54, 54, 55, 55, 52, 52, 53, - 53, 10, 11, 8, 9, 14, 15, 12, - 13, 2, 3, 0, 1, 6, 7, 4, - 5, 24, 30, 28, 18, 16, 22, 20, - 106, 110, 98, 102, 122, 114, 75, 90, - 213, 197, 245, 253, 229, 225, 237, 233, - 149, 151, 145, 147, 157, 159, 153, 155, - 133, 132, 135, 134, 129, 128, 131, 130, - 141, 140, 143, 142, 137, 136, 139, 138, - 181, 181, 180, 180, 183, 183, 182, 182, - 177, 177, 176, 176, 179, 179, 178, 178, - 189, 189, 188, 188, 191, 191, 190, 190, - 185, 185, 184, 184, 187, 187, 186, 186, - 165, 165, 165, 165, 164, 164, 164, 164, - 167, 167, 167, 167, 166, 166, 166, 166, - 161, 161, 161, 161, 160, 160, 160, 160, - 163, 163, 163, 163, 162, 162, 162, 162, - 173, 173, 173, 173, 172, 172, 172, 172, - 175, 175, 175, 175, 174, 174, 174, 174, - 169, 169, 169, 169, 168, 168, 168, 168, - 171, 171, 171, 171, 170, 170, 170, 170, -}; - -static int -feed_table_8(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, - uint32_t count, void *source) -{ - int j, sign, k; - uint8_t *tbl = (uint8_t *)f->data; - - if (count < PCM_8_BPS) - return (0); - - k = FEEDER_FEED(f->source, c, b, count, source); - if (k < PCM_8_BPS) - return (0); - - j = k; - sign = (f->desc->out & AFMT_SIGNED) ? 0x80 : 0x00; - - do { - j--; - b[j] = tbl[b[j]] ^ sign; - } while (j != 0); - - return (k); -} - -static int -feed_table_16(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, - uint32_t count, void *source) -{ - int i, j, sign, k; - uint8_t *tbl = (uint8_t *)f->data; - - if (count < PCM_16_BPS) - return (0); - - k = FEEDER_FEED(f->source, c, b, count >> 1, source); - if (k < PCM_8_BPS) - return (0); - - i = k; - k <<= 1; - j = k; - sign = (f->desc->out & AFMT_SIGNED) ? 0x80 : 0x00; - - if (f->desc->out & AFMT_BIGENDIAN) { - do { - b[--j] = 0; - b[--j] = tbl[b[--i]] ^ sign; - } while (i != 0); - } else { - do { - b[--j] = tbl[b[--i]] ^ sign; - b[--j] = 0; - } while (i != 0); - } - - return (k); -} - -static int -feed_table_xlaw(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, - uint32_t count, void *source) -{ - int j, sign, k; - uint8_t *tbl = (uint8_t *)f->data; - - if (count < PCM_8_BPS) - return (0); - - k = FEEDER_FEED(f->source, c, b, count, source); - if (k < PCM_8_BPS) - return (0); - - j = k ; - sign = (f->desc->in & AFMT_SIGNED) ? 0x80 : 0x00; - - do { - j--; - b[j] = tbl[b[j] ^ sign]; - } while (j != 0); - - return (k); -} - -static struct pcm_feederdesc feeder_ulawto8_desc[] = { - {FEEDER_FMT, AFMT_MU_LAW, AFMT_U8, 0}, - {FEEDER_FMT, AFMT_MU_LAW | AFMT_STEREO, AFMT_U8 | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_MU_LAW, AFMT_S8, 0}, - {FEEDER_FMT, AFMT_MU_LAW | AFMT_STEREO, AFMT_S8 | AFMT_STEREO, 0}, - {0, 0, 0, 0}, -}; -static kobj_method_t feeder_ulawto8_methods[] = { - KOBJMETHOD(feeder_feed, feed_table_8), - {0, 0} -}; -FEEDER_DECLARE(feeder_ulawto8, 0, ulaw_to_u8_tbl); - -static struct pcm_feederdesc feeder_alawto8_desc[] = { - {FEEDER_FMT, AFMT_A_LAW, AFMT_U8, 0}, - {FEEDER_FMT, AFMT_A_LAW | AFMT_STEREO, AFMT_U8 | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_A_LAW, AFMT_S8, 0}, - {FEEDER_FMT, AFMT_A_LAW | AFMT_STEREO, AFMT_S8 | AFMT_STEREO, 0}, - {0, 0, 0, 0}, -}; -static kobj_method_t feeder_alawto8_methods[] = { - KOBJMETHOD(feeder_feed, feed_table_8), - {0, 0} -}; -FEEDER_DECLARE(feeder_alawto8, 0, alaw_to_u8_tbl); - -static struct pcm_feederdesc feeder_ulawto16_desc[] = { - {FEEDER_FMT, AFMT_MU_LAW, AFMT_S16_LE, 0}, - {FEEDER_FMT, AFMT_MU_LAW | AFMT_STEREO, AFMT_S16_LE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_MU_LAW, AFMT_U16_LE, 0}, - {FEEDER_FMT, AFMT_MU_LAW | AFMT_STEREO, AFMT_U16_LE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_MU_LAW, AFMT_S16_BE, 0}, - {FEEDER_FMT, AFMT_MU_LAW | AFMT_STEREO, AFMT_S16_BE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_MU_LAW, AFMT_U16_BE, 0}, - {FEEDER_FMT, AFMT_MU_LAW | AFMT_STEREO, AFMT_U16_BE | AFMT_STEREO, 0}, - {0, 0, 0, 0}, -}; -static kobj_method_t feeder_ulawto16_methods[] = { - KOBJMETHOD(feeder_feed, feed_table_16), - {0, 0} -}; -FEEDER_DECLARE(feeder_ulawto16, 0, ulaw_to_u8_tbl); - -static struct pcm_feederdesc feeder_alawto16_desc[] = { - {FEEDER_FMT, AFMT_A_LAW, AFMT_S16_LE, 0}, - {FEEDER_FMT, AFMT_A_LAW | AFMT_STEREO, AFMT_S16_LE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_A_LAW, AFMT_U16_LE, 0}, - {FEEDER_FMT, AFMT_A_LAW | AFMT_STEREO, AFMT_U16_LE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_A_LAW, AFMT_S16_BE, 0}, - {FEEDER_FMT, AFMT_A_LAW | AFMT_STEREO, AFMT_S16_BE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_A_LAW, AFMT_U16_BE, 0}, - {FEEDER_FMT, AFMT_A_LAW | AFMT_STEREO, AFMT_U16_BE | AFMT_STEREO, 0}, - {0, 0, 0, 0}, -}; -static kobj_method_t feeder_alawto16_methods[] = { - KOBJMETHOD(feeder_feed, feed_table_16), - {0, 0} -}; -FEEDER_DECLARE(feeder_alawto16, 0, alaw_to_u8_tbl); - -static struct pcm_feederdesc feeder_8toulaw_desc[] = { - {FEEDER_FMT, AFMT_U8, AFMT_MU_LAW, 0}, - {FEEDER_FMT, AFMT_U8 | AFMT_STEREO, AFMT_MU_LAW | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S8, AFMT_MU_LAW, 0}, - {FEEDER_FMT, AFMT_S8 | AFMT_STEREO, AFMT_MU_LAW | AFMT_STEREO, 0}, - {0, 0, 0, 0}, -}; -static kobj_method_t feeder_8toulaw_methods[] = { - KOBJMETHOD(feeder_feed, feed_table_xlaw), - {0, 0} -}; -FEEDER_DECLARE(feeder_8toulaw, 0, u8_to_ulaw_tbl); - -static struct pcm_feederdesc feeder_8toalaw_desc[] = { - {FEEDER_FMT, AFMT_U8, AFMT_A_LAW, 0}, - {FEEDER_FMT, AFMT_U8 | AFMT_STEREO, AFMT_A_LAW | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S8, AFMT_A_LAW, 0}, - {FEEDER_FMT, AFMT_S8 | AFMT_STEREO, AFMT_A_LAW | AFMT_STEREO, 0}, - {0, 0, 0, 0}, -}; -static kobj_method_t feeder_8toalaw_methods[] = { - KOBJMETHOD(feeder_feed, feed_table_xlaw), - {0, 0} -}; -FEEDER_DECLARE(feeder_8toalaw, 0, u8_to_alaw_tbl); - -/* - * All conversion done in byte level to preserve endianess. - */ - -#define FEEDFMT_SWAP_SIGN(f) (((((f)->desc->in & AFMT_SIGNED) == 0) != \ - (((f)->desc->out & AFMT_SIGNED) == 0)) \ - ? 0x80 : 0x00) - -/* - * Bit conversion - */ - -#define FBIT_DATA(i, o, c) ((intptr_t)((((c) & 0xf) << 6) | \ - (((i) & 0x7) << 3) | ((o) & 0x7))) -#define FBIT_OUTBPS(m) ((m) & 0x7) -#define FBIT_INBPS(m) FBIT_OUTBPS((m) >> 3) -#define FBIT_CHANNELS(m) (((m) >> 6) & 0xf) - -static int -feed_updownbit_init(struct pcm_feeder *f) -{ - int ibps, obps, channels; - - if (f->desc->in == f->desc->out || (f->desc->in & AFMT_STEREO) != - (f->desc->out & AFMT_STEREO)) - return (-1); - - channels = (f->desc->in & AFMT_STEREO) ? 2 : 1; - - if (f->desc->in & AFMT_32BIT) - ibps = PCM_32_BPS; - else if (f->desc->in & AFMT_24BIT) - ibps = PCM_24_BPS; - else if (f->desc->in & AFMT_16BIT) - ibps = PCM_16_BPS; - else - ibps = PCM_8_BPS; - - if (f->desc->out & AFMT_32BIT) - obps = PCM_32_BPS; - else if (f->desc->out & AFMT_24BIT) - obps = PCM_24_BPS; - else if (f->desc->out & AFMT_16BIT) - obps = PCM_16_BPS; - else - obps = PCM_8_BPS; - - f->data = (void *)FBIT_DATA(ibps, obps, channels); - - return (0); -} - -static int -feed_upbit(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, - uint32_t count, void *source) -{ - int i, j, k, sign, ibps, ialign, obps, oalign, pad; - uint8_t *src, *dst; - - ibps = FBIT_INBPS((intptr_t)f->data); - obps = FBIT_OUTBPS((intptr_t)f->data); - - ialign = ibps * FBIT_CHANNELS((intptr_t)f->data); - oalign = obps * FBIT_CHANNELS((intptr_t)f->data); - - if (count < oalign) - return (0); - - k = FEEDER_FEED(f->source, c, b, (count / oalign) * ialign, source); - if (k < ialign) - return (0); - - k -= k % ialign; - j = (k / ibps) * obps; - pad = obps - ibps; - src = b + k; - dst = b + j; - sign = FEEDFMT_SWAP_SIGN(f); - - if (f->desc->out & AFMT_BIGENDIAN) { - do { - i = pad; - do { - *--dst = 0; - } while (--i != 0); - i = ibps; - while (--i != 0) - *--dst = *--src; - *--dst = *--src ^ sign; - } while (dst != b); - } else { - do { - *--dst = *--src ^ sign; - i = ibps; - while (--i != 0) - *--dst = *--src; - i = pad; - do { - *--dst = 0; - } while (--i != 0); - } while (dst != b); - } - - return (j); -} - -static struct pcm_feederdesc feeder_8to16_desc[] = { - {FEEDER_FMT, AFMT_U8, AFMT_U16_LE, 0}, - {FEEDER_FMT, AFMT_U8 | AFMT_STEREO, AFMT_U16_LE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S8, AFMT_S16_LE, 0}, - {FEEDER_FMT, AFMT_S8 | AFMT_STEREO, AFMT_S16_LE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_U8, AFMT_U16_BE, 0}, - {FEEDER_FMT, AFMT_U8 | AFMT_STEREO, AFMT_U16_BE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S8, AFMT_S16_BE, 0}, - {FEEDER_FMT, AFMT_S8 | AFMT_STEREO, AFMT_S16_BE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_U8, AFMT_S16_LE, 0}, - {FEEDER_FMT, AFMT_U8 | AFMT_STEREO, AFMT_S16_LE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S8, AFMT_U16_LE, 0}, - {FEEDER_FMT, AFMT_S8 | AFMT_STEREO, AFMT_U16_LE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_U8, AFMT_S16_BE, 0}, - {FEEDER_FMT, AFMT_U8 | AFMT_STEREO, AFMT_S16_BE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S8, AFMT_U16_BE, 0}, - {FEEDER_FMT, AFMT_S8 | AFMT_STEREO, AFMT_U16_BE | AFMT_STEREO, 0}, - {0, 0, 0, 0}, -}; -static kobj_method_t feeder_8to16_methods[] = { - KOBJMETHOD(feeder_init, feed_updownbit_init), - KOBJMETHOD(feeder_feed, feed_upbit), - {0, 0} -}; -FEEDER_DECLARE(feeder_8to16, 0, NULL); - -static struct pcm_feederdesc feeder_8to24_desc[] = { - {FEEDER_FMT, AFMT_U8, AFMT_U24_LE, 0}, - {FEEDER_FMT, AFMT_U8 | AFMT_STEREO, AFMT_U24_LE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S8, AFMT_S24_LE, 0}, - {FEEDER_FMT, AFMT_S8 | AFMT_STEREO, AFMT_S24_LE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_U8, AFMT_U24_BE, 0}, - {FEEDER_FMT, AFMT_U8 | AFMT_STEREO, AFMT_U24_BE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S8, AFMT_S24_BE, 0}, - {FEEDER_FMT, AFMT_S8 | AFMT_STEREO, AFMT_S24_BE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_U8, AFMT_S24_LE, 0}, - {FEEDER_FMT, AFMT_U8 | AFMT_STEREO, AFMT_S24_LE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S8, AFMT_U24_LE, 0}, - {FEEDER_FMT, AFMT_S8 | AFMT_STEREO, AFMT_U24_LE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_U8, AFMT_S24_BE, 0}, - {FEEDER_FMT, AFMT_U8 | AFMT_STEREO, AFMT_S24_BE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S8, AFMT_U24_BE, 0}, - {FEEDER_FMT, AFMT_S8 | AFMT_STEREO, AFMT_U24_BE | AFMT_STEREO, 0}, - {0, 0, 0, 0}, -}; -static kobj_method_t feeder_8to24_methods[] = { - KOBJMETHOD(feeder_init, feed_updownbit_init), - KOBJMETHOD(feeder_feed, feed_upbit), - {0, 0} -}; -FEEDER_DECLARE(feeder_8to24, 0, NULL); - -static struct pcm_feederdesc feeder_8to32_desc[] = { - {FEEDER_FMT, AFMT_U8, AFMT_U32_LE, 0}, - {FEEDER_FMT, AFMT_U8 | AFMT_STEREO, AFMT_U32_LE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S8, AFMT_S32_LE, 0}, - {FEEDER_FMT, AFMT_S8 | AFMT_STEREO, AFMT_S32_LE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_U8, AFMT_U32_BE, 0}, - {FEEDER_FMT, AFMT_U8 | AFMT_STEREO, AFMT_U32_BE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S8, AFMT_S32_BE, 0}, - {FEEDER_FMT, AFMT_S8 | AFMT_STEREO, AFMT_S32_BE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_U8, AFMT_S32_LE, 0}, - {FEEDER_FMT, AFMT_U8 | AFMT_STEREO, AFMT_S32_LE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S8, AFMT_U32_LE, 0}, - {FEEDER_FMT, AFMT_S8 | AFMT_STEREO, AFMT_U32_LE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_U8, AFMT_S32_BE, 0}, - {FEEDER_FMT, AFMT_U8 | AFMT_STEREO, AFMT_S32_BE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S8, AFMT_U32_BE, 0}, - {FEEDER_FMT, AFMT_S8 | AFMT_STEREO, AFMT_U32_BE | AFMT_STEREO, 0}, - {0, 0, 0, 0}, -}; -static kobj_method_t feeder_8to32_methods[] = { - KOBJMETHOD(feeder_init, feed_updownbit_init), - KOBJMETHOD(feeder_feed, feed_upbit), - {0, 0} -}; -FEEDER_DECLARE(feeder_8to32, 0, NULL); - -static struct pcm_feederdesc feeder_16to24_desc[] = { - {FEEDER_FMT, AFMT_U16_LE, AFMT_U24_LE, 0}, - {FEEDER_FMT, AFMT_U16_LE | AFMT_STEREO, AFMT_U24_LE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S16_LE, AFMT_S24_LE, 0}, - {FEEDER_FMT, AFMT_S16_LE | AFMT_STEREO, AFMT_S24_LE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_U16_BE, AFMT_U24_BE, 0}, - {FEEDER_FMT, AFMT_U16_BE | AFMT_STEREO, AFMT_U24_BE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S16_BE, AFMT_S24_BE, 0}, - {FEEDER_FMT, AFMT_S16_BE | AFMT_STEREO, AFMT_S24_BE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_U16_LE, AFMT_S24_LE, 0}, - {FEEDER_FMT, AFMT_U16_LE | AFMT_STEREO, AFMT_S24_LE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S16_LE, AFMT_U24_LE, 0}, - {FEEDER_FMT, AFMT_S16_LE | AFMT_STEREO, AFMT_U24_LE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_U16_BE, AFMT_S24_BE, 0}, - {FEEDER_FMT, AFMT_U16_BE | AFMT_STEREO, AFMT_S24_BE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S16_BE, AFMT_U24_BE, 0}, - {FEEDER_FMT, AFMT_S16_BE | AFMT_STEREO, AFMT_U24_BE | AFMT_STEREO, 0}, - {0, 0, 0, 0}, -}; -static kobj_method_t feeder_16to24_methods[] = { - KOBJMETHOD(feeder_init, feed_updownbit_init), - KOBJMETHOD(feeder_feed, feed_upbit), - {0, 0} -}; -FEEDER_DECLARE(feeder_16to24, 0, NULL); - -static struct pcm_feederdesc feeder_16to32_desc[] = { - {FEEDER_FMT, AFMT_U16_LE, AFMT_U32_LE, 0}, - {FEEDER_FMT, AFMT_U16_LE | AFMT_STEREO, AFMT_U32_LE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S16_LE, AFMT_S32_LE, 0}, - {FEEDER_FMT, AFMT_S16_LE | AFMT_STEREO, AFMT_S32_LE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_U16_BE, AFMT_U32_BE, 0}, - {FEEDER_FMT, AFMT_U16_BE | AFMT_STEREO, AFMT_U32_BE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S16_BE, AFMT_S32_BE, 0}, - {FEEDER_FMT, AFMT_S16_BE | AFMT_STEREO, AFMT_S32_BE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_U16_LE, AFMT_S32_LE, 0}, - {FEEDER_FMT, AFMT_U16_LE | AFMT_STEREO, AFMT_S32_LE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S16_LE, AFMT_U32_LE, 0}, - {FEEDER_FMT, AFMT_S16_LE | AFMT_STEREO, AFMT_U32_LE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_U16_BE, AFMT_S32_BE, 0}, - {FEEDER_FMT, AFMT_U16_BE | AFMT_STEREO, AFMT_S32_BE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S16_BE, AFMT_U32_BE, 0}, - {FEEDER_FMT, AFMT_S16_BE | AFMT_STEREO, AFMT_U32_BE | AFMT_STEREO, 0}, - {0, 0, 0, 0}, -}; -static kobj_method_t feeder_16to32_methods[] = { - KOBJMETHOD(feeder_init, feed_updownbit_init), - KOBJMETHOD(feeder_feed, feed_upbit), - {0, 0} -}; -FEEDER_DECLARE(feeder_16to32, 0, NULL); - -static struct pcm_feederdesc feeder_24to32_desc[] = { - {FEEDER_FMT, AFMT_U24_LE, AFMT_U32_LE, 0}, - {FEEDER_FMT, AFMT_U24_LE | AFMT_STEREO, AFMT_U32_LE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S24_LE, AFMT_S32_LE, 0}, - {FEEDER_FMT, AFMT_S24_LE | AFMT_STEREO, AFMT_S32_LE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_U24_BE, AFMT_U32_BE, 0}, - {FEEDER_FMT, AFMT_U24_BE | AFMT_STEREO, AFMT_U32_BE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S24_BE, AFMT_S32_BE, 0}, - {FEEDER_FMT, AFMT_S24_BE | AFMT_STEREO, AFMT_S32_BE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_U24_LE, AFMT_S32_LE, 0}, - {FEEDER_FMT, AFMT_U24_LE | AFMT_STEREO, AFMT_S32_LE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S24_LE, AFMT_U32_LE, 0}, - {FEEDER_FMT, AFMT_S24_LE | AFMT_STEREO, AFMT_U32_LE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_U24_BE, AFMT_S32_BE, 0}, - {FEEDER_FMT, AFMT_U24_BE | AFMT_STEREO, AFMT_S32_BE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S24_BE, AFMT_U32_BE, 0}, - {FEEDER_FMT, AFMT_S24_BE | AFMT_STEREO, AFMT_U32_BE | AFMT_STEREO, 0}, - {0, 0, 0, 0}, -}; -static kobj_method_t feeder_24to32_methods[] = { - KOBJMETHOD(feeder_init, feed_updownbit_init), - KOBJMETHOD(feeder_feed, feed_upbit), - {0, 0} -}; -FEEDER_DECLARE(feeder_24to32, 0, NULL); - -static int -feed_downbit(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, - uint32_t count, void *source) -{ - int i, j, k, sign, be, ibps, ialign, obps, oalign,dump; - uint8_t *src, *dst, *end; - uint8_t reservoir[FEEDFMT_RESERVOIR]; - - ibps = FBIT_INBPS((intptr_t)f->data); - obps = FBIT_OUTBPS((intptr_t)f->data); - - ialign = ibps * FBIT_CHANNELS((intptr_t)f->data); - oalign = obps * FBIT_CHANNELS((intptr_t)f->data); - - if (count < oalign) - return (0); - - dst = b; - dump = ibps - obps; - sign = FEEDFMT_SWAP_SIGN(f); - be = (f->desc->in & AFMT_BIGENDIAN) ? 1 : 0; - k = count - (count % oalign); - - do { - if (k < oalign) - break; - - if (k < ialign) { - src = reservoir; - j = ialign; - } else { - src = dst; - j = k; - } - - j = FEEDER_FEED(f->source, c, src, j - (j % ialign), source); - if (j < ialign) - break; - - j -= j % ialign; - j *= obps; - j /= ibps; - end = dst + j; - - if (be != 0) { - do { - *dst++ = *src++ ^ sign; - i = obps; - while (--i != 0) - *dst++ = *src++; - src += dump; - } while (dst != end); - } else { - do { - src += dump; - i = obps; - while (--i != 0) - *dst++ = *src++; - *dst++ = *src++ ^ sign; - } while (dst != end); - } - - k -= j; - } while (k != 0); - - return (dst - b); -} - -static struct pcm_feederdesc feeder_16to8_desc[] = { - {FEEDER_FMT, AFMT_U16_LE, AFMT_U8, 0}, - {FEEDER_FMT, AFMT_U16_LE | AFMT_STEREO, AFMT_U8 | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S16_LE, AFMT_S8, 0}, - {FEEDER_FMT, AFMT_S16_LE | AFMT_STEREO, AFMT_S8 | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_U16_BE, AFMT_U8, 0}, - {FEEDER_FMT, AFMT_U16_BE | AFMT_STEREO, AFMT_U8 | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S16_BE, AFMT_S8, 0}, - {FEEDER_FMT, AFMT_S16_BE | AFMT_STEREO, AFMT_S8 | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_U16_LE, AFMT_S8, 0}, - {FEEDER_FMT, AFMT_U16_LE | AFMT_STEREO, AFMT_S8 | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S16_LE, AFMT_U8, 0}, - {FEEDER_FMT, AFMT_S16_LE | AFMT_STEREO, AFMT_U8 | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_U16_BE, AFMT_S8, 0}, - {FEEDER_FMT, AFMT_U16_BE | AFMT_STEREO, AFMT_S8 | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S16_BE, AFMT_U8, 0}, - {FEEDER_FMT, AFMT_S16_BE | AFMT_STEREO, AFMT_U8 | AFMT_STEREO, 0}, - {0, 0, 0, 0}, -}; -static kobj_method_t feeder_16to8_methods[] = { - KOBJMETHOD(feeder_init, feed_updownbit_init), - KOBJMETHOD(feeder_feed, feed_downbit), - {0, 0} -}; -FEEDER_DECLARE(feeder_16to8, 0, NULL); - -static struct pcm_feederdesc feeder_24to8_desc[] = { - {FEEDER_FMT, AFMT_U24_LE, AFMT_U8, 0}, - {FEEDER_FMT, AFMT_U24_LE | AFMT_STEREO, AFMT_U8 | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S24_LE, AFMT_S8, 0}, - {FEEDER_FMT, AFMT_S24_LE | AFMT_STEREO, AFMT_S8 | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_U24_BE, AFMT_U8, 0}, - {FEEDER_FMT, AFMT_U24_BE | AFMT_STEREO, AFMT_U8 | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S24_BE, AFMT_S8, 0}, - {FEEDER_FMT, AFMT_S24_BE | AFMT_STEREO, AFMT_S8 | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_U24_LE, AFMT_S8, 0}, - {FEEDER_FMT, AFMT_U24_LE | AFMT_STEREO, AFMT_S8 | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S24_LE, AFMT_U8, 0}, - {FEEDER_FMT, AFMT_S24_LE | AFMT_STEREO, AFMT_U8 | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_U24_BE, AFMT_S8, 0}, - {FEEDER_FMT, AFMT_U24_BE | AFMT_STEREO, AFMT_S8 | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S24_BE, AFMT_U8, 0}, - {FEEDER_FMT, AFMT_S24_BE | AFMT_STEREO, AFMT_U8 | AFMT_STEREO, 0}, - {0, 0, 0, 0}, -}; -static kobj_method_t feeder_24to8_methods[] = { - KOBJMETHOD(feeder_init, feed_updownbit_init), - KOBJMETHOD(feeder_feed, feed_downbit), - {0, 0} -}; -FEEDER_DECLARE(feeder_24to8, 0, NULL); - -static struct pcm_feederdesc feeder_24to16_desc[] = { - {FEEDER_FMT, AFMT_U24_LE, AFMT_U16_LE, 0}, - {FEEDER_FMT, AFMT_U24_LE | AFMT_STEREO, AFMT_U16_LE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S24_LE, AFMT_S16_LE, 0}, - {FEEDER_FMT, AFMT_S24_LE | AFMT_STEREO, AFMT_S16_LE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_U24_BE, AFMT_U16_BE, 0}, - {FEEDER_FMT, AFMT_U24_BE | AFMT_STEREO, AFMT_U16_BE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S24_BE, AFMT_S16_BE, 0}, - {FEEDER_FMT, AFMT_S24_BE | AFMT_STEREO, AFMT_S16_BE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_U24_LE, AFMT_S16_LE, 0}, - {FEEDER_FMT, AFMT_U24_LE | AFMT_STEREO, AFMT_S16_LE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S24_LE, AFMT_U16_LE, 0}, - {FEEDER_FMT, AFMT_S24_LE | AFMT_STEREO, AFMT_U16_LE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_U24_BE, AFMT_S16_BE, 0}, - {FEEDER_FMT, AFMT_U24_BE | AFMT_STEREO, AFMT_S16_BE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S24_BE, AFMT_U16_BE, 0}, - {FEEDER_FMT, AFMT_S24_BE | AFMT_STEREO, AFMT_U16_BE | AFMT_STEREO, 0}, - {0, 0, 0, 0}, -}; -static kobj_method_t feeder_24to16_methods[] = { - KOBJMETHOD(feeder_init, feed_updownbit_init), - KOBJMETHOD(feeder_feed, feed_downbit), - {0, 0} -}; -FEEDER_DECLARE(feeder_24to16, 0, NULL); - -static struct pcm_feederdesc feeder_32to8_desc[] = { - {FEEDER_FMT, AFMT_U32_LE, AFMT_U8, 0}, - {FEEDER_FMT, AFMT_U32_LE | AFMT_STEREO, AFMT_U8 | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S32_LE, AFMT_S8, 0}, - {FEEDER_FMT, AFMT_S32_LE | AFMT_STEREO, AFMT_S8 | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_U32_BE, AFMT_U8, 0}, - {FEEDER_FMT, AFMT_U32_BE | AFMT_STEREO, AFMT_U8 | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S32_BE, AFMT_S8, 0}, - {FEEDER_FMT, AFMT_S32_BE | AFMT_STEREO, AFMT_S8 | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_U32_LE, AFMT_S8, 0}, - {FEEDER_FMT, AFMT_U32_LE | AFMT_STEREO, AFMT_S8 | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S32_LE, AFMT_U8, 0}, - {FEEDER_FMT, AFMT_S32_LE | AFMT_STEREO, AFMT_U8 | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_U32_BE, AFMT_S8, 0}, - {FEEDER_FMT, AFMT_U32_BE | AFMT_STEREO, AFMT_S8 | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S32_BE, AFMT_U8, 0}, - {FEEDER_FMT, AFMT_S32_BE | AFMT_STEREO, AFMT_U8 | AFMT_STEREO, 0}, - {0, 0, 0, 0}, -}; -static kobj_method_t feeder_32to8_methods[] = { - KOBJMETHOD(feeder_init, feed_updownbit_init), - KOBJMETHOD(feeder_feed, feed_downbit), - {0, 0} -}; -FEEDER_DECLARE(feeder_32to8, 0, NULL); - -static struct pcm_feederdesc feeder_32to16_desc[] = { - {FEEDER_FMT, AFMT_U32_LE, AFMT_U16_LE, 0}, - {FEEDER_FMT, AFMT_U32_LE | AFMT_STEREO, AFMT_U16_LE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S32_LE, AFMT_S16_LE, 0}, - {FEEDER_FMT, AFMT_S32_LE | AFMT_STEREO, AFMT_S16_LE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_U32_BE, AFMT_U16_BE, 0}, - {FEEDER_FMT, AFMT_U32_BE | AFMT_STEREO, AFMT_U16_BE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S32_BE, AFMT_S16_BE, 0}, - {FEEDER_FMT, AFMT_S32_BE | AFMT_STEREO, AFMT_S16_BE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_U32_LE, AFMT_S16_LE, 0}, - {FEEDER_FMT, AFMT_U32_LE | AFMT_STEREO, AFMT_S16_LE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S32_LE, AFMT_U16_LE, 0}, - {FEEDER_FMT, AFMT_S32_LE | AFMT_STEREO, AFMT_U16_LE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_U32_BE, AFMT_S16_BE, 0}, - {FEEDER_FMT, AFMT_U32_BE | AFMT_STEREO, AFMT_S16_BE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S32_BE, AFMT_U16_BE, 0}, - {FEEDER_FMT, AFMT_S32_BE | AFMT_STEREO, AFMT_U16_BE | AFMT_STEREO, 0}, - {0, 0, 0, 0}, -}; -static kobj_method_t feeder_32to16_methods[] = { - KOBJMETHOD(feeder_init, feed_updownbit_init), - KOBJMETHOD(feeder_feed, feed_downbit), - {0, 0} -}; -FEEDER_DECLARE(feeder_32to16, 0, NULL); - -static struct pcm_feederdesc feeder_32to24_desc[] = { - {FEEDER_FMT, AFMT_U32_LE, AFMT_U24_LE, 0}, - {FEEDER_FMT, AFMT_U32_LE | AFMT_STEREO, AFMT_U24_LE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S32_LE, AFMT_S24_LE, 0}, - {FEEDER_FMT, AFMT_S32_LE | AFMT_STEREO, AFMT_S24_LE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_U32_BE, AFMT_U24_BE, 0}, - {FEEDER_FMT, AFMT_U32_BE | AFMT_STEREO, AFMT_U24_BE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S32_BE, AFMT_S24_BE, 0}, - {FEEDER_FMT, AFMT_S32_BE | AFMT_STEREO, AFMT_S24_BE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_U32_LE, AFMT_S24_LE, 0}, - {FEEDER_FMT, AFMT_U32_LE | AFMT_STEREO, AFMT_S24_LE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S32_LE, AFMT_U24_LE, 0}, - {FEEDER_FMT, AFMT_S32_LE | AFMT_STEREO, AFMT_U24_LE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_U32_BE, AFMT_S24_BE, 0}, - {FEEDER_FMT, AFMT_U32_BE | AFMT_STEREO, AFMT_S24_BE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S32_BE, AFMT_U24_BE, 0}, - {FEEDER_FMT, AFMT_S32_BE | AFMT_STEREO, AFMT_U24_BE | AFMT_STEREO, 0}, - {0, 0, 0, 0}, -}; -static kobj_method_t feeder_32to24_methods[] = { - KOBJMETHOD(feeder_init, feed_updownbit_init), - KOBJMETHOD(feeder_feed, feed_downbit), - {0, 0} -}; -FEEDER_DECLARE(feeder_32to24, 0, NULL); -/* - * Bit conversion end - */ - -/* - * Channel conversion (mono -> stereo) - */ -static int -feed_monotostereo(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, - uint32_t count, void *source) -{ - int bps, i, j, k, l; - uint8_t v; - - bps = (int)((intptr_t)f->data); - if (count < (bps << 1)) - return (0); - - j = FEEDER_FEED(f->source, c, b, (count - (count % bps)) >> 1, source); - if (j < bps) - return (0); - - j -= j % bps; - i = j << 1; - l = i; - - do { - k = bps; - do { - v = b[--j]; - b[--i] = v; - b[i - bps] = v; - } while (--k != 0); - i -= bps; - } while (i != 0); - - return (l); -} - -static struct pcm_feederdesc feeder_monotostereo8_desc[] = { - {FEEDER_FMT, AFMT_U8, AFMT_U8 | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S8, AFMT_S8 | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_MU_LAW, AFMT_MU_LAW | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_A_LAW, AFMT_A_LAW | AFMT_STEREO, 0}, - {0, 0, 0, 0}, -}; -static kobj_method_t feeder_monotostereo8_methods[] = { - KOBJMETHOD(feeder_feed, feed_monotostereo), - {0, 0} -}; -FEEDER_DECLARE(feeder_monotostereo8, 0, (void *)PCM_8_BPS); - -static struct pcm_feederdesc feeder_monotostereo16_desc[] = { - {FEEDER_FMT, AFMT_U16_LE, AFMT_U16_LE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S16_LE, AFMT_S16_LE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_U16_BE, AFMT_U16_BE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S16_BE, AFMT_S16_BE | AFMT_STEREO, 0}, - {0, 0, 0, 0}, -}; -static kobj_method_t feeder_monotostereo16_methods[] = { - KOBJMETHOD(feeder_feed, feed_monotostereo), - {0, 0} -}; -FEEDER_DECLARE(feeder_monotostereo16, 0, (void *)PCM_16_BPS); - -static struct pcm_feederdesc feeder_monotostereo24_desc[] = { - {FEEDER_FMT, AFMT_U24_LE, AFMT_U24_LE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S24_LE, AFMT_S24_LE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_U24_BE, AFMT_U24_BE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S24_BE, AFMT_S24_BE | AFMT_STEREO, 0}, - {0, 0, 0, 0}, -}; -static kobj_method_t feeder_monotostereo24_methods[] = { - KOBJMETHOD(feeder_feed, feed_monotostereo), - {0, 0} -}; -FEEDER_DECLARE(feeder_monotostereo24, 0, (void *)PCM_24_BPS); - -static struct pcm_feederdesc feeder_monotostereo32_desc[] = { - {FEEDER_FMT, AFMT_U32_LE, AFMT_U32_LE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S32_LE, AFMT_S32_LE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_U32_BE, AFMT_U32_BE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S32_BE, AFMT_S32_BE | AFMT_STEREO, 0}, - {0, 0, 0, 0}, -}; -static kobj_method_t feeder_monotostereo32_methods[] = { - KOBJMETHOD(feeder_feed, feed_monotostereo), - {0, 0} -}; -FEEDER_DECLARE(feeder_monotostereo32, 0, (void *)PCM_32_BPS); -/* - * Channel conversion (mono -> stereo) end - */ - -/* - * Channel conversion (stereo -> mono) - */ -#define FEEDER_FMT_STEREODOWNMIX(FMTBIT, FMT_INTCAST, SIGN, \ - SIGNS, ENDIAN, ENDIANS) \ -static void \ -SIGNS##FMTBIT##ENDIANS##_stereodownmix(uint8_t *dst, uint8_t *sx, uint8_t *sy) \ -{ \ - int32_t v; \ - \ - v = ((FMT_INTCAST)PCM_READ_##SIGN##FMTBIT##_##ENDIAN(sx) + \ - PCM_READ_##SIGN##FMTBIT##_##ENDIAN(sy)) >> 1; \ - PCM_WRITE_##SIGN##FMTBIT##_##ENDIAN(dst, v); \ -} - -FEEDER_FMT_STEREODOWNMIX(8, int32_t, S, s, NE, ne); -FEEDER_FMT_STEREODOWNMIX(16, int32_t, S, s, LE, le); -FEEDER_FMT_STEREODOWNMIX(24, int32_t, S, s, LE, le); -FEEDER_FMT_STEREODOWNMIX(32, intpcm_t, S, s, LE, le); -FEEDER_FMT_STEREODOWNMIX(16, int32_t, S, s, BE, be); -FEEDER_FMT_STEREODOWNMIX(24, int32_t, S, s, BE, be); -FEEDER_FMT_STEREODOWNMIX(32, intpcm_t, S, s, BE, be); -FEEDER_FMT_STEREODOWNMIX(8, int32_t, U, u, NE, ne); -FEEDER_FMT_STEREODOWNMIX(16, int32_t, U, u, LE, le); -FEEDER_FMT_STEREODOWNMIX(24, int32_t, U, u, LE, le); -FEEDER_FMT_STEREODOWNMIX(32, intpcm_t, U, u, LE, le); -FEEDER_FMT_STEREODOWNMIX(16, int32_t, U, u, BE, be); -FEEDER_FMT_STEREODOWNMIX(24, int32_t, U, u, BE, be); -FEEDER_FMT_STEREODOWNMIX(32, intpcm_t, U, u, BE, be); - -static void -ulaw_stereodownmix(uint8_t *dst, uint8_t *sx, uint8_t *sy) -{ - uint8_t v; - - v = ((uint32_t)ulaw_to_u8_tbl[*sx] + ulaw_to_u8_tbl[*sy]) >> 1; - *dst = u8_to_ulaw_tbl[v]; -} - -static void -alaw_stereodownmix(uint8_t *dst, uint8_t *sx, uint8_t *sy) -{ - uint8_t v; - - v = ((uint32_t)alaw_to_u8_tbl[*sx] + alaw_to_u8_tbl[*sy]) >> 1; - *dst = u8_to_alaw_tbl[v]; -} - -typedef void (*feed_fmt_stereodownmix_filter)(uint8_t *, - uint8_t *, uint8_t *); - -struct feed_fmt_stereodownmix_info { - uint32_t format; - int bps; - feed_fmt_stereodownmix_filter func[2]; -}; - -static struct feed_fmt_stereodownmix_info feed_fmt_stereodownmix_tbl[] = { - { AFMT_S8, PCM_8_BPS, { NULL, s8ne_stereodownmix }}, - { AFMT_S16_LE, PCM_16_BPS, { NULL, s16le_stereodownmix }}, - { AFMT_S16_BE, PCM_16_BPS, { NULL, s16be_stereodownmix }}, - { AFMT_S24_LE, PCM_24_BPS, { NULL, s24le_stereodownmix }}, - { AFMT_S24_BE, PCM_24_BPS, { NULL, s24be_stereodownmix }}, - { AFMT_S32_LE, PCM_32_BPS, { NULL, s32le_stereodownmix }}, - { AFMT_S32_BE, PCM_32_BPS, { NULL, s32be_stereodownmix }}, - { AFMT_U8, PCM_8_BPS, { NULL, u8ne_stereodownmix }}, - { AFMT_A_LAW, PCM_8_BPS, { NULL, alaw_stereodownmix }}, - { AFMT_MU_LAW, PCM_8_BPS, { NULL, ulaw_stereodownmix }}, - { AFMT_U16_LE, PCM_16_BPS, { NULL, u16le_stereodownmix }}, - { AFMT_U16_BE, PCM_16_BPS, { NULL, u16be_stereodownmix }}, - { AFMT_U24_LE, PCM_24_BPS, { NULL, u24le_stereodownmix }}, - { AFMT_U24_BE, PCM_24_BPS, { NULL, u24be_stereodownmix }}, - { AFMT_U32_LE, PCM_32_BPS, { NULL, u32le_stereodownmix }}, - { AFMT_U32_BE, PCM_32_BPS, { NULL, u32be_stereodownmix }}, -}; - -#define FSM_DATA(i, j) ((intptr_t)((((i) & 0x1f) << 1) | ((j) & 0x1))) -#define FSM_INFOIDX(m) (((m) >> 1) & 0x1f) -#define FSM_FUNCIDX(m) ((m) & 0x1) - -static int -feed_stereotomono_init(struct pcm_feeder *f) -{ - int i, funcidx; - - if (!(f->desc->in & AFMT_STEREO) || (f->desc->out & AFMT_STEREO)) - return (-1); - - funcidx = (feeder_fmt_stereodownmix != 0) ? 1 : 0; - - for (i = 0; i < sizeof(feed_fmt_stereodownmix_tbl) / - sizeof(feed_fmt_stereodownmix_tbl[0]); i++) { - if (f->desc->out == feed_fmt_stereodownmix_tbl[i].format) { - f->data = (void *)FSM_DATA(i, funcidx); - return (0); - } - } - - return (-1); -} - -static int -feed_stereotomono(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, - uint32_t count, void *source) -{ - struct feed_fmt_stereodownmix_info *info; - feed_fmt_stereodownmix_filter stereodownmix; - int i, j, k, ibps, obps; - uint8_t *src, *dst, *end; - uint8_t reservoir[FEEDFMT_RESERVOIR]; - - info = &feed_fmt_stereodownmix_tbl[FSM_INFOIDX((intptr_t)f->data)]; - obps = info->bps; - - if (count < obps) - return (0); - - stereodownmix = info->func[FSM_FUNCIDX((intptr_t)f->data)]; - ibps = obps << 1; - dst = b; - k = count - (count % obps); - - do { - if (k < obps) - break; - - if (k < ibps) { - src = reservoir; - j = ibps; - } else { - src = dst; - j = k; - } - - j = FEEDER_FEED(f->source, c, src, j - (j % ibps), source); - if (j < ibps) - break; - - j -= j % ibps; - j >>= 1; - end = dst + j; - - if (stereodownmix != NULL) { - do { - stereodownmix(dst, src, src + obps); - dst += obps; - src += ibps; - } while (dst != end); - } else { - do { - i = obps; - do { - *dst++ = *src++; - } while (--i != 0); - src += obps; - } while (dst != end); - } - - k -= j; - } while (k != 0); - - return (dst - b); -} - -static struct pcm_feederdesc feeder_stereotomono8_desc[] = { - {FEEDER_FMT, AFMT_U8 | AFMT_STEREO, AFMT_U8, 0}, - {FEEDER_FMT, AFMT_S8 | AFMT_STEREO, AFMT_S8, 0}, - {FEEDER_FMT, AFMT_MU_LAW | AFMT_STEREO, AFMT_MU_LAW, 0}, - {FEEDER_FMT, AFMT_A_LAW | AFMT_STEREO, AFMT_A_LAW, 0}, - {0, 0, 0, 0}, -}; -static kobj_method_t feeder_stereotomono8_methods[] = { - KOBJMETHOD(feeder_init, feed_stereotomono_init), - KOBJMETHOD(feeder_feed, feed_stereotomono), - {0, 0} -}; -FEEDER_DECLARE(feeder_stereotomono8, 0, NULL); - -static struct pcm_feederdesc feeder_stereotomono16_desc[] = { - {FEEDER_FMT, AFMT_U16_LE | AFMT_STEREO, AFMT_U16_LE, 0}, - {FEEDER_FMT, AFMT_S16_LE | AFMT_STEREO, AFMT_S16_LE, 0}, - {FEEDER_FMT, AFMT_U16_BE | AFMT_STEREO, AFMT_U16_BE, 0}, - {FEEDER_FMT, AFMT_S16_BE | AFMT_STEREO, AFMT_S16_BE, 0}, - {0, 0, 0, 0}, -}; -static kobj_method_t feeder_stereotomono16_methods[] = { - KOBJMETHOD(feeder_init, feed_stereotomono_init), - KOBJMETHOD(feeder_feed, feed_stereotomono), - {0, 0} -}; -FEEDER_DECLARE(feeder_stereotomono16, 0, NULL); - -static struct pcm_feederdesc feeder_stereotomono24_desc[] = { - {FEEDER_FMT, AFMT_U24_LE | AFMT_STEREO, AFMT_U24_LE, 0}, - {FEEDER_FMT, AFMT_S24_LE | AFMT_STEREO, AFMT_S24_LE, 0}, - {FEEDER_FMT, AFMT_U24_BE | AFMT_STEREO, AFMT_U24_BE, 0}, - {FEEDER_FMT, AFMT_S24_BE | AFMT_STEREO, AFMT_S24_BE, 0}, - {0, 0, 0, 0}, -}; -static kobj_method_t feeder_stereotomono24_methods[] = { - KOBJMETHOD(feeder_init, feed_stereotomono_init), - KOBJMETHOD(feeder_feed, feed_stereotomono), - {0, 0} -}; -FEEDER_DECLARE(feeder_stereotomono24, 0, NULL); - -static struct pcm_feederdesc feeder_stereotomono32_desc[] = { - {FEEDER_FMT, AFMT_U32_LE | AFMT_STEREO, AFMT_U32_LE, 0}, - {FEEDER_FMT, AFMT_S32_LE | AFMT_STEREO, AFMT_S32_LE, 0}, - {FEEDER_FMT, AFMT_U32_BE | AFMT_STEREO, AFMT_U32_BE, 0}, - {FEEDER_FMT, AFMT_S32_BE | AFMT_STEREO, AFMT_S32_BE, 0}, - {0, 0, 0, 0}, -}; -static kobj_method_t feeder_stereotomono32_methods[] = { - KOBJMETHOD(feeder_init, feed_stereotomono_init), - KOBJMETHOD(feeder_feed, feed_stereotomono), - {0, 0} -}; -FEEDER_DECLARE(feeder_stereotomono32, 0, NULL); -/* - * Channel conversion (stereo -> mono) end - */ - -/* - * Sign conversion - */ -static int -feed_sign(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, - uint32_t count, void *source) -{ - int i, j, bps, ofs; - - bps = (int)((intptr_t)f->data); - if (count < bps) - return (0); - - i = FEEDER_FEED(f->source, c, b, count - (count % bps), source); - if (i < bps) - return (0); - - i -= i % bps; - j = i; - ofs = (f->desc->in & AFMT_BIGENDIAN) ? bps : 1; - - do { - b[i - ofs] ^= 0x80; - i -= bps; - } while (i != 0); - - return (j); -} -static struct pcm_feederdesc feeder_sign8_desc[] = { - {FEEDER_FMT, AFMT_U8, AFMT_S8, 0}, - {FEEDER_FMT, AFMT_U8 | AFMT_STEREO, AFMT_S8 | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S8, AFMT_U8, 0}, - {FEEDER_FMT, AFMT_S8 | AFMT_STEREO, AFMT_U8 | AFMT_STEREO, 0}, - {0, 0, 0, 0}, -}; -static kobj_method_t feeder_sign8_methods[] = { - KOBJMETHOD(feeder_feed, feed_sign), - {0, 0} -}; -FEEDER_DECLARE(feeder_sign8, 0, (void *)PCM_8_BPS); - -static struct pcm_feederdesc feeder_sign16_desc[] = { - {FEEDER_FMT, AFMT_U16_LE, AFMT_S16_LE, 0}, - {FEEDER_FMT, AFMT_U16_LE | AFMT_STEREO, AFMT_S16_LE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S16_LE, AFMT_U16_LE, 0}, - {FEEDER_FMT, AFMT_S16_LE | AFMT_STEREO, AFMT_U16_LE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_U16_BE, AFMT_S16_BE, 0}, - {FEEDER_FMT, AFMT_U16_BE | AFMT_STEREO, AFMT_S16_BE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S16_BE, AFMT_U16_BE, 0}, - {FEEDER_FMT, AFMT_S16_BE | AFMT_STEREO, AFMT_U16_BE | AFMT_STEREO, 0}, - {0, 0, 0, 0}, -}; -static kobj_method_t feeder_sign16_methods[] = { - KOBJMETHOD(feeder_feed, feed_sign), - {0, 0} -}; -FEEDER_DECLARE(feeder_sign16, 0, (void *)PCM_16_BPS); - -static struct pcm_feederdesc feeder_sign24_desc[] = { - {FEEDER_FMT, AFMT_U24_LE, AFMT_S24_LE, 0}, - {FEEDER_FMT, AFMT_U24_LE | AFMT_STEREO, AFMT_S24_LE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S24_LE, AFMT_U24_LE, 0}, - {FEEDER_FMT, AFMT_S24_LE | AFMT_STEREO, AFMT_U24_LE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_U24_BE, AFMT_S24_BE, 0}, - {FEEDER_FMT, AFMT_U24_BE | AFMT_STEREO, AFMT_S24_BE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S24_BE, AFMT_U24_BE, 0}, - {FEEDER_FMT, AFMT_S24_BE | AFMT_STEREO, AFMT_U24_BE | AFMT_STEREO, 0}, - {0, 0, 0, 0}, -}; -static kobj_method_t feeder_sign24_methods[] = { - KOBJMETHOD(feeder_feed, feed_sign), - {0, 0} -}; -FEEDER_DECLARE(feeder_sign24, 0, (void *)PCM_24_BPS); - -static struct pcm_feederdesc feeder_sign32_desc[] = { - {FEEDER_FMT, AFMT_U32_LE, AFMT_S32_LE, 0}, - {FEEDER_FMT, AFMT_U32_LE | AFMT_STEREO, AFMT_S32_LE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S32_LE, AFMT_U32_LE, 0}, - {FEEDER_FMT, AFMT_S32_LE | AFMT_STEREO, AFMT_U32_LE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_U32_BE, AFMT_S32_BE, 0}, - {FEEDER_FMT, AFMT_U32_BE | AFMT_STEREO, AFMT_S32_BE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S32_BE, AFMT_U32_BE, 0}, - {FEEDER_FMT, AFMT_S32_BE | AFMT_STEREO, AFMT_U32_BE | AFMT_STEREO, 0}, - {0, 0, 0, 0}, -}; -static kobj_method_t feeder_sign32_methods[] = { - KOBJMETHOD(feeder_feed, feed_sign), - {0, 0} -}; -FEEDER_DECLARE(feeder_sign32, 0, (void *)PCM_32_BPS); - -/* - * Endian conversion - */ -static int -feed_endian(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, - uint32_t count, void *source) -{ - int i, j, k, bps; - uint8_t *buf, v; - - bps = (int)((intptr_t)f->data); - if (count < bps) - return (0); - - k = FEEDER_FEED(f->source, c, b, count - (count % bps), source); - if (k < bps) - return (0); - - k -= k % bps; - j = bps >> 1; - buf = b + k; - - do { - buf -= bps; - i = j; - do { - v = buf[--i]; - buf[i] = buf[bps - i - 1]; - buf[bps - i - 1] = v; - } while (i != 0); - } while (buf != b); - - return (k); -} -static struct pcm_feederdesc feeder_endian16_desc[] = { - {FEEDER_FMT, AFMT_U16_LE, AFMT_U16_BE, 0}, - {FEEDER_FMT, AFMT_U16_LE | AFMT_STEREO, AFMT_U16_BE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S16_LE, AFMT_S16_BE, 0}, - {FEEDER_FMT, AFMT_S16_LE | AFMT_STEREO, AFMT_S16_BE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_U16_BE, AFMT_U16_LE, 0}, - {FEEDER_FMT, AFMT_U16_BE | AFMT_STEREO, AFMT_U16_LE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S16_BE, AFMT_S16_LE, 0}, - {FEEDER_FMT, AFMT_S16_BE | AFMT_STEREO, AFMT_S16_LE | AFMT_STEREO, 0}, - {0, 0, 0, 0}, -}; -static kobj_method_t feeder_endian16_methods[] = { - KOBJMETHOD(feeder_feed, feed_endian), - {0, 0} -}; -FEEDER_DECLARE(feeder_endian16, 0, (void *)PCM_16_BPS); - -static struct pcm_feederdesc feeder_endian24_desc[] = { - {FEEDER_FMT, AFMT_U24_LE, AFMT_U24_BE, 0}, - {FEEDER_FMT, AFMT_U24_LE | AFMT_STEREO, AFMT_U24_BE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S24_LE, AFMT_S24_BE, 0}, - {FEEDER_FMT, AFMT_S24_LE | AFMT_STEREO, AFMT_S24_BE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_U24_BE, AFMT_U24_LE, 0}, - {FEEDER_FMT, AFMT_U24_BE | AFMT_STEREO, AFMT_U24_LE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S24_BE, AFMT_S24_LE, 0}, - {FEEDER_FMT, AFMT_S24_BE | AFMT_STEREO, AFMT_S24_LE | AFMT_STEREO, 0}, - {0, 0, 0, 0}, -}; -static kobj_method_t feeder_endian24_methods[] = { - KOBJMETHOD(feeder_feed, feed_endian), - {0, 0} -}; -FEEDER_DECLARE(feeder_endian24, 0, (void *)PCM_24_BPS); - -static struct pcm_feederdesc feeder_endian32_desc[] = { - {FEEDER_FMT, AFMT_U32_LE, AFMT_U32_BE, 0}, - {FEEDER_FMT, AFMT_U32_LE | AFMT_STEREO, AFMT_U32_BE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S32_LE, AFMT_S32_BE, 0}, - {FEEDER_FMT, AFMT_S32_LE | AFMT_STEREO, AFMT_S32_BE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_U32_BE, AFMT_U32_LE, 0}, - {FEEDER_FMT, AFMT_U32_BE | AFMT_STEREO, AFMT_U32_LE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S32_BE, AFMT_S32_LE, 0}, - {FEEDER_FMT, AFMT_S32_BE | AFMT_STEREO, AFMT_S32_LE | AFMT_STEREO, 0}, - {0, 0, 0, 0}, -}; -static kobj_method_t feeder_endian32_methods[] = { - KOBJMETHOD(feeder_feed, feed_endian), - {0, 0} -}; -FEEDER_DECLARE(feeder_endian32, 0, (void *)PCM_32_BPS); -/* - * Endian conversion end - */ - -/* - * L/R swap conversion - */ -static int -feed_swaplr(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, - uint32_t count, void *source) -{ - int i, j, bps, smpsz; - uint8_t *buf, v; - - bps = (int)((intptr_t)f->data); - smpsz = bps << 1; - if (count < smpsz) - return (0); - - j = FEEDER_FEED(f->source, c, b, count - (count % smpsz), source); - if (j < smpsz) - return (0); - - j -= j % smpsz; - buf = b + j; - - do { - buf -= smpsz; - i = bps; - do { - v = buf[--i]; - buf[i] = buf[bps + i]; - buf[bps + i] = v; - } while (i != 0); - } while (buf != b); - - return (j); -} - -static struct pcm_feederdesc feeder_swaplr8_desc[] = { - {FEEDER_SWAPLR, AFMT_S8 | AFMT_STEREO, AFMT_S8 | AFMT_STEREO, 0}, - {FEEDER_SWAPLR, AFMT_U8 | AFMT_STEREO, AFMT_U8 | AFMT_STEREO, 0}, - {FEEDER_SWAPLR, AFMT_A_LAW | AFMT_STEREO, AFMT_A_LAW | AFMT_STEREO, 0}, - {FEEDER_SWAPLR, AFMT_MU_LAW | AFMT_STEREO, AFMT_A_LAW | AFMT_STEREO, 0}, - {0, 0, 0, 0}, -}; -static kobj_method_t feeder_swaplr8_methods[] = { - KOBJMETHOD(feeder_feed, feed_swaplr), - {0, 0} -}; -FEEDER_DECLARE(feeder_swaplr8, -1, (void *)PCM_8_BPS); - -static struct pcm_feederdesc feeder_swaplr16_desc[] = { - {FEEDER_SWAPLR, AFMT_S16_LE | AFMT_STEREO, AFMT_S16_LE | AFMT_STEREO, 0}, - {FEEDER_SWAPLR, AFMT_S16_BE | AFMT_STEREO, AFMT_S16_BE | AFMT_STEREO, 0}, - {FEEDER_SWAPLR, AFMT_U16_LE | AFMT_STEREO, AFMT_U16_LE | AFMT_STEREO, 0}, - {FEEDER_SWAPLR, AFMT_U16_BE | AFMT_STEREO, AFMT_U16_BE | AFMT_STEREO, 0}, - {0, 0, 0, 0}, -}; -static kobj_method_t feeder_swaplr16_methods[] = { - KOBJMETHOD(feeder_feed, feed_swaplr), - {0, 0} -}; -FEEDER_DECLARE(feeder_swaplr16, -1, (void *)PCM_16_BPS); - -static struct pcm_feederdesc feeder_swaplr24_desc[] = { - {FEEDER_SWAPLR, AFMT_S24_LE | AFMT_STEREO, AFMT_S24_LE | AFMT_STEREO, 0}, - {FEEDER_SWAPLR, AFMT_S24_BE | AFMT_STEREO, AFMT_S24_BE | AFMT_STEREO, 0}, - {FEEDER_SWAPLR, AFMT_U24_LE | AFMT_STEREO, AFMT_U24_LE | AFMT_STEREO, 0}, - {FEEDER_SWAPLR, AFMT_U24_BE | AFMT_STEREO, AFMT_U24_BE | AFMT_STEREO, 0}, - {0, 0, 0, 0}, -}; -static kobj_method_t feeder_swaplr24_methods[] = { - KOBJMETHOD(feeder_feed, feed_swaplr), - {0, 0} -}; -FEEDER_DECLARE(feeder_swaplr24, -1, (void *)PCM_24_BPS); - -static struct pcm_feederdesc feeder_swaplr32_desc[] = { - {FEEDER_SWAPLR, AFMT_S32_LE | AFMT_STEREO, AFMT_S32_LE | AFMT_STEREO, 0}, - {FEEDER_SWAPLR, AFMT_S32_BE | AFMT_STEREO, AFMT_S32_BE | AFMT_STEREO, 0}, - {FEEDER_SWAPLR, AFMT_U32_LE | AFMT_STEREO, AFMT_U32_LE | AFMT_STEREO, 0}, - {FEEDER_SWAPLR, AFMT_U32_BE | AFMT_STEREO, AFMT_U32_BE | AFMT_STEREO, 0}, - {0, 0, 0, 0}, -}; -static kobj_method_t feeder_swaplr32_methods[] = { - KOBJMETHOD(feeder_feed, feed_swaplr), - {0, 0} -}; -FEEDER_DECLARE(feeder_swaplr32, -1, (void *)PCM_32_BPS); -/* - * L/R swap conversion end - */ diff --git a/sys/dev/sound/pcm/feeder_format.c b/sys/dev/sound/pcm/feeder_format.c new file mode 100644 index 0000000..f6a4c93 --- /dev/null +++ b/sys/dev/sound/pcm/feeder_format.c @@ -0,0 +1,300 @@ +/*- + * 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. + */ + +/* + * feeder_format: New generation of generic, any-to-any format converter, as + * long as the sample values can be read _and_ write. + */ + +#ifdef _KERNEL +#ifdef HAVE_KERNEL_OPTION_HEADERS +#include "opt_snd.h" +#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 +#include "snd_fxdiv_gen.h" + +SND_DECLARE_FILE("$FreeBSD$"); +#endif + +#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; + 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); + + info->channels = AFMT_CHANNEL(f->desc->in); + + info->ibps = AFMT_BPS(f->desc->in); + info->ialign = info->ibps * info->channels; + info->read = rd_op; + + info->obps = AFMT_BPS(f->desc->out); + info->oalign = info->obps * info->channels; + info->write = wr_op; + + f->data = info; + + return (0); +} + +static int +feed_format_free(struct pcm_feeder *f) +{ + struct feed_format_info *info; + + info = f->data; + if (info != NULL) + free(info, M_DEVBUF); + + f->data = NULL; + + return (0); +} + +static int +feed_format_set(struct pcm_feeder *f, int what, int value) +{ + struct feed_format_info *info; + + info = f->data; + + switch (what) { + case FEEDFORMAT_CHANNELS: + if (value < SND_CHN_MIN || value > SND_CHN_MAX) + return (EINVAL); + info->channels = (uint32_t)value; + info->ialign = info->ibps * info->channels; + info->oalign = info->obps * info->channels; + break; + default: + return (EINVAL); + break; + } + + return (0); +} + +static int +feed_format_feed(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, + uint32_t count, void *source) +{ + struct feed_format_info *info; + intpcm_t v; + uint32_t j; + uint8_t *src, *dst; + + info = f->data; + dst = b; + count = SND_FXROUND(count, info->oalign); + + do { + if (count < info->oalign) + break; + + if (count < info->ialign) { + src = info->reservoir; + j = info->ialign; + } else { + if (info->ialign == info->oalign) + j = count; + else if (info->ialign > info->oalign) + j = SND_FXROUND(count, info->ialign); + else + j = SND_FXDIV(count, info->oalign) * + info->ialign; + src = dst + count - j; + } + + j = SND_FXDIV(FEEDER_FEED(f->source, c, src, j, source), + info->ialign); + if (j == 0) + break; + + j *= info->channels; + count -= j * info->obps; + + do { + v = info->read(src); + info->write(dst, v); + dst += info->obps; + src += info->ibps; + } while (--j != 0); + + } while (count != 0); + + return (dst - b); +} + +static struct pcm_feederdesc feeder_format_desc[] = { + { FEEDER_FORMAT, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0 } +}; + +static kobj_method_t feeder_format_methods[] = { + KOBJMETHOD(feeder_init, feed_format_init), + KOBJMETHOD(feeder_free, feed_format_free), + KOBJMETHOD(feeder_set, feed_format_set), + KOBJMETHOD(feeder_feed, feed_format_feed), + KOBJMETHOD_END +}; + +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 new file mode 100644 index 0000000..d2d9595 --- /dev/null +++ b/sys/dev/sound/pcm/feeder_matrix.c @@ -0,0 +1,825 @@ +/*- + * 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. + */ + +/* + * feeder_matrix: Generic any-to-any channel matrixing. Probably not the + * accurate way of doing things, but it should be fast and + * transparent enough, not to mention capable of handling + * possible non-standard way of multichannel interleaving + * order. In other words, it is tough to break. + * + * The Good: + * + very generic and compact, provided that the supplied matrix map is in a + * sane form. + * + should be fast enough. + * + * The Bad: + * + somebody might disagree with it. + * + 'matrix' is kind of 0x7a69, due to prolong mental block. + */ + +#ifdef _KERNEL +#ifdef HAVE_KERNEL_OPTION_HEADERS +#include "opt_snd.h" +#endif +#include <dev/sound/pcm/sound.h> +#include <dev/sound/pcm/pcm.h> +#include "feeder_if.h" + +#define SND_USE_FXDIV +#include "snd_fxdiv_gen.h" + +SND_DECLARE_FILE("$FreeBSD$"); +#endif + +#define FEEDMATRIX_RESERVOIR (SND_CHN_MAX * PCM_32_BPS) + +#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 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; + } matrix[SND_CHN_T_MAX + 1]; + uint8_t reservoir[FEEDMATRIX_RESERVOIR]; +}; + +static struct pcmchan_matrix feeder_matrix_maps[SND_CHN_MATRIX_MAX] = { + [SND_CHN_MATRIX_1_0] = SND_CHN_MATRIX_MAP_1_0, + [SND_CHN_MATRIX_2_0] = SND_CHN_MATRIX_MAP_2_0, + [SND_CHN_MATRIX_2_1] = SND_CHN_MATRIX_MAP_2_1, + [SND_CHN_MATRIX_3_0] = SND_CHN_MATRIX_MAP_3_0, + [SND_CHN_MATRIX_4_0] = SND_CHN_MATRIX_MAP_4_0, + [SND_CHN_MATRIX_4_1] = SND_CHN_MATRIX_MAP_4_1, + [SND_CHN_MATRIX_5_0] = SND_CHN_MATRIX_MAP_5_0, + [SND_CHN_MATRIX_5_1] = SND_CHN_MATRIX_MAP_5_1, + [SND_CHN_MATRIX_6_0] = SND_CHN_MATRIX_MAP_6_0, + [SND_CHN_MATRIX_6_1] = SND_CHN_MATRIX_MAP_6_1, + [SND_CHN_MATRIX_7_1] = SND_CHN_MATRIX_MAP_7_1 +}; + +static int feeder_matrix_default_ids[9] = { + [0] = SND_CHN_MATRIX_UNKNOWN, + [1] = SND_CHN_MATRIX_1, + [2] = SND_CHN_MATRIX_2, + [3] = SND_CHN_MATRIX_3, + [4] = SND_CHN_MATRIX_4, + [5] = SND_CHN_MATRIX_5, + [6] = SND_CHN_MATRIX_6, + [7] = SND_CHN_MATRIX_7, + [8] = SND_CHN_MATRIX_8 +}; + +#ifdef _KERNEL +#define FEEDMATRIX_CLIP_CHECK(...) +#else +#define FEEDMATRIX_CLIP_CHECK(v, BIT) do { \ + if ((v) < PCM_S##BIT##_MIN || (v) > PCM_S##BIT##_MAX) \ + errx(1, "\n\n%s(): Sample clipping: %jd\n", \ + __func__, (intmax_t)(v)); \ +} 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) +{ + 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) { + info->wr(dst, 0); + 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); + dst += info->bps; + continue; + } + + accum = 0; + for (j = 0; + info->matrix[i].chn[j] != SND_CHN_T_EOF; + j++) { + v = info->rd(src + info->matrix[i].chn[j]); + accum += v; + } + + accum = (accum * info->matrix[i].mul) >> + info->matrix[i].shift; + + FEEDMATRIX_CLIP_CHECK(accum, 32); + + v = (accum > PCM_S32_MAX) ? PCM_S32_MAX : + ((accum < PCM_S32_MIN) ? PCM_S32_MIN : accum); + info->wr(dst, v); + dst += info->bps; + } + src += info->ialign; + } while (--count != 0); +} +#endif + +static int +feed_matrix_setup(struct feed_matrix_info *info, struct pcmchan_matrix *m_in, + struct pcmchan_matrix *m_out) +{ + uint32_t i, j, ch, in_mask, merge_mask; + int mul, shift; + + + if (info == NULL || m_in == NULL || m_out == NULL || + AFMT_CHANNEL(info->in) != m_in->channels || + AFMT_CHANNEL(info->out) != m_out->channels || + m_in->channels < SND_CHN_MIN || m_in->channels > SND_CHN_MAX || + m_out->channels < SND_CHN_MIN || m_out->channels > SND_CHN_MAX) + return (EINVAL); + + feed_matrix_reset(info); + + /* + * If both in and out are part of standard matrix and identical, skip + * everything alltogether. + */ + if (m_in->id == m_out->id && !(m_in->id < SND_CHN_MATRIX_BEGIN || + m_in->id > SND_CHN_MATRIX_END)) + return (0); + + /* + * Special case for mono input matrix. If the output supports + * possible 'center' channel, route it there. Otherwise, let it be + * matrixed to left/right. + */ + if (m_in->id == SND_CHN_MATRIX_1_0) { + if (m_out->id == SND_CHN_MATRIX_1_0) + in_mask = SND_CHN_T_MASK_FL; + else if (m_out->mask & SND_CHN_T_MASK_FC) + in_mask = SND_CHN_T_MASK_FC; + else + in_mask = SND_CHN_T_MASK_FL | SND_CHN_T_MASK_FR; + } else + in_mask = m_in->mask; + + /* Merge, reduce, expand all possibilites. */ + for (ch = SND_CHN_T_BEGIN; ch <= SND_CHN_T_END && + m_out->map[ch].type != SND_CHN_T_MAX; ch += SND_CHN_T_STEP) { + merge_mask = m_out->map[ch].members & in_mask; + if (merge_mask == 0) { + info->matrix[ch].chn[0] = SND_CHN_T_NULL; + continue; + } + + j = 0; + for (i = SND_CHN_T_BEGIN; i <= SND_CHN_T_END; + i += SND_CHN_T_STEP) { + if (merge_mask & (1 << i)) { + if (m_in->offset[i] >= 0 && + m_in->offset[i] < (int)m_in->channels) + info->matrix[ch].chn[j++] = + m_in->offset[i] * info->bps; + else { + info->matrix[ch].chn[j++] = + SND_CHN_T_EOF; + break; + } + } + } + +#define FEEDMATRIX_ATTN_SHIFT 16 + + if (j > 1) { + /* + * XXX For channel that require accumulation from + * multiple channels, apply a slight attenuation to + * avoid clipping. + */ + mul = (1 << (FEEDMATRIX_ATTN_SHIFT - 1)) + 143 - j; + shift = FEEDMATRIX_ATTN_SHIFT; + while ((mul & 1) == 0 && shift > 0) { + mul >>= 1; + shift--; + } + info->matrix[ch].mul = mul; + info->matrix[ch].shift = shift; + } + } + +#ifndef _KERNEL + fprintf(stderr, "Total: %d\n", ch); + + for (i = 0; info->matrix[i].chn[0] != SND_CHN_T_EOF; i++) { + fprintf(stderr, "%d: [", i); + for (j = 0; info->matrix[i].chn[j] != SND_CHN_T_EOF; j++) { + if (j != 0) + fprintf(stderr, ", "); + fprintf(stderr, "%d", + (info->matrix[i].chn[j] == SND_CHN_T_NULL) ? + 0xffffffff : info->matrix[i].chn[j] / info->bps); + } + fprintf(stderr, "] attn: (x * %d) >> %d\n", + info->matrix[i].mul, info->matrix[i].shift); + } +#endif + + return (0); +} + +static int +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)) + return (EINVAL); + + info = malloc(sizeof(*info), M_DEVBUF, M_NOWAIT | M_ZERO); + if (info == NULL) + return (ENOMEM); + + info->in = f->desc->in; + info->out = f->desc->out; + 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); + + ret = feed_matrix_setup(info, m_in, m_out); + if (ret != 0) { + free(info, M_DEVBUF); + return (ret); + } + + f->data = info; + + return (0); +} + +static int +feed_matrix_free(struct pcm_feeder *f) +{ + struct feed_matrix_info *info; + + info = f->data; + if (info != NULL) + free(info, M_DEVBUF); + + f->data = NULL; + + return (0); +} + +static int +feed_matrix_feed(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, + uint32_t count, void *source) +{ + struct feed_matrix_info *info; + uint32_t j, inmax; + uint8_t *src, *dst; + + info = f->data; + if (info->matrix[0].chn[0] == SND_CHN_T_EOF) + return (FEEDER_FEED(f->source, c, b, count, source)); + + dst = b; + count = SND_FXROUND(count, info->oalign); + inmax = info->ialign + info->oalign; + + /* + * This loop might look simmilar to other feeder_* loops, but be + * advised: matrixing might involve overlapping (think about + * swapping end to front or something like that). In this regard it + * might be simmilar to feeder_format, but feeder_format works on + * 'sample' domain where it can be fitted into single 32bit integer + * while matrixing works on 'sample frame' domain. + */ + do { + if (count < info->oalign) + break; + + if (count < inmax) { + src = info->reservoir; + j = info->ialign; + } else { + if (info->ialign == info->oalign) + j = count - info->oalign; + else if (info->ialign > info->oalign) + j = SND_FXROUND(count - info->oalign, + info->ialign); + else + j = (SND_FXDIV(count, info->oalign) - 1) * + info->ialign; + src = dst + count - j; + } + + j = SND_FXDIV(FEEDER_FEED(f->source, c, src, j, source), + info->ialign); + if (j == 0) + break; + + info->apply(info, src, dst, j); + + j *= info->oalign; + dst += j; + count -= j; + + } while (count != 0); + + return (dst - b); +} + +static struct pcm_feederdesc feeder_matrix_desc[] = { + { FEEDER_MATRIX, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0 } +}; + +static kobj_method_t feeder_matrix_methods[] = { + KOBJMETHOD(feeder_init, feed_matrix_init), + KOBJMETHOD(feeder_free, feed_matrix_free), + KOBJMETHOD(feeder_feed, feed_matrix_feed), + KOBJMETHOD_END +}; + +FEEDER_DECLARE(feeder_matrix, NULL); + +/* External */ +int +feeder_matrix_setup(struct pcm_feeder *f, struct pcmchan_matrix *m_in, + struct pcmchan_matrix *m_out) +{ + + if (f == NULL || f->desc == NULL || f->desc->type != FEEDER_MATRIX || + f->data == NULL) + return (EINVAL); + + return (feed_matrix_setup(f->data, m_in, m_out)); +} + +/* + * feeder_matrix_default_id(): For a given number of channels, return + * default prefered id (example: both 5.1 and + * 6.0 are simply 6 channels, but 5.1 is more + * preferable). + */ +int +feeder_matrix_default_id(uint32_t ch) +{ + + if (ch < feeder_matrix_maps[SND_CHN_MATRIX_BEGIN].channels || + ch > feeder_matrix_maps[SND_CHN_MATRIX_END].channels) + return (SND_CHN_MATRIX_UNKNOWN); + + return (feeder_matrix_maps[feeder_matrix_default_ids[ch]].id); +} + +/* + * feeder_matrix_default_channel_map(): Ditto, but return matrix map + * instead. + */ +struct pcmchan_matrix * +feeder_matrix_default_channel_map(uint32_t ch) +{ + + if (ch < feeder_matrix_maps[SND_CHN_MATRIX_BEGIN].channels || + ch > feeder_matrix_maps[SND_CHN_MATRIX_END].channels) + return (NULL); + + return (&feeder_matrix_maps[feeder_matrix_default_ids[ch]]); +} + +/* + * feeder_matrix_default_format(): For a given audio format, return the + * proper audio format based on preferable + * matrix. + */ +uint32_t +feeder_matrix_default_format(uint32_t format) +{ + struct pcmchan_matrix *m; + uint32_t i, ch, ext; + + ch = AFMT_CHANNEL(format); + ext = AFMT_EXTCHANNEL(format); + + if (ext != 0) { + for (i = SND_CHN_MATRIX_BEGIN; i <= SND_CHN_MATRIX_END; i++) { + if (feeder_matrix_maps[i].channels == ch && + feeder_matrix_maps[i].ext == ext) + return (SND_FORMAT(format, ch, ext)); + } + } + + m = feeder_matrix_default_channel_map(ch); + if (m == NULL) + return (0x00000000); + + return (SND_FORMAT(format, ch, m->ext)); +} + +/* + * feeder_matrix_format_id(): For a given audio format, return its matrix + * id. + */ +int +feeder_matrix_format_id(uint32_t format) +{ + uint32_t i, ch, ext; + + ch = AFMT_CHANNEL(format); + ext = AFMT_EXTCHANNEL(format); + + for (i = SND_CHN_MATRIX_BEGIN; i <= SND_CHN_MATRIX_END; i++) { + if (feeder_matrix_maps[i].channels == ch && + feeder_matrix_maps[i].ext == ext) + return (feeder_matrix_maps[i].id); + } + + return (SND_CHN_MATRIX_UNKNOWN); +} + +/* + * feeder_matrix_format_map(): For a given audio format, return its matrix + * map. + */ +struct pcmchan_matrix * +feeder_matrix_format_map(uint32_t format) +{ + uint32_t i, ch, ext; + + ch = AFMT_CHANNEL(format); + ext = AFMT_EXTCHANNEL(format); + + for (i = SND_CHN_MATRIX_BEGIN; i <= SND_CHN_MATRIX_END; i++) { + if (feeder_matrix_maps[i].channels == ch && + feeder_matrix_maps[i].ext == ext) + return (&feeder_matrix_maps[i]); + } + + return (NULL); +} + +/* + * feeder_matrix_id_map(): For a given matrix id, return its matrix map. + */ +struct pcmchan_matrix * +feeder_matrix_id_map(int id) +{ + + if (id < SND_CHN_MATRIX_BEGIN || id > SND_CHN_MATRIX_END) + return (NULL); + + return (&feeder_matrix_maps[id]); +} + +/* + * feeder_matrix_compare(): Compare the simmilarities of matrices. + */ +int +feeder_matrix_compare(struct pcmchan_matrix *m_in, struct pcmchan_matrix *m_out) +{ + uint32_t i; + + if (m_in == m_out) + return (0); + + if (m_in->channels != m_out->channels || m_in->ext != m_out->ext || + m_in->mask != m_out->mask) + return (1); + + for (i = 0; i < (sizeof(m_in->map) / sizeof(m_in->map[0])); i++) { + if (m_in->map[i].type != m_out->map[i].type) + return (1); + if (m_in->map[i].type == SND_CHN_T_MAX) + break; + if (m_in->map[i].members != m_out->map[i].members) + return (1); + if (i <= SND_CHN_T_END) { + if (m_in->offset[m_in->map[i].type] != + m_out->offset[m_out->map[i].type]) + return (1); + } + } + + return (0); +} + +/* + * XXX 4front intepretation of "surround" is ambigous and sort of + * conflicting with "rear"/"back". Map it to "side". Well.. + * who cares? + */ +static int snd_chn_to_oss[SND_CHN_T_MAX] = { + [SND_CHN_T_FL] = CHID_L, + [SND_CHN_T_FR] = CHID_R, + [SND_CHN_T_FC] = CHID_C, + [SND_CHN_T_LF] = CHID_LFE, + [SND_CHN_T_SL] = CHID_LS, + [SND_CHN_T_SR] = CHID_RS, + [SND_CHN_T_BL] = CHID_LR, + [SND_CHN_T_BR] = CHID_RR +}; + +#define SND_CHN_OSS_VALIDMASK \ + (SND_CHN_T_MASK_FL | SND_CHN_T_MASK_FR | \ + SND_CHN_T_MASK_FC | SND_CHN_T_MASK_LF | \ + SND_CHN_T_MASK_SL | SND_CHN_T_MASK_SR | \ + SND_CHN_T_MASK_BL | SND_CHN_T_MASK_BR) + +#define SND_CHN_OSS_MAX 8 +#define SND_CHN_OSS_BEGIN CHID_L +#define SND_CHN_OSS_END CHID_RR + +static int oss_to_snd_chn[SND_CHN_OSS_END + 1] = { + [CHID_L] = SND_CHN_T_FL, + [CHID_R] = SND_CHN_T_FR, + [CHID_C] = SND_CHN_T_FC, + [CHID_LFE] = SND_CHN_T_LF, + [CHID_LS] = SND_CHN_T_SL, + [CHID_RS] = SND_CHN_T_SR, + [CHID_LR] = SND_CHN_T_BL, + [CHID_RR] = SND_CHN_T_BR +}; + +/* + * Used by SNDCTL_DSP_GET_CHNORDER. + */ +int +feeder_matrix_oss_get_channel_order(struct pcmchan_matrix *m, + unsigned long long *map) +{ + unsigned long long tmpmap; + uint32_t i; + + if (m == NULL || map == NULL || (m->mask & ~SND_CHN_OSS_VALIDMASK) || + m->channels > SND_CHN_OSS_MAX) + return (EINVAL); + + tmpmap = 0x0000000000000000ULL; + + for (i = 0; m->map[i].type != SND_CHN_T_MAX && + i < SND_CHN_OSS_MAX; i++) { + if ((1 << m->map[i].type) & ~SND_CHN_OSS_VALIDMASK) + return (EINVAL); + tmpmap |= + (unsigned long long)snd_chn_to_oss[m->map[i].type] << + (i * 4); + } + + *map = tmpmap; + + return (0); +} + +/* + * Used by SNDCTL_DSP_SET_CHNORDER. + */ +int +feeder_matrix_oss_set_channel_order(struct pcmchan_matrix *m, + unsigned long long *map) +{ + struct pcmchan_matrix tmp; + uint32_t chmask, i; + int ch, cheof; + + if (m == NULL || map == NULL || (m->mask & ~SND_CHN_OSS_VALIDMASK) || + m->channels > SND_CHN_OSS_MAX || (*map & 0xffffffff00000000ULL)) + return (EINVAL); + + tmp = *m; + tmp.channels = 0; + tmp.ext = 0; + tmp.mask = 0; + memset(tmp.offset, -1, sizeof(tmp.offset)); + cheof = 0; + + for (i = 0; i < SND_CHN_OSS_MAX; i++) { + ch = (*map >> (i * 4)) & 0xf; + if (ch < SND_CHN_OSS_BEGIN) { + if (cheof == 0 && m->map[i].type != SND_CHN_T_MAX) + return (EINVAL); + cheof++; + tmp.map[i] = m->map[i]; + continue; + } else if (ch > SND_CHN_OSS_END) + return (EINVAL); + else if (cheof != 0) + return (EINVAL); + ch = oss_to_snd_chn[ch]; + chmask = 1 << ch; + /* channel not exist in matrix */ + if (!(chmask & m->mask)) + return (EINVAL); + /* duplicated channel */ + if (chmask & tmp.mask) + return (EINVAL); + tmp.map[i] = m->map[m->offset[ch]]; + if (tmp.map[i].type != ch) + return (EINVAL); + tmp.offset[ch] = i; + tmp.mask |= chmask; + tmp.channels++; + if (chmask & SND_CHN_T_MASK_LF) + tmp.ext++; + } + + if (tmp.channels != m->channels || tmp.ext != m->ext || + tmp.mask != m->mask || + tmp.map[m->channels].type != SND_CHN_T_MAX) + return (EINVAL); + + *m = tmp; + + return (0); +} diff --git a/sys/dev/sound/pcm/feeder_mixer.c b/sys/dev/sound/pcm/feeder_mixer.c new file mode 100644 index 0000000..32978ac --- /dev/null +++ b/sys/dev/sound/pcm/feeder_mixer.c @@ -0,0 +1,402 @@ +/*- + * 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. + */ + +#ifdef _KERNEL +#ifdef HAVE_KERNEL_OPTION_HEADERS +#include "opt_snd.h" +#endif +#include <dev/sound/pcm/sound.h> +#include <dev/sound/pcm/pcm.h> +#include <dev/sound/pcm/vchan.h> +#include "feeder_if.h" + +#define SND_USE_FXDIV +#include "snd_fxdiv_gen.h" + +SND_DECLARE_FILE("$FreeBSD$"); +#endif + +#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; + 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 */ +}; + +#define FEEDMIXER_TAB_SIZE ((int32_t) \ + (sizeof(feed_mixer_info_tab) / \ + sizeof(feed_mixer_info_tab[0]))) + +#define FEEDMIXER_DATA(i, c) ((void *) \ + ((uintptr_t)((((i) & 0x1f) << 5) | \ + ((c) & 0x1f)))) +#define FEEDMIXER_INFOIDX(d) ((uint32_t)((uintptr_t)(d) >> 5) & 0x1f) +#define FEEDMIXER_CHANNELS(d) ((uint32_t)((uintptr_t)(d)) & 0x1f) + +static int +feed_mixer_init(struct pcm_feeder *f) +{ + int i; + + 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); + } + } + + return (EINVAL); +} + +static int +feed_mixer_set(struct pcm_feeder *f, int what, int value) +{ + + 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); + break; + default: + return (EINVAL); + break; + } + + return (0); +} + +static __inline int +feed_mixer_rec(struct pcm_channel *c) +{ + struct pcm_channel *ch; + struct snd_dbuf *b, *bs; + uint32_t cnt, maxfeed; + int rdy; + + /* + * Reset ready and moving pointer. We're not using bufsoft + * anywhere since its sole purpose is to become the primary + * distributor for the recorded buffer and also as an interrupt + * threshold progress indicator. + */ + b = c->bufsoft; + b->rp = 0; + b->rl = 0; + cnt = sndbuf_getsize(b); + maxfeed = SND_FXROUND(SND_FXDIV_MAX, sndbuf_getalign(b)); + + do { + cnt = FEEDER_FEED(c->feeder->source, c, b->tmpbuf, + min(cnt, maxfeed), c->bufhard); + if (cnt != 0) { + sndbuf_acquire(b, b->tmpbuf, cnt); + cnt = sndbuf_getfree(b); + } + } while (cnt != 0); + + /* Not enough data */ + if (b->rl < sndbuf_getalign(b)) { + b->rl = 0; + return (0); + } + + /* + * Keep track of ready and moving pointer since we will use + * bufsoft over and over again, pretending nothing has happened. + */ + rdy = b->rl; + + CHN_FOREACH(ch, c, children.busy) { + CHN_LOCK(ch); + if (CHN_STOPPED(ch) || (ch->flags & CHN_F_DIRTY)) { + CHN_UNLOCK(ch); + continue; + } +#ifdef SND_DEBUG + if ((c->flags & CHN_F_DIRTY) && VCHAN_SYNC_REQUIRED(ch)) { + if (vchan_sync(ch) != 0) { + CHN_UNLOCK(ch); + continue; + } + } +#endif + bs = ch->bufsoft; + if (ch->flags & CHN_F_MMAP) + sndbuf_dispose(bs, NULL, sndbuf_getready(bs)); + cnt = sndbuf_getfree(bs); + if (cnt < sndbuf_getalign(bs)) { + CHN_UNLOCK(ch); + continue; + } + maxfeed = SND_FXROUND(SND_FXDIV_MAX, sndbuf_getalign(bs)); + do { + cnt = FEEDER_FEED(ch->feeder, ch, bs->tmpbuf, + min(cnt, maxfeed), b); + if (cnt != 0) { + sndbuf_acquire(bs, bs->tmpbuf, cnt); + cnt = sndbuf_getfree(bs); + } + } while (cnt != 0); + /* + * Not entirely flushed out... + */ + if (b->rl != 0) + ch->xruns++; + CHN_UNLOCK(ch); + /* + * Rewind buffer position for next virtual channel. + */ + b->rp = 0; + b->rl = rdy; + } + + /* + * Set ready pointer to indicate that our children are ready + * to be woken up, also as an interrupt threshold progress + * indicator. + */ + b->rl = 1; + + c->flags &= ~CHN_F_DIRTY; + + /* + * Return 0 to bail out early from sndbuf_feed() loop. + * No need to increase feedcount counter since part of this + * feeder chains already include feed_root(). + */ + return (0); +} + +static int +feed_mixer_feed(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, + uint32_t count, void *source) +{ + struct feed_mixer_info *info; + struct snd_dbuf *src = source; + struct pcm_channel *ch; + uint32_t cnt, mcnt, rcnt, sz; + int passthrough; + uint8_t *tmp; + + if (c->direction == PCMDIR_REC) + return (feed_mixer_rec(c)); + + sz = sndbuf_getsize(src); + if (sz < count) + count = sz; + + info = &feed_mixer_info_tab[FEEDMIXER_INFOIDX(f->data)]; + sz = info->bps * FEEDMIXER_CHANNELS(f->data); + count = SND_FXROUND(count, sz); + if (count < sz) + return (0); + + /* + * We are going to use our source as a temporary buffer since it's + * got no other purpose. We obtain our data by traversing the channel + * list of children and calling mixer function to mix count bytes from + * each into our destination buffer, b. + */ + tmp = sndbuf_getbuf(src); + rcnt = 0; + mcnt = 0; + passthrough = 0; /* 'passthrough' / 'exclusive' marker */ + + CHN_FOREACH(ch, c, children.busy) { + CHN_LOCK(ch); + if (CHN_STOPPED(ch) || (ch->flags & CHN_F_DIRTY)) { + CHN_UNLOCK(ch); + continue; + } +#ifdef SND_DEBUG + if ((c->flags & CHN_F_DIRTY) && VCHAN_SYNC_REQUIRED(ch)) { + if (vchan_sync(ch) != 0) { + CHN_UNLOCK(ch); + continue; + } + } +#endif + if ((ch->flags & CHN_F_MMAP) && !(ch->flags & CHN_F_CLOSING)) + sndbuf_acquire(ch->bufsoft, NULL, + sndbuf_getfree(ch->bufsoft)); + if (info->mix == NULL) { + /* + * Passthrough. Dump the first digital/passthrough + * channel into destination buffer, and the rest into + * nothingness (mute effect). + */ + if (passthrough == 0 && + (ch->format & AFMT_PASSTHROUGH)) { + rcnt = SND_FXROUND(FEEDER_FEED(ch->feeder, ch, + b, count, ch->bufsoft), sz); + passthrough = 1; + } else + FEEDER_FEED(ch->feeder, ch, tmp, count, + ch->bufsoft); + } else if (c->flags & CHN_F_EXCLUSIVE) { + /* + * Exclusive. Dump the first 'exclusive' channel into + * destination buffer, and the rest into nothingness + * (mute effect). + */ + if (passthrough == 0 && (ch->flags & CHN_F_EXCLUSIVE)) { + rcnt = SND_FXROUND(FEEDER_FEED(ch->feeder, ch, + b, count, ch->bufsoft), sz); + passthrough = 1; + } else + FEEDER_FEED(ch->feeder, ch, tmp, count, + ch->bufsoft); + } else { + if (rcnt == 0) { + rcnt = SND_FXROUND(FEEDER_FEED(ch->feeder, ch, + b, count, ch->bufsoft), sz); + mcnt = count - rcnt; + } else { + cnt = SND_FXROUND(FEEDER_FEED(ch->feeder, ch, + tmp, count, ch->bufsoft), sz); + if (cnt != 0) { + if (mcnt != 0) { + memset(b + rcnt, + sndbuf_zerodata( + f->desc->out), mcnt); + mcnt = 0; + } + info->mix(tmp, b, cnt); + if (cnt > rcnt) + rcnt = cnt; + } + } + } + CHN_UNLOCK(ch); + } + + if (++c->feedcount == 0) + c->feedcount = 2; + + c->flags &= ~CHN_F_DIRTY; + + return (rcnt); +} + +static struct pcm_feederdesc feeder_mixer_desc[] = { + { FEEDER_MIXER, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0 } +}; + +static kobj_method_t feeder_mixer_methods[] = { + KOBJMETHOD(feeder_init, feed_mixer_init), + KOBJMETHOD(feeder_set, feed_mixer_set), + KOBJMETHOD(feeder_feed, feed_mixer_feed), + KOBJMETHOD_END +}; + +FEEDER_DECLARE(feeder_mixer, NULL); diff --git a/sys/dev/sound/pcm/feeder_rate.c b/sys/dev/sound/pcm/feeder_rate.c index cf9d7c1..73a780a 100644 --- a/sys/dev/sound/pcm/feeder_rate.c +++ b/sys/dev/sound/pcm/feeder_rate.c @@ -1,7 +1,5 @@ /*- - * Copyright (c) 1999 Cameron Grant <cg@FreeBSD.org> - * Copyright (c) 2003 Orion Hodson <orion@FreeBSD.org> - * Copyright (c) 2005 Ariff Abdullah <ariff@FreeBSD.org> + * Copyright (c) 2005-2009 Ariff Abdullah <ariff@FreeBSD.org> * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -27,102 +25,152 @@ */ /* - * 2006-02-21: - * ========== + * feeder_rate: (Codename: Z Resampler), which means any effort to create + * future replacement for this resampler are simply absurd unless + * the world decide to add new alphabet after Z. * - * Major cleanup and overhaul to remove much redundant codes. - * Highlights: - * 1) Support for signed / unsigned 16, 24 and 32 bit, - * big / little endian, - * 2) Unlimited channels. + * FreeBSD bandlimited sinc interpolator, technically based on + * "Digital Audio Resampling" by Julius O. Smith III + * - http://ccrma.stanford.edu/~jos/resample/ * - * 2005-06-11: - * ========== + * The Good: + * + all out fixed point integer operations, no soft-float or anything like + * that. + * + classic polyphase converters with high quality coefficient's polynomial + * interpolators. + * + fast, faster, or the fastest of its kind. + * + compile time configurable. + * + etc etc.. * - * *New* and rewritten soft sample rate converter supporting arbitrary sample - * rates, fine grained scaling/coefficients and a unified up/down stereo - * converter. Most of the disclaimers from orion's notes also applies - * here, regarding linear interpolation deficiencies and pre/post - * anti-aliasing filtering issues. This version comes with a much simpler and - * tighter interface, although it works almost exactly like the older one. - * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * * - * This new implementation is fully dedicated in memory of Cameron Grant, * - * the creator of the magnificent, highly addictive feeder infrastructure. * - * * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * - * Orion's notes: - * ============= - * - * This rate conversion code uses linear interpolation without any - * pre- or post- interpolation filtering to combat aliasing. This - * greatly limits the sound quality and should be addressed at some - * stage in the future. - * - * Since this accuracy of interpolation is sensitive and examination - * of the algorithm output is harder from the kernel, the code is - * designed to be compiled in the kernel and in a userland test - * harness. This is done by selectively including and excluding code - * with several portions based on whether _KERNEL is defined. It's a - * little ugly, but exceedingly useful. The testsuite and its - * revisions can be found at: - * http://people.freebsd.org/~orion/files/feedrate/ - * - * Special thanks to Ken Marx for exposing flaws in the code and for - * testing revisions. + * The Bad: + * - The z, z_, and Z_ . Due to mental block (or maybe just 0x7a69), I + * couldn't think of anything simpler than that (feeder_rate_xxx is just + * too long). Expect possible clashes with other zitizens (any?). */ +#ifdef _KERNEL +#ifdef HAVE_KERNEL_OPTION_HEADERS +#include "opt_snd.h" +#endif #include <dev/sound/pcm/sound.h> +#include <dev/sound/pcm/pcm.h> #include "feeder_if.h" +#define SND_USE_FXDIV +#include "snd_fxdiv_gen.h" + SND_DECLARE_FILE("$FreeBSD$"); +#endif -#define RATE_ASSERT(x, y) /* KASSERT(x,y) */ -#define RATE_TEST(x, y) /* if (!(x)) printf y */ -#define RATE_TRACE(x...) /* printf(x) */ +#include "feeder_rate_gen.h" -MALLOC_DEFINE(M_RATEFEEDER, "ratefeed", "pcm rate feeder"); +#if !defined(_KERNEL) && defined(SND_DIAGNOSTIC) +#undef Z_DIAGNOSTIC +#define Z_DIAGNOSTIC 1 +#elif defined(_KERNEL) +#undef Z_DIAGNOSTIC +#endif -/* - * Don't overflow 32bit integer, since everything is done - * within 32bit arithmetic. - */ -#define RATE_FACTOR_MIN 1 -#define RATE_FACTOR_MAX PCM_S24_MAX -#define RATE_FACTOR_SAFE(val) (!((val) < RATE_FACTOR_MIN || \ - (val) > RATE_FACTOR_MAX)) - -struct feed_rate_info; - -typedef uint32_t (*feed_rate_converter)(struct feed_rate_info *, - uint8_t *, uint32_t); - -struct feed_rate_info { - uint32_t src, dst; /* rounded source / destination rates */ - uint32_t rsrc, rdst; /* original source / destination rates */ - uint32_t gx, gy; /* interpolation / decimation ratio */ - uint32_t alpha; /* interpolation distance */ - uint32_t pos, bpos; /* current sample / buffer positions */ - uint32_t bufsz; /* total buffer size limit */ - uint32_t bufsz_init; /* allocated buffer size */ - uint32_t channels; /* total channels */ - uint32_t bps; /* bytes-per-sample */ -#ifdef FEEDRATE_STRAY - uint32_t stray; /* stray bytes */ -#endif - uint8_t *buffer; - feed_rate_converter convert; +#ifndef Z_QUALITY_DEFAULT +#define Z_QUALITY_DEFAULT Z_QUALITY_LINEAR +#endif + +#define Z_RESERVOIR 2048 +#define Z_RESERVOIR_MAX 131072 + +#define Z_SINC_MAX 0x3fffff +#define Z_SINC_DOWNMAX 48 /* 384000 / 8000 */ + +#ifdef _KERNEL +#define Z_POLYPHASE_MAX 183040 /* 286 taps, 640 phases */ +#else +#define Z_POLYPHASE_MAX 1464320 /* 286 taps, 5120 phases */ +#endif + +#define Z_RATE_DEFAULT 48000 + +#define Z_RATE_MIN FEEDRATE_RATEMIN +#define Z_RATE_MAX FEEDRATE_RATEMAX +#define Z_ROUNDHZ FEEDRATE_ROUNDHZ +#define Z_ROUNDHZ_MIN FEEDRATE_ROUNDHZ_MIN +#define Z_ROUNDHZ_MAX FEEDRATE_ROUNDHZ_MAX + +#define Z_RATE_SRC FEEDRATE_SRC +#define Z_RATE_DST FEEDRATE_DST +#define Z_RATE_QUALITY FEEDRATE_QUALITY +#define Z_RATE_CHANNELS FEEDRATE_CHANNELS + +#define Z_PARANOID 1 + +#define Z_MULTIFORMAT 1 + +#ifdef _KERNEL +#undef Z_USE_ALPHADRIFT +#define Z_USE_ALPHADRIFT 1 +#endif + +#define Z_FACTOR_MIN 1 +#define Z_FACTOR_MAX Z_MASK +#define Z_FACTOR_SAFE(v) (!((v) < Z_FACTOR_MIN || (v) > Z_FACTOR_MAX)) + +struct z_info; + +typedef void (*z_resampler_t)(struct z_info *, uint8_t *); + +struct z_info { + int32_t rsrc, rdst; /* original source / destination rates */ + int32_t src, dst; /* rounded source / destination rates */ + int32_t channels; /* total channels */ + int32_t bps; /* bytes-per-sample */ + int32_t quality; /* resampling quality */ + + int32_t z_gx, z_gy; /* interpolation / decimation ratio */ + int32_t z_alpha; /* output sample time phase / drift */ + uint8_t *z_delay; /* FIR delay line / linear buffer */ + int32_t *z_coeff; /* FIR coefficients */ + int32_t *z_dcoeff; /* FIR coefficients differences */ + int32_t *z_pcoeff; /* FIR polyphase coefficients */ + int32_t z_scale; /* output scaling */ + int32_t z_dx; /* input sample drift increment */ + int32_t z_dy; /* output sample drift increment */ +#ifdef Z_USE_ALPHADRIFT + int32_t z_alphadrift; /* alpha drift rate */ + int32_t z_startdrift; /* buffer start position drift rate */ +#endif + int32_t z_mask; /* delay line full length mask */ + int32_t z_size; /* half width of FIR taps */ + int32_t z_full; /* full size of delay line */ + int32_t z_alloc; /* largest allocated full size of delay line */ + int32_t z_start; /* buffer processing start position */ + int32_t z_pos; /* current position for the next feed */ +#ifdef Z_DIAGNOSTIC + uint32_t z_cycle; /* output cycle, purely for statistical */ +#endif + int32_t z_maxfeed; /* maximum feed to avoid 32bit overflow */ + + z_resampler_t z_resample; }; -int feeder_rate_min = FEEDRATE_RATEMIN; -int feeder_rate_max = FEEDRATE_RATEMAX; -int feeder_rate_round = FEEDRATE_ROUNDHZ; +int feeder_rate_min = Z_RATE_MIN; +int feeder_rate_max = Z_RATE_MAX; +int feeder_rate_round = Z_ROUNDHZ; +int feeder_rate_quality = Z_QUALITY_DEFAULT; + +static int feeder_rate_polyphase_max = Z_POLYPHASE_MAX; + +#ifdef _KERNEL +static const char feeder_rate_presets[] = FEEDER_RATE_PRESETS; +SYSCTL_STRING(_hw_snd, OID_AUTO, feeder_rate_presets, CTLFLAG_RD, + &feeder_rate_presets, 0, "compile-time rate presets"); TUNABLE_INT("hw.snd.feeder_rate_min", &feeder_rate_min); TUNABLE_INT("hw.snd.feeder_rate_max", &feeder_rate_max); TUNABLE_INT("hw.snd.feeder_rate_round", &feeder_rate_round); +TUNABLE_INT("hw.snd.feeder_rate_quality", &feeder_rate_quality); + +TUNABLE_INT("hw.snd.feeder_rate_polyphase_max", &feeder_rate_polyphase_max); +SYSCTL_INT(_hw_snd, OID_AUTO, feeder_rate_polyphase_max, CTLFLAG_RW, + &feeder_rate_polyphase_max, 0, "maximum allowable polyphase entries"); static int sysctl_hw_snd_feeder_rate_min(SYSCTL_HANDLER_ARGS) @@ -131,17 +179,20 @@ sysctl_hw_snd_feeder_rate_min(SYSCTL_HANDLER_ARGS) val = feeder_rate_min; err = sysctl_handle_int(oidp, &val, 0, req); - if (err != 0 || req->newptr == NULL) + + if (err != 0 || req->newptr == NULL || val == feeder_rate_min) return (err); - if (RATE_FACTOR_SAFE(val) && val < feeder_rate_max) - feeder_rate_min = val; - else - err = EINVAL; - return (err); + + if (!(Z_FACTOR_SAFE(val) && val < feeder_rate_max)) + return (EINVAL); + + feeder_rate_min = val; + + return (0); } SYSCTL_PROC(_hw_snd, OID_AUTO, feeder_rate_min, CTLTYPE_INT | CTLFLAG_RW, - 0, sizeof(int), sysctl_hw_snd_feeder_rate_min, "I", - "minimum allowable rate"); + 0, sizeof(int), sysctl_hw_snd_feeder_rate_min, "I", + "minimum allowable rate"); static int sysctl_hw_snd_feeder_rate_max(SYSCTL_HANDLER_ARGS) @@ -150,17 +201,20 @@ sysctl_hw_snd_feeder_rate_max(SYSCTL_HANDLER_ARGS) val = feeder_rate_max; err = sysctl_handle_int(oidp, &val, 0, req); - if (err != 0 || req->newptr == NULL) + + if (err != 0 || req->newptr == NULL || val == feeder_rate_max) return (err); - if (RATE_FACTOR_SAFE(val) && val > feeder_rate_min) - feeder_rate_max = val; - else - err = EINVAL; - return (err); + + if (!(Z_FACTOR_SAFE(val) && val > feeder_rate_min)) + return (EINVAL); + + feeder_rate_max = val; + + return (0); } SYSCTL_PROC(_hw_snd, OID_AUTO, feeder_rate_max, CTLTYPE_INT | CTLFLAG_RW, - 0, sizeof(int), sysctl_hw_snd_feeder_rate_max, "I", - "maximum allowable rate"); + 0, sizeof(int), sysctl_hw_snd_feeder_rate_max, "I", + "maximum allowable rate"); static int sysctl_hw_snd_feeder_rate_round(SYSCTL_HANDLER_ARGS) @@ -169,472 +223,1519 @@ sysctl_hw_snd_feeder_rate_round(SYSCTL_HANDLER_ARGS) val = feeder_rate_round; err = sysctl_handle_int(oidp, &val, 0, req); - if (err != 0 || req->newptr == NULL) + + if (err != 0 || req->newptr == NULL || val == feeder_rate_round) return (err); - if (val < FEEDRATE_ROUNDHZ_MIN || val > FEEDRATE_ROUNDHZ_MAX) - err = EINVAL; - else - feeder_rate_round = val - (val % FEEDRATE_ROUNDHZ); - return (err); + + if (val < Z_ROUNDHZ_MIN || val > Z_ROUNDHZ_MAX) + return (EINVAL); + + feeder_rate_round = val - (val % Z_ROUNDHZ); + + return (0); } SYSCTL_PROC(_hw_snd, OID_AUTO, feeder_rate_round, CTLTYPE_INT | CTLFLAG_RW, - 0, sizeof(int), sysctl_hw_snd_feeder_rate_round, "I", - "sample rate converter rounding threshold"); + 0, sizeof(int), sysctl_hw_snd_feeder_rate_round, "I", + "sample rate converter rounding threshold"); -#define FEEDER_RATE_CONVERT(FMTBIT, RATE_INTCAST, SIGN, SIGNS, ENDIAN, ENDIANS) \ -static uint32_t \ -feed_convert_##SIGNS##FMTBIT##ENDIANS(struct feed_rate_info *info, \ - uint8_t *dst, uint32_t max) \ -{ \ - uint32_t ret, smpsz, ch, pos, bpos, gx, gy, alpha, d1, d2; \ - int32_t x, y; \ - int i; \ - uint8_t *src, *sx, *sy; \ - \ - ret = 0; \ - alpha = info->alpha; \ - gx = info->gx; \ - gy = info->gy; \ - pos = info->pos; \ - bpos = info->bpos; \ - src = info->buffer + pos; \ - ch = info->channels; \ - smpsz = PCM_##FMTBIT##_BPS * ch; \ - for (;;) { \ - if (alpha < gx) { \ - alpha += gy; \ - pos += smpsz; \ - if (pos == bpos) \ - break; \ - src += smpsz; \ - } else { \ - alpha -= gx; \ - d1 = (alpha << PCM_FXSHIFT) / gy; \ - d2 = (1U << PCM_FXSHIFT) - d1; \ - sx = src - smpsz; \ - sy = src; \ - i = ch; \ - do { \ - x = PCM_READ_##SIGN##FMTBIT##_##ENDIAN(sx); \ - y = PCM_READ_##SIGN##FMTBIT##_##ENDIAN(sy); \ - x = (((RATE_INTCAST)x * d1) + \ - ((RATE_INTCAST)y * d2)) >> PCM_FXSHIFT; \ - PCM_WRITE_##SIGN##FMTBIT##_##ENDIAN(dst, x); \ - dst += PCM_##FMTBIT##_BPS; \ - sx += PCM_##FMTBIT##_BPS; \ - sy += PCM_##FMTBIT##_BPS; \ - ret += PCM_##FMTBIT##_BPS; \ - } while (--i != 0); \ - if (ret == max) \ - break; \ - } \ - } \ - info->alpha = alpha; \ - info->pos = pos; \ - return (ret); \ +static int +sysctl_hw_snd_feeder_rate_quality(SYSCTL_HANDLER_ARGS) +{ + struct snddev_info *d; + struct pcm_channel *c; + struct pcm_feeder *f; + int i, err, val; + + val = feeder_rate_quality; + err = sysctl_handle_int(oidp, &val, 0, req); + + if (err != 0 || req->newptr == NULL || val == feeder_rate_quality) + return (err); + + if (val < Z_QUALITY_MIN || val > Z_QUALITY_MAX) + return (EINVAL); + + feeder_rate_quality = val; + + /* + * Traverse all available channels on each device and try to + * set resampler quality if and only if it is exist as + * part of feeder chains and the channel is idle. + */ + 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_LOCK(d); + PCM_WAIT(d); + PCM_ACQUIRE(d); + CHN_FOREACH(c, d, channels.pcm) { + CHN_LOCK(c); + f = chn_findfeeder(c, FEEDER_RATE); + if (f == NULL || f->data == NULL || CHN_STARTED(c)) { + CHN_UNLOCK(c); + continue; + } + (void)FEEDER_SET(f, FEEDRATE_QUALITY, val); + CHN_UNLOCK(c); + } + PCM_RELEASE(d); + PCM_UNLOCK(d); + } + + return (0); } +SYSCTL_PROC(_hw_snd, OID_AUTO, feeder_rate_quality, CTLTYPE_INT | CTLFLAG_RW, + 0, sizeof(int), sysctl_hw_snd_feeder_rate_quality, "I", + "sample rate converter quality ("__XSTRING(Z_QUALITY_MIN)"=low .. " + __XSTRING(Z_QUALITY_MAX)"=high)"); +#endif /* _KERNEL */ -FEEDER_RATE_CONVERT(8, int32_t, S, s, NE, ne) -FEEDER_RATE_CONVERT(16, int32_t, S, s, LE, le) -FEEDER_RATE_CONVERT(24, int32_t, S, s, LE, le) -FEEDER_RATE_CONVERT(32, intpcm_t, S, s, LE, le) -FEEDER_RATE_CONVERT(16, int32_t, S, s, BE, be) -FEEDER_RATE_CONVERT(24, int32_t, S, s, BE, be) -FEEDER_RATE_CONVERT(32, intpcm_t, S, s, BE, be) -FEEDER_RATE_CONVERT(8, int32_t, U, u, NE, ne) -FEEDER_RATE_CONVERT(16, int32_t, U, u, LE, le) -FEEDER_RATE_CONVERT(24, int32_t, U, u, LE, le) -FEEDER_RATE_CONVERT(32, intpcm_t, U, u, LE, le) -FEEDER_RATE_CONVERT(16, int32_t, U, u, BE, be) -FEEDER_RATE_CONVERT(24, int32_t, U, u, BE, be) -FEEDER_RATE_CONVERT(32, intpcm_t, U, u, BE, be) -static void -feed_speed_ratio(uint32_t src, uint32_t dst, uint32_t *gx, uint32_t *gy) +/* + * Resampler type. + */ +#define Z_IS_ZOH(i) ((i)->quality == Z_QUALITY_ZOH) +#define Z_IS_LINEAR(i) ((i)->quality == Z_QUALITY_LINEAR) +#define Z_IS_SINC(i) ((i)->quality > Z_QUALITY_LINEAR) + +/* + * Macroses for accurate sample time drift calculations. + * + * gy2gx : given the amount of output, return the _exact_ required amount of + * input. + * gx2gy : given the amount of input, return the _maximum_ amount of output + * that will be generated. + * drift : given the amount of input and output, return the elapsed + * sample-time. + */ +#define _Z_GCAST(x) ((uint64_t)(x)) + +#if defined(__GNUCLIKE_ASM) && defined(__i386__) +/* + * This is where i386 being beaten to a pulp. Fortunately this function is + * rarely being called and if it is, it will decide the best (hopefully) + * fastest way to do the division. If we can ensure that everything is dword + * aligned, letting the compiler to call udivdi3 to do the division can be + * faster compared to this. + * + * amd64 is the clear winner here, no question about it. + */ +static __inline uint32_t +Z_DIV(uint64_t v, uint32_t d) +{ + uint32_t hi, lo, quo, rem; + + hi = v >> 32; + lo = v & 0xffffffff; + + /* + * As much as we can, try to avoid long division like a plague. + */ + if (hi == 0) + quo = lo / d; + else + __asm("divl %2" + : "=a" (quo), "=d" (rem) + : "r" (d), "0" (lo), "1" (hi)); + + return (quo); +} +#else +#define Z_DIV(x, y) ((x) / (y)) +#endif + +#define _Z_GY2GX(i, a, v) \ + Z_DIV(((_Z_GCAST((i)->z_gx) * (v)) + ((i)->z_gy - (a) - 1)), \ + (i)->z_gy) + +#define _Z_GX2GY(i, a, v) \ + Z_DIV(((_Z_GCAST((i)->z_gy) * (v)) + (a)), (i)->z_gx) + +#define _Z_DRIFT(i, x, y) \ + ((_Z_GCAST((i)->z_gy) * (x)) - (_Z_GCAST((i)->z_gx) * (y))) + +#define z_gy2gx(i, v) _Z_GY2GX(i, (i)->z_alpha, v) +#define z_gx2gy(i, v) _Z_GX2GY(i, (i)->z_alpha, v) +#define z_drift(i, x, y) _Z_DRIFT(i, x, y) + +/* + * Macroses for SINC coefficients table manipulations.. whatever. + */ +#define Z_SINC_COEFF_IDX(i) ((i)->quality - Z_QUALITY_LINEAR - 1) + +#define Z_SINC_LEN(i) \ + ((int32_t)(((uint64_t)z_coeff_tab[Z_SINC_COEFF_IDX(i)].len << \ + Z_SHIFT) / (i)->z_dy)) + +#define Z_SINC_BASE_LEN(i) \ + ((z_coeff_tab[Z_SINC_COEFF_IDX(i)].len - 1) >> (Z_DRIFT_SHIFT - 1)) + +/* + * Macroses for linear delay buffer operations. Alignment is not + * really necessary since we're not using true circular buffer, but it + * will help us guard against possible trespasser. To be honest, + * the linear block operations does not need guarding at all due to + * accurate drifting! + */ +#define z_align(i, v) ((v) & (i)->z_mask) +#define z_next(i, o, v) z_align(i, (o) + (v)) +#define z_prev(i, o, v) z_align(i, (o) - (v)) +#define z_fetched(i) (z_align(i, (i)->z_pos - (i)->z_start) - 1) +#define z_free(i) ((i)->z_full - (i)->z_pos) + +/* + * Macroses for Bla Bla .. :) + */ +#define z_copy(src, dst, sz) (void)memcpy(dst, src, sz) +#define z_feed(...) FEEDER_FEED(__VA_ARGS__) + +static __inline uint32_t +z_min(uint32_t x, uint32_t y) +{ + + return ((x < y) ? x : y); +} + +static int32_t +z_gcd(int32_t x, int32_t y) { - uint32_t w, x = src, y = dst; + int32_t w; while (y != 0) { w = x % y; x = y; y = w; } - *gx = src / x; - *gy = dst / x; + + return (x); } +static int32_t +z_roundpow2(int32_t v) +{ + int32_t i; + + i = 1; + + /* + * Let it overflow at will.. + */ + while (i > 0 && i < v) + i <<= 1; + + return (i); +} + +/* + * Zero Order Hold, the worst of the worst, an insult against quality, + * but super fast. + */ static void -feed_rate_reset(struct feed_rate_info *info) +z_feed_zoh(struct z_info *info, uint8_t *dst) { - info->src = info->rsrc - (info->rsrc % - ((feeder_rate_round > 0) ? feeder_rate_round : 1)); - info->dst = info->rdst - (info->rdst % - ((feeder_rate_round > 0) ? feeder_rate_round : 1)); - info->gx = 1; - info->gy = 1; - info->alpha = 0; - info->channels = 1; - info->bps = PCM_8_BPS; - info->convert = NULL; - info->bufsz = info->bufsz_init; - info->pos = 1; - info->bpos = 2; -#ifdef FEEDRATE_STRAY - info->stray = 0; +#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; + + cnt = info->channels * info->bps; + src = info->z_delay + (info->z_start * cnt); + + /* + * This is a bit faster than doing bcopy() since we're dealing + * with possible unaligned samples. + */ + do { + *dst++ = *src++; + } while (--cnt != 0); #endif } -static int -feed_rate_setup(struct pcm_feeder *f) +/* + * Linear Interpolation. This at least sounds better (perceptually) and fast, + * but without any proper filtering which means aliasing still exist and + * could become worst with a right sample. Interpolation centered within + * Z_LINEAR_ONE between the present and previous sample and everything is + * done with simple 32bit scaling arithmetic. + */ +#define Z_DECLARE_LINEAR(SIGN, BIT, ENDIAN) \ +static void \ +z_feed_linear_##SIGN##BIT##ENDIAN(struct z_info *info, uint8_t *dst) \ +{ \ + int32_t z; \ + intpcm_t x, y; \ + uint32_t ch; \ + uint8_t *sx, *sy; \ + \ + z = ((uint32_t)info->z_alpha * info->z_dx) >> Z_LINEAR_UNSHIFT; \ + \ + sx = info->z_delay + (info->z_start * info->channels * \ + PCM_##BIT##_BPS); \ + sy = sx - (info->channels * PCM_##BIT##_BPS); \ + \ + ch = info->channels; \ + \ + do { \ + x = _PCM_READ_##SIGN##BIT##_##ENDIAN(sx); \ + y = _PCM_READ_##SIGN##BIT##_##ENDIAN(sy); \ + x = Z_LINEAR_INTERPOLATE_##BIT(z, x, y); \ + _PCM_WRITE_##SIGN##BIT##_##ENDIAN(dst, x); \ + sx += PCM_##BIT##_BPS; \ + sy += PCM_##BIT##_BPS; \ + dst += PCM_##BIT##_BPS; \ + } while (--ch != 0); \ +} + +/* + * Userland clipping diagnostic check, not enabled in kernel compilation. + * While doing sinc interpolation, unrealistic samples like full scale sine + * wav will clip, but for other things this will not make any noise at all. + * Everybody should learn how to normalized perceived loudness of their own + * music/sounds/samples (hint: ReplayGain). + */ +#ifdef Z_DIAGNOSTIC +#define Z_CLIP_CHECK(v, BIT) do { \ + if ((v) > PCM_S##BIT##_MAX) { \ + fprintf(stderr, "Overflow: v=%jd, max=%jd\n", \ + (intmax_t)(v), (intmax_t)PCM_S##BIT##_MAX); \ + } else if ((v) < PCM_S##BIT##_MIN) { \ + fprintf(stderr, "Underflow: v=%jd, min=%jd\n", \ + (intmax_t)(v), (intmax_t)PCM_S##BIT##_MIN); \ + } \ +} while (0) +#else +#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 + * shifted to a full 32 bit, scaled and restored during write for + * maximum dynamic range (only for downsampling). + */ +#define _Z_SINC_ACCUMULATE(SIGN, BIT, ENDIAN, adv) \ + 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); \ + v += (intpcm64_t)x * coeff; \ + z += info->z_dy; \ + p adv##= info->channels * PCM_##BIT##_BPS + +/* + * XXX GCC4 optimization is such a !@#$%, need manual unrolling. + */ +#if defined(__GNUC__) && __GNUC__ >= 4 +#define Z_SINC_ACCUMULATE(...) do { \ + _Z_SINC_ACCUMULATE(__VA_ARGS__); \ + _Z_SINC_ACCUMULATE(__VA_ARGS__); \ +} while (0) +#define Z_SINC_ACCUMULATE_DECR 2 +#else +#define Z_SINC_ACCUMULATE(...) do { \ + _Z_SINC_ACCUMULATE(__VA_ARGS__); \ +} while (0) +#define Z_SINC_ACCUMULATE_DECR 1 +#endif + +#define Z_DECLARE_SINC(SIGN, BIT, ENDIAN) \ +static void \ +z_feed_sinc_##SIGN##BIT##ENDIAN(struct z_info *info, uint8_t *dst) \ +{ \ + intpcm64_t v; \ + intpcm_t x; \ + uint8_t *p; \ + int32_t coeff, z, *z_coeff, *z_dcoeff; \ + uint32_t c, center, ch, i; \ + \ + z_coeff = info->z_coeff; \ + z_dcoeff = info->z_dcoeff; \ + center = z_prev(info, info->z_start, info->z_size); \ + ch = info->channels * PCM_##BIT##_BPS; \ + dst += ch; \ + \ + do { \ + dst -= PCM_##BIT##_BPS; \ + ch -= PCM_##BIT##_BPS; \ + v = 0; \ + z = info->z_alpha * info->z_dx; \ + c = 0; \ + p = info->z_delay + (z_next(info, center, 1) * \ + info->channels * PCM_##BIT##_BPS) + ch; \ + for (i = info->z_size; i != 0; i -= Z_SINC_ACCUMULATE_DECR) \ + Z_SINC_ACCUMULATE(SIGN, BIT, ENDIAN, +); \ + z = info->z_dy - (info->z_alpha * info->z_dx); \ + c = 0; \ + p = info->z_delay + (center * info->channels * \ + PCM_##BIT##_BPS) + ch; \ + for (i = info->z_size; i != 0; i -= Z_SINC_ACCUMULATE_DECR) \ + Z_SINC_ACCUMULATE(SIGN, BIT, ENDIAN, -); \ + if (info->z_scale != Z_ONE) \ + v = Z_SCALE_##BIT(v, info->z_scale); \ + else \ + v >>= Z_COEFF_SHIFT; \ + Z_CLIP_CHECK(v, BIT); \ + _PCM_WRITE_##SIGN##BIT##_##ENDIAN(dst, Z_CLAMP(v, BIT)); \ + } while (ch != 0); \ +} + +#define Z_DECLARE_SINC_POLYPHASE(SIGN, BIT, ENDIAN) \ +static void \ +z_feed_sinc_polyphase_##SIGN##BIT##ENDIAN(struct z_info *info, uint8_t *dst) \ +{ \ + intpcm64_t v; \ + intpcm_t x; \ + uint8_t *p; \ + int32_t ch, i, start, *z_pcoeff; \ + \ + ch = info->channels * PCM_##BIT##_BPS; \ + dst += ch; \ + start = z_prev(info, info->z_start, (info->z_size << 1) - 1) * ch; \ + \ + do { \ + dst -= PCM_##BIT##_BPS; \ + ch -= PCM_##BIT##_BPS; \ + v = 0; \ + p = info->z_delay + start + ch; \ + 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); \ + v += (intpcm64_t)x * *z_pcoeff; \ + z_pcoeff++; \ + p += info->channels * PCM_##BIT##_BPS; \ + x = _PCM_READ_##SIGN##BIT##_##ENDIAN(p); \ + v += (intpcm64_t)x * *z_pcoeff; \ + z_pcoeff++; \ + p += info->channels * PCM_##BIT##_BPS; \ + } \ + if (info->z_scale != Z_ONE) \ + v = Z_SCALE_##BIT(v, info->z_scale); \ + else \ + v >>= Z_COEFF_SHIFT; \ + Z_CLIP_CHECK(v, BIT); \ + _PCM_WRITE_##SIGN##BIT##_##ENDIAN(dst, Z_CLAMP(v, BIT)); \ + } while (ch != 0); \ +} + +#define Z_DECLARE(SIGN, BIT, ENDIAN) \ + Z_DECLARE_LINEAR(SIGN, BIT, ENDIAN) \ + Z_DECLARE_SINC(SIGN, BIT, ENDIAN) \ + Z_DECLARE_SINC_POLYPHASE(SIGN, BIT, ENDIAN) + +#if BYTE_ORDER == LITTLE_ENDIAN || defined(SND_FEEDER_MULTIFORMAT) +Z_DECLARE(S, 16, LE) +Z_DECLARE(S, 32, LE) +#endif +#if BYTE_ORDER == BIG_ENDIAN || defined(SND_FEEDER_MULTIFORMAT) +Z_DECLARE(S, 16, BE) +Z_DECLARE(S, 32, BE) +#endif +#ifdef SND_FEEDER_MULTIFORMAT +Z_DECLARE(S, 8, NE) +Z_DECLARE(S, 24, LE) +Z_DECLARE(S, 24, BE) +Z_DECLARE(U, 8, NE) +Z_DECLARE(U, 16, LE) +Z_DECLARE(U, 24, LE) +Z_DECLARE(U, 32, LE) +Z_DECLARE(U, 16, BE) +Z_DECLARE(U, 24, BE) +Z_DECLARE(U, 32, BE) +#endif + +enum { + Z_RESAMPLER_ZOH, + Z_RESAMPLER_LINEAR, + Z_RESAMPLER_SINC, + Z_RESAMPLER_SINC_POLYPHASE, + Z_RESAMPLER_LAST +}; + +#define Z_RESAMPLER_IDX(i) \ + (Z_IS_SINC(i) ? Z_RESAMPLER_SINC : (i)->quality) + +#define Z_RESAMPLER_ENTRY(SIGN, BIT, ENDIAN) \ + { \ + AFMT_##SIGN##BIT##_##ENDIAN, \ + { \ + [Z_RESAMPLER_ZOH] = z_feed_zoh, \ + [Z_RESAMPLER_LINEAR] = z_feed_linear_##SIGN##BIT##ENDIAN, \ + [Z_RESAMPLER_SINC] = z_feed_sinc_##SIGN##BIT##ENDIAN, \ + [Z_RESAMPLER_SINC_POLYPHASE] = \ + z_feed_sinc_polyphase_##SIGN##BIT##ENDIAN \ + } \ + } + +static const struct { + uint32_t format; + z_resampler_t resampler[Z_RESAMPLER_LAST]; +} z_resampler_tab[] = { +#if BYTE_ORDER == LITTLE_ENDIAN || defined(SND_FEEDER_MULTIFORMAT) + Z_RESAMPLER_ENTRY(S, 16, LE), + Z_RESAMPLER_ENTRY(S, 32, LE), +#endif +#if BYTE_ORDER == BIG_ENDIAN || defined(SND_FEEDER_MULTIFORMAT) + Z_RESAMPLER_ENTRY(S, 16, BE), + Z_RESAMPLER_ENTRY(S, 32, BE), +#endif +#ifdef SND_FEEDER_MULTIFORMAT + Z_RESAMPLER_ENTRY(S, 8, NE), + Z_RESAMPLER_ENTRY(S, 24, LE), + Z_RESAMPLER_ENTRY(S, 24, BE), + Z_RESAMPLER_ENTRY(U, 8, NE), + Z_RESAMPLER_ENTRY(U, 16, LE), + Z_RESAMPLER_ENTRY(U, 24, LE), + Z_RESAMPLER_ENTRY(U, 32, LE), + Z_RESAMPLER_ENTRY(U, 16, BE), + Z_RESAMPLER_ENTRY(U, 24, BE), + Z_RESAMPLER_ENTRY(U, 32, BE), +#endif +}; + +#define Z_RESAMPLER_TAB_SIZE \ + ((int32_t)(sizeof(z_resampler_tab) / sizeof(z_resampler_tab[0]))) + +static void +z_resampler_reset(struct z_info *info) { - struct feed_rate_info *info = f->data; - static const struct { - uint32_t format; /* pcm / audio format */ - uint32_t bps; /* bytes-per-sample, regardless of - total channels */ - feed_rate_converter convert; - } convtbl[] = { - { AFMT_S8, PCM_8_BPS, feed_convert_s8ne }, - { AFMT_S16_LE, PCM_16_BPS, feed_convert_s16le }, - { AFMT_S24_LE, PCM_24_BPS, feed_convert_s24le }, - { AFMT_S32_LE, PCM_32_BPS, feed_convert_s32le }, - { AFMT_S16_BE, PCM_16_BPS, feed_convert_s16be }, - { AFMT_S24_BE, PCM_24_BPS, feed_convert_s24be }, - { AFMT_S32_BE, PCM_32_BPS, feed_convert_s32be }, - { AFMT_U8, PCM_8_BPS, feed_convert_u8ne }, - { AFMT_U16_LE, PCM_16_BPS, feed_convert_u16le }, - { AFMT_U24_LE, PCM_24_BPS, feed_convert_u24le }, - { AFMT_U32_LE, PCM_32_BPS, feed_convert_u32le }, - { AFMT_U16_BE, PCM_16_BPS, feed_convert_u16be }, - { AFMT_U24_BE, PCM_24_BPS, feed_convert_u24be }, - { AFMT_U32_BE, PCM_32_BPS, feed_convert_u32be }, - { 0, 0, NULL }, - }; - uint32_t i; - - feed_rate_reset(info); - - if (info->src != info->dst) - feed_speed_ratio(info->src, info->dst, &info->gx, &info->gy); - - if (!(RATE_FACTOR_SAFE(info->gx) && RATE_FACTOR_SAFE(info->gy))) + + info->src = info->rsrc - (info->rsrc % ((feeder_rate_round > 0 && + info->rsrc > feeder_rate_round) ? feeder_rate_round : 1)); + info->dst = info->rdst - (info->rdst % ((feeder_rate_round > 0 && + info->rdst > feeder_rate_round) ? feeder_rate_round : 1)); + info->z_gx = 1; + info->z_gy = 1; + info->z_alpha = 0; + info->z_resample = NULL; + info->z_size = 1; + info->z_coeff = NULL; + info->z_dcoeff = NULL; + if (info->z_pcoeff != NULL) { + free(info->z_pcoeff, M_DEVBUF); + info->z_pcoeff = NULL; + } + info->z_scale = Z_ONE; + info->z_dx = Z_FULL_ONE; + info->z_dy = Z_FULL_ONE; +#ifdef Z_DIAGNOSTIC + info->z_cycle = 0; +#endif + if (info->quality < Z_QUALITY_MIN) + info->quality = Z_QUALITY_MIN; + else if (info->quality > Z_QUALITY_MAX) + info->quality = Z_QUALITY_MAX; +} + +#ifdef Z_PARANOID +static int32_t +z_resampler_sinc_len(struct z_info *info) +{ + int32_t c, z, len, lmax; + + if (!Z_IS_SINC(info)) + return (1); + + /* + * A rather careful (or useless) way to calculate filter length. + * Z_SINC_LEN() itself is accurate enough to do its job. Extra + * sanity checking is not going to hurt though.. + */ + c = 0; + z = info->z_dy; + len = 0; + lmax = z_coeff_tab[Z_SINC_COEFF_IDX(info)].len; + + do { + c += z >> Z_SHIFT; + z &= Z_MASK; + z += info->z_dy; + } while (c < lmax && ++len > 0); + + if (len != Z_SINC_LEN(info)) { +#ifdef _KERNEL + printf("%s(): sinc l=%d != Z_SINC_LEN=%d\n", + __func__, len, Z_SINC_LEN(info)); +#else + fprintf(stderr, "%s(): sinc l=%d != Z_SINC_LEN=%d\n", + __func__, len, Z_SINC_LEN(info)); return (-1); +#endif + } + + return (len); +} +#else +#define z_resampler_sinc_len(i) (Z_IS_SINC(i) ? Z_SINC_LEN(i) : 1) +#endif + +#define Z_POLYPHASE_COEFF_SHIFT 0 + +/* + * Pick suitable polynomial interpolators based on filter oversampled ratio + * (2 ^ Z_DRIFT_SHIFT). + */ +#if !(defined(Z_COEFF_INTERP_ZOH) || defined(Z_COEFF_INTERP_LINEAR) || \ + defined(Z_COEFF_INTERP_QUADRATIC) || defined(Z_COEFF_INTERP_HERMITE) || \ + defined(Z_COEFF_INTER_BSPLINE) || defined(Z_COEFF_INTERP_OPT32X) || \ + defined(Z_COEFF_INTERP_OPT16X) || defined(Z_COEFF_INTERP_OPT8X) || \ + defined(Z_COEFF_INTERP_OPT4X) || defined(Z_COEFF_INTERP_OPT2X)) +#if Z_DRIFT_SHIFT >= 8 +#define Z_COEFF_INTERP_LINEAR 1 +#elif Z_DRIFT_SHIFT == 7 +#define Z_COEFF_INTERP_QUADRATIC 1 +#elif Z_DRIFT_SHIFT == 6 +#define Z_COEFF_INTERP_HERMITE 1 +#elif Z_DRIFT_SHIFT == 5 +#define Z_COEFF_INTERP_OPT32X 1 +#elif Z_DRIFT_SHIFT == 4 +#define Z_COEFF_INTERP_OPT16X 1 +#elif Z_DRIFT_SHIFT == 3 +#define Z_COEFF_INTERP_OPT8X 1 +#elif Z_DRIFT_SHIFT == 2 +#define Z_COEFF_INTERP_OPT4X 1 +#elif Z_DRIFT_SHIFT == 1 +#define Z_COEFF_INTERP_OPT2X 1 +#else +#error "Z_DRIFT_SHIFT screwed!" +#endif +#endif + +/* + * In classic polyphase mode, the actual coefficients for each phases need to + * be calculated based on default prototype filters. For highly oversampled + * filter, linear or quadradatic interpolator should be enough. Anything less + * than that require 'special' interpolators to reduce interpolation errors. + * + * "Polynomial Interpolators for High-Quality Resampling of Oversampled Audio" + * by Olli Niemitalo + * - http://www.student.oulu.fi/~oniemita/dsp/deip.pdf + * + */ +static int32_t +z_coeff_interpolate(int32_t z, int32_t *z_coeff) +{ + int32_t coeff; +#if defined(Z_COEFF_INTERP_ZOH) + + /* 1-point, 0th-order (Zero Order Hold) */ + z = z; + coeff = z_coeff[0]; +#elif defined(Z_COEFF_INTERP_LINEAR) + int32_t zl0, zl1; + + /* 2-point, 1st-order Linear */ + zl0 = z_coeff[0]; + zl1 = z_coeff[1] - z_coeff[0]; + + coeff = (((int64_t)zl1 * z) >> Z_SHIFT) + zl0; +#elif defined(Z_COEFF_INTERP_QUADRATIC) + int32_t zq0, zq1, zq2; + + /* 3-point, 2nd-order Quadratic */ + zq0 = z_coeff[0]; + zq1 = z_coeff[1] - z_coeff[-1]; + zq2 = z_coeff[1] + z_coeff[-1] - (z_coeff[0] << 1); + + coeff = ((((((int64_t)zq2 * z) >> Z_SHIFT) + + zq1) * z) >> (Z_SHIFT + 1)) + zq0; +#elif defined(Z_COEFF_INTERP_HERMITE) + int32_t zh0, zh1, zh2, zh3; + + /* 4-point, 3rd-order Hermite */ + zh0 = z_coeff[0]; + zh1 = z_coeff[1] - z_coeff[-1]; + zh2 = (z_coeff[-1] << 1) - (z_coeff[0] * 5) + (z_coeff[1] << 2) - + z_coeff[2]; + zh3 = z_coeff[2] - z_coeff[-1] + ((z_coeff[0] - z_coeff[1]) * 3); + + coeff = (((((((((int64_t)zh3 * z) >> Z_SHIFT) + + zh2) * z) >> Z_SHIFT) + zh1) * z) >> (Z_SHIFT + 1)) + zh0; +#elif defined(Z_COEFF_INTERP_BSPLINE) + int32_t zb0, zb1, zb2, zb3; + + /* 4-point, 3rd-order B-Spline */ + zb0 = (((int64_t)z_coeff[0] << 2) + z_coeff[-1] + z_coeff[1]) / 3; + zb1 = z_coeff[1] - z_coeff[-1]; + zb2 = z_coeff[-1] + z_coeff[1] - (z_coeff[0] << 1); + zb3 = (((z_coeff[0] - z_coeff[1]) * 3) + z_coeff[2] - z_coeff[-1]) / 3; + + coeff = ((((((((((int64_t)zb3 * z) >> Z_SHIFT) + + zb2) * z) >> Z_SHIFT) + zb1) * z) >> Z_SHIFT) + zb0) >> 1; +#elif defined(Z_COEFF_INTERP_OPT32X) + int32_t zoz, zoe1, zoe2, zoe3, zoo1, zoo2, zoo3; + int32_t zoc0, zoc1, zoc2, zoc3, zoc4, zoc5; + + /* 6-point, 5th-order Optimal 32x */ + zoz = z - (Z_ONE >> 1); + zoe1 = z_coeff[1] + z_coeff[0]; + zoe2 = z_coeff[2] + z_coeff[-1]; + zoe3 = z_coeff[3] + z_coeff[-2]; + zoo1 = z_coeff[1] - z_coeff[0]; + zoo2 = z_coeff[2] - z_coeff[-1]; + zoo3 = z_coeff[3] - z_coeff[-2]; + + zoc0 = (((0x1ac2260dLL * zoe1)) >> 30) + + ((0x0526cdcaLL * zoe2) >> 30) + ((0x00170c29LL * zoe3) >> 30); + zoc1 = ((0x14f8a49aLL * zoo1) >> 30) + + ((0x0d6d1109LL * zoo2) >> 30) + ((0x008cd4dcLL * zoo3) >> 30); + zoc2 = ((-0x0d3e94a4LL * zoe1) >> 30) + + ((0x0bddded4LL * zoe2) >> 30) + ((0x0160b5d0LL * zoe3) >> 30); + zoc3 = ((-0x0de10cc4LL * zoo1) >> 30) + + ((0x019b2a7dLL * zoo2) >> 30) + ((0x01cfe914LL * zoo3) >> 30); + zoc4 = ((0x02aa12d7LL * zoe1) >> 30) + + ((-0x03ff1bb3LL * zoe2) >> 30) + ((0x015508ddLL * zoe3) >> 30); + zoc5 = ((0x051d29e5LL * zoo1) >> 30) + + ((-0x028e7647LL * zoo2) >> 30) + ((0x0082d81aLL * zoo3) >> 30); + + coeff = (((((((((((((((int64_t)zoc5 * zoz) >> Z_SHIFT) + + zoc4) * zoz) >> Z_SHIFT) + zoc3) * zoz) >> Z_SHIFT) + + zoc2) * zoz) >> Z_SHIFT) + zoc1) * zoz) >> Z_SHIFT) + zoc0; +#elif defined(Z_COEFF_INTERP_OPT16X) + int32_t zoz, zoe1, zoe2, zoe3, zoo1, zoo2, zoo3; + int32_t zoc0, zoc1, zoc2, zoc3, zoc4, zoc5; + + /* 6-point, 5th-order Optimal 16x */ + zoz = z - (Z_ONE >> 1); + zoe1 = z_coeff[1] + z_coeff[0]; + zoe2 = z_coeff[2] + z_coeff[-1]; + zoe3 = z_coeff[3] + z_coeff[-2]; + zoo1 = z_coeff[1] - z_coeff[0]; + zoo2 = z_coeff[2] - z_coeff[-1]; + zoo3 = z_coeff[3] - z_coeff[-2]; + + zoc0 = (((0x1ac2260dLL * zoe1)) >> 30) + + ((0x0526cdcaLL * zoe2) >> 30) + ((0x00170c29LL * zoe3) >> 30); + zoc1 = ((0x14f8a49aLL * zoo1) >> 30) + + ((0x0d6d1109LL * zoo2) >> 30) + ((0x008cd4dcLL * zoo3) >> 30); + zoc2 = ((-0x0d3e94a4LL * zoe1) >> 30) + + ((0x0bddded4LL * zoe2) >> 30) + ((0x0160b5d0LL * zoe3) >> 30); + zoc3 = ((-0x0de10cc4LL * zoo1) >> 30) + + ((0x019b2a7dLL * zoo2) >> 30) + ((0x01cfe914LL * zoo3) >> 30); + zoc4 = ((0x02aa12d7LL * zoe1) >> 30) + + ((-0x03ff1bb3LL * zoe2) >> 30) + ((0x015508ddLL * zoe3) >> 30); + zoc5 = ((0x051d29e5LL * zoo1) >> 30) + + ((-0x028e7647LL * zoo2) >> 30) + ((0x0082d81aLL * zoo3) >> 30); + + coeff = (((((((((((((((int64_t)zoc5 * zoz) >> Z_SHIFT) + + zoc4) * zoz) >> Z_SHIFT) + zoc3) * zoz) >> Z_SHIFT) + + zoc2) * zoz) >> Z_SHIFT) + zoc1) * zoz) >> Z_SHIFT) + zoc0; +#elif defined(Z_COEFF_INTERP_OPT8X) + int32_t zoz, zoe1, zoe2, zoe3, zoo1, zoo2, zoo3; + int32_t zoc0, zoc1, zoc2, zoc3, zoc4, zoc5; - for (i = 0; i < sizeof(convtbl) / sizeof(convtbl[0]); i++) { - if (convtbl[i].format == 0) - return (-1); - if ((f->desc->out & ~AFMT_STEREO) == convtbl[i].format) { - info->bps = convtbl[i].bps; - info->convert = convtbl[i].convert; + /* 6-point, 5th-order Optimal 8x */ + zoz = z - (Z_ONE >> 1); + zoe1 = z_coeff[1] + z_coeff[0]; + zoe2 = z_coeff[2] + z_coeff[-1]; + zoe3 = z_coeff[3] + z_coeff[-2]; + zoo1 = z_coeff[1] - z_coeff[0]; + zoo2 = z_coeff[2] - z_coeff[-1]; + zoo3 = z_coeff[3] - z_coeff[-2]; + + zoc0 = (((0x1aa9b47dLL * zoe1)) >> 30) + + ((0x053d9944LL * zoe2) >> 30) + ((0x0018b23fLL * zoe3) >> 30); + zoc1 = ((0x14a104d1LL * zoo1) >> 30) + + ((0x0d7d2504LL * zoo2) >> 30) + ((0x0094b599LL * zoo3) >> 30); + zoc2 = ((-0x0d22530bLL * zoe1) >> 30) + + ((0x0bb37a2cLL * zoe2) >> 30) + ((0x016ed8e0LL * zoe3) >> 30); + zoc3 = ((-0x0d744b1cLL * zoo1) >> 30) + + ((0x01649591LL * zoo2) >> 30) + ((0x01dae93aLL * zoo3) >> 30); + zoc4 = ((0x02a7ee1bLL * zoe1) >> 30) + + ((-0x03fbdb24LL * zoe2) >> 30) + ((0x0153ed07LL * zoe3) >> 30); + zoc5 = ((0x04cf9b6cLL * zoo1) >> 30) + + ((-0x0266b378LL * zoo2) >> 30) + ((0x007a7c26LL * zoo3) >> 30); + + coeff = (((((((((((((((int64_t)zoc5 * zoz) >> Z_SHIFT) + + zoc4) * zoz) >> Z_SHIFT) + zoc3) * zoz) >> Z_SHIFT) + + zoc2) * zoz) >> Z_SHIFT) + zoc1) * zoz) >> Z_SHIFT) + zoc0; +#elif defined(Z_COEFF_INTERP_OPT4X) + int32_t zoz, zoe1, zoe2, zoe3, zoo1, zoo2, zoo3; + int32_t zoc0, zoc1, zoc2, zoc3, zoc4, zoc5; + + /* 6-point, 5th-order Optimal 4x */ + zoz = z - (Z_ONE >> 1); + zoe1 = z_coeff[1] + z_coeff[0]; + zoe2 = z_coeff[2] + z_coeff[-1]; + zoe3 = z_coeff[3] + z_coeff[-2]; + zoo1 = z_coeff[1] - z_coeff[0]; + zoo2 = z_coeff[2] - z_coeff[-1]; + zoo3 = z_coeff[3] - z_coeff[-2]; + + zoc0 = (((0x1a8eda43LL * zoe1)) >> 30) + + ((0x0556ee38LL * zoe2) >> 30) + ((0x001a3784LL * zoe3) >> 30); + zoc1 = ((0x143d863eLL * zoo1) >> 30) + + ((0x0d910e36LL * zoo2) >> 30) + ((0x009ca889LL * zoo3) >> 30); + zoc2 = ((-0x0d026821LL * zoe1) >> 30) + + ((0x0b837773LL * zoe2) >> 30) + ((0x017ef0c6LL * zoe3) >> 30); + zoc3 = ((-0x0cef1502LL * zoo1) >> 30) + + ((0x01207a8eLL * zoo2) >> 30) + ((0x01e936dbLL * zoo3) >> 30); + zoc4 = ((0x029fe643LL * zoe1) >> 30) + + ((-0x03ef3fc8LL * zoe2) >> 30) + ((0x014f5923LL * zoe3) >> 30); + zoc5 = ((0x043a9d08LL * zoo1) >> 30) + + ((-0x02154febLL * zoo2) >> 30) + ((0x00670dbdLL * zoo3) >> 30); + + coeff = (((((((((((((((int64_t)zoc5 * zoz) >> Z_SHIFT) + + zoc4) * zoz) >> Z_SHIFT) + zoc3) * zoz) >> Z_SHIFT) + + zoc2) * zoz) >> Z_SHIFT) + zoc1) * zoz) >> Z_SHIFT) + zoc0; +#elif defined(Z_COEFF_INTERP_OPT2X) + int32_t zoz, zoe1, zoe2, zoe3, zoo1, zoo2, zoo3; + int32_t zoc0, zoc1, zoc2, zoc3, zoc4, zoc5; + + /* 6-point, 5th-order Optimal 2x */ + zoz = z - (Z_ONE >> 1); + zoe1 = z_coeff[1] + z_coeff[0]; + zoe2 = z_coeff[2] + z_coeff[-1]; + zoe3 = z_coeff[3] + z_coeff[-2]; + zoo1 = z_coeff[1] - z_coeff[0]; + zoo2 = z_coeff[2] - z_coeff[-1]; + zoo3 = z_coeff[3] - z_coeff[-2]; + + zoc0 = (((0x19edb6fdLL * zoe1)) >> 30) + + ((0x05ebd062LL * zoe2) >> 30) + ((0x00267881LL * zoe3) >> 30); + zoc1 = ((0x1223af76LL * zoo1) >> 30) + + ((0x0de3dd6bLL * zoo2) >> 30) + ((0x00d683cdLL * zoo3) >> 30); + zoc2 = ((-0x0c3ee068LL * zoe1) >> 30) + + ((0x0a5c3769LL * zoe2) >> 30) + ((0x01e2aceaLL * zoe3) >> 30); + zoc3 = ((-0x0a8ab614LL * zoo1) >> 30) + + ((-0x0019522eLL * zoo2) >> 30) + ((0x022cefc7LL * zoo3) >> 30); + zoc4 = ((0x0276187dLL * zoe1) >> 30) + + ((-0x03a801e8LL * zoe2) >> 30) + ((0x0131d935LL * zoe3) >> 30); + zoc5 = ((0x02c373f5LL * zoo1) >> 30) + + ((-0x01275f83LL * zoo2) >> 30) + ((0x0018ee79LL * zoo3) >> 30); + + coeff = (((((((((((((((int64_t)zoc5 * zoz) >> Z_SHIFT) + + zoc4) * zoz) >> Z_SHIFT) + zoc3) * zoz) >> Z_SHIFT) + + zoc2) * zoz) >> Z_SHIFT) + zoc1) * zoz) >> Z_SHIFT) + zoc0; +#else +#error "Interpolation type screwed!" +#endif + +#if Z_POLYPHASE_COEFF_SHIFT > 0 + coeff = Z_RSHIFT(coeff, Z_POLYPHASE_COEFF_SHIFT); +#endif + return (coeff); +} + +static int +z_resampler_build_polyphase(struct z_info *info) +{ + int32_t alpha, c, i, z, idx; + + /* Let this be here first. */ + if (info->z_pcoeff != NULL) { + free(info->z_pcoeff, M_DEVBUF); + info->z_pcoeff = NULL; + } + + if (feeder_rate_polyphase_max < 1) + return (ENOTSUP); + + if (((int64_t)info->z_size * info->z_gy * 2) > + feeder_rate_polyphase_max) { +#ifndef _KERNEL + fprintf(stderr, "Polyphase entries exceed: [%d/%d] %jd > %d\n", + info->z_gx, info->z_gy, + (intmax_t)info->z_size * info->z_gy * 2, + feeder_rate_polyphase_max); +#endif + return (E2BIG); + } + + info->z_pcoeff = malloc(sizeof(int32_t) * + info->z_size * info->z_gy * 2, M_DEVBUF, M_NOWAIT | M_ZERO); + if (info->z_pcoeff == NULL) + return (ENOMEM); + + for (alpha = 0; alpha < info->z_gy; alpha++) { + z = alpha * info->z_dx; + c = 0; + for (i = info->z_size; i != 0; i--) { + c += z >> Z_SHIFT; + z &= Z_MASK; + idx = (alpha * info->z_size * 2) + + (info->z_size * 2) - i; + info->z_pcoeff[idx] = + z_coeff_interpolate(z, info->z_coeff + c); + z += info->z_dy; + } + z = info->z_dy - (alpha * info->z_dx); + c = 0; + for (i = info->z_size; i != 0; i--) { + c += z >> Z_SHIFT; + z &= Z_MASK; + idx = (alpha * info->z_size * 2) + i - 1; + info->z_pcoeff[idx] = + z_coeff_interpolate(z, info->z_coeff + c); + z += info->z_dy; + } + } + +#ifndef _KERNEL + fprintf(stderr, "Polyphase: [%d/%d] %d entries\n", + info->z_gx, info->z_gy, info->z_size * info->z_gy * 2); +#endif + + return (0); +} + +static int +z_resampler_setup(struct pcm_feeder *f) +{ + struct z_info *info; + int64_t gy2gx_max, gx2gy_max; + uint32_t format; + int32_t align, i, z_scale; + int adaptive; + + info = f->data; + z_resampler_reset(info); + + if (info->src == info->dst) + return (0); + + /* Shrink by greatest common divisor. */ + i = z_gcd(info->src, info->dst); + info->z_gx = info->src / i; + info->z_gy = info->dst / i; + + /* Too big, or too small. Bail out. */ + if (!(Z_FACTOR_SAFE(info->z_gx) && Z_FACTOR_SAFE(info->z_gy))) + return (EINVAL); + + format = f->desc->in; + adaptive = 0; + z_scale = 0; + + /* + * Setup everything: filter length, conversion factor, etc. + */ + if (Z_IS_SINC(info)) { + /* + * Downsampling, or upsampling scaling factor. As long as the + * factor can be represented by a fraction of 1 << Z_SHIFT, + * we're pretty much in business. Scaling is not needed for + * upsampling, so we just slap Z_ONE there. + */ + if (info->z_gx > info->z_gy) + /* + * If the downsampling ratio is beyond sanity, + * enable semi-adaptive mode. Although handling + * extreme ratio is possible, the result of the + * conversion is just pointless, unworthy, + * nonsensical noises, etc. + */ + if ((info->z_gx / info->z_gy) > Z_SINC_DOWNMAX) + z_scale = Z_ONE / Z_SINC_DOWNMAX; + else + z_scale = ((uint64_t)info->z_gy << Z_SHIFT) / + info->z_gx; + else + z_scale = Z_ONE; + + /* + * This is actually impossible, unless anything above + * overflow. + */ + if (z_scale < 1) + return (E2BIG); + + /* + * Calculate sample time/coefficients index drift. It is + * a constant for upsampling, but downsampling require + * heavy duty filtering with possible too long filters. + * If anything goes wrong, revisit again and enable + * adaptive mode. + */ +z_setup_adaptive_sinc: + if (info->z_pcoeff != NULL) { + free(info->z_pcoeff, M_DEVBUF); + info->z_pcoeff = NULL; + } + + if (adaptive == 0) { + info->z_dy = z_scale << Z_DRIFT_SHIFT; + if (info->z_dy < 1) + return (E2BIG); + info->z_scale = z_scale; + } else { + info->z_dy = Z_FULL_ONE; + 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; + + /* + * Overflow or underflow. Try adaptive, let it continue and + * retry. + */ + if (info->z_dx < 1) { + if (adaptive == 0) { + adaptive = 1; + goto z_setup_adaptive_sinc; + } + return (E2BIG); + } + + /* + * Round back output drift. + */ + info->z_dy = info->z_dx * info->z_gy; + + for (i = 0; i < Z_COEFF_TAB_SIZE; i++) { + if (Z_SINC_COEFF_IDX(info) != i) + continue; + /* + * Calculate required filter length and guard + * against possible abusive result. Note that + * this represents only 1/2 of the entire filter + * length. + */ + info->z_size = z_resampler_sinc_len(info); + + /* + * Multiple of 2 rounding, for better accumulator + * performance. + */ + info->z_size &= ~1; + + if (info->z_size < 2 || info->z_size > Z_SINC_MAX) { + if (adaptive == 0) { + adaptive = 1; + goto z_setup_adaptive_sinc; + } + return (E2BIG); + } + info->z_coeff = z_coeff_tab[i].coeff + Z_COEFF_OFFSET; + info->z_dcoeff = z_coeff_tab[i].dcoeff; break; } + + if (info->z_coeff == NULL || info->z_dcoeff == NULL) + return (EINVAL); + } else if (Z_IS_LINEAR(info)) { + /* + * Don't put much effort if we're doing linear interpolation. + * Just center the interpolation distance within Z_LINEAR_ONE, + * and be happy about it. + */ + info->z_dx = Z_LINEAR_FULL_ONE / info->z_gy; + } + + /* + * We're safe for now, lets continue.. Look for our resampler + * depending on configured format and quality. + */ + for (i = 0; i < Z_RESAMPLER_TAB_SIZE; i++) { + int ridx; + + if (AFMT_ENCODING(format) != z_resampler_tab[i].format) + continue; + if (Z_IS_SINC(info) && adaptive == 0 && + z_resampler_build_polyphase(info) == 0) + ridx = Z_RESAMPLER_SINC_POLYPHASE; + else + ridx = Z_RESAMPLER_IDX(info); + info->z_resample = z_resampler_tab[i].resampler[ridx]; + break; + } + + if (info->z_resample == NULL) + return (EINVAL); + + info->bps = AFMT_BPS(format); + align = info->channels * info->bps; + + /* + * Calculate largest value that can be fed into z_gy2gx() and + * z_gx2gy() without causing (signed) 32bit overflow. z_gy2gx() will + * be called early during feeding process to determine how much input + * samples that is required to generate requested output, while + * z_gx2gy() will be called just before samples filtering / + * accumulation process based on available samples that has been + * calculated using z_gx2gy(). + * + * Now that is damn confusing, I guess ;-) . + */ + gy2gx_max = (((uint64_t)info->z_gy * INT32_MAX) - info->z_gy + 1) / + info->z_gx; + + if ((gy2gx_max * align) > SND_FXDIV_MAX) + gy2gx_max = SND_FXDIV_MAX / align; + + if (gy2gx_max < 1) + return (E2BIG); + + gx2gy_max = (((uint64_t)info->z_gx * INT32_MAX) - info->z_gy) / + info->z_gy; + + if (gx2gy_max > INT32_MAX) + gx2gy_max = INT32_MAX; + + if (gx2gy_max < 1) + return (E2BIG); + + /* + * Ensure that z_gy2gx() at its largest possible calculated value + * (alpha = 0) will not cause overflow further late during z_gx2gy() + * stage. + */ + if (z_gy2gx(info, gy2gx_max) > _Z_GCAST(gx2gy_max)) + return (E2BIG); + + info->z_maxfeed = gy2gx_max * align; + +#ifdef Z_USE_ALPHADRIFT + info->z_startdrift = z_gy2gx(info, 1); + info->z_alphadrift = z_drift(info, info->z_startdrift, 1); +#endif + + i = z_gy2gx(info, 1); + info->z_full = z_roundpow2((info->z_size << 1) + i); + + /* + * Too big to be true, and overflowing left and right like mad .. + */ + if ((info->z_full * align) < 1) { + if (adaptive == 0 && Z_IS_SINC(info)) { + adaptive = 1; + goto z_setup_adaptive_sinc; + } + return (E2BIG); } /* - * No need to interpolate/decimate, just do plain copy. + * Increase full buffer size if its too small to reduce cyclic + * buffer shifting in main conversion/feeder loop. */ - if (info->gx == info->gy) - info->convert = NULL; + while (info->z_full < Z_RESERVOIR_MAX && + (info->z_full - (info->z_size << 1)) < Z_RESERVOIR) + info->z_full <<= 1; - info->channels = (f->desc->out & AFMT_STEREO) ? 2 : 1; - info->pos = info->bps * info->channels; - info->bpos = info->pos << 1; - info->bufsz -= info->bufsz % info->pos; + /* Initialize buffer position. */ + info->z_mask = info->z_full - 1; + info->z_start = z_prev(info, info->z_size << 1, 1); + info->z_pos = z_next(info, info->z_start, 1); - memset(info->buffer, sndbuf_zerodata(f->desc->out), info->bpos); + /* + * Allocate or reuse delay line buffer, whichever makes sense. + */ + i = info->z_full * align; + if (i < 1) + return (E2BIG); + + if (info->z_delay == NULL || info->z_alloc < i || + i <= (info->z_alloc >> 1)) { + if (info->z_delay != NULL) + free(info->z_delay, M_DEVBUF); + info->z_delay = malloc(i, M_DEVBUF, M_NOWAIT | M_ZERO); + if (info->z_delay == NULL) + return (ENOMEM); + info->z_alloc = i; + } + + /* + * Zero out head of buffer to avoid pops and clicks. + */ + memset(info->z_delay, sndbuf_zerodata(f->desc->out), + info->z_pos * align); - RATE_TRACE("%s: %u (%u) -> %u (%u) [%u/%u] , " - "format=0x%08x, channels=%u, bufsz=%u\n", - __func__, info->src, info->rsrc, info->dst, info->rdst, - info->gx, info->gy, f->desc->out, info->channels, - info->bufsz - info->pos); +#ifdef Z_DIAGNOSTIC + /* + * XXX Debuging mess !@#$%^ + */ +#define dumpz(x) fprintf(stderr, "\t%12s = %10u : %-11d\n", \ + "z_"__STRING(x), (uint32_t)info->z_##x, \ + (int32_t)info->z_##x) + fprintf(stderr, "\n%s():\n", __func__); + fprintf(stderr, "\tchannels=%d, bps=%d, format=0x%08x, quality=%d\n", + info->channels, info->bps, format, info->quality); + fprintf(stderr, "\t%d (%d) -> %d (%d), ", + info->src, info->rsrc, info->dst, info->rdst); + fprintf(stderr, "[%d/%d]\n", info->z_gx, info->z_gy); + fprintf(stderr, "\tminreq=%d, ", z_gy2gx(info, 1)); + if (adaptive != 0) + z_scale = Z_ONE; + fprintf(stderr, "factor=0x%08x/0x%08x (%f)\n", + z_scale, Z_ONE, (double)z_scale / Z_ONE); + fprintf(stderr, "\tbase_length=%d, ", Z_SINC_BASE_LEN(info)); + fprintf(stderr, "adaptive=%s\n", (adaptive != 0) ? "YES" : "NO"); + dumpz(size); + dumpz(alloc); + if (info->z_alloc < 1024) + fprintf(stderr, "\t%15s%10d Bytes\n", + "", info->z_alloc); + else if (info->z_alloc < (1024 << 10)) + fprintf(stderr, "\t%15s%10d KBytes\n", + "", info->z_alloc >> 10); + else if (info->z_alloc < (1024 << 20)) + fprintf(stderr, "\t%15s%10d MBytes\n", + "", info->z_alloc >> 20); + else + fprintf(stderr, "\t%15s%10d GBytes\n", + "", info->z_alloc >> 30); + fprintf(stderr, "\t%12s %10d (min output samples)\n", + "", + (int32_t)z_gx2gy(info, info->z_full - (info->z_size << 1))); + fprintf(stderr, "\t%12s %10d (min allocated output samples)\n", + "", + (int32_t)z_gx2gy(info, (info->z_alloc / align) - + (info->z_size << 1))); + fprintf(stderr, "\t%12s = %10d\n", + "z_gy2gx()", (int32_t)z_gy2gx(info, 1)); + fprintf(stderr, "\t%12s = %10d -> z_gy2gx() -> %d\n", + "Max", (int32_t)gy2gx_max, (int32_t)z_gy2gx(info, gy2gx_max)); + fprintf(stderr, "\t%12s = %10d\n", + "z_gx2gy()", (int32_t)z_gx2gy(info, 1)); + fprintf(stderr, "\t%12s = %10d -> z_gx2gy() -> %d\n", + "Max", (int32_t)gx2gy_max, (int32_t)z_gx2gy(info, gx2gy_max)); + dumpz(maxfeed); + dumpz(full); + dumpz(start); + dumpz(pos); + dumpz(scale); + fprintf(stderr, "\t%12s %10f\n", "", + (double)info->z_scale / Z_ONE); + dumpz(dx); + fprintf(stderr, "\t%12s %10f\n", "", + (double)info->z_dx / info->z_dy); + dumpz(dy); + fprintf(stderr, "\t%12s %10d (drift step)\n", "", + info->z_dy >> Z_SHIFT); + fprintf(stderr, "\t%12s %10d (scaling differences)\n", "", + (z_scale << Z_DRIFT_SHIFT) - info->z_dy); + fprintf(stderr, "\t%12s = %u bytes\n", + "intpcm32_t", sizeof(intpcm32_t)); + fprintf(stderr, "\t%12s = 0x%08x, smallest=%.16lf\n", + "Z_ONE", Z_ONE, (double)1.0 / (double)Z_ONE); +#endif return (0); } static int -feed_rate_set(struct pcm_feeder *f, int what, int32_t value) +z_resampler_set(struct pcm_feeder *f, int what, int32_t value) { - struct feed_rate_info *info = f->data; + struct z_info *info; + int32_t oquality; - if (value < feeder_rate_min || value > feeder_rate_max) - return (-1); + info = f->data; switch (what) { - case FEEDRATE_SRC: + case Z_RATE_SRC: + if (value < feeder_rate_min || value > feeder_rate_max) + return (E2BIG); + if (value == info->rsrc) + return (0); info->rsrc = value; break; - case FEEDRATE_DST: + case Z_RATE_DST: + if (value < feeder_rate_min || value > feeder_rate_max) + return (E2BIG); + if (value == info->rdst) + return (0); info->rdst = value; break; + case Z_RATE_QUALITY: + if (value < Z_QUALITY_MIN || value > Z_QUALITY_MAX) + return (EINVAL); + if (value == info->quality) + return (0); + /* + * If we failed to set the requested quality, restore + * the old one. We cannot afford leaving it broken since + * passive feeder chains like vchans never reinitialize + * itself. + */ + oquality = info->quality; + info->quality = value; + if (z_resampler_setup(f) == 0) + return (0); + info->quality = oquality; + break; + case Z_RATE_CHANNELS: + if (value < SND_CHN_MIN || value > SND_CHN_MAX) + return (EINVAL); + if (value == info->channels) + return (0); + info->channels = value; + break; default: - return (-1); + return (EINVAL); + break; } - return (feed_rate_setup(f)); + + return (z_resampler_setup(f)); } static int -feed_rate_get(struct pcm_feeder *f, int what) +z_resampler_get(struct pcm_feeder *f, int what) { - struct feed_rate_info *info = f->data; + struct z_info *info; + + info = f->data; switch (what) { - case FEEDRATE_SRC: + case Z_RATE_SRC: return (info->rsrc); - case FEEDRATE_DST: + break; + case Z_RATE_DST: return (info->rdst); + break; + case Z_RATE_QUALITY: + return (info->quality); + break; + case Z_RATE_CHANNELS: + return (info->channels); + break; default: - return (-1); + break; } + return (-1); } static int -feed_rate_init(struct pcm_feeder *f) +z_resampler_init(struct pcm_feeder *f) { - struct feed_rate_info *info; + struct z_info *info; + int ret; - if (f->desc->out != f->desc->in) + if (f->desc->in != f->desc->out) return (EINVAL); - info = malloc(sizeof(*info), M_RATEFEEDER, M_NOWAIT | M_ZERO); + info = malloc(sizeof(*info), M_DEVBUF, M_NOWAIT | M_ZERO); if (info == NULL) return (ENOMEM); - /* - * bufsz = sample from last cycle + conversion space - */ - info->bufsz_init = 8 + feeder_buffersize; - info->buffer = malloc(info->bufsz_init, M_RATEFEEDER, - M_NOWAIT | M_ZERO); - if (info->buffer == NULL) { - free(info, M_RATEFEEDER); - return (ENOMEM); - } - info->rsrc = DSP_DEFAULT_SPEED; - info->rdst = DSP_DEFAULT_SPEED; + + info->rsrc = Z_RATE_DEFAULT; + info->rdst = Z_RATE_DEFAULT; + info->quality = feeder_rate_quality; + info->channels = AFMT_CHANNEL(f->desc->in); + f->data = info; - return (feed_rate_setup(f)); + + ret = z_resampler_setup(f); + if (ret != 0) { + if (info->z_pcoeff != NULL) + free(info->z_pcoeff, M_DEVBUF); + if (info->z_delay != NULL) + free(info->z_delay, M_DEVBUF); + free(info, M_DEVBUF); + f->data = NULL; + } + + return (ret); } static int -feed_rate_free(struct pcm_feeder *f) +z_resampler_free(struct pcm_feeder *f) { - struct feed_rate_info *info = f->data; + struct z_info *info; + info = f->data; if (info != NULL) { - if (info->buffer != NULL) - free(info->buffer, M_RATEFEEDER); - free(info, M_RATEFEEDER); + if (info->z_pcoeff != NULL) + free(info->z_pcoeff, M_DEVBUF); + if (info->z_delay != NULL) + free(info->z_delay, M_DEVBUF); + free(info, M_DEVBUF); } + f->data = NULL; + return (0); } -static int -feed_rate(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, - uint32_t count, void *source) +static uint32_t +z_resampler_feed_internal(struct pcm_feeder *f, struct pcm_channel *c, + uint8_t *b, uint32_t count, void *source) { - struct feed_rate_info *info = f->data; - uint32_t i, smpsz; - int32_t fetch, slot; + struct z_info *info; + int32_t alphadrift, startdrift, reqout, ocount, reqin, align; + int32_t fetch, fetched, start, cp; + uint8_t *dst; - if (info->convert == NULL) - return (FEEDER_FEED(f->source, c, b, count, source)); + info = f->data; + if (info->z_resample == NULL) + return (z_feed(f->source, c, b, count, source)); /* - * This loop has been optimized to generalize both up / down - * sampling without causing missing samples or excessive buffer - * feeding. The tricky part is to calculate *precise* (slot) value - * needed for the entire conversion space since we are bound to - * return and fill up the buffer according to the requested 'count'. - * Too much feeding will cause the extra buffer stay within temporary - * circular buffer forever and always manifest itself as a truncated - * sound during end of playback / recording. Too few, and we end up - * with possible underruns and waste of cpu cycles. - * - * 'Stray' management exist to combat with possible unaligned - * buffering by the caller. + * Calculate sample size alignment and amount of sample output. + * We will do everything in sample domain, but at the end we + * will jump back to byte domain. */ - smpsz = info->bps * info->channels; - RATE_TEST(count >= smpsz && (count % smpsz) == 0, - ("%s: Count size not sample integral (%d)\n", __func__, count)); - if (count < smpsz) + align = info->channels * info->bps; + ocount = SND_FXDIV(count, align); + if (ocount == 0) return (0); - count -= count % smpsz; + /* - * This slot count formula will stay here for the next million years - * to come. This is the key of our circular buffering precision. + * Calculate amount of input samples that is needed to generate + * exact amount of output. */ - slot = (((info->gx * (count / smpsz)) + info->gy - info->alpha - 1) / - info->gy) * smpsz; - RATE_TEST((slot % smpsz) == 0, - ("%s: Slot count not sample integral (%d)\n", __func__, slot)); -#ifdef FEEDRATE_STRAY - RATE_TEST(info->stray == 0, ("%s: [1] Stray bytes: %u\n", __func__, - info->stray)); -#endif - if (info->pos != smpsz && info->bpos - info->pos == smpsz && - info->bpos + slot > info->bufsz) { - /* - * Copy last unit sample and its previous to - * beginning of buffer. - */ - bcopy(info->buffer + info->pos - smpsz, info->buffer, - smpsz << 1); - info->pos = smpsz; - info->bpos = smpsz << 1; - } - RATE_ASSERT(slot >= 0, ("%s: Negative Slot: %d\n", __func__, slot)); - i = 0; - for (;;) { - for (;;) { - fetch = info->bufsz - info->bpos; -#ifdef FEEDRATE_STRAY - fetch -= info->stray; -#endif - RATE_ASSERT(fetch >= 0, - ("%s: [1] Buffer overrun: %d > %d\n", __func__, - info->bpos, info->bufsz)); - if (slot < fetch) - fetch = slot; -#ifdef FEEDRATE_STRAY - if (fetch < 1) -#else - if (fetch < smpsz) -#endif - break; - RATE_ASSERT((int)(info->bpos -#ifdef FEEDRATE_STRAY - - info->stray -#endif - ) >= 0 && - (info->bpos - info->stray) < info->bufsz, - ("%s: DANGER - BUFFER OVERRUN! bufsz=%d, pos=%d\n", - __func__, info->bufsz, info->bpos -#ifdef FEEDRATE_STRAY - - info->stray -#endif - )); - fetch = FEEDER_FEED(f->source, c, - info->buffer + info->bpos -#ifdef FEEDRATE_STRAY - - info->stray -#endif - , fetch, source); -#ifdef FEEDRATE_STRAY - info->stray = 0; - if (fetch == 0) + reqin = z_gy2gx(info, ocount) - z_fetched(info); + +#ifdef Z_USE_ALPHADRIFT + startdrift = info->z_startdrift; + alphadrift = info->z_alphadrift; #else - if (fetch < smpsz) -#endif - break; - RATE_TEST((fetch % smpsz) == 0, - ("%s: Fetch size not sample integral (%d)\n", - __func__, fetch)); -#ifdef FEEDRATE_STRAY - info->stray += fetch % smpsz; - RATE_TEST(info->stray == 0, - ("%s: Stray bytes detected (%d)\n", __func__, - info->stray)); -#endif - fetch -= fetch % smpsz; - info->bpos += fetch; - slot -= fetch; - RATE_ASSERT(slot >= 0, ("%s: Negative Slot: %d\n", - __func__, slot)); - if (slot == 0 || info->bpos == info->bufsz) - break; - } - if (info->pos == info->bpos) { - RATE_TEST(info->pos == smpsz, - ("%s: EOF while in progress\n", __func__)); - break; + startdrift = _Z_GY2GX(info, 0, 1); + alphadrift = z_drift(info, startdrift, 1); +#endif + + dst = b; + + do { + if (reqin != 0) { + fetch = z_min(z_free(info), reqin); + if (fetch == 0) { + /* + * No more free spaces, so wind enough + * samples back to the head of delay line + * in byte domain. + */ + fetched = z_fetched(info); + start = z_prev(info, info->z_start, + (info->z_size << 1) - 1); + cp = (info->z_size << 1) + fetched; + z_copy(info->z_delay + (start * align), + info->z_delay, cp * align); + info->z_start = + z_prev(info, info->z_size << 1, 1); + info->z_pos = + z_next(info, info->z_start, fetched + 1); + fetch = z_min(z_free(info), reqin); +#ifdef Z_DIAGNOSTIC + if (1) { + static uint32_t kk = 0; + fprintf(stderr, + "Buffer Move: " + "start=%d fetched=%d cp=%d " + "cycle=%u [%u]\r", + start, fetched, cp, info->z_cycle, + ++kk); + } + info->z_cycle = 0; +#endif + } + if (fetch != 0) { + /* + * Fetch in byte domain and jump back + * to sample domain. + */ + fetched = SND_FXDIV(z_feed(f->source, c, + info->z_delay + (info->z_pos * align), + fetch * align, source), align); + /* + * Prepare to convert fetched buffer, + * or mark us done if we cannot fulfill + * the request. + */ + reqin -= fetched; + info->z_pos += fetched; + if (fetched != fetch) + reqin = 0; + } } - RATE_ASSERT(info->pos <= info->bpos, - ("%s: [2] Buffer overrun: %d > %d\n", __func__, info->pos, - info->bpos)); - RATE_ASSERT(info->pos < info->bpos, - ("%s: Zero buffer!\n", __func__)); - RATE_ASSERT(((info->bpos - info->pos) % smpsz) == 0, - ("%s: Buffer not sample integral (%d)\n", __func__, - info->bpos - info->pos)); - i += info->convert(info, b + i, count - i); - RATE_ASSERT(info->pos <= info->bpos, - ("%s: [3] Buffer overrun: %d > %d\n", __func__, info->pos, - info->bpos)); - if (info->pos == info->bpos) { + + reqout = z_min(z_gx2gy(info, z_fetched(info)), ocount); + if (reqout != 0) { + ocount -= reqout; + /* - * End of buffer cycle. Copy last unit sample - * to beginning of buffer so next cycle can - * interpolate using it. + * Drift.. drift.. drift.. + * + * Notice that there are 2 methods of doing the drift + * operations: The former is much cleaner (in a sense + * sense of mathematical readings of my eyes), but + * slower due to integer division in z_gy2gx(). + * Nevertheless, both should give the same exact + * accurate drifting results, so the later is + * favourable. */ -#ifdef FEEDRATE_STRAY - RATE_TEST(info->stray == 0, - ("%s: [2] Stray bytes: %u\n", __func__, - info->stray)); -#endif - bcopy(info->buffer + info->pos - smpsz, info->buffer, - smpsz); - info->bpos = smpsz; - info->pos = smpsz; + 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; + else { + info->z_start += startdrift - 1; + info->z_alpha -= info->z_gy; + } +#endif + dst += align; +#ifdef Z_DIAGNOSTIC + info->z_cycle++; +#endif + } while (--reqout != 0); } - if (i == count) - break; - } + } while (reqin != 0 && ocount != 0); - RATE_TEST((slot == 0 && count == i) || (slot > 0 && count > i && - info->pos == info->bpos && info->pos == smpsz), - ("%s: Inconsistent slot/count! " - "Count Expect: %u , Got: %u, Slot Left: %d\n", __func__, count, i, - slot)); + /* + * Back to byte domain.. + */ + return (dst - b); +} -#ifdef FEEDRATE_STRAY - RATE_TEST(info->stray == 0, ("%s: [3] Stray bytes: %u\n", __func__, - info->stray)); -#endif +static int +z_resampler_feed(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, + uint32_t count, void *source) +{ + uint32_t feed, maxfeed, left; - return (i); + /* + * Split count to smaller chunks to avoid possible 32bit overflow. + */ + maxfeed = ((struct z_info *)(f->data))->z_maxfeed; + left = count; + + do { + feed = z_resampler_feed_internal(f, c, b, + z_min(maxfeed, left), source); + b += feed; + left -= feed; + } while (left != 0 && feed != 0); + + return (count - left); } static struct pcm_feederdesc feeder_rate_desc[] = { - {FEEDER_RATE, AFMT_S8, AFMT_S8, 0}, - {FEEDER_RATE, AFMT_S16_LE, AFMT_S16_LE, 0}, - {FEEDER_RATE, AFMT_S24_LE, AFMT_S24_LE, 0}, - {FEEDER_RATE, AFMT_S32_LE, AFMT_S32_LE, 0}, - {FEEDER_RATE, AFMT_S16_BE, AFMT_S16_BE, 0}, - {FEEDER_RATE, AFMT_S24_BE, AFMT_S24_BE, 0}, - {FEEDER_RATE, AFMT_S32_BE, AFMT_S32_BE, 0}, - {FEEDER_RATE, AFMT_S8 | AFMT_STEREO, AFMT_S8 | AFMT_STEREO, 0}, - {FEEDER_RATE, AFMT_S16_LE | AFMT_STEREO, AFMT_S16_LE | AFMT_STEREO, 0}, - {FEEDER_RATE, AFMT_S24_LE | AFMT_STEREO, AFMT_S24_LE | AFMT_STEREO, 0}, - {FEEDER_RATE, AFMT_S32_LE | AFMT_STEREO, AFMT_S32_LE | AFMT_STEREO, 0}, - {FEEDER_RATE, AFMT_S16_BE | AFMT_STEREO, AFMT_S16_BE | AFMT_STEREO, 0}, - {FEEDER_RATE, AFMT_S24_BE | AFMT_STEREO, AFMT_S24_BE | AFMT_STEREO, 0}, - {FEEDER_RATE, AFMT_S32_BE | AFMT_STEREO, AFMT_S32_BE | AFMT_STEREO, 0}, - {FEEDER_RATE, AFMT_U8, AFMT_U8, 0}, - {FEEDER_RATE, AFMT_U16_LE, AFMT_U16_LE, 0}, - {FEEDER_RATE, AFMT_U24_LE, AFMT_U24_LE, 0}, - {FEEDER_RATE, AFMT_U32_LE, AFMT_U32_LE, 0}, - {FEEDER_RATE, AFMT_U16_BE, AFMT_U16_BE, 0}, - {FEEDER_RATE, AFMT_U24_BE, AFMT_U24_BE, 0}, - {FEEDER_RATE, AFMT_U32_BE, AFMT_U32_BE, 0}, - {FEEDER_RATE, AFMT_U8 | AFMT_STEREO, AFMT_U8 | AFMT_STEREO, 0}, - {FEEDER_RATE, AFMT_U16_LE | AFMT_STEREO, AFMT_U16_LE | AFMT_STEREO, 0}, - {FEEDER_RATE, AFMT_U24_LE | AFMT_STEREO, AFMT_U24_LE | AFMT_STEREO, 0}, - {FEEDER_RATE, AFMT_U32_LE | AFMT_STEREO, AFMT_U32_LE | AFMT_STEREO, 0}, - {FEEDER_RATE, AFMT_U16_BE | AFMT_STEREO, AFMT_U16_BE | AFMT_STEREO, 0}, - {FEEDER_RATE, AFMT_U24_BE | AFMT_STEREO, AFMT_U24_BE | AFMT_STEREO, 0}, - {FEEDER_RATE, AFMT_U32_BE | AFMT_STEREO, AFMT_U32_BE | AFMT_STEREO, 0}, - {0, 0, 0, 0}, + { FEEDER_RATE, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0 }, }; static kobj_method_t feeder_rate_methods[] = { - KOBJMETHOD(feeder_init, feed_rate_init), - KOBJMETHOD(feeder_free, feed_rate_free), - KOBJMETHOD(feeder_set, feed_rate_set), - KOBJMETHOD(feeder_get, feed_rate_get), - KOBJMETHOD(feeder_feed, feed_rate), - {0, 0} + KOBJMETHOD(feeder_init, z_resampler_init), + KOBJMETHOD(feeder_free, z_resampler_free), + KOBJMETHOD(feeder_set, z_resampler_set), + KOBJMETHOD(feeder_get, z_resampler_get), + KOBJMETHOD(feeder_feed, z_resampler_feed), + KOBJMETHOD_END }; -FEEDER_DECLARE(feeder_rate, 2, NULL); +FEEDER_DECLARE(feeder_rate, NULL); diff --git a/sys/dev/sound/pcm/feeder_volume.c b/sys/dev/sound/pcm/feeder_volume.c index 754a10e..1bd3789 100644 --- a/sys/dev/sound/pcm/feeder_volume.c +++ b/sys/dev/sound/pcm/feeder_volume.c @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2005 Ariff Abdullah <ariff@FreeBSD.org> + * Copyright (c) 2005-2009 Ariff Abdullah <ariff@FreeBSD.org> * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -26,159 +26,316 @@ /* feeder_volume, a long 'Lost Technology' rather than a new feature. */ +#ifdef _KERNEL +#ifdef HAVE_KERNEL_OPTION_HEADERS +#include "opt_snd.h" +#endif #include <dev/sound/pcm/sound.h> +#include <dev/sound/pcm/pcm.h> #include "feeder_if.h" +#define SND_USE_FXDIV +#include "snd_fxdiv_gen.h" + SND_DECLARE_FILE("$FreeBSD$"); +#endif + +typedef void (*feed_volume_t)(int *, int *, uint32_t, uint8_t *, uint32_t); -#define FVOL_OSS_SCALE 100 -#define FVOL_RESOLUTION PCM_FXSHIFT -#define FVOL_CLAMP(val) (((val) << FVOL_RESOLUTION) / FVOL_OSS_SCALE) -#define FVOL_LEFT(val) FVOL_CLAMP((val) & 0x7f) -#define FVOL_RIGHT(val) FVOL_LEFT((val) >> 8) -#define FVOL_MAX (1 << FVOL_RESOLUTION) -#define FVOL_CALC(sval, vval) (((sval) * (vval)) >> FVOL_RESOLUTION) - -typedef uint32_t (*feed_volume_filter)(uint8_t *, int *, uint32_t); - -#define FEEDER_VOLUME_FILTER(FMTBIT, VOL_INTCAST, SIGN, SIGNS, ENDIAN, ENDIANS) \ -static uint32_t \ -feed_volume_filter_##SIGNS##FMTBIT##ENDIANS(uint8_t *b, int *vol, \ - uint32_t count) \ -{ \ - int32_t j; \ - int i; \ - \ - i = count; \ - b += i; \ - \ - do { \ - b -= PCM_##FMTBIT##_BPS; \ - i -= PCM_##FMTBIT##_BPS; \ - j = PCM_READ_##SIGN##FMTBIT##_##ENDIAN(b); \ - j = FVOL_CALC((VOL_INTCAST)j, \ - vol[(i / PCM_##FMTBIT##_BPS) & 1]); \ - PCM_WRITE_##SIGN##FMTBIT##_##ENDIAN(b, j); \ - } while (i != 0); \ - \ - return (count); \ +#define FEEDVOLUME_CALC8(s, v) (SND_VOL_CALC_SAMPLE((intpcm_t) \ + (s) << 8, v) >> 8) +#define FEEDVOLUME_CALC16(s, v) SND_VOL_CALC_SAMPLE((intpcm_t)(s), v) +#define FEEDVOLUME_CALC24(s, v) SND_VOL_CALC_SAMPLE((intpcm64_t)(s), v) +#define FEEDVOLUME_CALC32(s, v) SND_VOL_CALC_SAMPLE((intpcm64_t)(s), v) + +#define FEEDVOLUME_DECLARE(SIGN, BIT, ENDIAN) \ +static void \ +feed_volume_##SIGN##BIT##ENDIAN(int *vol, int *matrix, \ + uint32_t channels, uint8_t *dst, uint32_t count) \ +{ \ + intpcm##BIT##_t v; \ + intpcm_t x; \ + uint32_t i; \ + \ + dst += count * PCM_##BIT##_BPS * channels; \ + do { \ + i = channels; \ + do { \ + dst -= PCM_##BIT##_BPS; \ + i--; \ + x = PCM_READ_##SIGN##BIT##_##ENDIAN(dst); \ + v = FEEDVOLUME_CALC##BIT(x, vol[matrix[i]]); \ + x = PCM_CLAMP_##SIGN##BIT(v); \ + _PCM_WRITE_##SIGN##BIT##_##ENDIAN(dst, x); \ + } while (i != 0); \ + } while (--count != 0); \ } -FEEDER_VOLUME_FILTER(8, int32_t, S, s, NE, ne) -FEEDER_VOLUME_FILTER(16, int32_t, S, s, LE, le) -FEEDER_VOLUME_FILTER(24, int32_t, S, s, LE, le) -FEEDER_VOLUME_FILTER(32, intpcm_t, S, s, LE, le) -FEEDER_VOLUME_FILTER(16, int32_t, S, s, BE, be) -FEEDER_VOLUME_FILTER(24, int32_t, S, s, BE, be) -FEEDER_VOLUME_FILTER(32, intpcm_t, S, s, BE, be) -FEEDER_VOLUME_FILTER(8, int32_t, U, u, NE, ne) -FEEDER_VOLUME_FILTER(16, int32_t, U, u, LE, le) -FEEDER_VOLUME_FILTER(24, int32_t, U, u, LE, le) -FEEDER_VOLUME_FILTER(32, intpcm_t, U, u, LE, le) -FEEDER_VOLUME_FILTER(16, int32_t, U, u, BE, be) -FEEDER_VOLUME_FILTER(24, int32_t, U, u, BE, be) -FEEDER_VOLUME_FILTER(32, intpcm_t, U, u, BE, be) +#if BYTE_ORDER == LITTLE_ENDIAN || defined(SND_FEEDER_MULTIFORMAT) +FEEDVOLUME_DECLARE(S, 16, LE) +FEEDVOLUME_DECLARE(S, 32, LE) +#endif +#if BYTE_ORDER == BIG_ENDIAN || defined(SND_FEEDER_MULTIFORMAT) +FEEDVOLUME_DECLARE(S, 16, BE) +FEEDVOLUME_DECLARE(S, 32, BE) +#endif +#ifdef SND_FEEDER_MULTIFORMAT +FEEDVOLUME_DECLARE(S, 8, NE) +FEEDVOLUME_DECLARE(S, 24, LE) +FEEDVOLUME_DECLARE(S, 24, BE) +FEEDVOLUME_DECLARE(U, 8, NE) +FEEDVOLUME_DECLARE(U, 16, LE) +FEEDVOLUME_DECLARE(U, 24, LE) +FEEDVOLUME_DECLARE(U, 32, LE) +FEEDVOLUME_DECLARE(U, 16, BE) +FEEDVOLUME_DECLARE(U, 24, BE) +FEEDVOLUME_DECLARE(U, 32, BE) +#endif struct feed_volume_info { - uint32_t format; - int bps; - feed_volume_filter filter; + uint32_t bps, channels; + feed_volume_t apply; + int volume_class; + int state; + int matrix[SND_CHN_MAX]; }; -static struct feed_volume_info feed_volume_tbl[] = { - { AFMT_S8, PCM_8_BPS, feed_volume_filter_s8ne }, - { AFMT_S16_LE, PCM_16_BPS, feed_volume_filter_s16le }, - { AFMT_S24_LE, PCM_24_BPS, feed_volume_filter_s24le }, - { AFMT_S32_LE, PCM_32_BPS, feed_volume_filter_s32le }, - { AFMT_S16_BE, PCM_16_BPS, feed_volume_filter_s16be }, - { AFMT_S24_BE, PCM_24_BPS, feed_volume_filter_s24be }, - { AFMT_S32_BE, PCM_32_BPS, feed_volume_filter_s32be }, - { AFMT_U8, PCM_8_BPS, feed_volume_filter_u8ne }, - { AFMT_U16_LE, PCM_16_BPS, feed_volume_filter_u16le }, - { AFMT_U24_LE, PCM_24_BPS, feed_volume_filter_u24le }, - { AFMT_U32_LE, PCM_32_BPS, feed_volume_filter_u32le }, - { AFMT_U16_BE, PCM_16_BPS, feed_volume_filter_u16be }, - { AFMT_U24_BE, PCM_24_BPS, feed_volume_filter_u24be }, - { AFMT_U32_BE, PCM_32_BPS, feed_volume_filter_u32be }, +#define FEEDVOLUME_ENTRY(SIGN, BIT, ENDIAN) \ + { \ + AFMT_##SIGN##BIT##_##ENDIAN, \ + feed_volume_##SIGN##BIT##ENDIAN \ + } + +static const struct { + uint32_t format; + feed_volume_t apply; +} feed_volume_info_tab[] = { +#if BYTE_ORDER == LITTLE_ENDIAN || defined(SND_FEEDER_MULTIFORMAT) + FEEDVOLUME_ENTRY(S, 16, LE), + FEEDVOLUME_ENTRY(S, 32, LE), +#endif +#if BYTE_ORDER == BIG_ENDIAN || defined(SND_FEEDER_MULTIFORMAT) + FEEDVOLUME_ENTRY(S, 16, BE), + FEEDVOLUME_ENTRY(S, 32, BE), +#endif +#ifdef SND_FEEDER_MULTIFORMAT + FEEDVOLUME_ENTRY(S, 8, NE), + FEEDVOLUME_ENTRY(S, 24, LE), + FEEDVOLUME_ENTRY(S, 24, BE), + FEEDVOLUME_ENTRY(U, 8, NE), + FEEDVOLUME_ENTRY(U, 16, LE), + FEEDVOLUME_ENTRY(U, 24, LE), + FEEDVOLUME_ENTRY(U, 32, LE), + FEEDVOLUME_ENTRY(U, 16, BE), + FEEDVOLUME_ENTRY(U, 24, BE), + FEEDVOLUME_ENTRY(U, 32, BE) +#endif }; -#define FVOL_DATA(i, c) ((intptr_t)((((i) & 0x1f) << 4) | ((c) & 0xf))) -#define FVOL_INFOIDX(m) (((m) >> 4) & 0x1f) -#define FVOL_CHANNELS(m) ((m) & 0xf) +#define FEEDVOLUME_TAB_SIZE ((int32_t) \ + (sizeof(feed_volume_info_tab) / \ + sizeof(feed_volume_info_tab[0]))) static int feed_volume_init(struct pcm_feeder *f) { - int i, channels; + struct feed_volume_info *info; + struct pcmchan_matrix *m; + uint32_t i; + int ret; - if (f->desc->in != f->desc->out) + if (f->desc->in != f->desc->out || + AFMT_CHANNEL(f->desc->in) > SND_CHN_MAX) return (EINVAL); - /* For now, this is mandatory! */ - if (!(f->desc->out & AFMT_STEREO)) - return (EINVAL); + for (i = 0; i < FEEDVOLUME_TAB_SIZE; i++) { + if (AFMT_ENCODING(f->desc->in) == + feed_volume_info_tab[i].format) { + info = malloc(sizeof(*info), M_DEVBUF, + M_NOWAIT | M_ZERO); + if (info == NULL) + return (ENOMEM); + + info->bps = AFMT_BPS(f->desc->in); + info->channels = AFMT_CHANNEL(f->desc->in); + info->apply = feed_volume_info_tab[i].apply; + info->volume_class = SND_VOL_C_PCM; + info->state = FEEDVOLUME_ENABLE; + + f->data = info; + m = feeder_matrix_default_channel_map(info->channels); + if (m == NULL) { + free(info, M_DEVBUF); + return (EINVAL); + } - channels = 2; + ret = feeder_volume_apply_matrix(f, m); + if (ret != 0) + free(info, M_DEVBUF); - for (i = 0; i < sizeof(feed_volume_tbl) / sizeof(feed_volume_tbl[0]); - i++) { - if ((f->desc->out & ~AFMT_STEREO) == - feed_volume_tbl[i].format) { - f->data = (void *)FVOL_DATA(i, channels); - return (0); + return (ret); } } - return (-1); + return (EINVAL); } static int -feed_volume(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, - uint32_t count, void *source) +feed_volume_free(struct pcm_feeder *f) { struct feed_volume_info *info; - int vol[2]; - int k, smpsz; - vol[0] = FVOL_LEFT(c->volume); - vol[1] = FVOL_RIGHT(c->volume); + info = f->data; + if (info != NULL) + free(info, M_DEVBUF); + + f->data = NULL; + + return (0); +} - if (vol[0] == FVOL_MAX && vol[1] == FVOL_MAX) +static int +feed_volume_set(struct pcm_feeder *f, int what, int value) +{ + struct feed_volume_info *info; + struct pcmchan_matrix *m; + int ret; + + info = f->data; + ret = 0; + + switch (what) { + case FEEDVOLUME_CLASS: + if (value < SND_VOL_C_BEGIN || value > SND_VOL_C_END) + return (EINVAL); + info->volume_class = value; + break; + case FEEDVOLUME_CHANNELS: + if (value < SND_CHN_MIN || value > SND_CHN_MAX) + return (EINVAL); + m = feeder_matrix_default_channel_map(value); + if (m == NULL) + return (EINVAL); + ret = feeder_volume_apply_matrix(f, m); + break; + case FEEDVOLUME_STATE: + if (!(value == FEEDVOLUME_ENABLE || value == FEEDVOLUME_BYPASS)) + return (EINVAL); + info->state = value; + break; + default: + return (EINVAL); + break; + } + + return (ret); +} + +static int +feed_volume_feed(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, + uint32_t count, void *source) +{ + struct feed_volume_info *info; + uint32_t j, align; + int i, *vol, *matrix; + uint8_t *dst; + + /* + * Fetch filter data operation. + */ + info = f->data; + + if (info->state == FEEDVOLUME_BYPASS) + return (FEEDER_FEED(f->source, c, b, count, source)); + + vol = c->volume[SND_VOL_C_VAL(info->volume_class)]; + matrix = info->matrix; + + /* + * First, let see if we really need to apply gain at all. + */ + j = 0; + i = info->channels; + do { + if (vol[matrix[--i]] != SND_VOL_FLAT) { + j = 1; + break; + } + } while (i != 0); + + /* Nope, just bypass entirely. */ + if (j == 0) return (FEEDER_FEED(f->source, c, b, count, source)); - info = &feed_volume_tbl[FVOL_INFOIDX((intptr_t)f->data)]; - smpsz = info->bps * FVOL_CHANNELS((intptr_t)f->data); - if (count < smpsz) - return (0); + dst = b; + align = info->bps * info->channels; - k = FEEDER_FEED(f->source, c, b, count - (count % smpsz), source); - if (k < smpsz) - return (0); + do { + if (count < align) + break; - k -= k % smpsz; - return (info->filter(b, vol, k)); + j = SND_FXDIV(FEEDER_FEED(f->source, c, dst, count, source), + align); + if (j == 0) + break; + + info->apply(vol, matrix, info->channels, dst, j); + + j *= align; + dst += j; + count -= j; + + } while (count != 0); + + return (dst - b); } static struct pcm_feederdesc feeder_volume_desc[] = { - {FEEDER_VOLUME, AFMT_S8 | AFMT_STEREO, AFMT_S8 | AFMT_STEREO, 0}, - {FEEDER_VOLUME, AFMT_S16_LE | AFMT_STEREO, AFMT_S16_LE | AFMT_STEREO, 0}, - {FEEDER_VOLUME, AFMT_S24_LE | AFMT_STEREO, AFMT_S24_LE | AFMT_STEREO, 0}, - {FEEDER_VOLUME, AFMT_S32_LE | AFMT_STEREO, AFMT_S32_LE | AFMT_STEREO, 0}, - {FEEDER_VOLUME, AFMT_S16_BE | AFMT_STEREO, AFMT_S16_BE | AFMT_STEREO, 0}, - {FEEDER_VOLUME, AFMT_S24_BE | AFMT_STEREO, AFMT_S24_BE | AFMT_STEREO, 0}, - {FEEDER_VOLUME, AFMT_S32_BE | AFMT_STEREO, AFMT_S32_BE | AFMT_STEREO, 0}, - {FEEDER_VOLUME, AFMT_U8 | AFMT_STEREO, AFMT_U8 | AFMT_STEREO, 0}, - {FEEDER_VOLUME, AFMT_U16_LE | AFMT_STEREO, AFMT_U16_LE | AFMT_STEREO, 0}, - {FEEDER_VOLUME, AFMT_U24_LE | AFMT_STEREO, AFMT_U24_LE | AFMT_STEREO, 0}, - {FEEDER_VOLUME, AFMT_U32_LE | AFMT_STEREO, AFMT_U32_LE | AFMT_STEREO, 0}, - {FEEDER_VOLUME, AFMT_U16_BE | AFMT_STEREO, AFMT_U16_BE | AFMT_STEREO, 0}, - {FEEDER_VOLUME, AFMT_U24_BE | AFMT_STEREO, AFMT_U24_BE | AFMT_STEREO, 0}, - {FEEDER_VOLUME, AFMT_U32_BE | AFMT_STEREO, AFMT_U32_BE | AFMT_STEREO, 0}, - {0, 0, 0, 0}, + { FEEDER_VOLUME, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0 } }; + static kobj_method_t feeder_volume_methods[] = { KOBJMETHOD(feeder_init, feed_volume_init), - KOBJMETHOD(feeder_feed, feed_volume), - {0, 0} + KOBJMETHOD(feeder_free, feed_volume_free), + KOBJMETHOD(feeder_set, feed_volume_set), + KOBJMETHOD(feeder_feed, feed_volume_feed), + KOBJMETHOD_END }; -FEEDER_DECLARE(feeder_volume, 2, NULL); + +FEEDER_DECLARE(feeder_volume, NULL); + +/* Extern */ + +/* + * feeder_volume_apply_matrix(): For given matrix map, apply its configuration + * to feeder_volume matrix structure. There are + * possibilites that feeder_volume be inserted + * before or after feeder_matrix, which in this + * case feeder_volume must be in a good terms + * with _current_ matrix. + */ +int +feeder_volume_apply_matrix(struct pcm_feeder *f, struct pcmchan_matrix *m) +{ + struct feed_volume_info *info; + uint32_t i; + + if (f == NULL || f->desc == NULL || f->desc->type != FEEDER_VOLUME || + f->data == NULL || m == NULL || m->channels < SND_CHN_MIN || + m->channels > SND_CHN_MAX) + return (EINVAL); + + info = f->data; + + for (i = 0; i < (sizeof(info->matrix) / sizeof(info->matrix[0])); i++) { + if (i < m->channels) + info->matrix[i] = m->map[i].type; + else + info->matrix[i] = SND_CHN_T_FL; + } + + info->channels = m->channels; + + return (0); +} diff --git a/sys/dev/sound/pcm/g711.h b/sys/dev/sound/pcm/g711.h new file mode 100644 index 0000000..49d7636 --- /dev/null +++ b/sys/dev/sound/pcm/g711.h @@ -0,0 +1,225 @@ +/*- + * 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. + * + * $FreeBSD$ + */ + +#ifndef _SND_G711_H_ +#define _SND_G711_H_ + +#define G711_TABLE_SIZE 256 + +#define ULAW_TO_U8 { \ + 3, 7, 11, 15, 19, 23, 27, 31, \ + 35, 39, 43, 47, 51, 55, 59, 63, \ + 66, 68, 70, 72, 74, 76, 78, 80, \ + 82, 84, 86, 88, 90, 92, 94, 96, \ + 98, 99, 100, 101, 102, 103, 104, 105, \ + 106, 107, 108, 109, 110, 111, 112, 113, \ + 113, 114, 114, 115, 115, 116, 116, 117, \ + 117, 118, 118, 119, 119, 120, 120, 121, \ + 121, 121, 122, 122, 122, 122, 123, 123, \ + 123, 123, 124, 124, 124, 124, 125, 125, \ + 125, 125, 125, 125, 126, 126, 126, 126, \ + 126, 126, 126, 126, 127, 127, 127, 127, \ + 127, 127, 127, 127, 127, 127, 127, 127, \ + 128, 128, 128, 128, 128, 128, 128, 128, \ + 128, 128, 128, 128, 128, 128, 128, 128, \ + 128, 128, 128, 128, 128, 128, 128, 128, \ + 253, 249, 245, 241, 237, 233, 229, 225, \ + 221, 217, 213, 209, 205, 201, 197, 193, \ + 190, 188, 186, 184, 182, 180, 178, 176, \ + 174, 172, 170, 168, 166, 164, 162, 160, \ + 158, 157, 156, 155, 154, 153, 152, 151, \ + 150, 149, 148, 147, 146, 145, 144, 143, \ + 143, 142, 142, 141, 141, 140, 140, 139, \ + 139, 138, 138, 137, 137, 136, 136, 135, \ + 135, 135, 134, 134, 134, 134, 133, 133, \ + 133, 133, 132, 132, 132, 132, 131, 131, \ + 131, 131, 131, 131, 130, 130, 130, 130, \ + 130, 130, 130, 130, 129, 129, 129, 129, \ + 129, 129, 129, 129, 129, 129, 129, 129, \ + 128, 128, 128, 128, 128, 128, 128, 128, \ + 128, 128, 128, 128, 128, 128, 128, 128, \ + 128, 128, 128, 128, 128, 128, 128, 128, \ + } + +#define ALAW_TO_U8 { \ + 108, 109, 106, 107, 112, 113, 110, 111, \ + 100, 101, 98, 99, 104, 105, 102, 103, \ + 118, 118, 117, 117, 120, 120, 119, 119, \ + 114, 114, 113, 113, 116, 116, 115, 115, \ + 43, 47, 35, 39, 59, 63, 51, 55, \ + 11, 15, 3, 7, 27, 31, 19, 23, \ + 86, 88, 82, 84, 94, 96, 90, 92, \ + 70, 72, 66, 68, 78, 80, 74, 76, \ + 127, 127, 127, 127, 127, 127, 127, 127, \ + 127, 127, 127, 127, 127, 127, 127, 127, \ + 128, 128, 128, 128, 128, 128, 128, 128, \ + 128, 128, 128, 128, 128, 128, 128, 128, \ + 123, 123, 123, 123, 124, 124, 124, 124, \ + 121, 121, 121, 121, 122, 122, 122, 122, \ + 126, 126, 126, 126, 126, 126, 126, 126, \ + 125, 125, 125, 125, 125, 125, 125, 125, \ + 148, 147, 150, 149, 144, 143, 146, 145, \ + 156, 155, 158, 157, 152, 151, 154, 153, \ + 138, 138, 139, 139, 136, 136, 137, 137, \ + 142, 142, 143, 143, 140, 140, 141, 141, \ + 213, 209, 221, 217, 197, 193, 205, 201, \ + 245, 241, 253, 249, 229, 225, 237, 233, \ + 170, 168, 174, 172, 162, 160, 166, 164, \ + 186, 184, 190, 188, 178, 176, 182, 180, \ + 129, 129, 129, 129, 129, 129, 129, 129, \ + 129, 129, 129, 129, 129, 129, 129, 129, \ + 128, 128, 128, 128, 128, 128, 128, 128, \ + 128, 128, 128, 128, 128, 128, 128, 128, \ + 133, 133, 133, 133, 132, 132, 132, 132, \ + 135, 135, 135, 135, 134, 134, 134, 134, \ + 130, 130, 130, 130, 130, 130, 130, 130, \ + 131, 131, 131, 131, 131, 131, 131, 131, \ + } + +#define U8_TO_ULAW { \ + 0, 0, 0, 0, 0, 1, 1, 1, \ + 1, 2, 2, 2, 2, 3, 3, 3, \ + 3, 4, 4, 4, 4, 5, 5, 5, \ + 5, 6, 6, 6, 6, 7, 7, 7, \ + 7, 8, 8, 8, 8, 9, 9, 9, \ + 9, 10, 10, 10, 10, 11, 11, 11, \ + 11, 12, 12, 12, 12, 13, 13, 13, \ + 13, 14, 14, 14, 14, 15, 15, 15, \ + 15, 16, 16, 17, 17, 18, 18, 19, \ + 19, 20, 20, 21, 21, 22, 22, 23, \ + 23, 24, 24, 25, 25, 26, 26, 27, \ + 27, 28, 28, 29, 29, 30, 30, 31, \ + 31, 32, 33, 34, 35, 36, 37, 38, \ + 39, 40, 41, 42, 43, 44, 45, 46, \ + 47, 49, 51, 53, 55, 57, 59, 61, \ + 63, 66, 70, 74, 78, 84, 92, 104, \ + 254, 231, 219, 211, 205, 201, 197, 193, \ + 190, 188, 186, 184, 182, 180, 178, 176, \ + 175, 174, 173, 172, 171, 170, 169, 168, \ + 167, 166, 165, 164, 163, 162, 161, 160, \ + 159, 159, 158, 158, 157, 157, 156, 156, \ + 155, 155, 154, 154, 153, 153, 152, 152, \ + 151, 151, 150, 150, 149, 149, 148, 148, \ + 147, 147, 146, 146, 145, 145, 144, 144, \ + 143, 143, 143, 143, 142, 142, 142, 142, \ + 141, 141, 141, 141, 140, 140, 140, 140, \ + 139, 139, 139, 139, 138, 138, 138, 138, \ + 137, 137, 137, 137, 136, 136, 136, 136, \ + 135, 135, 135, 135, 134, 134, 134, 134, \ + 133, 133, 133, 133, 132, 132, 132, 132, \ + 131, 131, 131, 131, 130, 130, 130, 130, \ + 129, 129, 129, 129, 128, 128, 128, 128, \ + } + +#define U8_TO_ALAW { \ + 42, 42, 42, 42, 42, 43, 43, 43, \ + 43, 40, 40, 40, 40, 41, 41, 41, \ + 41, 46, 46, 46, 46, 47, 47, 47, \ + 47, 44, 44, 44, 44, 45, 45, 45, \ + 45, 34, 34, 34, 34, 35, 35, 35, \ + 35, 32, 32, 32, 32, 33, 33, 33, \ + 33, 38, 38, 38, 38, 39, 39, 39, \ + 39, 36, 36, 36, 36, 37, 37, 37, \ + 37, 58, 58, 59, 59, 56, 56, 57, \ + 57, 62, 62, 63, 63, 60, 60, 61, \ + 61, 50, 50, 51, 51, 48, 48, 49, \ + 49, 54, 54, 55, 55, 52, 52, 53, \ + 53, 10, 11, 8, 9, 14, 15, 12, \ + 13, 2, 3, 0, 1, 6, 7, 4, \ + 5, 24, 30, 28, 18, 16, 22, 20, \ + 106, 110, 98, 102, 122, 114, 75, 90, \ + 213, 197, 245, 253, 229, 225, 237, 233, \ + 149, 151, 145, 147, 157, 159, 153, 155, \ + 133, 132, 135, 134, 129, 128, 131, 130, \ + 141, 140, 143, 142, 137, 136, 139, 138, \ + 181, 181, 180, 180, 183, 183, 182, 182, \ + 177, 177, 176, 176, 179, 179, 178, 178, \ + 189, 189, 188, 188, 191, 191, 190, 190, \ + 185, 185, 184, 184, 187, 187, 186, 186, \ + 165, 165, 165, 165, 164, 164, 164, 164, \ + 167, 167, 167, 167, 166, 166, 166, 166, \ + 161, 161, 161, 161, 160, 160, 160, 160, \ + 163, 163, 163, 163, 162, 162, 162, 162, \ + 173, 173, 173, 173, 172, 172, 172, 172, \ + 175, 175, 175, 175, 174, 174, 174, 174, \ + 169, 169, 169, 169, 168, 168, 168, 168, \ + 171, 171, 171, 171, 170, 170, 170, 170, \ + } + + +#define _G711_TO_INTPCM(t, v) ((intpcm_t) \ + ((int8_t)((t)[(uint8_t)(v)] ^ 0x80))) + +#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 new file mode 100644 index 0000000..99a7335 --- /dev/null +++ b/sys/dev/sound/pcm/intpcm.h @@ -0,0 +1,136 @@ +/*- + * 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. + * + * $FreeBSD$ + */ + +#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 new file mode 100644 index 0000000..e33d986 --- /dev/null +++ b/sys/dev/sound/pcm/matrix.h @@ -0,0 +1,218 @@ +/*- + * Copyright (c) 2007-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. + * + * $FreeBSD$ + */ + +#ifndef _SND_MATRIX_H_ +#define _SND_MATRIX_H_ + +#undef SND_MULTICHANNEL +#ifndef SND_OLDSTEREO +#define SND_MULTICHANNEL 1 +#endif + +/* + * XXX = unused, but part of the definition (will be used someday, maybe). + */ +#define SND_CHN_T_FL 0 /* Front Left */ +#define SND_CHN_T_FR 1 /* Front Right */ +#define SND_CHN_T_FC 2 /* Front Center */ +#define SND_CHN_T_LF 3 /* Low Frequency */ +#define SND_CHN_T_BL 4 /* Back Left */ +#define SND_CHN_T_BR 5 /* Back Right */ +#define SND_CHN_T_FLC 6 /* Front Left Center XXX */ +#define SND_CHN_T_FRC 7 /* Front Right Center XXX */ +#define SND_CHN_T_BC 8 /* Back Center */ +#define SND_CHN_T_SL 9 /* Side Left */ +#define SND_CHN_T_SR 10 /* Side Right */ +#define SND_CHN_T_TC 11 /* Top Center XXX */ +#define SND_CHN_T_TFL 12 /* Top Front Left XXX */ +#define SND_CHN_T_TFC 13 /* Top Front Center XXX */ +#define SND_CHN_T_TFR 14 /* Top Front Right XXX */ +#define SND_CHN_T_TBL 15 /* Top Back Left XXX */ +#define SND_CHN_T_TBC 16 /* Top Back Center XXX */ +#define SND_CHN_T_TBR 17 /* Top Back Right XXX */ +#define SND_CHN_T_MAX 18 /* Maximum channels */ + +#define SND_CHN_T_ZERO (SND_CHN_T_MAX + 1) /* Zero samples */ + +#define SND_CHN_T_LABELS { \ + "fl", "fr", "fc", "lf", "bl", "br", \ + "flc", "frc", "bc", "sl", "sr", "tc", \ + "tfl", "tfc", "tfr", "tbl", "tbc", "tbr" \ +} + +#define SND_CHN_T_NAMES { \ + "Front Left", "Front Right", "Front Center", \ + "Low Frequency Effects", \ + "Back Left", "Back Right", \ + "Front Left Center", "Front Right Center", \ + "Back Center", \ + "Side Left", "Side Right", \ + "Top Center", \ + "Top Front Left", "Top Front Center", "Top Front Right", \ + "Top Back Left", "Top Back Center", "Top Back Right" \ +} + +#define SND_CHN_T_MASK_FL (1 << SND_CHN_T_FL) +#define SND_CHN_T_MASK_FR (1 << SND_CHN_T_FR) +#define SND_CHN_T_MASK_FC (1 << SND_CHN_T_FC) +#define SND_CHN_T_MASK_LF (1 << SND_CHN_T_LF) +#define SND_CHN_T_MASK_BL (1 << SND_CHN_T_BL) +#define SND_CHN_T_MASK_BR (1 << SND_CHN_T_BR) +#define SND_CHN_T_MASK_FLC (1 << SND_CHN_T_FLC) +#define SND_CHN_T_MASK_FRC (1 << SND_CHN_T_FRC) +#define SND_CHN_T_MASK_BC (1 << SND_CHN_T_BC) +#define SND_CHN_T_MASK_SL (1 << SND_CHN_T_SL) +#define SND_CHN_T_MASK_SR (1 << SND_CHN_T_SR) +#define SND_CHN_T_MASK_TC (1 << SND_CHN_T_TC) +#define SND_CHN_T_MASK_TFL (1 << SND_CHN_T_TFL) +#define SND_CHN_T_MASK_TFC (1 << SND_CHN_T_TFC) +#define SND_CHN_T_MASK_TFR (1 << SND_CHN_T_TFR) +#define SND_CHN_T_MASK_TBL (1 << SND_CHN_T_TBL) +#define SND_CHN_T_MASK_TBC (1 << SND_CHN_T_TBC) +#define SND_CHN_T_MASK_TBR (1 << SND_CHN_T_TBR) + +#define SND_CHN_LEFT_MASK (SND_CHN_T_MASK_FL | \ + SND_CHN_T_MASK_BL | \ + SND_CHN_T_MASK_FLC | \ + SND_CHN_T_MASK_SL | \ + SND_CHN_T_MASK_TFL | \ + SND_CHN_T_MASK_TBL) + +#define SND_CHN_RIGHT_MASK (SND_CHN_T_MASK_FR | \ + SND_CHN_T_MASK_BR | \ + SND_CHN_T_MASK_FRC | \ + SND_CHN_T_MASK_SR | \ + SND_CHN_T_MASK_TFR | \ + SND_CHN_T_MASK_TBR) + +#define SND_CHN_CENTER_MASK (SND_CHN_T_MASK_FC | \ + SND_CHN_T_MASK_BC | \ + SND_CHN_T_MASK_TC | \ + SND_CHN_T_MASK_TFC | \ + SND_CHN_T_MASK_TBC | \ + SND_CHN_T_MASK_LF) /* XXX what?!? */ + +/* + * Matrix identity. + */ + +/* 1 @ Mono 1.0 */ +#define SND_CHN_MATRIX_1_0 0 +#define SND_CHN_MATRIX_1 SND_CHN_MATRIX_1_0 + +/* 2 @ Stereo 2.0 */ +#define SND_CHN_MATRIX_2_0 1 +#define SND_CHN_MATRIX_2 SND_CHN_MATRIX_2_0 + +/* 3 @ 2.1 (lfe), 3.0 (rear center, DEFAULT) */ +#define SND_CHN_MATRIX_2_1 2 +#define SND_CHN_MATRIX_3_0 3 +#define SND_CHN_MATRIX_3 SND_CHN_MATRIX_3_0 + +/* 4 @ 4.0 Quadraphonic */ +#define SND_CHN_MATRIX_4_0 4 +#define SND_CHN_MATRIX_4 SND_CHN_MATRIX_4_0 + +/* 5 @ 4.1 (lfe), 5.0 (center, DEFAULT) */ +#define SND_CHN_MATRIX_4_1 5 +#define SND_CHN_MATRIX_5_0 6 +#define SND_CHN_MATRIX_5 SND_CHN_MATRIX_5_0 + +/* 6 @ 5.1 (lfe, DEFAULT), 6.0 (rear center) */ +#define SND_CHN_MATRIX_5_1 7 +#define SND_CHN_MATRIX_6_0 8 +#define SND_CHN_MATRIX_6 SND_CHN_MATRIX_5_1 + +/* 7 @ 6.1 (lfe) */ +#define SND_CHN_MATRIX_6_1 9 +#define SND_CHN_MATRIX_7 SND_CHN_MATRIX_6_1 + +/* 8 @ 7.1 (lfe) */ +#define SND_CHN_MATRIX_7_1 10 +#define SND_CHN_MATRIX_8 SND_CHN_MATRIX_7_1 + +#define SND_CHN_MATRIX_MAX 11 + +#define SND_CHN_MATRIX_BEGIN SND_CHN_MATRIX_1_0 +#define SND_CHN_MATRIX_END SND_CHN_MATRIX_7_1 + +/* Custom matrix identity */ +#define SND_CHN_MATRIX_DRV -4 /* driver own identity */ +#define SND_CHN_MATRIX_PCMCHANNEL -3 /* PCM channel identity */ +#define SND_CHN_MATRIX_MISC -2 /* misc, custom defined */ +#define SND_CHN_MATRIX_UNKNOWN -1 /* unknown */ + +#define SND_CHN_T_VOL_0DB SND_CHN_T_MAX +#define SND_CHN_T_VOL_MAX (SND_CHN_T_VOL_0DB + 1) + +#define SND_CHN_T_BEGIN SND_CHN_T_FL +#define SND_CHN_T_END SND_CHN_T_TBR +#define SND_CHN_T_STEP 1 +#define SND_CHN_MIN 1 + +#ifdef SND_MULTICHANNEL +#define SND_CHN_MAX 8 +#else +#define SND_CHN_MAX 2 +#endif + +/* + * Multichannel interleaved volume matrix. Each calculated value relative + * to master and 0db will be stored in each CLASS + 1 as long as + * chn_setvolume_matrix() or the equivalent CHN_SETVOLUME() macros is + * used (see channel.c). + */ +#define SND_VOL_C_MASTER 0 +#define SND_VOL_C_PCM 1 +#define SND_VOL_C_PCM_VAL 2 +#define SND_VOL_C_MAX 3 + +#define SND_VOL_C_BEGIN SND_VOL_C_PCM +#define SND_VOL_C_END SND_VOL_C_PCM +#define SND_VOL_C_STEP 2 + +#define SND_VOL_C_VAL(x) ((x) + 1) + +#define SND_VOL_0DB_MIN 1 +#define SND_VOL_0DB_MAX 100 + +#define SND_VOL_0DB_MASTER 100 +#define SND_VOL_0DB_PCM 45 + +#define SND_VOL_RESOLUTION 8 +#define SND_VOL_FLAT (1 << SND_VOL_RESOLUTION) + +#define SND_VOL_CALC_SAMPLE(x, y) (((x) * (y)) >> SND_VOL_RESOLUTION) + +#define SND_VOL_CALC_VAL(x, y, z) \ + (((((x)[y][z] << SND_VOL_RESOLUTION) / \ + (x)[y][SND_CHN_T_VOL_0DB]) * \ + (x)[SND_VOL_C_MASTER][z]) / \ + (x)[SND_VOL_C_MASTER][SND_CHN_T_VOL_0DB]) \ + +#endif /* !_SND_MATRIX_H_ */ diff --git a/sys/dev/sound/pcm/matrix_map.h b/sys/dev/sound/pcm/matrix_map.h new file mode 100644 index 0000000..c13a051 --- /dev/null +++ b/sys/dev/sound/pcm/matrix_map.h @@ -0,0 +1,567 @@ +/*- + * 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. + * + * $FreeBSD$ + */ + +#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_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_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 d25d745..2b8d6ce 100644 --- a/sys/dev/sound/pcm/mixer.c +++ b/sys/dev/sound/pcm/mixer.c @@ -1,5 +1,7 @@ /*- - * Copyright (c) 1999 Cameron Grant <cg@freebsd.org> + * Copyright (c) 2005-2009 Ariff Abdullah <ariff@FreeBSD.org> + * Portions Copyright (c) Ryan Beasley <ryan.beasley@gmail.com> - GSoC 2006 + * Copyright (c) 1999 Cameron Grant <cg@FreeBSD.org> * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,14 +26,25 @@ * SUCH DAMAGE. */ +#ifdef HAVE_KERNEL_OPTION_HEADERS +#include "opt_snd.h" +#endif + #include <dev/sound/pcm/sound.h> +#include "feeder_if.h" #include "mixer_if.h" SND_DECLARE_FILE("$FreeBSD$"); MALLOC_DEFINE(M_MIXER, "mixer", "mixer"); +static int mixer_bypass = 1; +TUNABLE_INT("hw.snd.vpc_mixer_bypass", &mixer_bypass); +SYSCTL_INT(_hw_snd, OID_AUTO, vpc_mixer_bypass, CTLFLAG_RW, + &mixer_bypass, 0, + "control channel pcm/rec volume, bypassing real mixer device"); + #define MIXER_NAMELEN 16 struct snd_mixer { KOBJ_FIELDS; @@ -98,9 +111,7 @@ static struct cdevsw mixer_cdevsw = { */ int mixer_count = 0; -#ifdef USING_DEVFS static eventhandler_tag mixer_ehtag = NULL; -#endif static struct cdev * mixer_get_devt(device_t dev) @@ -112,7 +123,6 @@ mixer_get_devt(device_t dev) return snddev->mixer_dev; } -#ifdef SND_DYNSYSCTL static int mixer_lookup(char *devname) { @@ -124,21 +134,20 @@ mixer_lookup(char *devname) return i; return -1; } -#endif #define MIXER_SET_UNLOCK(x, y) do { \ if ((y) != 0) \ snd_mtxunlock((x)->lock); \ -} while(0) +} while (0) #define MIXER_SET_LOCK(x, y) do { \ if ((y) != 0) \ snd_mtxlock((x)->lock); \ -} while(0) +} while (0) static int mixer_set_softpcmvol(struct snd_mixer *m, struct snddev_info *d, - unsigned left, unsigned right) + u_int left, u_int right) { struct pcm_channel *c; int dropmtx, acquiremtx; @@ -166,22 +175,13 @@ mixer_set_softpcmvol(struct snd_mixer *m, struct snddev_info *d, MIXER_SET_UNLOCK(m, dropmtx); MIXER_SET_LOCK(d, acquiremtx); - if (CHN_EMPTY(d, channels.pcm.busy)) { - CHN_FOREACH(c, d, channels.pcm) { - CHN_LOCK(c); - if (c->direction == PCMDIR_PLAY && - (c->feederflags & (1 << FEEDER_VOLUME))) - chn_setvolume(c, left, right); - CHN_UNLOCK(c); - } - } else { - CHN_FOREACH(c, d, channels.pcm.busy) { - CHN_LOCK(c); - if (c->direction == PCMDIR_PLAY && - (c->feederflags & (1 << FEEDER_VOLUME))) - chn_setvolume(c, left, right); - CHN_UNLOCK(c); - } + CHN_FOREACH(c, d, channels.pcm.busy) { + CHN_LOCK(c); + if (c->direction == PCMDIR_PLAY && + (c->feederflags & (1 << FEEDER_VOLUME))) + chn_setvolume_multi(c, SND_VOL_C_MASTER, left, right, + (left + right) >> 1); + CHN_UNLOCK(c); } MIXER_SET_UNLOCK(d, acquiremtx); @@ -191,10 +191,62 @@ mixer_set_softpcmvol(struct snd_mixer *m, struct snddev_info *d, } static int -mixer_set(struct snd_mixer *m, unsigned dev, unsigned lev) +mixer_set_eq(struct snd_mixer *m, struct snddev_info *d, + u_int dev, u_int level) +{ + struct pcm_channel *c; + struct pcm_feeder *f; + int tone, dropmtx, acquiremtx; + + if (dev == SOUND_MIXER_TREBLE) + tone = FEEDEQ_TREBLE; + else if (dev == SOUND_MIXER_BASS) + tone = FEEDEQ_BASS; + else + return (EINVAL); + + if (!PCM_REGISTERED(d)) + return (EINVAL); + + if (mtx_owned(m->lock)) + dropmtx = 1; + else + dropmtx = 0; + + if (!(d->flags & SD_F_MPSAFE) || mtx_owned(d->lock) != 0) + acquiremtx = 0; + else + acquiremtx = 1; + + /* + * Be careful here. If we're coming from cdev ioctl, it is OK to + * not doing locking AT ALL (except on individual channel) since + * we've been heavily guarded by pcm cv, or if we're still + * under Giant influence. Since we also have mix_* calls, we cannot + * assume such protection and just do the lock as usuall. + */ + MIXER_SET_UNLOCK(m, dropmtx); + MIXER_SET_LOCK(d, acquiremtx); + + CHN_FOREACH(c, d, channels.pcm.busy) { + CHN_LOCK(c); + f = chn_findfeeder(c, FEEDER_EQ); + if (f != NULL) + (void)FEEDER_SET(f, tone, level); + CHN_UNLOCK(c); + } + + MIXER_SET_UNLOCK(d, acquiremtx); + MIXER_SET_LOCK(m, dropmtx); + + return (0); +} + +static int +mixer_set(struct snd_mixer *m, u_int dev, u_int lev) { struct snddev_info *d; - unsigned l, r, tl, tr; + u_int l, r, tl, tr; u_int32_t parent = SOUND_MIXER_NONE, child = 0; u_int32_t realdev; int i, dropmtx; @@ -243,7 +295,8 @@ mixer_set(struct snd_mixer *m, unsigned dev, unsigned lev) realdev = m->realdev[i]; tl = (l * (m->level[i] & 0x00ff)) / 100; tr = (r * ((m->level[i] & 0xff00) >> 8)) / 100; - if (i == SOUND_MIXER_PCM && (d->flags & SD_F_SOFTPCMVOL)) + if (i == SOUND_MIXER_PCM && + (d->flags & SD_F_SOFTPCMVOL)) (void)mixer_set_softpcmvol(m, d, tl, tr); else if (realdev != SOUND_MIXER_NONE) MIXER_SET(m, realdev, tl, tr); @@ -257,6 +310,9 @@ mixer_set(struct snd_mixer *m, unsigned dev, unsigned lev) } else { if (dev == SOUND_MIXER_PCM && (d->flags & SD_F_SOFTPCMVOL)) (void)mixer_set_softpcmvol(m, d, l, r); + else if ((dev == SOUND_MIXER_TREBLE || + dev == SOUND_MIXER_BASS) && (d->flags & SD_F_EQ)) + (void)mixer_set_eq(m, d, dev, (l + r) >> 1); else if (realdev != SOUND_MIXER_NONE && MIXER_SET(m, realdev, l, r) < 0) { MIXER_SET_LOCK(m, dropmtx); @@ -264,10 +320,10 @@ mixer_set(struct snd_mixer *m, unsigned dev, unsigned lev) } } - m->level[dev] = l | (r << 8); - MIXER_SET_LOCK(m, dropmtx); + m->level[dev] = l | (r << 8); + return 0; } @@ -284,6 +340,7 @@ static int mixer_setrecsrc(struct snd_mixer *mixer, u_int32_t src) { struct snddev_info *d; + u_int32_t recsrc; int dropmtx; d = device_get_softc(mixer->dev); @@ -298,8 +355,11 @@ mixer_setrecsrc(struct snd_mixer *mixer, u_int32_t src) src = SOUND_MASK_MIC; /* It is safe to drop this mutex due to Giant. */ MIXER_SET_UNLOCK(mixer, dropmtx); - mixer->recsrc = MIXER_SETRECSRC(mixer, src); + recsrc = MIXER_SETRECSRC(mixer, src); MIXER_SET_LOCK(mixer, dropmtx); + + mixer->recsrc = recsrc; + return 0; } @@ -398,6 +458,8 @@ mix_setdevs(struct snd_mixer *m, u_int32_t v) d = device_get_softc(m->dev); if (d != NULL && (d->flags & SD_F_SOFTPCMVOL)) v |= SOUND_MASK_PCM; + if (d != NULL && (d->flags & SD_F_EQ)) + v |= SOUND_MASK_TREBLE | SOUND_MASK_BASS; for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { if (m->parent[i] < SOUND_MIXER_NRDEVICES) v |= 1 << m->parent[i]; @@ -623,6 +685,20 @@ mixer_init(device_t dev, kobj_class_t cls, void *devinfo) struct cdev *pdev; int i, unit, devunit, val; + snddev = device_get_softc(dev); + if (snddev == NULL) + return (-1); + + if (resource_int_value(device_get_name(dev), + device_get_unit(dev), "eq", &val) == 0 && val != 0) { + snddev->flags |= SD_F_EQ; + if ((val & SD_F_EQ_MASK) == val) + snddev->flags |= val; + else + snddev->flags |= SD_F_EQ_DEFAULT; + snddev->eqpreamp = 0; + } + m = mixer_obj_create(dev, cls, devinfo, MIXER_TYPE_PRIMARY, NULL); if (m == NULL) return (-1); @@ -644,10 +720,9 @@ mixer_init(device_t dev, kobj_class_t cls, void *devinfo) unit = device_get_unit(dev); devunit = snd_mkunit(unit, SND_DEV_CTL, 0); - pdev = make_dev(&mixer_cdevsw, devunit, + pdev = make_dev(&mixer_cdevsw, PCMMINOR(devunit), UID_ROOT, GID_WHEEL, 0666, "mixer%d", unit); pdev->si_drv1 = m; - snddev = device_get_softc(dev); snddev->mixer_dev = pdev; ++mixer_count; @@ -674,6 +749,8 @@ mixer_init(device_t dev, kobj_class_t cls, void *devinfo) } if (snddev->flags & SD_F_SOFTPCMVOL) device_printf(dev, "Soft PCM mixer ENABLED\n"); + if (snddev->flags & SD_F_EQ) + device_printf(dev, "EQ Treble/Bass ENABLED\n"); } return (0); @@ -760,7 +837,6 @@ mixer_reinit(device_t dev) return 0; } -#ifdef SND_DYNSYSCTL static int sysctl_hw_snd_hwvol_mixer(SYSCTL_HANDLER_ARGS) { @@ -788,7 +864,6 @@ sysctl_hw_snd_hwvol_mixer(SYSCTL_HANDLER_ARGS) snd_mtxunlock(m->lock); return error; } -#endif int mixer_hwvol_init(device_t dev) @@ -801,7 +876,6 @@ mixer_hwvol_init(device_t dev) m->hwvol_mixer = SOUND_MIXER_VOLUME; m->hwvol_step = 5; -#ifdef SND_DYNSYSCTL SYSCTL_ADD_INT(device_get_sysctl_ctx(dev), SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "hwvol_step", CTLFLAG_RW, &m->hwvol_step, 0, ""); @@ -809,7 +883,6 @@ mixer_hwvol_init(device_t dev) SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "hwvol_mixer", CTLTYPE_STRING | CTLFLAG_RW, m, 0, sysctl_hw_snd_hwvol_mixer, "A", ""); -#endif return 0; } @@ -986,6 +1059,114 @@ mixer_close(struct cdev *i_dev, int flags, int mode, struct thread *td) } static int +mixer_ioctl_channel(struct cdev *dev, u_long cmd, caddr_t arg, int mode, + struct thread *td, int from) +{ + struct snddev_info *d; + struct snd_mixer *m; + struct pcm_channel *c, *rdch, *wrch; + pid_t pid; + int j, ret; + + if (td == NULL || td->td_proc == NULL) + return (-1); + + m = dev->si_drv1; + d = device_get_softc(m->dev); + j = cmd & 0xff; + + switch (j) { + case SOUND_MIXER_PCM: + case SOUND_MIXER_RECLEV: + case SOUND_MIXER_DEVMASK: + case SOUND_MIXER_CAPS: + case SOUND_MIXER_STEREODEVS: + break; + default: + return (-1); + break; + } + + pid = td->td_proc->p_pid; + rdch = NULL; + wrch = NULL; + c = NULL; + ret = -1; + + /* + * This is unfair. Imagine single proc opening multiple + * instances of same direction. What we do right now + * is looking for the first matching proc/pid, and just + * that. Nothing more. Consider it done. + * + * The better approach of controlling specific channel + * pcm or rec volume is by doing mixer ioctl + * (SNDCTL_DSP_[SET|GET][PLAY|REC]VOL / SOUND_MIXER_[PCM|RECLEV] + * on its open fd, rather than cracky mixer bypassing here. + */ + CHN_FOREACH(c, d, channels.pcm.opened) { + CHN_LOCK(c); + if (c->pid != pid || + !(c->feederflags & (1 << FEEDER_VOLUME))) { + CHN_UNLOCK(c); + continue; + } + if (rdch == NULL && c->direction == PCMDIR_REC) { + rdch = c; + if (j == SOUND_MIXER_RECLEV) + goto mixer_ioctl_channel_proc; + } else if (wrch == NULL && c->direction == PCMDIR_PLAY) { + wrch = c; + if (j == SOUND_MIXER_PCM) + goto mixer_ioctl_channel_proc; + } + CHN_UNLOCK(c); + if (rdch != NULL && wrch != NULL) + break; + } + + if (rdch == NULL && wrch == NULL) + return (-1); + + if ((j == SOUND_MIXER_DEVMASK || j == SOUND_MIXER_CAPS || + j == SOUND_MIXER_STEREODEVS) && + (cmd & MIXER_READ(0)) == MIXER_READ(0)) { + snd_mtxlock(m->lock); + *(int *)arg = mix_getdevs(m); + snd_mtxunlock(m->lock); + if (rdch != NULL) + *(int *)arg |= SOUND_MASK_RECLEV; + if (wrch != NULL) + *(int *)arg |= SOUND_MASK_PCM; + ret = 0; + } + + return (ret); + +mixer_ioctl_channel_proc: + + KASSERT(c != NULL, ("%s(): NULL channel", __func__)); + CHN_LOCKASSERT(c); + + if ((cmd & MIXER_WRITE(0)) == MIXER_WRITE(0)) { + int left, right, center; + + left = *(int *)arg & 0x7f; + right = (*(int *)arg >> 8) & 0x7f; + center = (left + right) >> 1; + chn_setvolume_multi(c, SND_VOL_C_PCM, left, right, center); + } else if ((cmd & MIXER_READ(0)) == MIXER_READ(0)) { + *(int *)arg = CHN_GETVOLUME(c, SND_VOL_C_PCM, SND_CHN_T_FL); + *(int *)arg |= + CHN_GETVOLUME(c, SND_VOL_C_PCM, SND_CHN_T_FR) << 8; + } + + CHN_UNLOCK(c); + + return (0); +} + +static int mixer_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread *td) { @@ -1002,7 +1183,15 @@ mixer_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, PCM_GIANT_ENTER(d); PCM_ACQUIRE_QUICK(d); - ret = mixer_ioctl_cmd(i_dev, cmd, arg, mode, td, MIXER_CMD_CDEV); + ret = -1; + + if (mixer_bypass != 0 && (d->flags & SD_F_VPC)) + ret = mixer_ioctl_channel(i_dev, cmd, arg, mode, td, + MIXER_CMD_CDEV); + + if (ret == -1) + ret = mixer_ioctl_cmd(i_dev, cmd, arg, mode, td, + MIXER_CMD_CDEV); PCM_RELEASE_QUICK(d); PCM_GIANT_LEAVE(d); @@ -1012,7 +1201,7 @@ mixer_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, /* * XXX Make sure you can guarantee concurrency safety before calling this - * function, be it through Giant, PCM_CV_*, etc ! + * function, be it through Giant, PCM_*, etc ! */ int mixer_ioctl_cmd(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, @@ -1112,7 +1301,6 @@ mixer_ioctl_cmd(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, return (ret); } -#ifdef USING_DEVFS static void mixer_clone(void *arg, #if __FreeBSD_version >= 600034 @@ -1152,7 +1340,6 @@ 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); -#endif /** * @brief Handler for SNDCTL_MIXERINFO @@ -1204,8 +1391,8 @@ mixer_oss_mixerinfo(struct cdev *i_dev, oss_mixerinfo *mi) /* XXX Need Giant magic entry */ /* See the note in function docblock. */ - mtx_assert(d->lock, MA_NOTOWNED); - pcm_lock(d); + 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) || @@ -1288,7 +1475,7 @@ mixer_oss_mixerinfo(struct cdev *i_dev, oss_mixerinfo *mi) } else ++nmix; - pcm_unlock(d); + PCM_UNLOCK(d); if (m != NULL) return (0); diff --git a/sys/dev/sound/pcm/mixer.h b/sys/dev/sound/pcm/mixer.h index d03338b..4248a9c 100644 --- a/sys/dev/sound/pcm/mixer.h +++ b/sys/dev/sound/pcm/mixer.h @@ -1,5 +1,6 @@ /*- - * Copyright (c) 1999 Cameron Grant <cg@freebsd.org> + * Copyright (c) 2005-2009 Ariff Abdullah <ariff@FreeBSD.org> + * Copyright (c) 1999 Cameron Grant <cg@FreeBSD.org> * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -73,4 +74,15 @@ extern int mixer_count; #define MIXER_SIZE (512 + sizeof(struct kobj) + \ sizeof(oss_mixer_enuminfo)) +#ifdef SND_DEBUG +#define MIXER_DECLARE(mixer) \ + static struct kobj_class mixer##_class = { \ + .name = #mixer, \ + .methods = mixer##_methods, \ + .size = MIXER_SIZE, \ + .baseclasses = NULL, \ + .refs = 0 \ + } +#else #define MIXER_DECLARE(name) static DEFINE_CLASS(name, name ## _methods, MIXER_SIZE) +#endif diff --git a/sys/dev/sound/pcm/pcm.h b/sys/dev/sound/pcm/pcm.h new file mode 100644 index 0000000..aab3750 --- /dev/null +++ b/sys/dev/sound/pcm/pcm.h @@ -0,0 +1,438 @@ +/*- + * Copyright (c) 2006-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. + * + * $FreeBSD$ + */ + +#ifndef _SND_PCM_H_ +#define _SND_PCM_H_ + +#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. + */ + +/* + * Automatically turn on 64bit arithmetic on suitable archs + * (amd64 64bit, ia64, etc..) for wider 32bit samples / integer processing. + */ +#if LONG_BIT >= 64 +#undef SND_PCM_64 +#define SND_PCM_64 1 +#endif + +typedef int32_t intpcm_t; + +typedef int32_t intpcm8_t; +typedef int32_t intpcm16_t; +typedef int32_t intpcm24_t; + +typedef uint32_t uintpcm_t; + +typedef uint32_t uintpcm8_t; +typedef uint32_t uintpcm16_t; +typedef uint32_t uintpcm24_t; + +#ifdef SND_PCM_64 +typedef int64_t intpcm32_t; +typedef uint64_t uintpcm32_t; +#else +typedef int32_t intpcm32_t; +typedef uint32_t uintpcm32_t; +#endif + +typedef int64_t intpcm64_t; +typedef uint64_t uintpcm64_t; + +/* 32bit fixed point shift */ +#define PCM_FXSHIFT 8 + +#define PCM_S8_MAX 0x7f +#define PCM_S8_MIN -0x80 +#define PCM_S16_MAX 0x7fff +#define PCM_S16_MIN -0x8000 +#define PCM_S24_MAX 0x7fffff +#define PCM_S24_MIN -0x800000 +#ifdef SND_PCM_64 +#if LONG_BIT >= 64 +#define PCM_S32_MAX 0x7fffffffL +#define PCM_S32_MIN -0x80000000L +#else +#define PCM_S32_MAX 0x7fffffffLL +#define PCM_S32_MIN -0x80000000LL +#endif +#else +#define PCM_S32_MAX 0x7fffffff +#define PCM_S32_MIN (-0x7fffffff - 1) +#endif + +/* Bytes-per-sample definition */ +#define PCM_8_BPS 1 +#define PCM_16_BPS 2 +#define PCM_24_BPS 3 +#define PCM_32_BPS 4 + +#define INTPCM_T(v) ((intpcm_t)(v)) +#define INTPCM8_T(v) ((intpcm8_t)(v)) +#define INTPCM16_T(v) ((intpcm16_t)(v)) +#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 */ +/* + * 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. + */ +#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)) + +#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) + +/* + * Common macross. Use this instead of "_", unless we want + * the real sample value. + */ + +/* 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. + */ +#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))) + +#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) + +#endif /* !_SND_PCM_H_ */ diff --git a/sys/dev/sound/pcm/sndstat.c b/sys/dev/sound/pcm/sndstat.c index 9d9f9db..6d35803 100644 --- a/sys/dev/sound/pcm/sndstat.c +++ b/sys/dev/sound/pcm/sndstat.c @@ -1,5 +1,6 @@ /*- - * Copyright (c) 2001 Cameron Grant <cg@freebsd.org> + * Copyright (c) 2005-2009 Ariff Abdullah <ariff@FreeBSD.org> + * Copyright (c) 2001 Cameron Grant <cg@FreeBSD.org> * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,12 +25,14 @@ * SUCH DAMAGE. */ +#ifdef HAVE_KERNEL_OPTION_HEADERS +#include "opt_snd.h" +#endif + #include <dev/sound/pcm/sound.h> -#include <dev/sound/pcm/vchan.h> +#include <dev/sound/pcm/pcm.h> #include <dev/sound/version.h> -#ifdef USING_MUTEX #include <sys/sx.h> -#endif SND_DECLARE_FILE("$FreeBSD$"); @@ -60,9 +63,7 @@ struct sndstat_entry { int type, unit; }; -#ifdef USING_MUTEX static struct mtx sndstat_lock; -#endif static struct sbuf sndstat_sbuf; static struct cdev *sndstat_dev = NULL; static int sndstat_bufptr = -1; @@ -76,16 +77,12 @@ static int sndstat_files = 0; sbuf_delete(&sndstat_sbuf); \ sndstat_bufptr = -1; \ } \ -} while(0) +} while (0) static SLIST_HEAD(, sndstat_entry) sndstat_devlist = SLIST_HEAD_INITIALIZER(none); int snd_verbose = 1; -#ifdef USING_MUTEX TUNABLE_INT("hw.snd.verbose", &snd_verbose); -#else -TUNABLE_INT_DECL("hw.snd.verbose", 1, snd_verbose); -#endif #ifdef SND_DEBUG static int @@ -354,7 +351,7 @@ sndstat_prepare(struct sbuf *s) int i, j; sbuf_printf(s, "FreeBSD Audio Driver (newpcm: %ubit %d/%s)\n", - (u_int)sizeof(intpcm_t) << 3, SND_DRV_VERSION, MACHINE_ARCH); + (u_int)sizeof(intpcm32_t) << 3, SND_DRV_VERSION, MACHINE_ARCH); if (SLIST_EMPTY(&sndstat_devlist)) { sbuf_printf(s, "No devices installed.\n"); sbuf_finish(s); diff --git a/sys/dev/sound/pcm/sndstat.h b/sys/dev/sound/pcm/sndstat.h new file mode 100644 index 0000000..0c3a9bf --- /dev/null +++ b/sys/dev/sound/pcm/sndstat.h @@ -0,0 +1,163 @@ +/*- + * Copyright (c) 2007-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. + * + * $FreeBSD$ + */ + +#ifndef _SND_SNDSTAT_H_ +#define _SND_SNDSTAT_H_ + +#define SNDSTAT_PREPARE_PCM_ARGS \ + struct sbuf *s, device_t dev, int verbose + +#define SNDSTAT_PREPARE_PCM_BEGIN() do { \ + struct snddev_info *d; \ + struct pcm_channel *c; \ + struct pcm_feeder *f; \ + \ + if (verbose < 1) \ + return (0); \ + \ + d = device_get_softc(dev); \ + PCM_BUSYASSERT(d); \ + \ + if (CHN_EMPTY(d, channels.pcm)) { \ + sbuf_printf(s, " (mixer only)"); \ + return (0); \ + } \ + \ + sbuf_printf(s, " (%dp:%dv/%dr:%dv channels %splex%s)", \ + d->playcount, d->pvchancount, d->reccount, d->rvchancount, \ + (d->flags & SD_F_SIMPLEX) ? "sim" : "du", \ + (device_get_unit(dev) == snd_unit) ? " default" : "") + + +#define SNDSTAT_PREPARE_PCM_END() \ + if (verbose <= 1) \ + return (0); \ + \ + sbuf_printf(s, "\n\t"); \ + sbuf_printf(s, "snddev flags=0x%b", d->flags, SD_F_BITS); \ + \ + CHN_FOREACH(c, d, channels.pcm) { \ + \ + KASSERT(c->bufhard != NULL && c->bufsoft != NULL, \ + ("hosed pcm channel setup")); \ + \ + sbuf_printf(s, "\n\t"); \ + \ + sbuf_printf(s, "%s[%s]: ", \ + (c->parentchannel != NULL) ? \ + c->parentchannel->name : "", c->name); \ + sbuf_printf(s, "spd %d", c->speed); \ + if (c->speed != sndbuf_getspd(c->bufhard)) \ + sbuf_printf(s, "/%d", \ + sndbuf_getspd(c->bufhard)); \ + sbuf_printf(s, ", fmt 0x%08x", c->format); \ + if (c->format != sndbuf_getfmt(c->bufhard)) \ + sbuf_printf(s, "/0x%08x", \ + sndbuf_getfmt(c->bufhard)); \ + sbuf_printf(s, ", flags 0x%08x, 0x%08x", \ + c->flags, c->feederflags); \ + if (c->pid != -1) \ + sbuf_printf(s, ", pid %d (%s)", \ + c->pid, c->comm); \ + sbuf_printf(s, "\n\t"); \ + \ + sbuf_printf(s, "interrupts %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]", \ + c->xruns, c->feedcount, \ + sndbuf_getfree(c->bufhard), \ + sndbuf_getfree(c->bufsoft), \ + sndbuf_getsize(c->bufhard), \ + sndbuf_getblksz(c->bufhard), \ + sndbuf_getblkcnt(c->bufhard), \ + sndbuf_getsize(c->bufsoft), \ + sndbuf_getblksz(c->bufsoft), \ + sndbuf_getblkcnt(c->bufsoft)); \ + else \ + sbuf_printf(s, \ + "underruns %d, feed %u, ready %d " \ + "[b:%d/%d/%d|bs:%d/%d/%d]", \ + c->xruns, c->feedcount, \ + sndbuf_getready(c->bufsoft), \ + sndbuf_getsize(c->bufhard), \ + sndbuf_getblksz(c->bufhard), \ + sndbuf_getblkcnt(c->bufhard), \ + sndbuf_getsize(c->bufsoft), \ + sndbuf_getblksz(c->bufsoft), \ + sndbuf_getblkcnt(c->bufsoft)); \ + sbuf_printf(s, "\n\t"); \ + \ + sbuf_printf(s, "channel flags=0x%b", c->flags, \ + CHN_F_BITS); \ + sbuf_printf(s, "\n\t"); \ + \ + sbuf_printf(s, "{%s}", \ + (c->direction == PCMDIR_REC) ? "hardware" : \ + "userland"); \ + sbuf_printf(s, " -> "); \ + f = c->feeder; \ + while (f->source != NULL) \ + f = f->source; \ + while (f != NULL) { \ + sbuf_printf(s, "%s", f->class->name); \ + if (f->desc->type == FEEDER_FORMAT) \ + sbuf_printf(s, "(0x%08x -> 0x%08x)", \ + f->desc->in, f->desc->out); \ + else if (f->desc->type == FEEDER_MATRIX) \ + sbuf_printf(s, "(%d.%d -> %d.%d)", \ + 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(s, \ + "(0x%08x q:%d %d -> %d)", \ + f->desc->out, \ + FEEDER_GET(f, FEEDRATE_QUALITY), \ + FEEDER_GET(f, FEEDRATE_SRC), \ + FEEDER_GET(f, FEEDRATE_DST)); \ + else \ + sbuf_printf(s, "(0x%08x)", \ + f->desc->out); \ + sbuf_printf(s, " -> "); \ + f = f->parent; \ + } \ + sbuf_printf(s, "{%s}", \ + (c->direction == PCMDIR_REC) ? "userland" : \ + "hardware"); \ + } \ + \ + return (0); \ +} while (0) + +#endif /* !_SND_SNDSTAT_H_ */ diff --git a/sys/dev/sound/pcm/sound.c b/sys/dev/sound/pcm/sound.c index 03e8f49d..d1b7be5 100644 --- a/sys/dev/sound/pcm/sound.c +++ b/sys/dev/sound/pcm/sound.c @@ -1,5 +1,7 @@ /*- - * Copyright (c) 1999 Cameron Grant <cg@freebsd.org> + * Copyright (c) 2005-2009 Ariff Abdullah <ariff@FreeBSD.org> + * Portions Copyright (c) Ryan Beasley <ryan.beasley@gmail.com> - GSoC 2006 + * Copyright (c) 1999 Cameron Grant <cg@FreeBSD.org> * Copyright (c) 1997 Luigi Rizzo * All rights reserved. * @@ -25,10 +27,15 @@ * 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/vchan.h> #include <dev/sound/pcm/dsp.h> +#include <dev/sound/pcm/sndstat.h> #include <dev/sound/version.h> #include <sys/limits.h> #include <sys/sysctl.h> @@ -41,10 +48,8 @@ devclass_t pcm_devclass; int pcm_veto_load = 1; -#ifdef USING_DEVFS int snd_unit = -1; TUNABLE_INT("hw.snd.default_unit", &snd_unit); -#endif static int snd_unit_auto = 0; TUNABLE_INT("hw.snd.default_auto", &snd_unit_auto); @@ -66,83 +71,56 @@ SYSCTL_NODE(_hw, OID_AUTO, snd, CTLFLAG_RD, 0, "Sound driver"); static const 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"); + 0, "driver version/arch"); /** * @brief Unit number allocator for syncgroup IDs */ struct unrhdr *pcmsg_unrhdr = NULL; -static int sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose); +static int +sndstat_prepare_pcm(SNDSTAT_PREPARE_PCM_ARGS) +{ + SNDSTAT_PREPARE_PCM_BEGIN(); + SNDSTAT_PREPARE_PCM_END(); +} void * snd_mtxcreate(const char *desc, const char *type) { -#ifdef USING_MUTEX struct mtx *m; m = malloc(sizeof(*m), M_DEVBUF, M_WAITOK | M_ZERO); mtx_init(m, desc, type, MTX_DEF); return m; -#else - return (void *)0xcafebabe; -#endif } void snd_mtxfree(void *m) { -#ifdef USING_MUTEX struct mtx *mtx = m; - /* mtx_assert(mtx, MA_OWNED); */ mtx_destroy(mtx); free(mtx, M_DEVBUF); -#endif } void snd_mtxassert(void *m) { -#ifdef USING_MUTEX #ifdef INVARIANTS struct mtx *mtx = m; mtx_assert(mtx, MA_OWNED); #endif -#endif } -/* -void -snd_mtxlock(void *m) -{ -#ifdef USING_MUTEX - struct mtx *mtx = m; - - mtx_lock(mtx); -#endif -} - -void -snd_mtxunlock(void *m) -{ -#ifdef USING_MUTEX - struct mtx *mtx = m; - mtx_unlock(mtx); -#endif -} -*/ int snd_setup_intr(device_t dev, struct resource *res, int flags, driver_intr_t hand, void *param, void **cookiep) { struct snddev_info *d; -#ifdef USING_MUTEX + flags &= INTR_MPSAFE; flags |= INTR_TYPE_AV; -#else - flags = INTR_TYPE_AV; -#endif d = device_get_softc(dev); if (d != NULL && (flags & INTR_MPSAFE)) d->flags |= SD_F_MPSAFE; @@ -154,26 +132,6 @@ snd_setup_intr(device_t dev, struct resource *res, int flags, driver_intr_t hand hand, param, cookiep); } -#ifndef PCM_DEBUG_MTX -void -pcm_lock(struct snddev_info *d) -{ - snd_mtxlock(d->lock); -} - -void -pcm_unlock(struct snddev_info *d) -{ - snd_mtxunlock(d->lock); -} -#endif - -struct pcm_channel * -pcm_getfakechan(struct snddev_info *d) -{ - return d->fakechan; -} - static void pcm_clonereset(struct snddev_info *d) { @@ -183,20 +141,21 @@ pcm_clonereset(struct snddev_info *d) cmax = d->playcount + d->reccount - 1; if (d->pvchancount > 0) - cmax += MAX(d->pvchancount, snd_maxautovchans) - 1; + cmax += max(d->pvchancount, snd_maxautovchans) - 1; if (d->rvchancount > 0) - cmax += MAX(d->rvchancount, snd_maxautovchans) - 1; + cmax += max(d->rvchancount, snd_maxautovchans) - 1; if (cmax > PCMMAXCLONE) cmax = PCMMAXCLONE; (void)snd_clone_gc(d->clones); (void)snd_clone_setmaxunit(d->clones, cmax); } -static int +int pcm_setvchans(struct snddev_info *d, int direction, int newcnt, int num) { struct pcm_channel *c, *ch, *nch; - int err, vcnt; + struct pcmchan_caps *caps; + int i, err, vcnt; PCM_BUSYASSERT(d); @@ -228,9 +187,33 @@ pcm_setvchans(struct snddev_info *d, int direction, int newcnt, int num) 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))))) { - ch = c; - break; + /* + * 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); } @@ -267,11 +250,13 @@ pcm_setvchans(struct snddev_info *d, int direction, int newcnt, int num) } CHN_FOREACH_SAFE(ch, c, nch, children) { CHN_LOCK(ch); - if (!(ch->flags & CHN_F_BUSY)) { + if (vcnt == 1 && c->refcount > 0) { CHN_UNLOCK(ch); - CHN_UNLOCK(c); + break; + } + if (!(ch->flags & CHN_F_BUSY) && + ch->refcount < 1) { err = vchan_destroy(ch); - CHN_LOCK(c); if (err == 0) vcnt--; } else @@ -291,10 +276,10 @@ pcm_setvchans(struct snddev_info *d, int direction, int newcnt, int num) /* return error status and a locked channel */ int pcm_chnalloc(struct snddev_info *d, struct pcm_channel **ch, int direction, - pid_t pid, int devunit) + pid_t pid, char *comm, int devunit) { struct pcm_channel *c; - int err, vchancount; + int err, vchancount, vchan_num; KASSERT(d != NULL && ch != NULL && (devunit == -1 || !(devunit & ~(SND_U_MASK | SND_D_MASK | SND_C_MASK))) && @@ -309,35 +294,51 @@ pcm_chnalloc(struct snddev_info *d, struct pcm_channel **ch, int direction, case SND_DEV_DSPHW_PLAY: case SND_DEV_DSPHW_VPLAY: if (direction != PCMDIR_PLAY) - return (EOPNOTSUPP); + return (ENOTSUP); break; case SND_DEV_DSPHW_REC: case SND_DEV_DSPHW_VREC: if (direction != PCMDIR_REC) - return (EOPNOTSUPP); + return (ENOTSUP); break; default: if (!(direction == PCMDIR_PLAY || direction == PCMDIR_REC)) - return (EOPNOTSUPP); + return (ENOTSUP); break; } } + *ch = NULL; + vchan_num = 0; + vchancount = (direction == PCMDIR_PLAY) ? d->pvchancount : + d->rvchancount; + retry_chnalloc: - err = EOPNOTSUPP; + err = ENOTSUP; /* scan for a free channel */ CHN_FOREACH(c, d, channels.pcm) { CHN_LOCK(c); + if (devunit == -1 && c->direction == direction && + (c->flags & CHN_F_VIRTUAL)) { + if (vchancount < snd_maxautovchans && + vchan_num < CHN_CHAN(c)) { + CHN_UNLOCK(c); + goto vchan_alloc; + } + vchan_num++; + } if (c->direction == direction && !(c->flags & CHN_F_BUSY) && (devunit == -1 || devunit == -2 || c->unit == devunit)) { 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->unit == devunit) { if (c->direction != direction) - err = EOPNOTSUPP; + err = ENOTSUP; else if (c->flags & CHN_F_BUSY) err = EBUSY; else @@ -353,13 +354,10 @@ retry_chnalloc: if (devunit == -2) return (err); +vchan_alloc: /* no channel available */ if (devunit == -1 || snd_unit2d(devunit) == SND_DEV_DSPHW_VPLAY || snd_unit2d(devunit) == SND_DEV_DSPHW_VREC) { - if (direction == PCMDIR_PLAY) - vchancount = d->pvchancount; - else - vchancount = d->rvchancount; if (!(vchancount > 0 && vchancount < snd_maxautovchans) && (devunit == -1 || snd_unit2c(devunit) < snd_maxautovchans)) return (err); @@ -384,6 +382,7 @@ pcm_chnrelease(struct pcm_channel *c) c->flags &= ~CHN_F_BUSY; c->pid = -1; + strlcpy(c->comm, CHN_COMM_UNUSED, sizeof(c->comm)); CHN_UNLOCK(c); return (0); @@ -403,7 +402,7 @@ pcm_chnref(struct pcm_channel *c, int ref) int pcm_inprog(struct snddev_info *d, int delta) { - snd_mtxassert(d->lock); + PCM_LOCKASSERT(d); d->inprog += delta; @@ -431,7 +430,6 @@ pcm_setmaxautovchans(struct snddev_info *d, int num) pcm_clonereset(d); } -#ifdef USING_DEVFS static int sysctl_hw_snd_default_unit(SYSCTL_HANDLER_ARGS) { @@ -451,7 +449,6 @@ sysctl_hw_snd_default_unit(SYSCTL_HANDLER_ARGS) /* 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_RW, 0, sizeof(int), sysctl_hw_snd_default_unit, "I", "default sound device"); -#endif static int sysctl_hw_snd_maxautovchans(SYSCTL_HANDLER_ARGS) @@ -491,7 +488,7 @@ pcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t c char *dirs, *devname, buf[CHN_NAMELEN]; PCM_BUSYASSERT(d); - snd_mtxassert(d->lock); + PCM_LOCKASSERT(d); KASSERT(num >= -1, ("invalid num=%d", num)); @@ -570,11 +567,12 @@ pcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t c return (NULL); } - pcm_unlock(d); + 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->unit = udc; ch->pid = -1; + strlcpy(ch->comm, CHN_COMM_UNUSED, sizeof(ch->comm)); ch->parentsnddev = d; ch->parentchannel = parent; ch->dev = d->dev; @@ -583,7 +581,7 @@ pcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t c device_get_nameunit(ch->dev), dirs, devname); err = chn_init(ch, devinfo, dir, direction); - pcm_lock(d); + PCM_LOCK(d); if (err) { device_printf(d->dev, "chn_init(%s) failed: err = %d\n", ch->name, err); @@ -620,46 +618,12 @@ pcm_chn_destroy(struct pcm_channel *ch) int pcm_chn_add(struct snddev_info *d, struct pcm_channel *ch) { - struct pcm_channel *tmp, *after; - int num; - PCM_BUSYASSERT(d); - snd_mtxassert(d->lock); + PCM_LOCKASSERT(d); KASSERT(ch != NULL && (ch->direction == PCMDIR_PLAY || ch->direction == PCMDIR_REC), ("Invalid pcm channel")); - after = NULL; - tmp = NULL; - num = 0; - - /* - * Look for possible device collision. - */ - CHN_FOREACH(tmp, d, channels.pcm) { - if (tmp->unit == ch->unit) { - device_printf(d->dev, "%s(): Device collision " - "old=%p new=%p devunit=0x%08x\n", - __func__, tmp, ch, ch->unit); - return (ENODEV); - } - if (CHN_DEV(tmp) < CHN_DEV(ch)) { - if (num == 0) - after = tmp; - continue; - } else if (CHN_DEV(tmp) > CHN_DEV(ch)) - break; - num++; - if (CHN_CHAN(tmp) < CHN_CHAN(ch)) - after = tmp; - else if (CHN_CHAN(tmp) > CHN_CHAN(ch)) - break; - } - - if (after != NULL) { - CHN_INSERT_AFTER(after, ch, channels.pcm); - } else { - CHN_INSERT_HEAD(d, ch, channels.pcm); - } + CHN_INSERT_SORT_ASCEND(d, ch, channels.pcm); switch (CHN_DEV(ch)) { case SND_DEV_DSPHW_PLAY: @@ -689,7 +653,7 @@ pcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch) struct pcm_channel *tmp; PCM_BUSYASSERT(d); - snd_mtxassert(d->lock); + PCM_LOCKASSERT(d); tmp = NULL; @@ -734,17 +698,17 @@ pcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo) PCM_BUSYASSERT(d); - pcm_lock(d); + PCM_LOCK(d); ch = pcm_chn_create(d, NULL, cls, dir, -1, devinfo); if (!ch) { device_printf(d->dev, "pcm_chn_create(%s, %d, %p) failed\n", cls->name, dir, devinfo); - pcm_unlock(d); + PCM_UNLOCK(d); return (ENODEV); } err = pcm_chn_add(d, ch); - pcm_unlock(d); + PCM_UNLOCK(d); if (err) { device_printf(d->dev, "pcm_chn_add(%s) failed, err=%d\n", ch->name, err); @@ -765,9 +729,9 @@ pcm_killchan(device_t dev) ch = CHN_FIRST(d, channels.pcm); - pcm_lock(d); + PCM_LOCK(d); error = pcm_chn_remove(d, ch); - pcm_unlock(d); + PCM_UNLOCK(d); if (error) return (error); return (pcm_chn_destroy(ch)); @@ -793,7 +757,7 @@ pcm_setstatus(device_t dev, char *str) strlcpy(d->status, str, SND_STATUSLEN); - pcm_lock(d); + PCM_LOCK(d); /* Last stage, enable cloning. */ if (d->clones != NULL) @@ -804,7 +768,7 @@ pcm_setstatus(device_t dev, char *str) PCM_RELEASE(d); - pcm_unlock(d); + PCM_UNLOCK(d); if (snd_unit < 0 || snd_unit_auto != 0) snd_unit = device_get_unit(dev); @@ -866,7 +830,44 @@ pcm_getbuffersize(device_t dev, unsigned int minbufsz, unsigned int deflt, unsig return sz; } -#if defined(SND_DYNSYSCTL) && defined(SND_DEBUG) +static int +sysctl_dev_pcm_bitperfect(SYSCTL_HANDLER_ARGS) +{ + struct snddev_info *d; + int err, val; + + d = oidp->oid_arg1; + if (!PCM_REGISTERED(d)) + return (ENODEV); + + PCM_LOCK(d); + PCM_WAIT(d); + val = (d->flags & SD_F_BITPERFECT) ? 1 : 0; + PCM_ACQUIRE(d); + PCM_UNLOCK(d); + + err = sysctl_handle_int(oidp, &val, 0, req); + + if (err == 0 && req->newptr != NULL) { + if (!(val == 0 || val == 1)) { + PCM_RELEASE_QUICK(d); + return (EINVAL); + } + + PCM_LOCK(d); + + d->flags &= ~SD_F_BITPERFECT; + d->flags |= (val != 0) ? SD_F_BITPERFECT : 0; + + PCM_RELEASE(d); + PCM_UNLOCK(d); + } else + PCM_RELEASE_QUICK(d); + + return (err); +} + +#ifdef SND_DEBUG static int sysctl_dev_pcm_clone_flags(SYSCTL_HANDLER_ARGS) { @@ -981,6 +982,7 @@ int pcm_register(device_t dev, void *devinfo, int numplay, int numrec) { 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); @@ -1010,6 +1012,15 @@ pcm_register(device_t dev, void *devinfo, int numplay, int numrec) */ d->flags = 0; #endif + i = 0; + if (resource_int_value(device_get_name(dev), device_get_unit(dev), + "vpc", &i) != 0 || i != 0) + d->flags |= SD_F_VPC; + + if (resource_int_value(device_get_name(dev), device_get_unit(dev), + "bitperfect", &i) == 0 && i != 0) + d->flags |= SD_F_BITPERFECT; + d->devinfo = devinfo; d->devcount = 0; d->reccount = 0; @@ -1024,7 +1035,7 @@ pcm_register(device_t dev, void *devinfo, int numplay, int numrec) /* * Create clone manager, disabled by default. Cloning will be - * enabled during final stage of driver iniialization through + * enabled during final stage of driver initialization through * pcm_setstatus(). */ d->clones = snd_clone_create(SND_U_MASK | SND_D_MASK, PCMMAXCLONE, @@ -1041,15 +1052,12 @@ pcm_register(device_t dev, void *devinfo, int numplay, int numrec) CHN_INIT(d, channels.pcm); CHN_INIT(d, channels.pcm.busy); + CHN_INIT(d, channels.pcm.opened); /* XXX This is incorrect, but lets play along for now. */ if ((numplay == 0 || numrec == 0) && numplay != numrec) d->flags |= SD_F_SIMPLEX; - d->fakechan = fkchan_setup(dev); - chn_init(d->fakechan, NULL, 0, 0); - -#ifdef SND_DYNSYSCTL 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", @@ -1063,6 +1071,11 @@ pcm_register(device_t dev, void *devinfo, int numplay, int numrec) SYSCTL_ADD_INT(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_RW, d, sizeof(d), + sysctl_dev_pcm_bitperfect, "I", + "bit-perfect playback/recording (0=disable, 1=enable)"); #ifdef SND_DEBUG SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, @@ -1080,13 +1093,15 @@ pcm_register(device_t dev, void *devinfo, int numplay, int numrec) sysctl_dev_pcm_clone_gc, "I", "clone garbage collector"); #endif -#endif if (numplay > 0 || numrec > 0) { d->flags |= SD_F_AUTOVCHAN; vchan_initsys(dev); } + if (d->flags & SD_F_EQ) + feeder_eq_initsys(dev); + sndstat_register(dev, d->status, sndstat_prepare_pcm); return 0; @@ -1113,18 +1128,18 @@ pcm_unregister(device_t dev) return (EBUSY); } - pcm_lock(d); + PCM_LOCK(d); PCM_WAIT(d); if (d->inprog != 0) { device_printf(dev, "unregister: operation in progress\n"); - pcm_unlock(d); + PCM_UNLOCK(d); sndstat_release(td); return (EBUSY); } PCM_ACQUIRE(d); - pcm_unlock(d); + PCM_UNLOCK(d); CHN_FOREACH(ch, d, channels.pcm) { CHN_LOCK(ch); @@ -1147,27 +1162,27 @@ pcm_unregister(device_t dev) sndstat_release(td); return (EBUSY); } else { - pcm_lock(d); + PCM_LOCK(d); (void)snd_clone_disable(d->clones); - pcm_unlock(d); + PCM_UNLOCK(d); } } if (mixer_uninit(dev) == EBUSY) { device_printf(dev, "unregister: mixer busy\n"); - pcm_lock(d); + PCM_LOCK(d); if (d->clones != NULL) (void)snd_clone_enable(d->clones); PCM_RELEASE(d); - pcm_unlock(d); + PCM_UNLOCK(d); sndstat_release(td); return (EBUSY); } - pcm_lock(d); + PCM_LOCK(d); d->flags |= SD_F_DYING; d->flags &= ~SD_F_REGISTERED; - pcm_unlock(d); + PCM_UNLOCK(d); /* * No lock being held, so this thing can be flushed without @@ -1178,7 +1193,6 @@ pcm_unregister(device_t dev) d->clones = NULL; } -#ifdef SND_DYNSYSCTL if (d->play_sysctl_tree != NULL) { sysctl_ctx_free(&d->play_sysctl_ctx); d->play_sysctl_tree = NULL; @@ -1187,20 +1201,16 @@ pcm_unregister(device_t dev) sysctl_ctx_free(&d->rec_sysctl_ctx); d->rec_sysctl_tree = NULL; } -#endif while (!CHN_EMPTY(d, channels.pcm)) pcm_killchan(dev); - chn_kill(d->fakechan); - fkchan_kill(d->fakechan); - dsp_cdevinfo_flush(d); - pcm_lock(d); + PCM_LOCK(d); PCM_RELEASE(d); cv_destroy(&d->cv); - pcm_unlock(d); + PCM_UNLOCK(d); snd_mtxfree(d->lock); sndstat_unregister(dev); sndstat_release(td); @@ -1228,162 +1238,6 @@ pcm_unregister(device_t dev) /************************************************************************/ -static int -sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose) -{ - struct snddev_info *d; - struct pcm_channel *c; - struct pcm_feeder *f; - - if (verbose < 1) - return 0; - - d = device_get_softc(dev); - if (!d) - return ENXIO; - - PCM_BUSYASSERT(d); - - if (CHN_EMPTY(d, channels.pcm)) { - sbuf_printf(s, " (mixer only)"); - return 0; - } - - sbuf_printf(s, " (%dp:%dv/%dr:%dv channels%s%s)", - d->playcount, d->pvchancount, - d->reccount, d->rvchancount, - (d->flags & SD_F_SIMPLEX)? "" : " duplex", -#ifdef USING_DEVFS - (device_get_unit(dev) == snd_unit)? " default" : "" -#else - "" -#endif - ); - - if (verbose <= 1) - return 0; - - CHN_FOREACH(c, d, channels.pcm) { - - KASSERT(c->bufhard != NULL && c->bufsoft != NULL, - ("hosed pcm channel setup")); - - sbuf_printf(s, "\n\t"); - - /* it would be better to indent child channels */ - sbuf_printf(s, "%s[%s]: ", c->parentchannel? c->parentchannel->name : "", c->name); - sbuf_printf(s, "spd %d", c->speed); - if (c->speed != sndbuf_getspd(c->bufhard)) - sbuf_printf(s, "/%d", sndbuf_getspd(c->bufhard)); - sbuf_printf(s, ", fmt 0x%08x", c->format); - if (c->format != sndbuf_getfmt(c->bufhard)) - sbuf_printf(s, "/0x%08x", sndbuf_getfmt(c->bufhard)); - sbuf_printf(s, ", flags 0x%08x, 0x%08x", c->flags, c->feederflags); - if (c->pid != -1) - sbuf_printf(s, ", pid %d", c->pid); - sbuf_printf(s, "\n\t"); - - sbuf_printf(s, "interrupts %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]", - c->xruns, c->feedcount, sndbuf_getfree(c->bufhard), sndbuf_getfree(c->bufsoft), - sndbuf_getsize(c->bufhard), sndbuf_getblksz(c->bufhard), - sndbuf_getblkcnt(c->bufhard), - sndbuf_getsize(c->bufsoft), sndbuf_getblksz(c->bufsoft), - sndbuf_getblkcnt(c->bufsoft)); - else - sbuf_printf(s, "underruns %d, feed %u, ready %d [b:%d/%d/%d|bs:%d/%d/%d]", - c->xruns, c->feedcount, sndbuf_getready(c->bufsoft), - sndbuf_getsize(c->bufhard), sndbuf_getblksz(c->bufhard), - sndbuf_getblkcnt(c->bufhard), - sndbuf_getsize(c->bufsoft), sndbuf_getblksz(c->bufsoft), - sndbuf_getblkcnt(c->bufsoft)); - sbuf_printf(s, "\n\t"); - - sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "hardware" : "userland"); - sbuf_printf(s, " -> "); - f = c->feeder; - while (f->source != NULL) - f = f->source; - while (f != NULL) { - sbuf_printf(s, "%s", f->class->name); - if (f->desc->type == FEEDER_FMT) - sbuf_printf(s, "(0x%08x -> 0x%08x)", f->desc->in, f->desc->out); - if (f->desc->type == FEEDER_RATE) - sbuf_printf(s, "(%d -> %d)", FEEDER_GET(f, FEEDRATE_SRC), FEEDER_GET(f, FEEDRATE_DST)); - if (f->desc->type == FEEDER_ROOT || f->desc->type == FEEDER_MIXER || - f->desc->type == FEEDER_VOLUME) - sbuf_printf(s, "(0x%08x)", f->desc->out); - sbuf_printf(s, " -> "); - f = f->parent; - } - sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "userland" : "hardware"); - } - - return 0; -} - -/************************************************************************/ - -#ifdef SND_DYNSYSCTL -int -sysctl_hw_snd_vchans(SYSCTL_HANDLER_ARGS) -{ - struct snddev_info *d; - int direction, vchancount; - int err, cnt; - - d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1)); - if (!PCM_REGISTERED(d) || !(d->flags & SD_F_AUTOVCHAN)) - return (EINVAL); - - 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; - break; - case VCHAN_REC: - direction = PCMDIR_REC; - vchancount = d->rvchancount; - cnt = d->reccount; - break; - default: - pcm_unlock(d); - return (EINVAL); - break; - } - - if (cnt < 1) { - pcm_unlock(d); - return (ENODEV); - } - - 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); - } - - PCM_RELEASE_QUICK(d); - - return err; -} -#endif - -/************************************************************************/ - /** * @brief Handle OSSv4 SNDCTL_SYSINFO ioctl. * @@ -1438,14 +1292,14 @@ sound_oss_sysinfo(oss_sysinfo *si) /* XXX Need Giant magic entry ??? */ /* See note in function's docblock */ - mtx_assert(d->lock, MA_NOTOWNED); - pcm_lock(d); + PCM_UNLOCKASSERT(d); + PCM_LOCK(d); si->numaudios += d->devcount; ++ncards; CHN_FOREACH(c, d, channels.pcm) { - mtx_assert(c->lock, MA_NOTOWNED); + CHN_UNLOCKASSERT(c); CHN_LOCK(c); if (c->flags & CHN_F_BUSY) si->openedaudio[j / intnbits] |= @@ -1454,7 +1308,7 @@ sound_oss_sysinfo(oss_sysinfo *si) j++; } - pcm_unlock(d); + PCM_UNLOCK(d); } si->numaudioengines = si->numaudios; @@ -1510,8 +1364,8 @@ sound_oss_card_info(oss_card_info *si) if (ncards++ != si->card) continue; - mtx_assert(d->lock, MA_NOTOWNED); - pcm_lock(d); + PCM_UNLOCKASSERT(d); + PCM_LOCK(d); strlcpy(si->shortname, device_get_nameunit(d->dev), sizeof(si->shortname)); @@ -1519,7 +1373,9 @@ sound_oss_card_info(oss_card_info *si) sizeof(si->longname)); strlcpy(si->hw_info, d->status, sizeof(si->hw_info)); si->intr_count = si->ack_count = 0; - pcm_unlock(d); + + PCM_UNLOCK(d); + return (0); } return (ENXIO); @@ -1551,7 +1407,7 @@ sound_modevent(module_t mod, int type, void *data) } break; default: - ret = EOPNOTSUPP; + ret = ENOTSUP; } return ret; diff --git a/sys/dev/sound/pcm/sound.h b/sys/dev/sound/pcm/sound.h index ebc91cb..6a085aa 100644 --- a/sys/dev/sound/pcm/sound.h +++ b/sys/dev/sound/pcm/sound.h @@ -1,5 +1,6 @@ /*- - * Copyright (c) 1999 Cameron Grant <cg@freebsd.org> + * Copyright (c) 2005-2009 Ariff Abdullah <ariff@FreeBSD.org> + * Copyright (c) 1999 Cameron Grant <cg@FreeBSD.org> * Copyright (c) 1995 Hannu Savolainen * All rights reserved. * @@ -65,32 +66,32 @@ #include <sys/soundcard.h> #include <sys/sysctl.h> #include <sys/kobj.h> +#ifdef SND_DEBUG +#undef KOBJMETHOD +#define KOBJMETHOD(NAME, FUNC) \ + { \ + &NAME##_desc, \ + (kobjop_t) ((FUNC != (NAME##_t *)NULL) ? FUNC : NULL) \ + } +#endif +#ifndef KOBJMETHOD_END +#define KOBJMETHOD_END { NULL, NULL } +#endif #include <vm/vm.h> #include <vm/pmap.h> -#undef USING_MUTEX -#undef USING_DEVFS - -#if __FreeBSD_version > 500000 #include <sys/lock.h> #include <sys/mutex.h> #include <sys/condvar.h> -#define USING_MUTEX -#define USING_DEVFS -#else -#define INTR_TYPE_AV INTR_TYPE_TTY -#define INTR_MPSAFE 0 -#endif - -#define SND_DYNSYSCTL - struct pcm_channel; struct pcm_feeder; struct snd_dbuf; 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> @@ -98,11 +99,11 @@ struct snd_mixer; #include <dev/sound/clone.h> #include <dev/sound/unit.h> -#define PCM_SOFTC_SIZE 512 +#define PCM_SOFTC_SIZE (sizeof(struct snddev_info)) #define SND_STATUSLEN 64 -#define SOUND_MODVER 2 +#define SOUND_MODVER 5 #define SOUND_MINVER SOUND_MODVER #define SOUND_PREFVER SOUND_MODVER @@ -125,6 +126,13 @@ struct snd_mixer; #define PCMDEV(x) (snd_unit2d(dev2unit(x))) #define PCMCHAN(x) (snd_unit2c(dev2unit(x))) +/* XXX unit2minor compat */ +#if __FreeBSD_version >= 800062 +#define PCMMINOR(x) (x) +#else +#define PCMMINOR(x) unit2minor(x) +#endif + /* * By design, limit possible channels for each direction. */ @@ -134,13 +142,28 @@ struct snd_mixer; #define SD_F_SIMPLEX 0x00000001 #define SD_F_AUTOVCHAN 0x00000002 #define SD_F_SOFTPCMVOL 0x00000004 +/* + * Obsolete due to better matrixing + */ +#if 0 #define SD_F_PSWAPLR 0x00000008 #define SD_F_RSWAPLR 0x00000010 -#define SD_F_DYING 0x00000020 -#define SD_F_SUICIDE 0x00000040 -#define SD_F_BUSY 0x00000080 -#define SD_F_MPSAFE 0x00000100 -#define SD_F_REGISTERED 0x00000200 +#endif +#define SD_F_DYING 0x00000008 +#define SD_F_SUICIDE 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_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 @@ -148,6 +171,25 @@ struct snd_mixer; #define SD_F_DIR_SET 0x40000000 #define SD_F_TRANSIENT 0xf0000000 +#define SD_F_BITS "\020" \ + "\001SIMPLEX" \ + "\002AUTOVCHAN" \ + "\003SOFTPCMVOL" \ + "\004DYING" \ + "\005SUICIDE" \ + "\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) && \ @@ -158,293 +200,97 @@ struct snd_mixer; (((var)<(low))? (low) : ((var)>(high))? (high) : (var)) #define DSP_BUFFSIZE (8192) -/* - * 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 PCM_USE_64BIT_ARITH is defined. - */ +/* 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 /* - * Automatically turn on 64bit arithmetic on suitable archs - * (amd64 64bit, ia64, etc..) for wider 32bit samples / integer processing. + * We're simply using unused, contiguous bits from various AFMT_ definitions. + * ~(0xb00ff7ff) */ -#if LONG_BIT >= 64 -#undef PCM_USE_64BIT_ARITH -#define PCM_USE_64BIT_ARITH 1 -#else -#if 0 -#undef PCM_USE_64BIT_ARITH -#define PCM_USE_64BIT_ARITH 1 -#endif -#endif +#define AFMT_ENCODING_MASK 0xf00fffff +#define AFMT_CHANNEL_MASK 0x01f00000 +#define AFMT_CHANNEL_SHIFT 20 +#define AFMT_EXTCHANNEL_MASK 0x0e000000 +#define AFMT_EXTCHANNEL_SHIFT 25 -#ifdef PCM_USE_64BIT_ARITH -typedef int64_t intpcm_t; -#else -typedef int32_t intpcm_t; -#endif +#define AFMT_ENCODING(v) ((v) & AFMT_ENCODING_MASK) -/* 32bit fixed point shift */ -#define PCM_FXSHIFT 8 - -#define PCM_S8_MAX 0x7f -#define PCM_S8_MIN -0x80 -#define PCM_S16_MAX 0x7fff -#define PCM_S16_MIN -0x8000 -#define PCM_S24_MAX 0x7fffff -#define PCM_S24_MIN -0x800000 -#ifdef PCM_USE_64BIT_ARITH -#if LONG_BIT >= 64 -#define PCM_S32_MAX 0x7fffffffL -#define PCM_S32_MIN -0x80000000L -#else -#define PCM_S32_MAX 0x7fffffffLL -#define PCM_S32_MIN -0x80000000LL -#endif -#else -#define PCM_S32_MAX 0x7fffffff -#define PCM_S32_MIN (-0x7fffffff - 1) -#endif +#define AFMT_EXTCHANNEL(v) (((v) & AFMT_EXTCHANNEL_MASK) >> \ + AFMT_EXTCHANNEL_SHIFT) -/* Bytes-per-sample definition */ -#define PCM_8_BPS 1 -#define PCM_16_BPS 2 -#define PCM_24_BPS 3 -#define PCM_32_BPS 4 +#define AFMT_CHANNEL(v) (((v) & AFMT_CHANNEL_MASK) >> \ + AFMT_CHANNEL_SHIFT) -#if BYTE_ORDER == LITTLE_ENDIAN -#define PCM_READ_S16_LE(b8) *((int16_t *)(b8)) -#define _PCM_READ_S32_LE(b8) *((int32_t *)(b8)) -#define PCM_READ_S16_BE(b8) \ - ((int32_t)((b8)[1] | ((int8_t)((b8)[0])) << 8)) -#define _PCM_READ_S32_BE(b8) \ - ((int32_t)((b8)[3] | (b8)[2] << 8 | (b8)[1] << 16 | \ - ((int8_t)((b8)[0])) << 24)) - -#define PCM_WRITE_S16_LE(b8, val) *((int16_t *)(b8)) = (val) -#define _PCM_WRITE_S32_LE(b8, val) *((int32_t *)(b8)) = (val) -#define PCM_WRITE_S16_BE(bb8, vval) do { \ - int32_t val = (vval); \ - uint8_t *b8 = (bb8); \ - b8[1] = val; \ - b8[0] = val >> 8; \ - } while(0) -#define _PCM_WRITE_S32_BE(bb8, vval) do { \ - int32_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) ((int16_t)(*((uint16_t *)(b8)) ^ 0x8000)) -#define _PCM_READ_U32_LE(b8) ((int32_t)(*((uint32_t *)(b8)) ^ 0x80000000)) -#define PCM_READ_U16_BE(b8) \ - ((int32_t)((b8)[1] | ((int8_t)((b8)[0] ^ 0x80)) << 8)) -#define _PCM_READ_U32_BE(b8) \ - ((int32_t)((b8)[3] | (b8)[2] << 8 | (b8)[1] << 16 | \ - ((int8_t)((b8)[0] ^ 0x80)) << 24)) - -#define PCM_WRITE_U16_LE(b8, val) *((uint16_t *)(b8)) = (val) ^ 0x8000 -#define _PCM_WRITE_U32_LE(b8, val) *((uint32_t *)(b8)) = (val) ^ 0x80000000 -#define PCM_WRITE_U16_BE(bb8, vval) do { \ - int32_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 { \ - int32_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) -#else /* !LITTLE_ENDIAN */ -#define PCM_READ_S16_LE(b8) \ - ((int32_t)((b8)[0] | ((int8_t)((b8)[1])) << 8)) -#define _PCM_READ_S32_LE(b8) \ - ((int32_t)((b8)[0] | (b8)[1] << 8 | (b8)[2] << 16 | \ - ((int8_t)((b8)[3])) << 24)) -#define PCM_READ_S16_BE(b8) *((int16_t *)(b8)) -#define _PCM_READ_S32_BE(b8) *((int32_t *)(b8)) - -#define PCM_WRITE_S16_LE(bb8, vval) do { \ - int32_t val = (vval); \ - uint8_t *b8 = (bb8); \ - b8[0] = val; \ - b8[1] = val >> 8; \ - } while(0) -#define _PCM_WRITE_S32_LE(bb8, vval) do { \ - int32_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) *((int16_t *)(b8)) = (val) -#define _PCM_WRITE_S32_BE(b8, val) *((int32_t *)(b8)) = (val) - -#define PCM_READ_U16_LE(b8) \ - ((int32_t)((b8)[0] | ((int8_t)((b8)[1] ^ 0x80)) << 8)) -#define _PCM_READ_U32_LE(b8) \ - ((int32_t)((b8)[0] | (b8)[1] << 8 | (b8)[2] << 16 | \ - ((int8_t)((b8)[3] ^ 0x80)) << 24)) -#define PCM_READ_U16_BE(b8) ((int16_t)(*((uint16_t *)(b8)) ^ 0x8000)) -#define _PCM_READ_U32_BE(b8) ((int32_t)(*((uint32_t *)(b8)) ^ 0x80000000)) - -#define PCM_WRITE_U16_LE(bb8, vval) do { \ - int32_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 { \ - int32_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) *((uint16_t *)(b8)) = (val) ^ 0x8000 -#define _PCM_WRITE_U32_BE(b8, val) *((uint32_t *)(b8)) = (val) ^ 0x80000000 -#endif +#define AFMT_BIT(v) (((v) & AFMT_32BIT) ? 32 : \ + (((v) & AFMT_24BIT) ? 24 : \ + ((((v) & AFMT_16BIT) || \ + ((v) & AFMT_PASSTHROUGH)) ? 16 : 8))) -#define PCM_READ_S24_LE(b8) \ - ((int32_t)((b8)[0] | (b8)[1] << 8 | ((int8_t)((b8)[2])) << 16)) -#define PCM_READ_S24_BE(b8) \ - ((int32_t)((b8)[2] | (b8)[1] << 8 | ((int8_t)((b8)[0])) << 16)) - -#define PCM_WRITE_S24_LE(bb8, vval) do { \ - int32_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 { \ - int32_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) \ - ((int32_t)((b8)[0] | (b8)[1] << 8 | \ - ((int8_t)((b8)[2] ^ 0x80)) << 16)) -#define PCM_READ_U24_BE(b8) \ - ((int32_t)((b8)[2] | (b8)[1] << 8 | \ - ((int8_t)((b8)[0] ^ 0x80)) << 16)) - -#define PCM_WRITE_U24_LE(bb8, vval) do { \ - int32_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 { \ - int32_t val = (vval); \ - uint8_t *b8 = (bb8); \ - b8[2] = val; \ - b8[1] = val >> 8; \ - b8[0] = (val >> 16) ^ 0x80; \ - } while(0) - -#ifdef PCM_USE_64BIT_ARITH -#define PCM_READ_S32_LE(b8) _PCM_READ_S32_LE(b8) -#define PCM_READ_S32_BE(b8) _PCM_READ_S32_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_READ_U32_LE(b8) _PCM_READ_U32_LE(b8) -#define PCM_READ_U32_BE(b8) _PCM_READ_U32_BE(b8) -#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) -#else /* !PCM_USE_64BIT_ARITH */ -/* - * 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. - */ -#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_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_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_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) -#endif +#define AFMT_BPS(v) (AFMT_BIT(v) >> 3) +#define AFMT_ALIGN(v) (AFMT_BPS(v) * AFMT_CHANNEL(v)) -/* - * 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. - */ -#define PCM_READ_S8(b8) *((int8_t *)(b8)) -#define PCM_READ_S8_NE(b8) PCM_READ_S8(b8) -#define PCM_READ_U8(b8) ((int8_t)(*((uint8_t *)(b8)) ^ 0x80)) -#define PCM_READ_U8_NE(b8) PCM_READ_U8(b8) - -#define PCM_WRITE_S8(b8, val) *((int8_t *)(b8)) = (val) -#define PCM_WRITE_S8_NE(b8, val) PCM_WRITE_S8(b8, val) -#define PCM_WRITE_U8(b8, val) *((uint8_t *)(b8)) = (val) ^ 0x80 -#define PCM_WRITE_U8_NE(b8, val) PCM_WRITE_U8(b8, val) - -#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))) - -#ifdef PCM_USE_64BIT_ARITH -#define PCM_CLAMP_S32(val) \ - (((val) > PCM_S32_MAX) ? PCM_S32_MAX : \ - (((val) < PCM_S32_MIN) ? PCM_S32_MIN : (val))) +#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 + +#undef AFMT_S16_NE + +#if BYTE_ORDER == LITTLE_ENDIAN +#define AFMT_S16_NE AFMT_S16_LE +#define AFMT_S24_NE AFMT_S24_LE +#define AFMT_S32_NE AFMT_S32_LE +#define AFMT_U16_NE AFMT_U16_LE +#define AFMT_U24_NE AFMT_U24_LE +#define AFMT_U32_NE AFMT_U32_LE +#define AFMT_S16_OE AFMT_S16_BE +#define AFMT_S24_OE AFMT_S24_BE +#define AFMT_S32_OE AFMT_S32_BE +#define AFMT_U16_OE AFMT_U16_BE +#define AFMT_U24_OE AFMT_U24_BE +#define AFMT_U32_OE AFMT_U32_BE #else -#define PCM_CLAMP_S32(val) \ - (((val) > PCM_S24_MAX) ? PCM_S32_MAX : \ - (((val) < PCM_S24_MIN) ? PCM_S32_MIN : \ - ((val) << PCM_FXSHIFT))) +#define AFMT_S16_OE AFMT_S16_LE +#define AFMT_S24_OE AFMT_S24_LE +#define AFMT_S32_OE AFMT_S32_LE +#define AFMT_U16_OE AFMT_U16_LE +#define AFMT_U24_OE AFMT_U24_LE +#define AFMT_U32_OE AFMT_U32_LE +#define AFMT_S16_NE AFMT_S16_BE +#define AFMT_S24_NE AFMT_S24_BE +#define AFMT_S32_NE AFMT_S32_BE +#define AFMT_U16_NE AFMT_U16_BE +#define AFMT_U24_NE AFMT_U24_BE +#define AFMT_U32_NE AFMT_U32_BE #endif -#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) +#define AFMT_SIGNED_NE (AFMT_S8_NE | AFMT_S16_NE | AFMT_S24_NE | AFMT_S32_NE) -/* 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_8BIT (AFMT_MU_LAW | AFMT_A_LAW | 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) - -struct pcm_channel *fkchan_setup(device_t dev); -int fkchan_kill(struct pcm_channel *c); +#define AFMT_NE (AFMT_SIGNED_NE | AFMT_U8_NE | AFMT_U16_NE | \ + AFMT_U24_NE | AFMT_U32_NE) /* * Minor numbers for the sound driver. @@ -475,9 +321,18 @@ int fkchan_kill(struct pcm_channel *c); #define SND_DEV_DSPHW_CD 15 /* s16le/stereo 44100Hz CD */ -#define SND_DEV_DSP_MMAP 16 /* OSSv4 compatible /dev/dsp_mmap */ +/* + * 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 */ -#define SND_DEV_LAST SND_DEV_DSP_MMAP +#define SND_DEV_LAST SND_DEV_DSP_SPDIFIN #define SND_DEV_MAX PCMMAXDEV #define DSP_DEFAULT_SPEED 8000 @@ -505,8 +360,9 @@ extern struct unrhdr *pcmsg_unrhdr; SYSCTL_DECL(_hw_snd); -struct pcm_channel *pcm_getfakechan(struct snddev_info *d); -int pcm_chnalloc(struct snddev_info *d, struct pcm_channel **ch, int direction, pid_t pid, int devunit); +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, int devunit); int pcm_chnrelease(struct pcm_channel *c); int pcm_chnref(struct pcm_channel *c, int ref); int pcm_inprog(struct snddev_info *d, int delta); @@ -535,8 +391,6 @@ void snd_mtxassert(void *m); #define snd_mtxlock(m) mtx_lock(m) #define snd_mtxunlock(m) mtx_unlock(m) -int sysctl_hw_snd_vchans(SYSCTL_HANDLER_ARGS); - typedef int (*sndstat_handler)(struct sbuf *s, device_t dev, int verbose); int sndstat_acquire(struct thread *td); int sndstat_release(struct thread *td); @@ -564,8 +418,6 @@ int sndstat_unregisterfile(char *str); #define DV_F_DEV_MASK 0x0000ff00 /* force device type/class */ #define DV_F_DEV_SHIFT 8 /* force device type/class */ -#define PCM_DEBUG_MTX - /* * 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. @@ -579,11 +431,13 @@ struct snddev_info { struct { SLIST_HEAD(, pcm_channel) head; } busy; + struct { + SLIST_HEAD(, pcm_channel) head; + } opened; } pcm; } channels; TAILQ_HEAD(dsp_cdevinfo_linkhead, dsp_cdevinfo) dsp_cdevinfo_pool; struct snd_clone *clones; - struct pcm_channel *fakechan; unsigned devcount, playcount, reccount, pvchancount, rvchancount ; unsigned flags; int inprog; @@ -595,6 +449,7 @@ struct snddev_info { struct cdev *mixer_dev; uint32_t pvchanrate, pvchanformat; uint32_t rvchanrate, rvchanformat; + 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; @@ -603,21 +458,20 @@ struct snddev_info { void sound_oss_sysinfo(oss_sysinfo *); int sound_oss_card_info(oss_card_info *); -#ifdef PCM_DEBUG_MTX -#define pcm_lock(d) mtx_lock(((struct snddev_info *)(d))->lock) -#define pcm_unlock(d) mtx_unlock(((struct snddev_info *)(d))->lock) -#else -void pcm_lock(struct snddev_info *d); -void pcm_unlock(struct snddev_info *d); -#endif +#define PCM_LOCKOWNED(d) mtx_owned((d)->lock) +#define PCM_LOCK(d) mtx_lock((d)->lock) +#define PCM_UNLOCK(d) mtx_unlock((d)->lock) +#define PCM_TRYLOCK(d) mtx_trylock((d)->lock) +#define PCM_LOCKASSERT(d) mtx_assert((d)->lock, MA_OWNED) +#define PCM_UNLOCKASSERT(d) mtx_assert((d)->lock, MA_NOTOWNED) /* - * For PCM_CV_[WAIT | ACQUIRE | RELEASE], be sure to surround these - * with pcm_lock/unlock() sequence, or I'll come to gnaw upon you! + * For PCM_[WAIT | ACQUIRE | RELEASE], be sure to surround these + * with PCM_LOCK/UNLOCK() sequence, or I'll come to gnaw upon you! */ #ifdef SND_DIAGNOSTIC #define PCM_WAIT(x) do { \ - if (mtx_owned((x)->lock) == 0) \ + if (!PCM_LOCKOWNED(x)) \ panic("%s(%d): [PCM WAIT] Mutex not owned!", \ __func__, __LINE__); \ while ((x)->flags & SD_F_BUSY) { \ @@ -627,20 +481,20 @@ void pcm_unlock(struct snddev_info *d); __func__, __LINE__); \ cv_wait(&(x)->cv, (x)->lock); \ } \ -} while(0) +} while (0) #define PCM_ACQUIRE(x) do { \ - if (mtx_owned((x)->lock) == 0) \ + if (!PCM_LOCKOWNED(x)) \ panic("%s(%d): [PCM ACQUIRE] Mutex not owned!", \ __func__, __LINE__); \ if ((x)->flags & SD_F_BUSY) \ panic("%s(%d): [PCM ACQUIRE] " \ "Trying to acquire BUSY cv!", __func__, __LINE__); \ (x)->flags |= SD_F_BUSY; \ -} while(0) +} while (0) #define PCM_RELEASE(x) do { \ - if (mtx_owned((x)->lock) == 0) \ + if (!PCM_LOCKOWNED(x)) \ panic("%s(%d): [PCM RELEASE] Mutex not owned!", \ __func__, __LINE__); \ if ((x)->flags & SD_F_BUSY) { \ @@ -657,37 +511,37 @@ void pcm_unlock(struct snddev_info *d); } else \ panic("%s(%d): [PCM RELEASE] Releasing non-BUSY cv!", \ __func__, __LINE__); \ -} while(0) +} while (0) /* Quick version, for shorter path. */ #define PCM_ACQUIRE_QUICK(x) do { \ - if (mtx_owned((x)->lock) != 0) \ + if (PCM_LOCKOWNED(x)) \ panic("%s(%d): [PCM ACQUIRE QUICK] Mutex owned!", \ __func__, __LINE__); \ - pcm_lock(x); \ + PCM_LOCK(x); \ PCM_WAIT(x); \ PCM_ACQUIRE(x); \ - pcm_unlock(x); \ -} while(0) + PCM_UNLOCK(x); \ +} while (0) #define PCM_RELEASE_QUICK(x) do { \ - if (mtx_owned((x)->lock) != 0) \ + if (PCM_LOCKOWNED(x)) \ panic("%s(%d): [PCM RELEASE QUICK] Mutex owned!", \ __func__, __LINE__); \ - pcm_lock(x); \ + PCM_LOCK(x); \ PCM_RELEASE(x); \ - pcm_unlock(x); \ -} while(0) + PCM_UNLOCK(x); \ +} while (0) #define PCM_BUSYASSERT(x) do { \ if (!((x) != NULL && ((x)->flags & SD_F_BUSY))) \ panic("%s(%d): [PCM BUSYASSERT] " \ "Failed, snddev_info=%p", __func__, __LINE__, x); \ -} while(0) +} while (0) #define PCM_GIANT_ENTER(x) do { \ int _pcm_giant = 0; \ - if (mtx_owned((x)->lock) != 0) \ + if (PCM_LOCKOWNED(x)) \ panic("%s(%d): [GIANT ENTER] PCM lock owned!", \ __func__, __LINE__); \ if (mtx_owned(&Giant) != 0 && snd_verbose > 3) \ @@ -698,10 +552,10 @@ void pcm_unlock(struct snddev_info *d); do { \ mtx_lock(&Giant); \ _pcm_giant = 1; \ - } while(0) + } while (0) #define PCM_GIANT_EXIT(x) do { \ - if (mtx_owned((x)->lock) != 0) \ + if (PCM_LOCKOWNED(x)) \ panic("%s(%d): [GIANT EXIT] PCM lock owned!", \ __func__, __LINE__); \ if (!(_pcm_giant == 0 || _pcm_giant == 1)) \ @@ -723,47 +577,47 @@ void pcm_unlock(struct snddev_info *d); _pcm_giant = 0; \ mtx_unlock(&Giant); \ } \ -} while(0) +} while (0) #else /* SND_DIAGNOSTIC */ #define PCM_WAIT(x) do { \ - mtx_assert((x)->lock, MA_OWNED); \ + PCM_LOCKASSERT(x); \ while ((x)->flags & SD_F_BUSY) \ cv_wait(&(x)->cv, (x)->lock); \ -} while(0) +} while (0) #define PCM_ACQUIRE(x) do { \ - mtx_assert((x)->lock, MA_OWNED); \ + PCM_LOCKASSERT(x); \ KASSERT(!((x)->flags & SD_F_BUSY), \ ("%s(%d): [PCM ACQUIRE] Trying to acquire BUSY cv!", \ __func__, __LINE__)); \ (x)->flags |= SD_F_BUSY; \ -} while(0) +} while (0) #define PCM_RELEASE(x) do { \ - mtx_assert((x)->lock, MA_OWNED); \ + PCM_LOCKASSERT(x); \ KASSERT((x)->flags & SD_F_BUSY, \ ("%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); \ -} while(0) +} while (0) /* Quick version, for shorter path. */ #define PCM_ACQUIRE_QUICK(x) do { \ - mtx_assert((x)->lock, MA_NOTOWNED); \ - pcm_lock(x); \ + PCM_UNLOCKASSERT(x); \ + PCM_LOCK(x); \ PCM_WAIT(x); \ PCM_ACQUIRE(x); \ - pcm_unlock(x); \ -} while(0) + PCM_UNLOCK(x); \ +} while (0) #define PCM_RELEASE_QUICK(x) do { \ - mtx_assert((x)->lock, MA_NOTOWNED); \ - pcm_lock(x); \ + PCM_UNLOCKASSERT(x); \ + PCM_LOCK(x); \ PCM_RELEASE(x); \ - pcm_unlock(x); \ -} while(0) + PCM_UNLOCK(x); \ +} while (0) #define PCM_BUSYASSERT(x) KASSERT(x != NULL && \ ((x)->flags & SD_F_BUSY), \ @@ -773,15 +627,15 @@ void pcm_unlock(struct snddev_info *d); #define PCM_GIANT_ENTER(x) do { \ int _pcm_giant = 0; \ - mtx_assert((x)->lock, MA_NOTOWNED); \ + PCM_UNLOCKASSERT(x); \ if (!((x)->flags & SD_F_MPSAFE) && mtx_owned(&Giant) == 0) \ do { \ mtx_lock(&Giant); \ _pcm_giant = 1; \ - } while(0) + } while (0) #define PCM_GIANT_EXIT(x) do { \ - mtx_assert((x)->lock, MA_NOTOWNED); \ + PCM_UNLOCKASSERT(x); \ KASSERT(_pcm_giant == 0 || _pcm_giant == 1, \ ("%s(%d): [GIANT EXIT] _pcm_giant screwed!", \ __func__, __LINE__)); \ @@ -794,12 +648,12 @@ void pcm_unlock(struct snddev_info *d); _pcm_giant = 0; \ mtx_unlock(&Giant); \ } \ -} while(0) +} while (0) #endif /* !SND_DIAGNOSTIC */ #define PCM_GIANT_LEAVE(x) \ PCM_GIANT_EXIT(x); \ -} while(0) +} while (0) #ifdef KLD_MODULE #define PCM_KLDSTRING(a) ("kld " # a) diff --git a/sys/dev/sound/pcm/vchan.c b/sys/dev/sound/pcm/vchan.c index d881654..cba9607 100644 --- a/sys/dev/sound/pcm/vchan.c +++ b/sys/dev/sound/pcm/vchan.c @@ -1,6 +1,6 @@ /*- + * Copyright (c) 2006-2009 Ariff Abdullah <ariff@FreeBSD.org> * Copyright (c) 2001 Cameron Grant <cg@FreeBSD.org> - * Copyright (c) 2006 Ariff Abdullah <ariff@FreeBSD.org> * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -27,383 +27,79 @@ /* Almost entirely rewritten to add multi-format/channels mixing support. */ +#ifdef HAVE_KERNEL_OPTION_HEADERS +#include "opt_snd.h" +#endif + #include <dev/sound/pcm/sound.h> #include <dev/sound/pcm/vchan.h> -#include "feeder_if.h" SND_DECLARE_FILE("$FreeBSD$"); -MALLOC_DEFINE(M_VCHANFEEDER, "vchanfeed", "pcm vchan feeder"); - -typedef uint32_t (*feed_vchan_mixer)(uint8_t *, uint8_t *, uint32_t); - -struct vchinfo { - struct pcm_channel *channel; - struct pcmchan_caps caps; - uint32_t fmtlist[2]; - int trigger; -}; - -/* support everything (mono / stereo), except a-law / mu-law */ -static struct afmtstr_table vchan_supported_fmts[] = { - { "u8", AFMT_U8 }, { "s8", AFMT_S8 }, - { "s16le", AFMT_S16_LE }, { "s16be", AFMT_S16_BE }, - { "u16le", AFMT_U16_LE }, { "u16be", AFMT_U16_BE }, - { "s24le", AFMT_S24_LE }, { "s24be", AFMT_S24_BE }, - { "u24le", AFMT_U24_LE }, { "u24be", AFMT_U24_BE }, - { "s32le", AFMT_S32_LE }, { "s32be", AFMT_S32_BE }, - { "u32le", AFMT_U32_LE }, { "u32be", AFMT_U32_BE }, - { NULL, 0 }, -}; - -/* alias table, shorter. */ -static const struct { - char *alias, *fmtstr; -} vchan_fmtstralias[] = { - { "8", "u8" }, { "16", "s16le" }, - { "24", "s24le" }, { "32", "s32le" }, - { NULL, NULL }, -}; - -#define vchan_valid_format(fmt) \ - afmt2afmtstr(vchan_supported_fmts, fmt, NULL, 0, 0, \ - AFMTSTR_STEREO_RETURN) -#define vchan_valid_strformat(strfmt) \ - afmtstr2afmt(vchan_supported_fmts, strfmt, AFMTSTR_STEREO_RETURN); - /* - * Need specialized WRITE macros since 32bit might involved saturation - * if calculation is done within 32bit arithmetic. + * [ac3 , dts , linear , 0, linear, 0] */ -#define VCHAN_PCM_WRITE_S8_NE(b8, val) PCM_WRITE_S8(b8, val) -#define VCHAN_PCM_WRITE_S16_LE(b8, val) PCM_WRITE_S16_LE(b8, val) -#define VCHAN_PCM_WRITE_S24_LE(b8, val) PCM_WRITE_S24_LE(b8, val) -#define VCHAN_PCM_WRITE_S32_LE(b8, val) _PCM_WRITE_S32_LE(b8, val) -#define VCHAN_PCM_WRITE_S16_BE(b8, val) PCM_WRITE_S16_BE(b8, val) -#define VCHAN_PCM_WRITE_S24_BE(b8, val) PCM_WRITE_S24_BE(b8, val) -#define VCHAN_PCM_WRITE_S32_BE(b8, val) _PCM_WRITE_S32_BE(b8, val) -#define VCHAN_PCM_WRITE_U8_NE(b8, val) PCM_WRITE_U8(b8, val) -#define VCHAN_PCM_WRITE_U16_LE(b8, val) PCM_WRITE_U16_LE(b8, val) -#define VCHAN_PCM_WRITE_U24_LE(b8, val) PCM_WRITE_U24_LE(b8, val) -#define VCHAN_PCM_WRITE_U32_LE(b8, val) _PCM_WRITE_U32_LE(b8, val) -#define VCHAN_PCM_WRITE_U16_BE(b8, val) PCM_WRITE_U16_BE(b8, val) -#define VCHAN_PCM_WRITE_U24_BE(b8, val) PCM_WRITE_U24_BE(b8, val) -#define VCHAN_PCM_WRITE_U32_BE(b8, val) _PCM_WRITE_U32_BE(b8, val) - -#define FEEDER_VCHAN_MIX(FMTBIT, VCHAN_INTCAST, SIGN, SIGNS, ENDIAN, ENDIANS) \ -static uint32_t \ -feed_vchan_mix_##SIGNS##FMTBIT##ENDIANS(uint8_t *to, uint8_t *tmp, \ - uint32_t count) \ -{ \ - int32_t x, y; \ - VCHAN_INTCAST z; \ - int i; \ - \ - i = count; \ - tmp += i; \ - to += i; \ - \ - do { \ - tmp -= PCM_##FMTBIT##_BPS; \ - to -= PCM_##FMTBIT##_BPS; \ - i -= PCM_##FMTBIT##_BPS; \ - x = PCM_READ_##SIGN##FMTBIT##_##ENDIAN(tmp); \ - y = PCM_READ_##SIGN##FMTBIT##_##ENDIAN(to); \ - z = (VCHAN_INTCAST)x + y; \ - x = PCM_CLAMP_##SIGN##FMTBIT(z); \ - VCHAN_PCM_WRITE_##SIGN##FMTBIT##_##ENDIAN(to, x); \ - } while (i != 0); \ - \ - return (count); \ -} - -FEEDER_VCHAN_MIX(8, int32_t, S, s, NE, ne) -FEEDER_VCHAN_MIX(16, int32_t, S, s, LE, le) -FEEDER_VCHAN_MIX(24, int32_t, S, s, LE, le) -FEEDER_VCHAN_MIX(32, intpcm_t, S, s, LE, le) -FEEDER_VCHAN_MIX(16, int32_t, S, s, BE, be) -FEEDER_VCHAN_MIX(24, int32_t, S, s, BE, be) -FEEDER_VCHAN_MIX(32, intpcm_t, S, s, BE, be) -FEEDER_VCHAN_MIX(8, int32_t, U, u, NE, ne) -FEEDER_VCHAN_MIX(16, int32_t, U, u, LE, le) -FEEDER_VCHAN_MIX(24, int32_t, U, u, LE, le) -FEEDER_VCHAN_MIX(32, intpcm_t, U, u, LE, le) -FEEDER_VCHAN_MIX(16, int32_t, U, u, BE, be) -FEEDER_VCHAN_MIX(24, int32_t, U, u, BE, be) -FEEDER_VCHAN_MIX(32, intpcm_t, U, u, BE, be) - -struct feed_vchan_info { - uint32_t format; - int bps; - feed_vchan_mixer mix; -}; - -static struct feed_vchan_info feed_vchan_info_tbl[] = { - { AFMT_S8, PCM_8_BPS, feed_vchan_mix_s8ne }, - { AFMT_S16_LE, PCM_16_BPS, feed_vchan_mix_s16le }, - { AFMT_S24_LE, PCM_24_BPS, feed_vchan_mix_s24le }, - { AFMT_S32_LE, PCM_32_BPS, feed_vchan_mix_s32le }, - { AFMT_S16_BE, PCM_16_BPS, feed_vchan_mix_s16be }, - { AFMT_S24_BE, PCM_24_BPS, feed_vchan_mix_s24be }, - { AFMT_S32_BE, PCM_32_BPS, feed_vchan_mix_s32be }, - { AFMT_U8, PCM_8_BPS, feed_vchan_mix_u8ne }, - { AFMT_U16_LE, PCM_16_BPS, feed_vchan_mix_u16le }, - { AFMT_U24_LE, PCM_24_BPS, feed_vchan_mix_u24le }, - { AFMT_U32_LE, PCM_32_BPS, feed_vchan_mix_u32le }, - { AFMT_U16_BE, PCM_16_BPS, feed_vchan_mix_u16be }, - { AFMT_U24_BE, PCM_24_BPS, feed_vchan_mix_u24be }, - { AFMT_U32_BE, PCM_32_BPS, feed_vchan_mix_u32be }, -}; - -#define FVCHAN_DATA(i, c) ((intptr_t)((((i) & 0x1f) << 4) | ((c) & 0xf))) -#define FVCHAN_INFOIDX(m) (((m) >> 4) & 0x1f) -#define FVCHAN_CHANNELS(m) ((m) & 0xf) - -static int -feed_vchan_init(struct pcm_feeder *f) -{ - int i, channels; - - if (f->desc->out != f->desc->in) - return (EINVAL); - - channels = (f->desc->out & AFMT_STEREO) ? 2 : 1; - - for (i = 0; i < sizeof(feed_vchan_info_tbl) / - sizeof(feed_vchan_info_tbl[0]); i++) { - if ((f->desc->out & ~AFMT_STEREO) == - feed_vchan_info_tbl[i].format) { - f->data = (void *)FVCHAN_DATA(i, channels); - return (0); - } - } - - return (-1); -} - -static __inline int -feed_vchan_rec(struct pcm_channel *c) -{ - struct pcm_channel *ch; - struct snd_dbuf *b, *bs; - int cnt, rdy; - - /* - * Reset ready and moving pointer. We're not using bufsoft - * anywhere since its sole purpose is to become the primary - * distributor for the recorded buffer and also as an interrupt - * threshold progress indicator. - */ - b = c->bufsoft; - b->rp = 0; - b->rl = 0; - cnt = sndbuf_getsize(b); - - do { - cnt = FEEDER_FEED(c->feeder->source, c, b->tmpbuf, cnt, - c->bufhard); - if (cnt != 0) { - sndbuf_acquire(b, b->tmpbuf, cnt); - cnt = sndbuf_getfree(b); - } - } while (cnt != 0); - - /* Not enough data */ - if (b->rl < sndbuf_getbps(b)) { - b->rl = 0; - return (0); - } - - /* - * Keep track of ready and moving pointer since we will use - * bufsoft over and over again, pretending nothing has happened. - */ - rdy = b->rl; - - CHN_FOREACH(ch, c, children.busy) { - CHN_LOCK(ch); - if (!(ch->flags & CHN_F_TRIGGERED)) { - CHN_UNLOCK(ch); - continue; - } - bs = ch->bufsoft; - if (ch->flags & CHN_F_MAPPED) - sndbuf_dispose(bs, NULL, sndbuf_getready(bs)); - cnt = sndbuf_getfree(bs); - if (cnt < sndbuf_getbps(bs)) { - CHN_UNLOCK(ch); - continue; - } - do { - cnt = FEEDER_FEED(ch->feeder, ch, bs->tmpbuf, cnt, b); - if (cnt != 0) { - sndbuf_acquire(bs, bs->tmpbuf, cnt); - cnt = sndbuf_getfree(bs); - } - } while (cnt != 0); - /* - * Not entirely flushed out... - */ - if (b->rl != 0) - ch->xruns++; - CHN_UNLOCK(ch); - /* - * Rewind buffer position for next virtual channel. - */ - b->rp = 0; - b->rl = rdy; - } - - /* - * Set ready pointer to indicate that our children are ready - * to be woken up, also as an interrupt threshold progress - * indicator. - */ - b->rl = 1; - - /* - * Return 0 to bail out early from sndbuf_feed() loop. - * No need to increase feedcount counter since part of this - * feeder chains already include feed_root(). - */ - return (0); -} - -static int -feed_vchan(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, - uint32_t count, void *source) -{ - struct feed_vchan_info *info; - struct snd_dbuf *src = source; - struct pcm_channel *ch; - uint32_t cnt, mcnt, rcnt, sz; - uint8_t *tmp; - - if (c->direction == PCMDIR_REC) - return (feed_vchan_rec(c)); - - sz = sndbuf_getsize(src); - if (sz < count) - count = sz; - - info = &feed_vchan_info_tbl[FVCHAN_INFOIDX((intptr_t)f->data)]; - sz = info->bps * FVCHAN_CHANNELS((intptr_t)f->data); - count -= count % sz; - if (count < sz) - return (0); - - /* - * we are going to use our source as a temporary buffer since it's - * got no other purpose. we obtain our data by traversing the channel - * list of children and calling vchan_mix_* to mix count bytes from - * each into our destination buffer, b - */ - tmp = sndbuf_getbuf(src); - rcnt = 0; - mcnt = 0; - - CHN_FOREACH(ch, c, children.busy) { - CHN_LOCK(ch); - if (!(ch->flags & CHN_F_TRIGGERED)) { - CHN_UNLOCK(ch); - continue; - } - if ((ch->flags & CHN_F_MAPPED) && !(ch->flags & CHN_F_CLOSING)) - sndbuf_acquire(ch->bufsoft, NULL, - sndbuf_getfree(ch->bufsoft)); - if (rcnt == 0) { - rcnt = FEEDER_FEED(ch->feeder, ch, b, count, - ch->bufsoft); - rcnt -= rcnt % sz; - mcnt = count - rcnt; - } else { - cnt = FEEDER_FEED(ch->feeder, ch, tmp, count, - ch->bufsoft); - cnt -= cnt % sz; - if (cnt != 0) { - if (mcnt != 0) { - memset(b + rcnt, - sndbuf_zerodata(f->desc->out), - mcnt); - mcnt = 0; - } - cnt = info->mix(b, tmp, cnt); - if (cnt > rcnt) - rcnt = cnt; - } - } - CHN_UNLOCK(ch); - } +#define FMTLIST_MAX 6 +#define FMTLIST_OFFSET 4 +#define DIGFMTS_MAX 2 - if (++c->feedcount == 0) - c->feedcount = 2; +#ifdef SND_DEBUG +static int snd_passthrough_verbose = 0; +SYSCTL_INT(_hw_snd, OID_AUTO, passthrough_verbose, CTLFLAG_RW, + &snd_passthrough_verbose, 0, "passthrough verbosity"); - return (rcnt); -} +#endif -static struct pcm_feederdesc feeder_vchan_desc[] = { - {FEEDER_MIXER, AFMT_S8, AFMT_S8, 0}, - {FEEDER_MIXER, AFMT_S16_LE, AFMT_S16_LE, 0}, - {FEEDER_MIXER, AFMT_S24_LE, AFMT_S24_LE, 0}, - {FEEDER_MIXER, AFMT_S32_LE, AFMT_S32_LE, 0}, - {FEEDER_MIXER, AFMT_S16_BE, AFMT_S16_BE, 0}, - {FEEDER_MIXER, AFMT_S24_BE, AFMT_S24_BE, 0}, - {FEEDER_MIXER, AFMT_S32_BE, AFMT_S32_BE, 0}, - {FEEDER_MIXER, AFMT_S8 | AFMT_STEREO, AFMT_S8 | AFMT_STEREO, 0}, - {FEEDER_MIXER, AFMT_S16_LE | AFMT_STEREO, AFMT_S16_LE | AFMT_STEREO, 0}, - {FEEDER_MIXER, AFMT_S24_LE | AFMT_STEREO, AFMT_S24_LE | AFMT_STEREO, 0}, - {FEEDER_MIXER, AFMT_S32_LE | AFMT_STEREO, AFMT_S32_LE | AFMT_STEREO, 0}, - {FEEDER_MIXER, AFMT_S16_BE | AFMT_STEREO, AFMT_S16_BE | AFMT_STEREO, 0}, - {FEEDER_MIXER, AFMT_S24_BE | AFMT_STEREO, AFMT_S24_BE | AFMT_STEREO, 0}, - {FEEDER_MIXER, AFMT_S32_BE | AFMT_STEREO, AFMT_S32_BE | AFMT_STEREO, 0}, - {FEEDER_MIXER, AFMT_U8, AFMT_U8, 0}, - {FEEDER_MIXER, AFMT_U16_LE, AFMT_U16_LE, 0}, - {FEEDER_MIXER, AFMT_U24_LE, AFMT_U24_LE, 0}, - {FEEDER_MIXER, AFMT_U32_LE, AFMT_U32_LE, 0}, - {FEEDER_MIXER, AFMT_U16_BE, AFMT_U16_BE, 0}, - {FEEDER_MIXER, AFMT_U24_BE, AFMT_U24_BE, 0}, - {FEEDER_MIXER, AFMT_U32_BE, AFMT_U32_BE, 0}, - {FEEDER_MIXER, AFMT_U8 | AFMT_STEREO, AFMT_U8 | AFMT_STEREO, 0}, - {FEEDER_MIXER, AFMT_U16_LE | AFMT_STEREO, AFMT_U16_LE | AFMT_STEREO, 0}, - {FEEDER_MIXER, AFMT_U24_LE | AFMT_STEREO, AFMT_U24_LE | AFMT_STEREO, 0}, - {FEEDER_MIXER, AFMT_U32_LE | AFMT_STEREO, AFMT_U32_LE | AFMT_STEREO, 0}, - {FEEDER_MIXER, AFMT_U16_BE | AFMT_STEREO, AFMT_U16_BE | AFMT_STEREO, 0}, - {FEEDER_MIXER, AFMT_U24_BE | AFMT_STEREO, AFMT_U24_BE | AFMT_STEREO, 0}, - {FEEDER_MIXER, AFMT_U32_BE | AFMT_STEREO, AFMT_U32_BE | AFMT_STEREO, 0}, - {0, 0, 0, 0}, -}; -static kobj_method_t feeder_vchan_methods[] = { - KOBJMETHOD(feeder_init, feed_vchan_init), - KOBJMETHOD(feeder_feed, feed_vchan), - {0, 0} +struct vchan_info { + struct pcm_channel *channel; + struct pcmchan_caps caps; + uint32_t fmtlist[FMTLIST_MAX]; + int trigger; }; -FEEDER_DECLARE(feeder_vchan, 2, NULL); - -/************************************************************/ static void * vchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir) { - struct vchinfo *ch; + struct vchan_info *info; + struct pcm_channel *p; + uint32_t i, j, *fmtlist; KASSERT(dir == PCMDIR_PLAY || dir == PCMDIR_REC, ("vchan_init: bad direction")); KASSERT(c != NULL && c->parentchannel != NULL, ("vchan_init: bad channels")); - ch = malloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO); - ch->channel = c; - ch->trigger = PCMTRIG_STOP; + info = malloc(sizeof(*info), M_DEVBUF, M_WAITOK | M_ZERO); + info->channel = c; + info->trigger = PCMTRIG_STOP; + p = c->parentchannel; + + CHN_LOCK(p); + + fmtlist = chn_getcaps(p)->fmtlist; + for (i = 0, j = 0; fmtlist[i] != 0 && j < DIGFMTS_MAX; i++) { + if (fmtlist[i] & AFMT_PASSTHROUGH) + info->fmtlist[j++] = fmtlist[i]; + } + if (p->format & AFMT_VCHAN) + info->fmtlist[j] = p->format; + else + info->fmtlist[j] = VCHAN_DEFAULT_FORMAT; + info->caps.fmtlist = info->fmtlist + + ((p->flags & CHN_F_VCHAN_DYNAMIC) ? 0 : FMTLIST_OFFSET); + + CHN_UNLOCK(p); c->flags |= CHN_F_VIRTUAL; - return (ch); + return (info); } static int vchan_free(kobj_t obj, void *data) { + free(data, M_DEVBUF); return (0); @@ -412,88 +108,132 @@ vchan_free(kobj_t obj, void *data) static int vchan_setformat(kobj_t obj, void *data, uint32_t format) { - struct vchinfo *ch = data; + struct vchan_info *info; + + info = data; + + CHN_LOCKASSERT(info->channel); - if (fmtvalid(format, ch->fmtlist) == 0) + if (!snd_fmtvalid(format, info->caps.fmtlist)) return (-1); return (0); } -static int +static uint32_t vchan_setspeed(kobj_t obj, void *data, uint32_t speed) { - struct vchinfo *ch = data; - struct pcm_channel *p = ch->channel->parentchannel; + struct vchan_info *info; + + info = data; + + CHN_LOCKASSERT(info->channel); - return (sndbuf_getspd(p->bufsoft)); + return (info->caps.maxspeed); } static int vchan_trigger(kobj_t obj, void *data, int go) { - struct vchinfo *ch = data; + struct vchan_info *info; struct pcm_channel *c, *p; - int err, otrigger; + int ret, otrigger; - if (!PCMTRIG_COMMON(go) || go == ch->trigger) + info = data; + + if (!PCMTRIG_COMMON(go) || go == info->trigger) return (0); - c = ch->channel; + c = info->channel; p = c->parentchannel; - otrigger = ch->trigger; - ch->trigger = go; + otrigger = info->trigger; + info->trigger = go; + + CHN_LOCKASSERT(c); CHN_UNLOCK(c); CHN_LOCK(p); switch (go) { case PCMTRIG_START: - if (otrigger != PCMTRIG_START) { + if (otrigger != PCMTRIG_START) CHN_INSERT_HEAD(p, c, children.busy); - } break; case PCMTRIG_STOP: case PCMTRIG_ABORT: - if (otrigger == PCMTRIG_START) { + if (otrigger == PCMTRIG_START) CHN_REMOVE(p, c, children.busy); - } break; default: break; } - err = chn_notify(p, CHN_N_TRIGGER); + ret = chn_notify(p, CHN_N_TRIGGER); + + CHN_LOCK(c); + + if (ret == 0 && go == PCMTRIG_START && VCHAN_SYNC_REQUIRED(c)) + ret = vchan_sync(c); + + CHN_UNLOCK(c); CHN_UNLOCK(p); CHN_LOCK(c); - return (err); + return (ret); } static struct pcmchan_caps * vchan_getcaps(kobj_t obj, void *data) { - struct vchinfo *ch = data; - struct pcm_channel *c, *p; - uint32_t fmt; - - c = ch->channel; - p = c->parentchannel; - ch->caps.minspeed = sndbuf_getspd(p->bufsoft); - ch->caps.maxspeed = ch->caps.minspeed; - ch->caps.caps = 0; - ch->fmtlist[1] = 0; - fmt = sndbuf_getfmt(p->bufsoft); - if (fmt != vchan_valid_format(fmt)) { - device_printf(c->dev, - "%s: WARNING: invalid vchan format! (0x%08x)\n", - __func__, fmt); - fmt = VCHAN_DEFAULT_AFMT; + struct vchan_info *info; + struct pcm_channel *c; + uint32_t pformat, pspeed, pflags, i; + + info = data; + c = info->channel; + pformat = c->parentchannel->format; + pspeed = c->parentchannel->speed; + pflags = c->parentchannel->flags; + + CHN_LOCKASSERT(c); + + if (pflags & CHN_F_VCHAN_DYNAMIC) { + info->caps.fmtlist = info->fmtlist; + if (pformat & AFMT_VCHAN) { + for (i = 0; info->caps.fmtlist[i] != 0; i++) { + if (info->caps.fmtlist[i] & AFMT_PASSTHROUGH) + continue; + break; + } + info->caps.fmtlist[i] = pformat; + } + if (c->format & AFMT_PASSTHROUGH) + info->caps.minspeed = c->speed; + else + info->caps.minspeed = pspeed; + info->caps.maxspeed = info->caps.minspeed; + } else { + info->caps.fmtlist = info->fmtlist + FMTLIST_OFFSET; + if (pformat & AFMT_VCHAN) + info->caps.fmtlist[0] = pformat; + else { + device_printf(c->dev, + "%s(): invalid vchan format 0x%08x", + __func__, pformat); + info->caps.fmtlist[0] = VCHAN_DEFAULT_FORMAT; + } + info->caps.minspeed = pspeed; + info->caps.maxspeed = info->caps.minspeed; } - ch->fmtlist[0] = fmt; - ch->caps.fmtlist = ch->fmtlist; - return (&ch->caps); + return (&info->caps); +} + +static struct pcmchan_matrix * +vchan_getmatrix(kobj_t obj, void *data, uint32_t format) +{ + + return (feeder_matrix_format_map(format)); } static kobj_method_t vchan_methods[] = { @@ -503,27 +243,215 @@ static kobj_method_t vchan_methods[] = { KOBJMETHOD(channel_setspeed, vchan_setspeed), KOBJMETHOD(channel_trigger, vchan_trigger), KOBJMETHOD(channel_getcaps, vchan_getcaps), - {0, 0} + KOBJMETHOD(channel_getmatrix, vchan_getmatrix), + KOBJMETHOD_END }; 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; + + d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1)); + if (!PCM_REGISTERED(d) || !(d->flags & SD_F_AUTOVCHAN)) + return (EINVAL); + + 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; + break; + case VCHAN_REC: + direction = PCMDIR_REC; + vchancount = d->rvchancount; + cnt = d->reccount; + break; + default: + PCM_UNLOCK(d); + return (EINVAL); + break; + } + + if (cnt < 1) { + PCM_UNLOCK(d); + return (ENODEV); + } + + 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); + } + + PCM_RELEASE_QUICK(d); + + return err; +} + +static int +sysctl_dev_pcm_vchanmode(SYSCTL_HANDLER_ARGS) +{ + struct snddev_info *d; + struct pcm_channel *c; + uint32_t dflags; + int direction, ret; + char dtype[16]; + + d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1)); + if (!PCM_REGISTERED(d) || !(d->flags & SD_F_AUTOVCHAN)) + return (EINVAL); + + PCM_LOCK(d); + PCM_WAIT(d); + + switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) { + case VCHAN_PLAY: + direction = PCMDIR_PLAY; + break; + case VCHAN_REC: + direction = PCMDIR_REC; + 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) + strlcpy(dtype, "passthrough", sizeof(dtype)); + else if (c->flags & 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); + } + CHN_LOCK(c); + if (dflags == (c->flags & CHN_F_VCHAN_DYNAMIC) || + (c->flags & CHN_F_PASSTHROUGH)) { + CHN_UNLOCK(c); + PCM_RELEASE_QUICK(d); + return (0); + } + c->flags &= ~CHN_F_VCHAN_DYNAMIC; + c->flags |= dflags; + CHN_UNLOCK(c); + } + + PCM_RELEASE_QUICK(d); + + return (ret); +} + /* - * On the fly vchan rate settings + * On the fly vchan rate/format settings */ -#ifdef SND_DYNSYSCTL + +#define VCHAN_ACCESSIBLE(c) (!((c)->flags & (CHN_F_PASSTHROUGH | \ + CHN_F_EXCLUSIVE)) && \ + (((c)->flags & CHN_F_VCHAN_DYNAMIC) || \ + CHN_STOPPED(c))) static int -sysctl_hw_snd_vchanrate(SYSCTL_HANDLER_ARGS) +sysctl_dev_pcm_vchanrate(SYSCTL_HANDLER_ARGS) { struct snddev_info *d; - struct pcm_channel *c, *ch = NULL; + struct pcm_channel *c, *ch; struct pcmchan_caps *caps; - int *vchanrate, vchancount, direction, err, newspd; + int *vchanrate, vchancount, direction, ret, newspd, restart; d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1)); if (!PCM_REGISTERED(d) || !(d->flags & SD_F_AUTOVCHAN)) return (EINVAL); - pcm_lock(d); + PCM_LOCK(d); PCM_WAIT(d); switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) { @@ -538,102 +466,101 @@ sysctl_hw_snd_vchanrate(SYSCTL_HANDLER_ARGS) vchanrate = &d->rvchanrate; break; default: - pcm_unlock(d); + PCM_UNLOCK(d); return (EINVAL); break; } if (vchancount < 1) { - pcm_unlock(d); + PCM_UNLOCK(d); return (EINVAL); } PCM_ACQUIRE(d); - pcm_unlock(d); + PCM_UNLOCK(d); - newspd = 0; + if (direction == PCMDIR_PLAY) + pcm_getparentchannel(d, &c, NULL); + else + pcm_getparentchannel(d, NULL, &c); - CHN_FOREACH(c, d, channels.pcm) { - CHN_LOCK(c); - if (c->direction == direction) { - if (c->flags & CHN_F_VIRTUAL) { - /* Sanity check */ - if (ch != NULL && ch != c->parentchannel) { - CHN_UNLOCK(c); - PCM_RELEASE_QUICK(d); - return (EINVAL); - } - } else if (c->flags & CHN_F_HAS_VCHAN) { - /* No way!! */ - if (ch != NULL) { - CHN_UNLOCK(c); - PCM_RELEASE_QUICK(d); - return (EINVAL); - } - ch = c; - newspd = ch->speed; - } - } - CHN_UNLOCK(c); + if (c == NULL) { + PCM_RELEASE_QUICK(d); + return (EINVAL); } - if (ch == NULL) { + + KASSERT(direction == c->direction, ("%s(): invalid direction %d/%d", + __func__, direction, c->direction)); + + CHN_LOCK(c); + newspd = c->speed; + CHN_UNLOCK(c); + + ret = sysctl_handle_int(oidp, &newspd, 0, req); + if (ret != 0 || req->newptr == NULL) { + PCM_RELEASE_QUICK(d); + return (ret); + } + + if (newspd < 1 || newspd < feeder_rate_min || + newspd > feeder_rate_max) { PCM_RELEASE_QUICK(d); return (EINVAL); } - err = sysctl_handle_int(oidp, &newspd, 0, req); - if (err == 0 && req->newptr != NULL) { - if (newspd < 1 || newspd < feeder_rate_min || - newspd > feeder_rate_max) { - PCM_RELEASE_QUICK(d); - return (EINVAL); - } - CHN_LOCK(ch); + 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(ch); - if (caps == NULL || newspd < caps->minspeed || - newspd > caps->maxspeed) { - CHN_UNLOCK(ch); - PCM_RELEASE_QUICK(d); - return (EINVAL); - } + caps = chn_getcaps(c); + RANGE(newspd, caps->minspeed, caps->maxspeed); + newspd = CHANNEL_SETSPEED(c->methods, + c->devinfo, newspd); } - if (CHN_STOPPED(ch) && newspd != ch->speed) { - err = chn_setspeed(ch, newspd); - /* - * Try to avoid FEEDER_RATE on parent channel if the - * requested value is not supported by the hardware. - */ - if (!err && feeder_rate_round && - (ch->feederflags & (1 << FEEDER_RATE))) { - newspd = sndbuf_getspd(ch->bufhard); - err = chn_setspeed(ch, newspd); + + 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); + } + c->flags |= CHN_F_DIRTY; + ret = chn_start(c, 1); } - if (err == 0) - *vchanrate = newspd; } - CHN_UNLOCK(ch); } + CHN_UNLOCK(c); + PCM_RELEASE_QUICK(d); - return (err); + return (ret); } static int -sysctl_hw_snd_vchanformat(SYSCTL_HANDLER_ARGS) +sysctl_dev_pcm_vchanformat(SYSCTL_HANDLER_ARGS) { struct snddev_info *d; - struct pcm_channel *c, *ch = NULL; - uint32_t newfmt, spd; - int *vchanformat, vchancount, direction, err, i; - char fmtstr[AFMTSTR_MAXSZ]; + struct pcm_channel *c, *ch; + uint32_t newfmt; + int *vchanformat, vchancount, direction, ret, restart; + char fmtstr[AFMTSTR_LEN]; d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1)); if (!PCM_REGISTERED(d) || !(d->flags & SD_F_AUTOVCHAN)) return (EINVAL); - pcm_lock(d); + PCM_LOCK(d); PCM_WAIT(d); switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) { @@ -648,85 +575,84 @@ sysctl_hw_snd_vchanformat(SYSCTL_HANDLER_ARGS) vchanformat = &d->rvchanformat; break; default: - pcm_unlock(d); + PCM_UNLOCK(d); return (EINVAL); break; } if (vchancount < 1) { - pcm_unlock(d); + PCM_UNLOCK(d); return (EINVAL); } PCM_ACQUIRE(d); - pcm_unlock(d); + PCM_UNLOCK(d); - CHN_FOREACH(c, d, channels.pcm) { - CHN_LOCK(c); - if (c->direction == direction) { - if (c->flags & CHN_F_VIRTUAL) { - /* Sanity check */ - if (ch != NULL && ch != c->parentchannel) { - CHN_UNLOCK(c); - PCM_RELEASE_QUICK(d); - return (EINVAL); - } - } else if (c->flags & CHN_F_HAS_VCHAN) { - /* No way!! */ - if (ch != NULL) { - CHN_UNLOCK(c); - PCM_RELEASE_QUICK(d); - return (EINVAL); - } - ch = c; - if (ch->format != - afmt2afmtstr(vchan_supported_fmts, - ch->format, fmtstr, sizeof(fmtstr), - AFMTSTR_FULL, AFMTSTR_STEREO_RETURN)) { - strlcpy(fmtstr, VCHAN_DEFAULT_STRFMT, - sizeof(fmtstr)); - } - } - } - CHN_UNLOCK(c); + if (direction == PCMDIR_PLAY) + pcm_getparentchannel(d, &c, NULL); + else + pcm_getparentchannel(d, NULL, &c); + + if (c == NULL) { + PCM_RELEASE_QUICK(d); + return (EINVAL); } - if (ch == NULL) { + + 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) + 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); + return (ret); + } + + newfmt = snd_str2afmt(fmtstr); + if (newfmt == 0 || !(newfmt & AFMT_VCHAN)) { PCM_RELEASE_QUICK(d); return (EINVAL); } - err = sysctl_handle_string(oidp, fmtstr, sizeof(fmtstr), req); - if (err == 0 && req->newptr != NULL) { - for (i = 0; vchan_fmtstralias[i].alias != NULL; i++) { - if (strcmp(fmtstr, vchan_fmtstralias[i].alias) == 0) { - strlcpy(fmtstr, vchan_fmtstralias[i].fmtstr, - sizeof(fmtstr)); - break; + 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); + } + c->flags |= CHN_F_DIRTY; + ret = chn_start(c, 1); } } - newfmt = vchan_valid_strformat(fmtstr); - if (newfmt == 0) { - PCM_RELEASE_QUICK(d); - return (EINVAL); - } - CHN_LOCK(ch); - if (CHN_STOPPED(ch) && newfmt != ch->format) { - /* Get channel speed, before chn_reset() screw it. */ - spd = ch->speed; - err = chn_reset(ch, newfmt); - if (err == 0) - err = chn_setspeed(ch, spd); - if (err == 0) - *vchanformat = newfmt; - } - CHN_UNLOCK(ch); } + CHN_UNLOCK(c); + PCM_RELEASE_QUICK(d); - return (err); + return (ret); } -#endif /* virtual channel interface */ @@ -738,254 +664,279 @@ sysctl_hw_snd_vchanformat(SYSCTL_HANDLER_ARGS) int vchan_create(struct pcm_channel *parent, int num) { - struct snddev_info *d = parent->parentsnddev; - struct pcm_channel *ch, *tmp, *after; + struct snddev_info *d; + struct pcm_channel *ch; struct pcmchan_caps *parent_caps; - uint32_t vchanfmt; - int err, first, speed, r; - int direction; + uint32_t vchanfmt, vchanspd; + int ret, direction, r, save; + + 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); + if (parent->direction == PCMDIR_PLAY) { direction = PCMDIR_PLAY_VIRTUAL; vchanfmt = d->pvchanformat; - speed = d->pvchanrate; - } else if (parent->direction == PCMDIR_REC) { + vchanspd = d->pvchanrate; + } else { direction = PCMDIR_REC_VIRTUAL; vchanfmt = d->rvchanformat; - speed = d->rvchanrate; - } else - return (EINVAL); - CHN_UNLOCK(parent); + vchanspd = d->rvchanrate; + } /* create a new playback channel */ - pcm_lock(d); ch = pcm_chn_create(d, parent, &vchan_class, direction, num, parent); if (ch == NULL) { - pcm_unlock(d); + PCM_UNLOCK(d); CHN_LOCK(parent); return (ENODEV); } /* add us to our grandparent's channel list */ - err = pcm_chn_add(d, ch); - pcm_unlock(d); - if (err) { + ret = pcm_chn_add(d, ch); + PCM_UNLOCK(d); + if (ret != 0) { pcm_chn_destroy(ch); CHN_LOCK(parent); - return (err); + return (ret); } CHN_LOCK(parent); - /* add us to our parent channel's children */ - first = CHN_EMPTY(parent, children); - after = NULL; - CHN_FOREACH(tmp, parent, children) { - if (CHN_CHAN(tmp) > CHN_CHAN(ch)) - after = tmp; - else if (CHN_CHAN(tmp) < CHN_CHAN(ch)) - break; - } - if (after != NULL) { - CHN_INSERT_AFTER(after, ch, children); - } else { - CHN_INSERT_HEAD(parent, ch, children); - } + /* + * 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); + + if (parent->flags & CHN_F_HAS_VCHAN) + return (0); + parent->flags |= CHN_F_HAS_VCHAN; - if (first) { - parent_caps = chn_getcaps(parent); - if (parent_caps == NULL) - err = EINVAL; - - if (!err) { - if (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 = vchan_valid_strformat(vfmt); - for (r = 0; vchanfmt == 0 && - vchan_fmtstralias[r].alias != NULL; - r++) { - if (strcmp(vfmt, vchan_fmtstralias[r].alias) == 0) { - vchanfmt = vchan_valid_strformat(vchan_fmtstralias[r].fmtstr); - break; - } - } - } - if (vchanfmt == 0) - vchanfmt = VCHAN_DEFAULT_AFMT; - } - err = chn_reset(parent, vchanfmt); - } + parent_caps = chn_getcaps(parent); + if (parent_caps == NULL) + ret = EINVAL; - if (!err) { - /* - * 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. - */ - if (speed < 1) { - CHN_UNLOCK(parent); - r = resource_int_value( - device_get_name(parent->dev), - device_get_unit(parent->dev), - VCHAN_SPD_HINT(direction), - &speed); - 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: - speed = 44100; - break; - default: - speed = VCHAN_DEFAULT_SPEED; - if (speed > parent_caps->maxspeed) - speed = parent_caps->maxspeed; - break; - } - if (speed < parent_caps->minspeed) - speed = parent_caps->minspeed; - } - } + save = 0; - if (feeder_rate_round) { - /* - * Limit speed based on driver caps. - * This is supposed to help fixed rate, non-VRA - * AC97 cards, but.. (see below) - */ - if (speed < parent_caps->minspeed) - speed = parent_caps->minspeed; - if (speed > parent_caps->maxspeed) - speed = parent_caps->maxspeed; - } + if (ret == 0 && vchanfmt == 0) { + const char *vfmt; - /* - * We still need to limit the speed between - * feeder_rate_min <-> feeder_rate_max. This is - * just an escape goat if all of the above failed - * miserably. - */ - if (speed < feeder_rate_min) - speed = feeder_rate_min; - if (speed > feeder_rate_max) - speed = feeder_rate_max; + 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; + } - err = chn_setspeed(parent, speed); + 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) { /* - * Try to avoid FEEDER_RATE on parent channel if the - * requested value is not supported by the hardware. + * No saved value, no hint, NOTHING. + * + * Workaround for sb16 running + * poorly at 45k / 49k. */ - if (!err && feeder_rate_round && - (parent->feederflags & (1 << FEEDER_RATE))) { - speed = sndbuf_getspd(parent->bufhard); - err = chn_setspeed(parent, speed); + 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; + } - if (!err) { - /* - * Save new value. - */ - CHN_UNLOCK(parent); - if (direction == PCMDIR_PLAY_VIRTUAL) { - d->pvchanformat = vchanfmt; - d->pvchanrate = speed; - } else { - d->rvchanformat = vchanfmt; - d->rvchanrate = speed; - } - CHN_LOCK(parent); - } + 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); } - - if (err) { - 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); - pcm_chn_destroy(ch); - } else - pcm_unlock(d); - CHN_LOCK(parent); - return (err); + + 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 the parent channel supports digital format, + * enable passthrough mode. + */ + if (ret == 0 && snd_fmtvalid(AFMT_PASSTHROUGH, parent_caps->fmtlist)) { + parent->flags &= ~CHN_F_VCHAN_DYNAMIC; + parent->flags |= CHN_F_VCHAN_PASSTHROUGH; + } - return (0); + 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); + pcm_chn_destroy(ch); + } else + PCM_UNLOCK(d); + CHN_LOCK(parent); + } + + return (ret); } int vchan_destroy(struct pcm_channel *c) { - struct pcm_channel *parent = c->parentchannel; - struct snddev_info *d = parent->parentsnddev; - uint32_t spd; - int err; + struct pcm_channel *parent; + struct snddev_info *d; + int ret; + + KASSERT(c != NULL && c->parentchannel != NULL && + c->parentsnddev != NULL, ("%s(): invalid channel=%p", + __func__, c)); + + CHN_LOCKASSERT(c); + + d = c->parentsnddev; + parent = c->parentchannel; PCM_BUSYASSERT(d); + CHN_LOCKASSERT(parent); - CHN_LOCK(parent); - if (!(parent->flags & CHN_F_BUSY)) { - CHN_UNLOCK(parent); + CHN_UNLOCK(c); + + if (!(parent->flags & CHN_F_BUSY)) return (EBUSY); - } - if (CHN_EMPTY(parent, children)) { - CHN_UNLOCK(parent); + + 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); - spd = parent->speed; - if (chn_reset(parent, parent->format) == 0) - chn_setspeed(parent, spd); + chn_reset(parent, parent->format, parent->speed); } CHN_UNLOCK(parent); /* remove us from our grandparent's channel list */ - pcm_lock(d); - err = pcm_chn_remove(d, c); - pcm_unlock(d); + PCM_LOCK(d); + ret = pcm_chn_remove(d, c); + PCM_UNLOCK(d); /* destroy ourselves */ - if (!err) - err = pcm_chn_destroy(c); + if (ret == 0) + ret = pcm_chn_destroy(c); + + CHN_LOCK(parent); - return (err); + return (ret); } int +#ifdef SND_DEBUG +vchan_passthrough(struct pcm_channel *c, const char *caller) +#else +vchan_sync(struct pcm_channel *c) +#endif +{ + int ret; + + KASSERT(c != NULL && c->parentchannel != NULL && + (c->flags & CHN_F_VIRTUAL), + ("%s(): invalid passthrough", __func__)); + CHN_LOCKASSERT(c); + CHN_LOCKASSERT(c->parentchannel); + + sndbuf_setspd(c->bufhard, c->parentchannel->speed); + c->flags |= CHN_F_PASSTHROUGH; + ret = feeder_chain(c); + c->flags &= ~(CHN_F_DIRTY | CHN_F_PASSTHROUGH); + if (ret != 0) + 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->unit); + device_printf(c->dev, + "%s(%s/%s) %s() -> re-sync err=%d\n", + __func__, (devname != NULL) ? devname : "dspX", c->comm, + caller, ret); + } +#endif + + return (ret); +} + +void vchan_initsys(device_t dev) { -#ifdef SND_DYNSYSCTL struct snddev_info *d; int unit; @@ -997,34 +948,43 @@ vchan_initsys(device_t dev) SYSCTL_CHILDREN(d->play_sysctl_tree), OID_AUTO, "vchans", CTLTYPE_INT | CTLFLAG_RW, VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE, - sysctl_hw_snd_vchans, "I", "total allocated virtual channel"); + sysctl_dev_pcm_vchans, "I", "total allocated virtual channel"); + SYSCTL_ADD_PROC(&d->play_sysctl_ctx, + SYSCTL_CHILDREN(d->play_sysctl_tree), + OID_AUTO, "vchanmode", CTLTYPE_STRING | CTLFLAG_RW, + VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE, + sysctl_dev_pcm_vchanmode, "A", + "vchan format/rate selection: 0=fixed, 1=passthrough, 2=adaptive"); SYSCTL_ADD_PROC(&d->play_sysctl_ctx, SYSCTL_CHILDREN(d->play_sysctl_tree), OID_AUTO, "vchanrate", CTLTYPE_INT | CTLFLAG_RW, VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE, - sysctl_hw_snd_vchanrate, "I", "virtual channel mixing speed/rate"); + sysctl_dev_pcm_vchanrate, "I", "virtual channel mixing speed/rate"); SYSCTL_ADD_PROC(&d->play_sysctl_ctx, SYSCTL_CHILDREN(d->play_sysctl_tree), OID_AUTO, "vchanformat", CTLTYPE_STRING | CTLFLAG_RW, VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE, - sysctl_hw_snd_vchanformat, "A", "virtual channel format"); + sysctl_dev_pcm_vchanformat, "A", "virtual channel mixing format"); /* Rec */ SYSCTL_ADD_PROC(&d->rec_sysctl_ctx, SYSCTL_CHILDREN(d->rec_sysctl_tree), OID_AUTO, "vchans", CTLTYPE_INT | CTLFLAG_RW, VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE, - sysctl_hw_snd_vchans, "I", "total allocated virtual channel"); + sysctl_dev_pcm_vchans, "I", "total allocated virtual channel"); + SYSCTL_ADD_PROC(&d->rec_sysctl_ctx, + SYSCTL_CHILDREN(d->rec_sysctl_tree), + OID_AUTO, "vchanmode", CTLTYPE_STRING | CTLFLAG_RW, + VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE, + sysctl_dev_pcm_vchanmode, "A", + "vchan format/rate selection: 0=fixed, 1=passthrough, 2=adaptive"); SYSCTL_ADD_PROC(&d->rec_sysctl_ctx, SYSCTL_CHILDREN(d->rec_sysctl_tree), OID_AUTO, "vchanrate", CTLTYPE_INT | CTLFLAG_RW, VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE, - sysctl_hw_snd_vchanrate, "I", "virtual channel base speed/rate"); + sysctl_dev_pcm_vchanrate, "I", "virtual channel mixing speed/rate"); SYSCTL_ADD_PROC(&d->rec_sysctl_ctx, SYSCTL_CHILDREN(d->rec_sysctl_tree), OID_AUTO, "vchanformat", CTLTYPE_STRING | CTLFLAG_RW, VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE, - sysctl_hw_snd_vchanformat, "A", "virtual channel format"); -#endif - - return (0); + sysctl_dev_pcm_vchanformat, "A", "virtual channel mixing format"); } diff --git a/sys/dev/sound/pcm/vchan.h b/sys/dev/sound/pcm/vchan.h index 076ec82..04c2b4f 100644 --- a/sys/dev/sound/pcm/vchan.h +++ b/sys/dev/sound/pcm/vchan.h @@ -1,5 +1,6 @@ /*- - * Copyright (c) 2001 Cameron Grant <cg@freebsd.org> + * Copyright (c) 2005-2009 Ariff Abdullah <ariff@FreeBSD.org> + * Copyright (c) 2001 Cameron Grant <cg@FreeBSD.org> * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -26,16 +27,31 @@ * $FreeBSD$ */ -int vchan_create(struct pcm_channel *parent, int num); -int vchan_destroy(struct pcm_channel *c); -int vchan_initsys(device_t dev); +#ifndef _SND_VCHAN_H_ +#define _SND_VCHAN_H_ + +int vchan_create(struct pcm_channel *, int); +int vchan_destroy(struct pcm_channel *); + +#ifdef SND_DEBUG +int vchan_passthrough(struct pcm_channel *, const char *); +#define vchan_sync(c) vchan_passthrough(c, __func__) +#else +int vchan_sync(struct pcm_channel *); +#endif + +#define VCHAN_SYNC_REQUIRED(c) \ + (((c)->flags & CHN_F_VIRTUAL) && (((c)->flags & CHN_F_DIRTY) || \ + sndbuf_getfmt((c)->bufhard) != (c)->parentchannel->format || \ + sndbuf_getspd((c)->bufhard) != (c)->parentchannel->speed)) + +void vchan_initsys(device_t); /* - * Default speed / format + * Default format / rate */ -#define VCHAN_DEFAULT_SPEED 48000 -#define VCHAN_DEFAULT_AFMT (AFMT_S16_LE | AFMT_STEREO) -#define VCHAN_DEFAULT_STRFMT "s16le" +#define VCHAN_DEFAULT_FORMAT SND_FORMAT(AFMT_S16_LE, 2, 0) +#define VCHAN_DEFAULT_RATE 48000 #define VCHAN_PLAY 0 #define VCHAN_REC 1 @@ -50,3 +66,5 @@ int vchan_initsys(device_t dev); #define VCHAN_SYSCTL_DATA_SIZE sizeof(void *) #define VCHAN_SYSCTL_UNIT(x) ((int)(((intptr_t)(x) >> 2) & 0xfff) - 1) #define VCHAN_SYSCTL_DIR(x) ((int)((intptr_t)(x) & 0x3) - 1) + +#endif /* _SND_VCHAN_H_ */ diff --git a/sys/dev/sound/sbus/cs4231.c b/sys/dev/sound/sbus/cs4231.c index a785f96..90c947f 100644 --- a/sys/dev/sound/sbus/cs4231.c +++ b/sys/dev/sound/sbus/cs4231.c @@ -50,6 +50,10 @@ __FBSDID("$FreeBSD$"); #include <machine/bus.h> #include <machine/ofw_machdep.h> +#ifdef HAVE_KERNEL_OPTION_HEADERS +#include "opt_snd.h" +#endif + #include <dev/sound/pcm/sound.h> #include <dev/sound/sbus/apcdmareg.h> #include <dev/sound/sbus/cs4231.h> @@ -260,18 +264,18 @@ MODULE_VERSION(snd_audiocs, 1); static u_int32_t cs4231_fmt[] = { - AFMT_U8, - AFMT_STEREO | AFMT_U8, - AFMT_MU_LAW, - AFMT_STEREO | AFMT_MU_LAW, - AFMT_A_LAW, - AFMT_STEREO | AFMT_A_LAW, - AFMT_IMA_ADPCM, - AFMT_STEREO | AFMT_IMA_ADPCM, - AFMT_S16_LE, - AFMT_STEREO | AFMT_S16_LE, - AFMT_S16_BE, - AFMT_STEREO | AFMT_S16_BE, + SND_FORMAT(AFMT_U8, 1, 0), + SND_FORMAT(AFMT_U8, 2, 0), + SND_FORMAT(AFMT_MU_LAW, 1, 0), + SND_FORMAT(AFMT_MU_LAW, 2, 0), + SND_FORMAT(AFMT_A_LAW, 1, 0), + SND_FORMAT(AFMT_A_LAW, 2, 0), + SND_FORMAT(AFMT_IMA_ADPCM, 1, 0), + SND_FORMAT(AFMT_IMA_ADPCM, 2, 0), + SND_FORMAT(AFMT_S16_LE, 1, 0), + SND_FORMAT(S16_LE, 2, 0), + SND_FORMAT(AFMT_S16_BE, 1, 0), + SND_FORMAT(AFMT_S16_BE, 2, 0), 0 }; @@ -288,7 +292,7 @@ static kobj_method_t cs4231_chan_methods[] = { KOBJMETHOD(channel_trigger, cs4231_chan_trigger), KOBJMETHOD(channel_getptr, cs4231_chan_getptr), KOBJMETHOD(channel_getcaps, cs4231_chan_getcaps), - { 0, 0 } + KOBJMETHOD_END }; CHANNEL_DECLARE(cs4231_chan); @@ -299,7 +303,7 @@ static kobj_method_t cs4231_mixer_methods[] = { KOBJMETHOD(mixer_init, cs4231_mixer_init), KOBJMETHOD(mixer_set, cs4231_mixer_set), KOBJMETHOD(mixer_setrecsrc, cs4231_mixer_setrecsrc), - { 0, 0 } + KOBJMETHOD_END }; MIXER_DECLARE(cs4231_mixer); @@ -1057,7 +1061,7 @@ cs4231_chan_setformat(kobj_t obj, void *data, u_int32_t format) return (0); } - encoding = format & ~AFMT_STEREO; + encoding = AFMT_ENCODING(format); fs = 0; switch (encoding) { case AFMT_U8: @@ -1084,7 +1088,7 @@ cs4231_chan_setformat(kobj_t obj, void *data, u_int32_t format) break; } - if (format & AFMT_STEREO) + if (AFMT_CHANNEL(format) > 1) fs |= CS_AFMT_STEREO; DPRINTF(("FORMAT: %s : 0x%x\n", ch->dir == PCMDIR_PLAY ? "playback" : diff --git a/sys/dev/sound/unit.c b/sys/dev/sound/unit.c index 3a9cbb6..f263e04 100644 --- a/sys/dev/sound/unit.c +++ b/sys/dev/sound/unit.c @@ -29,6 +29,10 @@ #include <sys/param.h> #include <sys/systm.h> +#ifdef HAVE_KERNEL_OPTION_HEADERS +#include "opt_snd.h" +#endif + #include <dev/sound/unit.h> /* @@ -65,7 +69,7 @@ static int snd_unit_initialized = 0; #define SND_UNIT_ASSERT() do { \ if (snd_unit_initialized == 0) \ panic("%s(): Uninitialized sound unit!", __func__); \ -} while(0) +} while (0) #else #define SND_UNIT_ASSERT() KASSERT(snd_unit_initialized != 0, \ ("%s(): Uninitialized sound unit!", \ diff --git a/sys/dev/sound/usb/uaudio.c b/sys/dev/sound/usb/uaudio.c index cff13f6..1912008 100644 --- a/sys/dev/sound/usb/uaudio.c +++ b/sys/dev/sound/usb/uaudio.c @@ -67,6 +67,10 @@ #include <sys/reboot.h> /* for bootverbose */ +#ifdef HAVE_KERNEL_OPTION_HEADERS +#include "opt_snd.h" +#endif + #include <dev/sound/pcm/sound.h> #include <dev/sound/usb/uaudioreg.h> #include <dev/sound/usb/uaudio.h> @@ -664,7 +668,7 @@ uaudio_attach_sub(device_t dev, kobj_class_t mixer_class, kobj_class_t chan_clas if (sc->sc_uq_audio_swap_lr) { DPRINTF("hardware has swapped left and right\n"); - uaudio_pcm_setflags(dev, SD_F_PSWAPLR); + /* uaudio_pcm_setflags(dev, SD_F_PSWAPLR); */ } if (!(sc->sc_mix_info & SOUND_MASK_PCM)) { @@ -688,6 +692,8 @@ uaudio_attach_sub(device_t dev, kobj_class_t mixer_class, kobj_class_t chan_clas sc->sc_rec_chan.valid ? 1 : 0)) { goto detach; } + + uaudio_pcm_setflags(dev, SD_F_MPSAFE); sc->sc_pcm_registered = 1; if (sc->sc_play_chan.valid) { @@ -1336,11 +1342,13 @@ uaudio_chan_init(struct uaudio_softc *sc, struct snd_dbuf *b, ch->pcm_cap.minspeed = ch->sample_rate; ch->pcm_cap.maxspeed = ch->sample_rate; - ch->pcm_cap.fmtlist[0] = ch->p_fmt->freebsd_fmt; + if (ch->p_asf1d->bNrChannels >= 2) + ch->pcm_cap.fmtlist[0] = + SND_FORMAT(ch->p_fmt->freebsd_fmt, 2, 0); + else + ch->pcm_cap.fmtlist[0] = + SND_FORMAT(ch->p_fmt->freebsd_fmt, 1, 0); - if (ch->p_asf1d->bNrChannels >= 2) { - ch->pcm_cap.fmtlist[0] |= AFMT_STEREO; - } ch->pcm_cap.fmtlist[1] = 0; @@ -1452,6 +1460,51 @@ uaudio_chan_getcaps(struct uaudio_chan *ch) return (&ch->pcm_cap); } +static struct pcmchan_matrix uaudio_chan_matrix_swap_2_0 = { + .id = SND_CHN_MATRIX_DRV, + .channels = 2, + .ext = 0, + .map = { + /* Right */ + [0] = { + .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 + }, + /* Left */ + [1] = { + .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 + }, + [2] = { + .type = SND_CHN_T_MAX, + .members = 0 + } + }, + .mask = SND_CHN_T_MASK_FR | SND_CHN_T_MASK_FL, + .offset = { 1, 0, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1 } +}; + +struct pcmchan_matrix * +uaudio_chan_getmatrix(struct uaudio_chan *ch, uint32_t format) +{ + struct uaudio_softc *sc; + + sc = ch->priv_sc; + + if (sc != NULL && sc->sc_uq_audio_swap_lr != 0 && + AFMT_CHANNEL(format) == 2) + return (&uaudio_chan_matrix_swap_2_0); + + return (feeder_matrix_format_map(format)); +} + int uaudio_chan_set_param_format(struct uaudio_chan *ch, uint32_t format) { diff --git a/sys/dev/sound/usb/uaudio.h b/sys/dev/sound/usb/uaudio.h index e763c6d..622cb3b 100644 --- a/sys/dev/sound/usb/uaudio.h +++ b/sys/dev/sound/usb/uaudio.h @@ -47,6 +47,8 @@ extern int uaudio_chan_set_param_speed(struct uaudio_chan *ch, uint32_t speed); extern int uaudio_chan_getptr(struct uaudio_chan *ch); extern struct pcmchan_caps *uaudio_chan_getcaps(struct uaudio_chan *ch); +extern struct pcmchan_matrix *uaudio_chan_getmatrix(struct uaudio_chan *ch, + uint32_t format); extern int uaudio_chan_set_param_format(struct uaudio_chan *ch, uint32_t format); extern int uaudio_chan_start(struct uaudio_chan *ch); diff --git a/sys/dev/sound/usb/uaudio_pcm.c b/sys/dev/sound/usb/uaudio_pcm.c index e227f1b..b57d0fb 100644 --- a/sys/dev/sound/usb/uaudio_pcm.c +++ b/sys/dev/sound/usb/uaudio_pcm.c @@ -27,7 +27,10 @@ */ -#include <sys/soundcard.h> +#ifdef HAVE_KERNEL_OPTION_HEADERS +#include "opt_snd.h" +#endif + #include <dev/sound/pcm/sound.h> #include <dev/sound/chip.h> #include <dev/sound/usb/uaudio.h> @@ -57,13 +60,13 @@ ua_chan_setformat(kobj_t obj, void *data, uint32_t format) return (uaudio_chan_set_param_format(data, format)); } -static int +static uint32_t ua_chan_setspeed(kobj_t obj, void *data, uint32_t speed) { return (uaudio_chan_set_param_speed(data, speed)); } -static int +static uint32_t ua_chan_setblocksize(kobj_t obj, void *data, uint32_t blocksize) { return (uaudio_chan_set_param_blocksize(data, blocksize)); @@ -88,7 +91,7 @@ ua_chan_trigger(kobj_t obj, void *data, int go) } } -static int +static uint32_t ua_chan_getptr(kobj_t obj, void *data) { return (uaudio_chan_getptr(data)); @@ -100,6 +103,12 @@ ua_chan_getcaps(kobj_t obj, void *data) return (uaudio_chan_getcaps(data)); } +static struct pcmchan_matrix * +ua_chan_getmatrix(kobj_t obj, void *data, uint32_t format) +{ + return (uaudio_chan_getmatrix(data, format)); +} + static kobj_method_t ua_chan_methods[] = { KOBJMETHOD(channel_init, ua_chan_init), KOBJMETHOD(channel_free, ua_chan_free), @@ -110,7 +119,8 @@ static kobj_method_t ua_chan_methods[] = { KOBJMETHOD(channel_trigger, ua_chan_trigger), KOBJMETHOD(channel_getptr, ua_chan_getptr), KOBJMETHOD(channel_getcaps, ua_chan_getcaps), - {0, 0} + KOBJMETHOD(channel_getmatrix, ua_chan_getmatrix), + KOBJMETHOD_END }; CHANNEL_DECLARE(ua_chan); @@ -141,7 +151,7 @@ ua_mixer_set(struct snd_mixer *m, unsigned type, unsigned left, unsigned right) return (left | (right << 8)); } -static int +static uint32_t ua_mixer_setrecsrc(struct snd_mixer *m, uint32_t src) { struct mtx *mtx = mixer_get_lock(m); @@ -172,8 +182,7 @@ static kobj_method_t ua_mixer_methods[] = { KOBJMETHOD(mixer_uninit, ua_mixer_uninit), KOBJMETHOD(mixer_set, ua_mixer_set), KOBJMETHOD(mixer_setrecsrc, ua_mixer_setrecsrc), - - {0, 0} + KOBJMETHOD_END }; MIXER_DECLARE(ua_mixer); diff --git a/sys/dev/sound/version.h b/sys/dev/sound/version.h index a0961af..d4f85e7 100644 --- a/sys/dev/sound/version.h +++ b/sys/dev/sound/version.h @@ -37,6 +37,6 @@ * Last 2 decimal places reserved for daily versioning, starting * with 0. */ -#define SND_DRV_VERSION 2007061600 +#define SND_DRV_VERSION 2009060800 #endif /* !_SND_VERSION_H_ */ diff --git a/sys/modules/sound/sound/Makefile b/sys/modules/sound/sound/Makefile index 8f71b02..4e48c18 100644 --- a/sys/modules/sound/sound/Makefile +++ b/sys/modules/sound/sound/Makefile @@ -9,13 +9,27 @@ KMOD= sound SRCS= device_if.h bus_if.h isa_if.h pci_if.h opt_isa.h SRCS+= ac97_if.h channel_if.h feeder_if.h mixer_if.h SRCS+= ac97_if.c channel_if.c feeder_if.c mixer_if.c +SRCS+= feeder.c feeder_rate.c feeder_volume.c +SRCS+= feeder_chain.c feeder_eq.c feeder_format.c +SRCS+= feeder_matrix.c feeder_mixer.c +SRCS+= feeder_eq_gen.h feeder_rate_gen.h snd_fxdiv_gen.h SRCS+= mpu_if.h mpufoi_if.h synth_if.h SRCS+= mpu_if.c mpufoi_if.c synth_if.c SRCS+= ac97.c ac97_patch.c buffer.c channel.c clone.c dsp.c -SRCS+= fake.c feeder.c feeder_fmt.c feeder_rate.c feeder_volume.c SRCS+= mixer.c sndstat.c sound.c unit.c vchan.c SRCS+= midi.c mpu401.c sequencer.c +feeder_eq_gen.h: + ${AWK} -f @/tools/feeder_eq_mkfilter.awk -- ${FEEDER_EQ_PRESETS} > ${.TARGET} + +feeder_rate_gen.h: + ${AWK} -f @/tools/feeder_rate_mkfilter.awk -- ${FEEDER_RATE_PRESETS} > ${.TARGET} + +snd_fxdiv_gen.h: + ${AWK} -f @/tools/snd_fxdiv_gen.awk -- > ${.TARGET} + +CLEANFILES+= feeder_eq_gen.h feeder_rate_gen.h snd_fxdiv_gen.h + EXPORT_SYMS= YES # XXX evaluate .if ${MACHINE_ARCH} == "sparc64" || ${MACHINE_ARCH} == "powerpc" diff --git a/sys/sys/soundcard.h b/sys/sys/soundcard.h index e5401f3..7f304cd 100644 --- a/sys/sys/soundcard.h +++ b/sys/sys/soundcard.h @@ -1665,7 +1665,8 @@ typedef struct #define SNDCTL_DSP_GET_CHNORDER _IOR ('P', 42, unsigned long long) #define SNDCTL_DSP_SET_CHNORDER _IOWR('P', 42, unsigned long long) # define CHID_UNDEF 0 -# define CHID_L 1 # define CHID_R 2 +# define CHID_L 1 +# define CHID_R 2 # define CHID_C 3 # define CHID_LFE 4 # define CHID_LS 5 @@ -1682,6 +1683,25 @@ typedef unsigned short oss_peaks_t[MAX_PEAK_CHANNELS]; #define SNDCTL_DSP_POLICY _IOW('P', 45, int) /* See the manual */ /* + **************************************************************************** + * Few ioctl calls that are not official parts of OSS. They have been used + * by few freeware implementations of OSS. + */ +#define SNDCTL_DSP_GETCHANNELMASK _IOWR('P', 64, int) +#define SNDCTL_DSP_BIND_CHANNEL _IOWR('P', 65, int) +#define DSP_BIND_QUERY 0x00000000 +#define DSP_BIND_FRONT 0x00000001 +#define DSP_BIND_SURR 0x00000002 +#define DSP_BIND_CENTER_LFE 0x00000004 +#define DSP_BIND_HANDSET 0x00000008 +#define DSP_BIND_MIC 0x00000010 +#define DSP_BIND_MODEM1 0x00000020 +#define DSP_BIND_MODEM2 0x00000040 +#define DSP_BIND_I2S 0x00000080 +#define DSP_BIND_SPDIF 0x00000100 +#define DSP_BIND_REAR 0x00000200 + +/* * OSS_SYSIFO is obsolete. Use SNDCTL_SYSINFO insteads. */ #define OSS_GETVERSION _IOR ('M', 118, int) diff --git a/sys/tools/feeder_eq_mkfilter.awk b/sys/tools/feeder_eq_mkfilter.awk new file mode 100644 index 0000000..5c2efb6 --- /dev/null +++ b/sys/tools/feeder_eq_mkfilter.awk @@ -0,0 +1,467 @@ +#!/usr/bin/awk -f +# +# 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. +# +# $FreeBSD$ +# + +# +# Biquad coefficients generator for Parametric Software Equalizer. Not as ugly +# as 'feeder_rate_mkfilter.awk' +# +# Based on: +# +# "Cookbook formulae for audio EQ biquad filter coefficients" +# by Robert Bristow-Johnson <rbj@audioimagination.com> +# +# - http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt +# + + + +# +# Some basic Math functions. +# +function abs(x) +{ + return (((x < 0) ? -x : x) + 0); +} + +function fabs(x) +{ + return (((x < 0.0) ? -x : x) + 0.0); +} + +function floor(x, r) +{ + r = int(x); + if (r > x) + r--; + return (r + 0); +} + +function pow(x, y) +{ + return (exp(1.0 * y * log(1.0 * x))); +} + +# +# What the hell... +# +function shl(x, y) +{ + while (y > 0) { + x *= 2; + y--; + } + return (x); +} + +function feedeq_w0(fc, rate) +{ + return ((2.0 * M_PI * fc) / (1.0 * rate)); +} + +function feedeq_A(gain, A) +{ + if (FEEDEQ_TYPE == FEEDEQ_TYPE_PEQ || FEEDEQ_TYPE == FEEDEQ_TYPE_SHELF) + A = pow(10, gain / 40.0); + else + A = sqrt(pow(10, gain / 20.0)); + + return (A); +} + +function feedeq_alpha(w0, A, QS) +{ + if (FEEDEQ_TYPE == FEEDEQ_TYPE_PEQ) + alpha = sin(w0) / (2.0 * QS); + else if (FEEDEQ_TYPE == FEEDEQ_TYPE_SHELF) + alpha = sin(w0) * 0.5 * sqrt(A + ((1.0 / A) * \ + ((1.0 / QS) - 1.0)) + 2.0); + else + alpha = 0.0; + + return (alpha); +} + +function feedeq_fx_floor(v, r) +{ + if (fabs(v) < fabs(smallest)) + smallest = v; + if (fabs(v) > fabs(largest)) + largest = v; + + r = floor((v * FEEDEQ_COEFF_ONE) + 0.5); + + if (r < INT32_MIN || r > INT32_MAX) + printf("\n#error overflow v=%f, " \ + "please reduce FEEDEQ_COEFF_SHIFT\n", v); + + return (r); +} + +function feedeq_gen_biquad_coeffs(coeffs, rate, gain, \ + w0, A, alpha, a0, a1, a2, b0, b1, b2) +{ + w0 = feedeq_w0(FEEDEQ_TREBLE_SFREQ, 1.0 * rate); + A = feedeq_A(1.0 * gain); + alpha = feedeq_alpha(w0, A, FEEDEQ_TREBLE_SLOPE); + + if (FEEDEQ_TYPE == FEEDEQ_TYPE_PEQ) { + b0 = 1.0 + (alpha * A); + b1 = -2.0 * cos(w0); + b2 = 1.0 - (alpha * A); + a0 = 1.0 + (alpha / A); + a1 = -2.0 * cos(w0); + a2 = 1.0 - (alpha / A); + } else if (FEEDEQ_TYPE == FEEDEQ_TYPE_SHELF) { + b0 = A*((A+1.0)+((A-1.0)*cos(w0))+(2.0*sqrt(A)*alpha)); + b1 = -2.0*A*((A-1.0)+((A+1.0)*cos(w0)) ); + b2 = A*((A+1.0)+((A-1.0)*cos(w0))-(2.0*sqrt(A)*alpha)); + a0 = (A+1.0)-((A-1.0)*cos(w0))+(2.0*sqrt(A)*alpha ); + a1 = 2.0 * ((A-1.0)-((A+1.0)*cos(w0)) ); + a2 = (A+1.0)-((A-1.0)*cos(w0))-(2.0*sqrt(A)*alpha ); + } else + b0 = b1 = b2 = a0 = a1 = a2 = 0.0; + + b0 /= a0; + b1 /= a0; + b2 /= a0; + a1 /= a0; + a2 /= a0; + + coeffs["treble", gain, 0] = feedeq_fx_floor(a0); + coeffs["treble", gain, 1] = feedeq_fx_floor(a1); + coeffs["treble", gain, 2] = feedeq_fx_floor(a2); + coeffs["treble", gain, 3] = feedeq_fx_floor(b0); + coeffs["treble", gain, 4] = feedeq_fx_floor(b1); + coeffs["treble", gain, 5] = feedeq_fx_floor(b2); + + w0 = feedeq_w0(FEEDEQ_BASS_SFREQ, 1.0 * rate); + A = feedeq_A(1.0 * gain); + alpha = feedeq_alpha(w0, A, FEEDEQ_BASS_SLOPE); + + if (FEEDEQ_TYPE == FEEDEQ_TYPE_PEQ) { + b0 = 1.0 + (alpha * A); + b1 = -2.0 * cos(w0); + b2 = 1.0 - (alpha * A); + a0 = 1.0 + (alpha / A); + a1 = -2.0 * cos(w0); + a2 = 1.0 - (alpha / A); + } else if (FEEDEQ_TYPE == FEEDEQ_TYPE_SHELF) { + b0 = A*((A+1.0)-((A-1.0)*cos(w0))+(2.0*sqrt(A)*alpha)); + b1 = 2.0*A*((A-1.0)-((A+1.0)*cos(w0)) ); + b2 = A*((A+1.0)-((A-1.0)*cos(w0))-(2.0*sqrt(A)*alpha)); + a0 = (A+1.0)+((A-1.0)*cos(w0))+(2.0*sqrt(A)*alpha ); + a1 = -2.0 * ((A-1.0)+((A+1.0)*cos(w0)) ); + a2 = (A+1.0)+((A-1.0)*cos(w0))-(2.0*sqrt(A)*alpha ); + } else + b0 = b1 = b2 = a0 = a1 = a2 = 0.0; + + b0 /= a0; + b1 /= a0; + b2 /= a0; + a1 /= a0; + a2 /= a0; + + coeffs["bass", gain, 0] = feedeq_fx_floor(a0); + coeffs["bass", gain, 1] = feedeq_fx_floor(a1); + coeffs["bass", gain, 2] = feedeq_fx_floor(a2); + coeffs["bass", gain, 3] = feedeq_fx_floor(b0); + coeffs["bass", gain, 4] = feedeq_fx_floor(b1); + coeffs["bass", gain, 5] = feedeq_fx_floor(b2); +} + +function feedeq_gen_freq_coeffs(frq, g, i, v) +{ + coeffs[0] = 0; + + for (g = (FEEDEQ_GAIN_MIN * FEEDEQ_GAIN_DIV); \ + g <= (FEEDEQ_GAIN_MAX * FEEDEQ_GAIN_DIV); \ + g += FEEDEQ_GAIN_STEP) { + feedeq_gen_biquad_coeffs(coeffs, frq, \ + g * FEEDEQ_GAIN_RECIPROCAL); + } + + printf("\nstatic struct feed_eq_coeff eq_%d[%d] " \ + "= {\n", frq, FEEDEQ_LEVELS); + for (g = (FEEDEQ_GAIN_MIN * FEEDEQ_GAIN_DIV); \ + g <= (FEEDEQ_GAIN_MAX * FEEDEQ_GAIN_DIV); \ + g += FEEDEQ_GAIN_STEP) { + printf(" {{ "); + for (i = 1; i < 6; i++) { + v = coeffs["treble", g * FEEDEQ_GAIN_RECIPROCAL, i]; + printf("%s0x%08x%s", \ + (v < 0) ? "-" : " ", abs(v), \ + (i == 5) ? " " : ", "); + } + printf("},\n { "); + for (i = 1; i < 6; i++) { + v = coeffs["bass", g * FEEDEQ_GAIN_RECIPROCAL, i]; + printf("%s0x%08x%s", \ + (v < 0) ? "-" : " ", abs(v), \ + (i == 5) ? " " : ", "); + } + printf("}}%s\n", \ + (g < (FEEDEQ_GAIN_MAX * FEEDEQ_GAIN_DIV)) ? "," : ""); + } + printf("};\n"); +} + +function feedeq_calc_preamp(norm, gain, shift, mul, bit, attn) +{ + shift = FEEDEQ_PREAMP_SHIFT; + + if (floor(FEEDEQ_PREAMP_BITDB) == 6 && \ + (1.0 * floor(gain)) == gain && (floor(gain) % 6) == 0) { + mul = 1; + shift = floor(floor(gain) / 6); + } else { + bit = 32.0 - ((1.0 * gain) / (1.0 * FEEDEQ_PREAMP_BITDB)); + attn = pow(2.0, bit) / pow(2.0, 32.0); + mul = floor((attn * FEEDEQ_PREAMP_ONE) + 0.5); + } + + while ((mul % 2) == 0 && shift > 0) { + mul = floor(mul / 2); + shift--; + } + + norm["mul"] = mul; + norm["shift"] = shift; +} + +BEGIN { + M_PI = atan2(0.0, -1.0); + + INT32_MAX = 1 + ((shl(1, 30) - 1) * 2); + INT32_MIN = -1 - INT32_MAX; + + FEEDEQ_TYPE_PEQ = 0; + FEEDEQ_TYPE_SHELF = 1; + + FEEDEQ_TYPE = FEEDEQ_TYPE_PEQ; + + FEEDEQ_COEFF_SHIFT = 24; + FEEDEQ_COEFF_ONE = shl(1, FEEDEQ_COEFF_SHIFT); + + FEEDEQ_PREAMP_SHIFT = 31; + FEEDEQ_PREAMP_ONE = shl(1, FEEDEQ_PREAMP_SHIFT); + FEEDEQ_PREAMP_BITDB = 6; # 20.0 * (log(2.0) / log(10.0)); + + FEEDEQ_GAIN_DIV = 10; + i = 0; + j = 1; + while (j < FEEDEQ_GAIN_DIV) { + j *= 2; + i++; + } + FEEDEQ_GAIN_SHIFT = i; + FEEDEQ_GAIN_FMASK = shl(1, FEEDEQ_GAIN_SHIFT) - 1; + + FEEDEQ_GAIN_RECIPROCAL = 1.0 / FEEDEQ_GAIN_DIV; + + if (ARGC == 2) { + i = 1; + split(ARGV[1], arg, ":"); + while (match(arg[i], "^[^0-9]*$")) { + if (arg[i] == "PEQ") { + FEEDEQ_TYPE = FEEDEQ_TYPE_PEQ; + } else if (arg[i] == "SHELF") { + FEEDEQ_TYPE = FEEDEQ_TYPE_SHELF; + } + i++; + } + split(arg[i++], subarg, ","); + FEEDEQ_TREBLE_SFREQ = 1.0 * subarg[1]; + FEEDEQ_TREBLE_SLOPE = 1.0 * subarg[2]; + split(arg[i++], subarg, ","); + FEEDEQ_BASS_SFREQ = 1.0 * subarg[1]; + FEEDEQ_BASS_SLOPE = 1.0 * subarg[2]; + split(arg[i++], subarg, ","); + FEEDEQ_GAIN_MIN = floor(1.0 * subarg[1]); + FEEDEQ_GAIN_MAX = floor(1.0 * subarg[2]); + if (length(subarg) > 2) { + j = floor(1.0 * FEEDEQ_GAIN_DIV * subarg[3]); + if (j < 2) + j = 1; + else if (j < 5) + j = 2; + else if (j < 10) + j = 5; + else + j = 10; + if (j > FEEDEQ_GAIN_DIV || (FEEDEQ_GAIN_DIV % j) != 0) + j = FEEDEQ_GAIN_DIV; + FEEDEQ_GAIN_STEP = j; + } else + FEEDEQ_GAIN_STEP = FEEDEQ_GAIN_DIV; + split(arg[i], subarg, ","); + for (i = 1; i <= length(subarg); i++) + allfreq[i - 1] = floor(1.0 * subarg[i]); + } else { + FEEDEQ_TREBLE_SFREQ = 16000.0; + FEEDEQ_TREBLE_SLOPE = 0.25; + FEEDEQ_BASS_SFREQ = 62.0; + FEEDEQ_BASS_SLOPE = 0.25; + + FEEDEQ_GAIN_MIN = -9; + FEEDEQ_GAIN_MAX = 9; + + FEEDEQ_GAIN_STEP = FEEDEQ_GAIN_DIV; + + + allfreq[0] = 44100; + allfreq[1] = 48000; + allfreq[2] = 88200; + allfreq[3] = 96000; + allfreq[4] = 176400; + allfreq[5] = 192000; + } + + FEEDEQ_LEVELS = ((FEEDEQ_GAIN_MAX - FEEDEQ_GAIN_MIN) * \ + floor(FEEDEQ_GAIN_DIV / FEEDEQ_GAIN_STEP)) + 1; + + FEEDEQ_ERR_CLIP = 0; + + smallest = 10.000000; + largest = 0.000010; + + printf("#ifndef _FEEDER_EQ_GEN_H_\n"); + printf("#define _FEEDER_EQ_GEN_H_\n\n"); + printf("/*\n"); + printf(" * Generated using feeder_eq_mkfilter.awk, heaven, wind and awesome.\n"); + printf(" *\n"); + printf(" * DO NOT EDIT!\n"); + printf(" */\n\n"); + printf("/*\n"); + printf(" * EQ: %s\n", (FEEDEQ_TYPE == FEEDEQ_TYPE_SHELF) ? \ + "Shelving" : "Peaking EQ"); + printf(" */\n"); + printf("#define FEEDER_EQ_PRESETS\t\""); + printf("%s:%d,%.4f,%d,%.4f:%d,%d,%.1f:", \ + (FEEDEQ_TYPE == FEEDEQ_TYPE_SHELF) ? "SHELF" : "PEQ", \ + FEEDEQ_TREBLE_SFREQ, FEEDEQ_TREBLE_SLOPE, \ + FEEDEQ_BASS_SFREQ, FEEDEQ_BASS_SLOPE, \ + FEEDEQ_GAIN_MIN, FEEDEQ_GAIN_MAX, \ + FEEDEQ_GAIN_STEP * FEEDEQ_GAIN_RECIPROCAL); + for (i = 0; i < length(allfreq); i++) { + if (i != 0) + printf(","); + printf("%d", allfreq[i]); + } + printf("\"\n\n"); + printf("struct feed_eq_coeff_tone {\n"); + printf("\tint32_t a1, a2;\n"); + printf("\tint32_t b0, b1, b2;\n"); + printf("};\n\n"); + printf("struct feed_eq_coeff {\n"); + #printf("\tstruct {\n"); + #printf("\t\tint32_t a1, a2;\n"); + #printf("\t\tint32_t b0, b1, b2;\n"); + #printf("\t} treble, bass;\n"); + printf("\tstruct feed_eq_coeff_tone treble;\n"); + printf("\tstruct feed_eq_coeff_tone bass;\n"); + #printf("\tstruct {\n"); + #printf("\t\tint32_t a1, a2;\n"); + #printf("\t\tint32_t b0, b1, b2;\n"); + #printf("\t} bass;\n"); + printf("};\n"); + for (i = 0; i < length(allfreq); i++) + feedeq_gen_freq_coeffs(allfreq[i]); + printf("\n"); + printf("static const struct {\n"); + printf("\tuint32_t rate;\n"); + printf("\tstruct feed_eq_coeff *coeff;\n"); + printf("} feed_eq_tab[] = {\n"); + for (i = 0; i < length(allfreq); i++) { + printf("\t{ %6d, eq_%-6d },\n", allfreq[i], allfreq[i]); + } + printf("};\n"); + + printf("\n#define FEEDEQ_RATE_MIN\t\t%d\n", allfreq[0]); + printf("#define FEEDEQ_RATE_MAX\t\t%d\n", allfreq[length(allfreq) - 1]); + printf("\n#define FEEDEQ_TAB_SIZE\t\t\t\t\t\t\t\\\n"); + printf("\t((int32_t)(sizeof(feed_eq_tab) / sizeof(feed_eq_tab[0])))\n"); + + printf("\nstatic const struct {\n"); + printf("\tint32_t mul, shift;\n"); + printf("} feed_eq_preamp[] = {\n"); + for (i = (FEEDEQ_GAIN_MAX * 2 * FEEDEQ_GAIN_DIV); i >= 0; \ + i -= FEEDEQ_GAIN_STEP) { + feedeq_calc_preamp(norm, i * FEEDEQ_GAIN_RECIPROCAL); + dbgain = ((FEEDEQ_GAIN_MAX * FEEDEQ_GAIN_DIV) - i) * \ + FEEDEQ_GAIN_RECIPROCAL; + printf("\t{ 0x%08x, 0x%08x },\t/* %+5.1f dB */\n", \ + norm["mul"], norm["shift"], dbgain); + } + printf("};\n"); + + printf("\n#define FEEDEQ_GAIN_MIN\t\t%d", FEEDEQ_GAIN_MIN); + printf("\n#define FEEDEQ_GAIN_MAX\t\t%d\n", FEEDEQ_GAIN_MAX); + + printf("\n#define FEEDEQ_GAIN_SHIFT\t%d\n", FEEDEQ_GAIN_SHIFT); + printf("#define FEEDEQ_GAIN_DIV\t\t%d\n", FEEDEQ_GAIN_DIV); + printf("#define FEEDEQ_GAIN_FMASK\t0x%08x\n", FEEDEQ_GAIN_FMASK); + printf("#define FEEDEQ_GAIN_STEP\t%d\n", FEEDEQ_GAIN_STEP); + + #printf("\n#define FEEDEQ_PREAMP_MIN\t-%d\n", \ + # shl(FEEDEQ_GAIN_MAX, FEEDEQ_GAIN_SHIFT)); + #printf("#define FEEDEQ_PREAMP_MAX\t%d\n", \ + # shl(FEEDEQ_GAIN_MAX, FEEDEQ_GAIN_SHIFT)); + + printf("\n#define FEEDEQ_COEFF_SHIFT\t%d\n", FEEDEQ_COEFF_SHIFT); + + #feedeq_calc_preamp(norm, FEEDEQ_GAIN_MAX); + + #printf("#define FEEDEQ_COEFF_NORM(v)\t("); + #if (norm["mul"] == 1) + # printf("(v) >> %d", norm["shift"]); + #else + # printf("(0x%xLL * (v)) >> %d", norm["mul"], norm["shift"]); + #printf(")\n"); + + #printf("\n#define FEEDEQ_LEVELS\t\t%d\n", FEEDEQ_LEVELS); + if (FEEDEQ_ERR_CLIP != 0) + printf("\n#define FEEDEQ_ERR_CLIP\t\t%d\n", FEEDEQ_ERR_CLIP); + printf("\n/*\n"); + printf(" * volume level mapping (0 - 100):\n"); + printf(" *\n"); + + for (i = 0; i <= 100; i++) { + ind = floor((i * FEEDEQ_LEVELS) / 100); + if (ind >= FEEDEQ_LEVELS) + ind = FEEDEQ_LEVELS - 1; + printf(" *\t%3d -> %3d (%+5.1f dB)\n", \ + i, ind, FEEDEQ_GAIN_MIN + \ + (ind * (FEEDEQ_GAIN_RECIPROCAL * FEEDEQ_GAIN_STEP))); + } + + printf(" */\n"); + printf("\n/*\n * smallest: %.32f\n * largest: %.32f\n */\n", \ + smallest, largest); + printf("\n#endif\t/* !_FEEDER_EQ_GEN_H_ */\n"); +} diff --git a/sys/tools/feeder_rate_mkfilter.awk b/sys/tools/feeder_rate_mkfilter.awk new file mode 100644 index 0000000..898c737 --- /dev/null +++ b/sys/tools/feeder_rate_mkfilter.awk @@ -0,0 +1,767 @@ +#!/usr/bin/awk -f +# +# Copyright (c) 2007-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. +# +# $FreeBSD$ +# + +# +# FIR filter design by windowing method. This might become one of the +# funniest joke I've ever written due to too many tricks being applied to +# ensure maximum precision (well, in fact this is already have the same +# precision granularity compared to its C counterpart). Nevertheless, it's +# working, precise, dynamically tunable based on "presets". +# +# XXX EXPECT TOTAL REWRITE! DON'T ARGUE! +# +# TODO: Using ultraspherical window might be a good idea. +# +# Based on: +# +# "Digital Audio Resampling" by Julius O. Smith III +# +# - http://ccrma.stanford.edu/~jos/resample/ +# + +# +# Some basic Math functions. +# +function abs(x) +{ + return (((x < 0) ? -x : x) + 0); +} + +function fabs(x) +{ + return (((x < 0.0) ? -x : x) + 0.0); +} + +function ceil(x, r) +{ + r = int(x); + if (r < x) + r++; + return (r + 0); +} + +function floor(x, r) +{ + r = int(x); + if (r > x) + r--; + return (r + 0); +} + +function pow(x, y) +{ + return (exp(1.0 * y * log(1.0 * x))); +} + +# +# What the hell... +# +function shl(x, y) +{ + while (y > 0) { + x *= 2; + y--; + } + return (x); +} + +function shr(x, y) +{ + while (y > 0 && x != 0) { + x = floor(x / 2); + y--; + } + return (x); +} + +function fx_floor(v, o, r) +{ + if (fabs(v) < fabs(smallest)) + smallest = v; + if (fabs(v) > fabs(largest)) + largest = v; + + r = floor((v * o) + 0.5); + if (r < INT32_MIN || r > INT32_MAX) + printf("\n#error overflow v=%f, please reduce %d\n", v, o); + + return (r); +} + +# +# Kaiser linear piecewise functions. +# +function kaiserAttn2Beta(attn, beta) +{ + if (attn < 0.0) + return (Z_KAISER_BETA_DEFAULT); + + if (attn > 50.0) + beta = 0.1102 * ((1.0 * attn) - 8.7); + else if (attn > 21.0) + beta = (0.5842 * pow((1.0 * attn) - 21.0, 0.4)) + \ + (0.07886 * ((1.0 * attn) - 21.0)); + else + beta = 0.0; + + return (beta); +} + +function kaiserBeta2Attn(beta, x, y, i, attn, xbeta) +{ + if (beta < Z_WINDOW_KAISER) + return (Z_KAISER_ATTN_DEFAULT); + + if (beta > kaiserAttn2Beta(50.0)) + attn = ((1.0 * beta) / 0.1102) + 8.7; + else { + x = 21.0; + y = 50.0; + attn = 0.5 * (x + y); + for (i = 0; i < 128; i++) { + xbeta = kaiserAttn2Beta(attn) + if (beta == xbeta || \ + (i > 63 && \ + fabs(beta - xbeta) < Z_KAISER_EPSILON)) + break; + if (beta > xbeta) + x = attn; + else + y = attn; + attn = 0.5 * (x + y); + } + } + + return (attn); +} + +function kaiserRolloff(len, attn) +{ + return (1.0 - (((1.0 * attn) - 7.95) / (((1.0 * len) - 1.0) * 14.36))); +} + +# +# 0th order modified Bessel function of the first kind. +# +function I0(x, s, u, n, h, t) +{ + s = n = u = 1.0; + h = x * 0.5; + + do { + t = h / n; + n += 1.0; + t *= t; + u *= t; + s += u; + } while (u >= (I0_EPSILON * s)); + + return (s); +} + +function wname(beta) +{ + if (beta >= Z_WINDOW_KAISER) + return ("Kaiser"); + else if (beta == Z_WINDOW_BLACKMAN_NUTTALL) + return ("Blackman - Nuttall"); + else if (beta == Z_WINDOW_NUTTALL) + return ("Nuttall"); + else if (beta == Z_WINDOW_BLACKMAN_HARRIS) + return ("Blackman - Harris"); + else if (beta == Z_WINDOW_BLACKMAN) + return ("Blackman"); + else if (beta == Z_WINDOW_HAMMING) + return ("Hamming"); + else if (beta == Z_WINDOW_HANN) + return ("Hann"); + else + return ("What The Hell !?!?"); +} + +function rolloff_round(x) +{ + if (x < 0.67) + x = 0.67; + else if (x > 1.0) + x = 1.0; + + return (x); +} + +function tap_round(x, y) +{ + y = floor(x + 3); + y -= y % 4; + return (y); +} + +function lpf(imp, n, rolloff, beta, num, i, j, x, nm, ibeta, w) +{ + rolloff = rolloff_round(rolloff + (Z_NYQUIST_HOVER * (1.0 - rolloff))); + imp[0] = rolloff; + + # + # Generate ideal sinc impulses, locate the last zero-crossing and pad + # the remaining with 0. + # + # Note that there are other (faster) ways of calculating this without + # the misery of traversing the entire sinc given the fact that the + # distance between each zero crossings is actually the bandwidth of + # the impulses, but it seems having 0.0001% chances of failure due to + # limited precision. + # + j = n; + for (i = 1; i < n; i++) { + x = (M_PI * i) / (1.0 * num); + imp[i] = sin(x * rolloff) / x; + if (i != 1 && (imp[i] * imp[i - 1]) <= 0.0) + j = i; + } + + for (i = j; i < n; i++) + imp[i] = 0.0; + + nm = 1.0 * (j - 1); + + if (beta >= Z_WINDOW_KAISER) + ibeta = I0(beta); + + for (i = 1; i < j; i++) { + if (beta >= Z_WINDOW_KAISER) { + # Kaiser window... + x = (1.0 * i) / nm; + w = I0(beta * sqrt(1.0 - (x * x))) / ibeta; + } else { + # Cosined windows... + x = (M_PI * i) / nm; + if (beta == Z_WINDOW_BLACKMAN_NUTTALL) { + # Blackman - Nuttall + w = 0.36335819 + (0.4891775 * cos(x)) + \ + (0.1365995 * cos(2 * x)) + \ + (0.0106411 * cos(3 * x)); + } else if (beta == Z_WINDOW_NUTTALL) { + # Nuttall + w = 0.355768 + (0.487396 * cos(x)) + \ + (0.144232 * cos(2 * x)) + \ + (0.012604 * cos(3 * x)); + } else if (beta == Z_WINDOW_BLACKMAN_HARRIS) { + # Blackman - Harris + w = 0.422323 + (0.49755 * cos(x)) + \ + (0.07922 * cos(2 * x)); + } else if (beta == Z_WINDOW_BLACKMAN) { + # Blackman + w = 0.42 + (0.50 * cos(x)) + \ + (0.08 * cos(2 * x)); + } else if (beta == Z_WINDOW_HAMMING) { + # Hamming + w = 0.54 + (0.46 * cos(x)); + } else if (beta == Z_WINDOW_HANN) { + # Hann + w = 0.50 + (0.50 * cos(x)); + } else { + # What The Hell !?!? + w = 0.0; + } + } + imp[i] *= w; + } + + imp["impulse_length"] = j; + imp["rolloff"] = rolloff; +} + +function mkfilter(imp, nmult, rolloff, beta, num, \ + nwing, mwing, nrolloff, i, dcgain, v, quality) +{ + nwing = floor((nmult * num) / 2) + 1; + + lpf(imp, nwing, rolloff, beta, num); + + mwing = imp["impulse_length"]; + nrolloff = imp["rolloff"]; + quality = imp["quality"]; + + dcgain = 0.0; + for (i = num; i < mwing; i += num) + dcgain += imp[i]; + dcgain *= 2.0; + dcgain += imp[0]; + + for (i = 0; i < nwing; i++) + imp[i] /= dcgain; + + if (quality > 2) + printf("\n"); + printf("/*\n"); + printf(" * quality = %d\n", quality); + printf(" * window = %s\n", wname(beta)); + if (beta >= Z_WINDOW_KAISER) { + printf(" * beta: %.2f\n", beta); + printf(" * stop: -%.2f dB\n", \ + kaiserBeta2Attn(beta)); + } + printf(" * length = %d\n", nmult); + printf(" * bandwidth = %.2f%%", rolloff * 100.0); + if (rolloff != nrolloff) { + printf(" + %.2f%% = %.2f%% (nyquist hover: %.2f%%)", \ + (nrolloff - rolloff) * 100.0, nrolloff * 100.0, \ + Z_NYQUIST_HOVER * 100.0); + } + printf("\n"); + printf(" * drift = %d\n", num); + printf(" * width = %d\n", mwing); + printf(" */\n"); + printf("static int32_t z_coeff_q%d[%d] = {", \ + quality, nwing + (Z_COEFF_OFFSET * 2)); + for (i = 0; i < (nwing + (Z_COEFF_OFFSET * 2)); i++) { + if ((i % 5) == 0) + printf("\n "); + if (i < Z_COEFF_OFFSET) + v = fx_floor(imp[Z_COEFF_OFFSET - i], Z_COEFF_ONE); + else if ((i - Z_COEFF_OFFSET) >= nwing) + v = fx_floor( \ + imp[nwing + nwing - i + Z_COEFF_OFFSET - 1],\ + Z_COEFF_ONE); + else + v = fx_floor(imp[i - Z_COEFF_OFFSET], Z_COEFF_ONE); + printf(" %s0x%08x,", (v < 0) ? "-" : " ", abs(v)); + } + printf("\n};\n\n"); + printf("/*\n"); + printf(" * interpolated q%d differences.\n", quality); + printf(" */\n"); + printf("static int32_t z_dcoeff_q%d[%d] = {", quality, nwing); + for (i = 1; i <= nwing; i++) { + if ((i % 5) == 1) + printf("\n "); + v = -imp[i - 1]; + if (i != nwing) + v += imp[i]; + v = fx_floor(v, Z_INTERP_COEFF_ONE); + if (abs(v) > abs(largest_interp)) + largest_interp = v; + printf(" %s0x%08x,", (v < 0) ? "-" : " ", abs(v)); + } + printf("\n};\n"); + + return (nwing); +} + +function filter_parse(s, a, i, attn, alen) +{ + split(s, a, ":"); + alen = length(a); + + if (alen == 1 || alen == 2) { + if (a[1] == "nyquist_hover") { + i = 1.0 * a[2]; + Z_NYQUIST_HOVER = (i > 0.0 && i < 1.0) ? i : 0.0; + return (-1); + } + i = 1; + if (alen == 1) { + attn = Z_KAISER_ATTN_DEFAULT; + Popts["beta"] = Z_KAISER_BETA_DEFAULT; + } else if (Z_WINDOWS[a[1]] < Z_WINDOW_KAISER) { + Popts["beta"] = Z_WINDOWS[a[1]]; + i = tap_round(a[2]); + Popts["nmult"] = i; + if (i < 28) + i = 28; + i = 1.0 - (6.44 / i); + Popts["rolloff"] = rolloff_round(i); + return (0); + } else { + attn = 1.0 * a[i++]; + Popts["beta"] = kaiserAttn2Beta(attn); + } + i = tap_round(a[i]); + Popts["nmult"] = i; + if (i > 7 && i < 28) + i = 27; + i = kaiserRolloff(i, attn); + Popts["rolloff"] = rolloff_round(i); + + return (0); + } + + if (!(alen == 3 || alen == 4)) + return (-1); + + i = 2; + + if (a[1] == "kaiser") { + if (alen > 2) + Popts["beta"] = 1.0 * a[i++]; + else + Popts["beta"] = Z_KAISER_BETA_DEFAULT; + } else if (Z_WINDOWS[a[1]] < Z_WINDOW_KAISER) + Popts["beta"] = Z_WINDOWS[a[1]]; + else if (1.0 * a[1] < Z_WINDOW_KAISER) + return (-1); + else + Popts["beta"] = kaiserAttn2Beta(1.0 * a[1]); + Popts["nmult"] = tap_round(a[i++]); + if (a[1] == "kaiser" && alen == 3) + i = kaiserRolloff(Popts["nmult"], \ + kaiserBeta2Attn(Popts["beta"])); + else + i = 1.0 * a[i]; + Popts["rolloff"] = rolloff_round(i); + + return (0); +} + +function genscale(bit, s1, s2, scale) +{ + s1 = Z_COEFF_SHIFT - (32 - bit); + s2 = Z_SHIFT + (32 - bit); + + if (s1 == 0) + scale = "v"; + else if (s1 < 0) + scale = sprintf("(v) << %d", abs(s1)); + else + scale = sprintf("(v) >> %d", s1); + + scale = sprintf("(%s) * Z_SCALE_CAST(s)", scale); + + if (s2 != 0) + scale = sprintf("(%s) >> %d", scale, s2); + + printf("#define Z_SCALE_%d(v, s)\t%s(%s)\n", \ + bit, (bit < 10) ? "\t" : "", scale); +} + +function genlerp(bit, use64, lerp) +{ + if ((bit + Z_LINEAR_SHIFT) <= 32) { + lerp = sprintf("(((y) - (x)) * (z)) >> %d", Z_LINEAR_SHIFT); + } else if (use64 != 0) { + if ((bit + Z_LINEAR_SHIFT) <= 64) { + lerp = sprintf( \ + "(((int64_t)(y) - (x)) * (z)) " \ + ">> %d", \ + Z_LINEAR_SHIFT); + } else { + lerp = sprintf( \ + "((int64_t)((y) >> %d) - ((x) >> %d)) * ", \ + "(z)" \ + bit + Z_LINEAR_SHIFT - 64, \ + bit + Z_LINEAR_SHIFT - 64); + if ((64 - bit) != 0) + lerp = sprintf("(%s) >> %d", lerp, 64 - bit); + } + } else { + lerp = sprintf( \ + "(((y) >> %d) - ((x) >> %d)) * (z)", \ + bit + Z_LINEAR_SHIFT - 32, \ + bit + Z_LINEAR_SHIFT - 32); + if ((32 - bit) != 0) + lerp = sprintf("(%s) >> %d", lerp, 32 - bit); + } + + printf("#define Z_LINEAR_INTERPOLATE_%d(z, x, y)" \ + "\t\t\t\t%s\\\n\t((x) + (%s))\n", \ + bit, (bit < 10) ? "\t" : "", lerp); +} + +BEGIN { + I0_EPSILON = 1e-21; + M_PI = atan2(0.0, -1.0); + + INT32_MAX = 1 + ((shl(1, 30) - 1) * 2); + INT32_MIN = -1 - INT32_MAX; + + Z_COEFF_OFFSET = 5; + + Z_FULL_SHIFT = 30; + Z_FULL_ONE = shl(1, Z_FULL_SHIFT); + + Z_COEFF_SHIFT = 28; + Z_COEFF_ONE = shl(1, Z_COEFF_SHIFT); + + Z_INTERP_COEFF_SHIFT = 24; + Z_INTERP_COEFF_ONE = shl(1, Z_INTERP_COEFF_SHIFT); + + # + # Filter oversampling factor. + # + # 6, 7, or 8 depending on how much you can trade off between memory + # consumption (due to large tables) and precision / quality. + # + Z_DRIFT_SHIFT = 7; + Z_DRIFT_ONE = shl(1, Z_DRIFT_SHIFT); + + Z_SHIFT = Z_FULL_SHIFT - Z_DRIFT_SHIFT; + Z_ONE = shl(1, Z_SHIFT); + Z_MASK = Z_ONE - 1; + + Z_LINEAR_FULL_SHIFT = Z_FULL_SHIFT; + Z_LINEAR_FULL_ONE = shl(1, Z_LINEAR_FULL_SHIFT); + Z_LINEAR_SHIFT = 8; + Z_LINEAR_UNSHIFT = Z_LINEAR_FULL_SHIFT - Z_LINEAR_SHIFT; + Z_LINEAR_ONE = shl(1, Z_LINEAR_SHIFT) + + # meehhhh... let it overflow... + #Z_SCALE_SHIFT = 31; + #Z_SCALE_ONE = shl(1, Z_SCALE_SHIFT); + + Z_WINDOW_KAISER = 0.0; + Z_WINDOW_BLACKMAN_NUTTALL = -1.0; + Z_WINDOW_NUTTALL = -2.0; + Z_WINDOW_BLACKMAN_HARRIS = -3.0; + Z_WINDOW_BLACKMAN = -4.0; + Z_WINDOW_HAMMING = -5.0; + Z_WINDOW_HANN = -6.0; + + Z_WINDOWS["blackman_nuttall"] = Z_WINDOW_BLACKMAN_NUTTALL; + Z_WINDOWS["nuttall"] = Z_WINDOW_NUTTALL; + Z_WINDOWS["blackman_harris"] = Z_WINDOW_BLACKMAN_HARRIS; + Z_WINDOWS["blackman"] = Z_WINDOW_BLACKMAN; + Z_WINDOWS["hamming"] = Z_WINDOW_HAMMING; + Z_WINDOWS["hann"] = Z_WINDOW_HANN; + + Z_KAISER_2_BLACKMAN_BETA = 8.568611; + Z_KAISER_2_BLACKMAN_NUTTALL_BETA = 11.98; + + Z_KAISER_ATTN_DEFAULT = 100; + Z_KAISER_BETA_DEFAULT = kaiserAttn2Beta(Z_KAISER_ATTN_DEFAULT); + + Z_KAISER_EPSILON = 1e-21; + + # + # This is practically a joke. + # + Z_NYQUIST_HOVER = 0.0; + + smallest = 10.000000; + largest = 0.000010; + largest_interp = 0; + + if (ARGC < 2) { + ARGC = 1; + ARGV[ARGC++] = "100:8:0.85"; + ARGV[ARGC++] = "100:36:0.90"; + ARGV[ARGC++] = "100:164:0.97"; + #ARGV[ARGC++] = "100:8"; + #ARGV[ARGC++] = "100:16"; + #ARGV[ARGC++] = "100:32:0.7929"; + #ARGV[ARGC++] = "100:64:0.8990"; + #ARGV[ARGC++] = "100:128:0.9499"; + } + + printf("#ifndef _FEEDER_RATE_GEN_H_\n"); + printf("#define _FEEDER_RATE_GEN_H_\n\n"); + printf("/*\n"); + printf(" * Generated using feeder_rate_mkfilter.awk, heaven, wind and awesome.\n"); + printf(" *\n"); + printf(" * DO NOT EDIT!\n"); + printf(" */\n\n"); + printf("#define FEEDER_RATE_PRESETS\t\""); + for (i = 1; i < ARGC; i++) + printf("%s%s", (i == 1) ? "" : " ", ARGV[i]); + printf("\"\n\n"); + imp["quality"] = 2; + for (i = 1; i < ARGC; i++) { + if (filter_parse(ARGV[i]) == 0) { + beta = Popts["beta"]; + nmult = Popts["nmult"]; + rolloff = Popts["rolloff"]; + ztab[imp["quality"] - 2] = \ + mkfilter(imp, nmult, rolloff, beta, Z_DRIFT_ONE); + imp["quality"]++; + } + } + + printf("\n"); + # + # XXX + # + #if (length(ztab) > 0) { + # j = 0; + # for (i = 0; i < length(ztab); i++) { + # if (ztab[i] > j) + # j = ztab[i]; + # } + # printf("static int32_t z_coeff_zero[%d] = {", j); + # for (i = 0; i < j; i++) { + # if ((i % 19) == 0) + # printf("\n"); + # printf(" 0,"); + # } + # printf("\n};\n\n"); + #} + # + # XXX + # + printf("static const struct {\n"); + printf("\tint32_t len;\n"); + printf("\tint32_t *coeff;\n"); + printf("\tint32_t *dcoeff;\n"); + printf("} z_coeff_tab[] = {\n"); + if (length(ztab) > 0) { + j = 0; + for (i = 0; i < length(ztab); i++) { + if (ztab[i] > j) + j = ztab[i]; + } + j = length(sprintf("%d", j)); + lfmt = sprintf("%%%dd", j); + j = length(sprintf("z_coeff_q%d", length(ztab) + 1)); + zcfmt = sprintf("%%-%ds", j); + zdcfmt = sprintf("%%-%ds", j + 1); + + for (i = 0; i < length(ztab); i++) { + l = sprintf(lfmt, ztab[i]); + zc = sprintf("z_coeff_q%d", i + 2); + zc = sprintf(zcfmt, zc); + zdc = sprintf("z_dcoeff_q%d", i + 2); + zdc = sprintf(zdcfmt, zdc); + printf("\t{ %s, %s, %s },\n", l, zc, zdc); + } + } else + printf("\t{ 0, NULL, NULL }\n"); + printf("};\n\n"); + + #Z_UNSHIFT = 0; + #v = shr(Z_ONE - 1, Z_UNSHIFT) * abs(largest_interp); + #while (v < 0 || v > INT32_MAX) { + # Z_UNSHIFT += 1; + # v = shr(Z_ONE - 1, Z_UNSHIFT) * abs(largest_interp); + #} + v = ((Z_ONE - 1) * abs(largest_interp)) / INT32_MAX; + Z_UNSHIFT = ceil(log(v) / log(2.0)); + Z_INTERP_SHIFT = Z_SHIFT - Z_UNSHIFT + Z_INTERP_COEFF_SHIFT; + + Z_INTERP_UNSHIFT = (Z_SHIFT - Z_UNSHIFT) + Z_INTERP_COEFF_SHIFT \ + - Z_COEFF_SHIFT; + + printf("#define Z_COEFF_TAB_SIZE\t\t\t\t\t\t\\\n"); + printf("\t((int32_t)(sizeof(z_coeff_tab) /"); + printf(" sizeof(z_coeff_tab[0])))\n\n"); + printf("#define Z_COEFF_OFFSET\t\t%d\n\n", Z_COEFF_OFFSET); + printf("#define Z_RSHIFT(x, y)\t\t(((x) + " \ + "(1 << ((y) - 1))) >> (y))\n"); + printf("#define Z_RSHIFT_L(x, y)\t(((x) + " \ + "(1LL << ((y) - 1))) >> (y))\n\n"); + printf("#define Z_FULL_SHIFT\t\t%d\n", Z_FULL_SHIFT); + printf("#define Z_FULL_ONE\t\t0x%08x%s\n", Z_FULL_ONE, \ + (Z_FULL_ONE > INT32_MAX) ? "U" : ""); + printf("\n"); + printf("#define Z_DRIFT_SHIFT\t\t%d\n", Z_DRIFT_SHIFT); + #printf("#define Z_DRIFT_ONE\t\t0x%08x\n", Z_DRIFT_ONE); + printf("\n"); + printf("#define Z_SHIFT\t\t\t%d\n", Z_SHIFT); + printf("#define Z_ONE\t\t\t0x%08x\n", Z_ONE); + printf("#define Z_MASK\t\t\t0x%08x\n", Z_MASK); + printf("\n"); + printf("#define Z_COEFF_SHIFT\t\t%d\n", Z_COEFF_SHIFT); + zinterphp = "(z) * (d)"; + zinterpunshift = Z_SHIFT + Z_INTERP_COEFF_SHIFT - Z_COEFF_SHIFT; + if (zinterpunshift > 0) { + v = (Z_ONE - 1) * abs(largest_interp); + if (v < INT32_MIN || v > INT32_MAX) + zinterphp = sprintf("(int64_t)%s", zinterphp); + zinterphp = sprintf("(%s) >> %d", zinterphp, zinterpunshift); + } else if (zinterpunshift < 0) + zinterphp = sprintf("(%s) << %d", zinterphp, \ + abs(zinterpunshift)); + if (Z_UNSHIFT == 0) + zinterp = "z"; + else + zinterp = sprintf("(z) >> %d", Z_UNSHIFT); + zinterp = sprintf("(%s) * (d)", zinterp); + if (Z_INTERP_UNSHIFT < 0) + zinterp = sprintf("(%s) << %d", zinterp, \ + abs(Z_INTERP_UNSHIFT)); + else if (Z_INTERP_UNSHIFT > 0) + zinterp = sprintf("(%s) >> %d", zinterp, Z_INTERP_UNSHIFT); + if (zinterphp != zinterp) { + printf("\n#ifdef SND_FEEDER_RATE_HP\n"); + printf("#define Z_COEFF_INTERPOLATE(z, c, d)" \ + "\t\t\t\t\t\\\n\t((c) + (%s))\n", zinterphp); + printf("#else\n"); + printf("#define Z_COEFF_INTERPOLATE(z, c, d)" \ + "\t\t\t\t\t\\\n\t((c) + (%s))\n", zinterp); + printf("#endif\n"); + } else + printf("#define Z_COEFF_INTERPOLATE(z, c, d)" \ + "\t\t\t\t\t\\\n\t((c) + (%s))\n", zinterp); + #printf("\n"); + #printf("#define Z_SCALE_SHIFT\t\t%d\n", Z_SCALE_SHIFT); + #printf("#define Z_SCALE_ONE\t\t0x%08x%s\n", Z_SCALE_ONE, \ + # (Z_SCALE_ONE > INT32_MAX) ? "U" : ""); + printf("\n"); + printf("#define Z_SCALE_CAST(s)\t\t((uint32_t)(s))\n"); + genscale(8); + genscale(16); + genscale(24); + genscale(32); + printf("\n"); + printf("#define Z_LINEAR_FULL_ONE\t0x%08xU\n", Z_LINEAR_FULL_ONE); + printf("#define Z_LINEAR_SHIFT\t\t%d\n", Z_LINEAR_SHIFT); + printf("#define Z_LINEAR_UNSHIFT\t%d\n", Z_LINEAR_UNSHIFT); + printf("#define Z_LINEAR_ONE\t\t0x%08x\n", Z_LINEAR_ONE); + printf("\n"); + printf("#ifdef SND_PCM_64\n"); + genlerp(8, 1); + genlerp(16, 1); + genlerp(24, 1); + genlerp(32, 1); + printf("#else\t/* !SND_PCM_64 */\n"); + genlerp(8, 0); + genlerp(16, 0); + genlerp(24, 0); + genlerp(32, 0); + printf("#endif\t/* SND_PCM_64 */\n"); + printf("\n"); + printf("#define Z_QUALITY_ZOH\t\t0\n"); + printf("#define Z_QUALITY_LINEAR\t1\n"); + printf("#define Z_QUALITY_SINC\t\t%d\n", \ + floor((length(ztab) - 1) / 2) + 2); + printf("\n"); + printf("#define Z_QUALITY_MIN\t\t0\n"); + printf("#define Z_QUALITY_MAX\t\t%d\n", length(ztab) + 1); + printf("\n/*\n * smallest: %.32f\n * largest: %.32f\n *\n", \ + smallest, largest); + printf(" * z_unshift=%d, z_interp_shift=%d\n *\n", \ + Z_UNSHIFT, Z_INTERP_SHIFT); + v = shr(Z_ONE - 1, Z_UNSHIFT) * abs(largest_interp); + printf(" * largest interpolation multiplication: %d\n */\n", v); + if (v < INT32_MIN || v > INT32_MAX) { + printf("\n#ifndef SND_FEEDER_RATE_HP\n"); + printf("#error interpolation overflow, please reduce" \ + " Z_INTERP_SHIFT\n"); + printf("#endif\n"); + } + + printf("\n#endif /* !_FEEDER_RATE_GEN_H_ */\n"); +} diff --git a/sys/tools/snd_fxdiv_gen.awk b/sys/tools/snd_fxdiv_gen.awk new file mode 100644 index 0000000..5828062 --- /dev/null +++ b/sys/tools/snd_fxdiv_gen.awk @@ -0,0 +1,142 @@ +#!/usr/bin/awk -f +# +# 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. +# +# $FreeBSD$ +# + +function floor(x, r) +{ + r = int(x); + if (r > x) + r--; + return (r + 0); +} + +function shl(x, y) +{ + while (y > 0) { + x *= 2; + y--; + } + return (x); +} + +function shr(x, y) +{ + while (y > 0 && x != 0) { + x = floor(x / 2); + y--; + } + return (x); +} + +function calcdiv(r, x, y, z) +{ + y = floor(FXONE / x); + z = FXSHIFT; + + while (shr((y * x), z) < 1) + y++; + + while ((y % 2) == 0 && z > 0) { + y = floor(y / 2); + z--; + } + + r["mul"] = y; + r["shift"] = z; +} + +BEGIN { + FXSHIFT = 16; + FXONE = shl(1, FXSHIFT); + + SND_CHN_MAX = 18; + + PCM_8_BPS = 1; + PCM_16_BPS = 2; + PCM_24_BPS = 3; + PCM_32_BPS = 4; + + SND_MAX_ALIGN = SND_CHN_MAX * PCM_32_BPS; + + for (i = 1; i <= SND_CHN_MAX; i++) { + aligns[PCM_8_BPS * i] = 1; + aligns[PCM_16_BPS * i] = 1; + aligns[PCM_24_BPS * i] = 1; + aligns[PCM_32_BPS * i] = 1; + } + + printf("#ifndef _SND_FXDIV_GEN_H_\n"); + printf("#define _SND_FXDIV_GEN_H_\n\n"); + + printf("/*\n"); + printf(" * Generated using snd_fxdiv_gen.awk, heaven, wind and awesome.\n"); + printf(" *\n"); + printf(" * DO NOT EDIT!\n"); + printf(" */\n\n"); + printf("#ifdef SND_USE_FXDIV\n\n"); + + printf("/*\n"); + printf(" * Fast unsigned 32bit integer division and rounding, accurate for\n"); + printf(" * x = 1 - %d. This table should be enough to handle possible\n", FXONE); + printf(" * division for 1 - 72 (more can be generated though..).\n"); + printf(" *\n"); + printf(" * 72 = SND_CHN_MAX * PCM_32_BPS, which is why....\n"); + printf(" */\n\n"); + + printf("static const uint32_t snd_fxdiv_table[][2] = {\n"); + + for (i = 1; i <= SND_MAX_ALIGN; i++) { + if (aligns[i] != 1) + continue; + calcdiv(r, i); + printf("\t[0x%02x] = { 0x%04x, 0x%02x },", \ + i, r["mul"], r["shift"]); + printf("\t/* x / %-2d = (x * %-5d) >> %-2d */\n", \ + i, r["mul"], r["shift"]); + } + + printf("};\n\n"); + + printf("#define SND_FXDIV_MAX\t\t0x%08x\n", FXONE); + printf("#define SND_FXDIV(x, y)\t\t(((uint32_t)(x) *\t\t\t\\\n"); + printf("\t\t\t\t snd_fxdiv_table[y][0]) >>\t\t\\\n"); + printf("\t\t\t\t snd_fxdiv_table[y][1])\n"); + printf("#define SND_FXROUND(x, y)\t(SND_FXDIV(x, y) * (y))\n"); + printf("#define SND_FXMOD(x, y)\t\t((x) - SND_FXROUND(x, y))\n\n"); + + printf("#else\t/* !SND_USE_FXDIV */\n\n"); + + printf("#define SND_FXDIV_MAX\t\t0x%08x\n", 131072); + printf("#define SND_FXDIV(x, y)\t\t((x) / (y))\n"); + printf("#define SND_FXROUND(x, y)\t((x) - ((x) %% (y)))\n"); + printf("#define SND_FXMOD(x, y)\t\t((x) %% (y))\n\n"); + + printf("#endif\t/* SND_USE_FXDIV */\n\n"); + + printf("#endif\t/* !_SND_FXDIV_GEN_H_ */\n"); +} |