summaryrefslogtreecommitdiffstats
path: root/sys
diff options
context:
space:
mode:
authorariff <ariff@FreeBSD.org>2009-06-07 19:12:08 +0000
committerariff <ariff@FreeBSD.org>2009-06-07 19:12:08 +0000
commite3faadaafebd1b18fd6b8ed30f654df974d390a6 (patch)
tree906f402638735c3f32e226f6868f207db569d6a9 /sys
parentf482e4a9e0f02a82e107e2bbee139c6a0b48be80 (diff)
downloadFreeBSD-src-e3faadaafebd1b18fd6b8ed30f654df974d390a6.zip
FreeBSD-src-e3faadaafebd1b18fd6b8ed30f654df974d390a6.tar.gz
Sound Mega-commit. Expect further cleanup until code freeze.
For a slightly thorough explaination, please refer to [1] http://people.freebsd.org/~ariff/SOUND_4.TXT.html . Summary of changes includes: 1 Volume Per-Channel (vpc). Provides private / standalone volume control unique per-stream pcm channel without touching master volume / pcm. Applications can directly use SNDCTL_DSP_[GET|SET][PLAY|REC]VOL, or for backwards compatibility, SOUND_MIXER_PCM through the opened dsp device instead of /dev/mixer. Special "bypass" mode is enabled through /dev/mixer which will automatically detect if the adjustment is made through /dev/mixer and forward its request to this private volume controller. Changes to this volume object will not interfere with other channels. Requirements: - SNDCTL_DSP_[GET|SET][PLAY|REC]_VOL are newer ioctls (OSSv4) which require specific application modifications (preferred). - No modifications required for using bypass mode, so applications like mplayer or xmms should work out of the box. Kernel hints: - hint.pcm.%d.vpc (0 = disable vpc). Kernel sysctls: - hw.snd.vpc_mixer_bypass (default: 1). Enable or disable /dev/mixer bypass mode. - hw.snd.vpc_autoreset (default: 1). By default, closing/opening /dev/dsp will reset the volume back to 0 db gain/attenuation. Setting this to 0 will preserve its settings across device closing/opening. - hw.snd.vpc_reset (default: 0). Panic/reset button to reset all volume settings back to 0 db. - hw.snd.vpc_0db (default: 45). 0 db relative to linear mixer value. 2 High quality fixed-point Bandlimited SINC sampling rate converter, based on Julius O'Smith's Digital Audio Resampling - http://ccrma.stanford.edu/~jos/resample/. It includes a filter design script written in awk (the clumsiest joke I've ever written) - 100% 32bit fixed-point, 64bit accumulator. - Possibly among the fastest (if not fastest) of its kind. - Resampling quality is tunable, either runtime or during kernel compilation (FEEDER_RATE_PRESETS). - Quality can be further customized during kernel compilation by defining FEEDER_RATE_PRESETS in /etc/make.conf. Kernel sysctls: - hw.snd.feeder_rate_quality. 0 - Zero-order Hold (ZOH). Fastest, bad quality. 1 - Linear Interpolation (LINEAR). Slightly slower than ZOH, better quality but still does not eliminate aliasing. 2 - (and above) - Sinc Interpolation(SINC). Best quality. SINC quality always start from 2 and above. Rough quality comparisons: - http://people.freebsd.org/~ariff/z_comparison/ 3 Bit-perfect mode. Bypasses all feeder/dsp effects. Pure sound will be directly fed into the hardware. 4 Parametric (compile time) Software Equalizer (Bass/Treble mixer). Can be customized by defining FEEDER_EQ_PRESETS in /etc/make.conf. 5 Transparent/Adaptive Virtual Channel. Now you don't have to disable vchans in order to make digital format pass through. It also makes vchans more dynamic by choosing a better format/rate among all the concurrent streams, which means that dev.pcm.X.play.vchanformat/rate becomes sort of optional. 6 Exclusive Stream, with special open() mode O_EXCL. This will "mute" other concurrent vchan streams and only allow a single channel with O_EXCL set to keep producing sound. Other Changes: * most feeder_* stuffs are compilable in userland. Let's not speculate whether we should go all out for it (save that for FreeBSD 16.0-RELEASE). * kobj signature fixups, thanks to Andriy Gapon <avg@freebsd.org> * pull out channel mixing logic out of vchan.c and create its own feeder_mixer for world justice. * various refactoring here and there, for good or bad. * activation of few more OSSv4 ioctls() (see [1] above). * opt_snd.h for possible compile time configuration: (mostly for debugging purposes, don't try these at home) SND_DEBUG SND_DIAGNOSTIC SND_FEEDER_MULTIFORMAT SND_FEEDER_FULL_MULTIFORMAT SND_FEEDER_RATE_HP SND_PCM_64 SND_OLDSTEREO Manual page updates are on the way. Tested by: joel, Olivier SMEDTS <olivier at gid0 d org>, too many unsung / unnamed heroes.
Diffstat (limited to 'sys')
-rw-r--r--sys/conf/files37
-rw-r--r--sys/conf/options9
-rw-r--r--sys/dev/sound/clone.c8
-rw-r--r--sys/dev/sound/driver.c6
-rw-r--r--sys/dev/sound/isa/ad1816.c53
-rw-r--r--sys/dev/sound/isa/ess.c50
-rw-r--r--sys/dev/sound/isa/gusc.c16
-rw-r--r--sys/dev/sound/isa/mss.c64
-rw-r--r--sys/dev/sound/isa/sb16.c36
-rw-r--r--sys/dev/sound/isa/sb8.c30
-rw-r--r--sys/dev/sound/isa/sbc.c4
-rw-r--r--sys/dev/sound/isa/sndbuf_dma.c4
-rw-r--r--sys/dev/sound/macio/aoa.c18
-rw-r--r--sys/dev/sound/macio/davbus.c18
-rw-r--r--sys/dev/sound/macio/i2s.c8
-rw-r--r--sys/dev/sound/macio/snapper.c12
-rw-r--r--sys/dev/sound/macio/tumbler.c12
-rw-r--r--sys/dev/sound/midi/midi.c20
-rw-r--r--sys/dev/sound/midi/mpu401.c54
-rw-r--r--sys/dev/sound/midi/sequencer.c22
-rw-r--r--sys/dev/sound/midi/sequencer.h2
-rw-r--r--sys/dev/sound/pci/als4000.c44
-rw-r--r--sys/dev/sound/pci/atiixp.c38
-rw-r--r--sys/dev/sound/pci/aureal.c28
-rw-r--r--sys/dev/sound/pci/cmi.c40
-rw-r--r--sys/dev/sound/pci/cs4281.c59
-rw-r--r--sys/dev/sound/pci/csa.c10
-rw-r--r--sys/dev/sound/pci/csapcm.c41
-rw-r--r--sys/dev/sound/pci/ds1.c62
-rw-r--r--sys/dev/sound/pci/emu10k1.c69
-rw-r--r--sys/dev/sound/pci/emu10kx-midi.c15
-rw-r--r--sys/dev/sound/pci/emu10kx-pcm.c76
-rw-r--r--sys/dev/sound/pci/emu10kx.c14
-rw-r--r--sys/dev/sound/pci/envy24.c49
-rw-r--r--sys/dev/sound/pci/envy24ht.c49
-rw-r--r--sys/dev/sound/pci/es137x.c46
-rw-r--r--sys/dev/sound/pci/fm801.c37
-rw-r--r--sys/dev/sound/pci/hda/hdac.c50
-rw-r--r--sys/dev/sound/pci/ich.c23
-rw-r--r--sys/dev/sound/pci/maestro.c80
-rw-r--r--sys/dev/sound/pci/maestro3.c58
-rw-r--r--sys/dev/sound/pci/neomagic.c26
-rw-r--r--sys/dev/sound/pci/solo.c54
-rw-r--r--sys/dev/sound/pci/spicds.c4
-rw-r--r--sys/dev/sound/pci/t4dwave.c60
-rw-r--r--sys/dev/sound/pci/via8233.c51
-rw-r--r--sys/dev/sound/pci/via82c686.c28
-rw-r--r--sys/dev/sound/pci/vibes.c32
-rw-r--r--sys/dev/sound/pcm/ac97.c31
-rw-r--r--sys/dev/sound/pcm/ac97_patch.c4
-rw-r--r--sys/dev/sound/pcm/buffer.c105
-rw-r--r--sys/dev/sound/pcm/buffer.h11
-rw-r--r--sys/dev/sound/pcm/channel.c1597
-rw-r--r--sys/dev/sound/pcm/channel.h221
-rw-r--r--sys/dev/sound/pcm/channel_if.m44
-rw-r--r--sys/dev/sound/pcm/dsp.c855
-rw-r--r--sys/dev/sound/pcm/dsp.h4
-rw-r--r--sys/dev/sound/pcm/fake.c164
-rw-r--r--sys/dev/sound/pcm/feeder.c447
-rw-r--r--sys/dev/sound/pcm/feeder.h188
-rw-r--r--sys/dev/sound/pcm/feeder_chain.c843
-rw-r--r--sys/dev/sound/pcm/feeder_eq.c703
-rw-r--r--sys/dev/sound/pcm/feeder_fmt.c1435
-rw-r--r--sys/dev/sound/pcm/feeder_format.c300
-rw-r--r--sys/dev/sound/pcm/feeder_matrix.c825
-rw-r--r--sys/dev/sound/pcm/feeder_mixer.c402
-rw-r--r--sys/dev/sound/pcm/feeder_rate.c2025
-rw-r--r--sys/dev/sound/pcm/feeder_volume.c385
-rw-r--r--sys/dev/sound/pcm/g711.h225
-rw-r--r--sys/dev/sound/pcm/intpcm.h136
-rw-r--r--sys/dev/sound/pcm/matrix.h218
-rw-r--r--sys/dev/sound/pcm/matrix_map.h567
-rw-r--r--sys/dev/sound/pcm/mixer.c273
-rw-r--r--sys/dev/sound/pcm/mixer.h14
-rw-r--r--sys/dev/sound/pcm/pcm.h438
-rw-r--r--sys/dev/sound/pcm/sndstat.c21
-rw-r--r--sys/dev/sound/pcm/sndstat.h163
-rw-r--r--sys/dev/sound/pcm/sound.c486
-rw-r--r--sys/dev/sound/pcm/sound.h556
-rw-r--r--sys/dev/sound/pcm/vchan.c1358
-rw-r--r--sys/dev/sound/pcm/vchan.h34
-rw-r--r--sys/dev/sound/sbus/cs4231.c36
-rw-r--r--sys/dev/sound/unit.c6
-rw-r--r--sys/dev/sound/usb/uaudio.c63
-rw-r--r--sys/dev/sound/usb/uaudio.h2
-rw-r--r--sys/dev/sound/usb/uaudio_pcm.c25
-rw-r--r--sys/dev/sound/version.h2
-rw-r--r--sys/modules/sound/sound/Makefile16
-rw-r--r--sys/sys/soundcard.h22
-rw-r--r--sys/tools/feeder_eq_mkfilter.awk467
-rw-r--r--sys/tools/feeder_rate_mkfilter.awk767
-rw-r--r--sys/tools/snd_fxdiv_gen.awk142
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");
+}
OpenPOWER on IntegriCloud