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.
This commit is contained in:
Ariff Abdullah 2009-06-07 19:12:08 +00:00
parent 0a276edef9
commit 90da2b2859
92 changed files with 12572 additions and 5890 deletions

View File

@ -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

View File

@ -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

View File

@ -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
/*

View File

@ -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;

View File

@ -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,25 +319,21 @@ 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;
sndbuf_dmasetup(ch->buffer, (dir == PCMDIR_PLAY) ? ad1816->drq1 :
ad1816->drq2);
if (SND_DMA(ch->buffer))
sndbuf_dmasetdir(ch->buffer, dir);
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);
ch->dir = dir;
return 0;
}
static int
ad1816chan_setformat(kobj_t obj, void *data, u_int32_t format)
{
@ -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);

View File

@ -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);

View File

@ -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 */

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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>

View File

@ -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>

View File

@ -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);

View File

@ -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);

View File

@ -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)) {

View File

@ -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);

View File

@ -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);

View File

@ -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

View File

@ -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";
}

View File

@ -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 @@ seq_eventthread(void *arg)
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)

View File

@ -57,7 +57,7 @@ extern int seq_debug;
if (seq_debug >= y) { \
(x); \
} \
} while(0)
} while (0)
SYSCTL_DECL(_hw_midi);

View File

@ -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);

View File

@ -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),

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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;
}

View File

@ -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 @@ envy24chan_trigger(kobj_t obj, void *data, int go)
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);

View File

@ -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 @@ envy24htchan_trigger(kobj_t obj, void *data, int go)
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);

View File

@ -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

View File

@ -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);

View File

@ -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
}
/****************************************************************************

View File

@ -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);
}

View File

@ -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");

View File

@ -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;

View File

@ -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);

View File

@ -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);

View File

@ -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>

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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>

View File

@ -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)

View File

@ -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);

File diff suppressed because it is too large Load Diff

View File

@ -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);
#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 OSSV4_EXPERIMENT
int chn_getpeaks(struct pcm_channel *c, int *lpeak, int *rpeak);
#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)
#endif
#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)
int fmtvalid(u_int32_t fmt, u_int32_t *fmtlist);
int snd_fmtvalid(uint32_t fmt, uint32_t *fmtlist);
#define AFMTSTR_NONE 0 /* "s16le" */
#define AFMTSTR_SIMPLE 1 /* "s16le:s" */
#define AFMTSTR_NUM 2 /* "s16le:2" */
#define AFMTSTR_FULL 3 /* "s16le:stereo" */
uint32_t snd_str2afmt(const char *);
uint32_t snd_afmt2str(uint32_t, char *, size_t);
#define AFMTSTR_MAXSZ 13 /* include null terminator */
#define AFMTSTR_LEN 16
#define AFMTSTR_MONO_RETURN 0
#define AFMTSTR_STEREO_RETURN 1
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_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_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_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

View File

@ -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
@ -34,12 +36,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)
{
@ -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;

File diff suppressed because it is too large Load Diff

View File

@ -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

View File

@ -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;
}

View File

@ -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,
};

View File

@ -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_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 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
enum {
FEEDER_ROOT,
FEEDER_FORMAT,
FEEDER_MIXER,
FEEDER_RATE,
FEEDER_EQ,
FEEDER_VOLUME,
FEEDER_MATRIX,
FEEDER_LAST,
};
#define FEEDRATE_SRC 1
#define FEEDRATE_DST 2
/* 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_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

View File

@ -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);
}

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);

File diff suppressed because it is too large Load Diff

View File

@ -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
#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 void (*feed_volume_t)(int *, int *, uint32_t, uint8_t *, uint32_t);
typedef uint32_t (*feed_volume_filter)(uint8_t *, int *, uint32_t);
#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 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_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 bps, channels;
feed_volume_t apply;
int volume_class;
int state;
int matrix[SND_CHN_MAX];
};
#define FEEDVOLUME_ENTRY(SIGN, BIT, ENDIAN) \
{ \
AFMT_##SIGN##BIT##_##ENDIAN, \
feed_volume_##SIGN##BIT##ENDIAN \
}
static const struct {
uint32_t format;
int bps;
feed_volume_filter filter;
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
};
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 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);
channels = 2;
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;
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);
f->data = info;
m = feeder_matrix_default_channel_map(info->channels);
if (m == NULL) {
free(info, M_DEVBUF);
return (EINVAL);
}
ret = feeder_volume_apply_matrix(f, m);
if (ret != 0)
free(info, M_DEVBUF);
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);
if (vol[0] == FVOL_MAX && vol[1] == FVOL_MAX)
f->data = NULL;
return (0);
}
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));
info = &feed_volume_tbl[FVOL_INFOIDX((intptr_t)f->data)];
smpsz = info->bps * FVOL_CHANNELS((intptr_t)f->data);
if (count < smpsz)
return (0);
vol = c->volume[SND_VOL_C_VAL(info->volume_class)];
matrix = info->matrix;
k = FEEDER_FEED(f->source, c, b, count - (count % smpsz), source);
if (k < smpsz)
return (0);
/*
* 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);
k -= k % smpsz;
return (info->filter(b, vol, k));
/* Nope, just bypass entirely. */
if (j == 0)
return (FEEDER_FEED(f->source, c, b, count, source));
dst = b;
align = info->bps * info->channels;
do {
if (count < align)
break;
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);
}

225
sys/dev/sound/pcm/g711.h Normal file
View File

@ -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_ */

136
sys/dev/sound/pcm/intpcm.h Normal file
View File

@ -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_ */

218
sys/dev/sound/pcm/matrix.h Normal file
View File

@ -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_ */

View File

@ -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_ */

View File

@ -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;
}
@ -985,6 +1058,114 @@ mixer_close(struct cdev *i_dev, int flags, int mode, struct thread *td)
return (ret);
}
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);

View File

@ -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

438
sys/dev/sound/pcm/pcm.h Normal file
View File

@ -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_ */

View File

@ -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,13 +25,15 @@
* SUCH DAMAGE.
*/
#include <dev/sound/pcm/sound.h>
#include <dev/sound/pcm/vchan.h>
#include <dev/sound/version.h>
#ifdef USING_MUTEX
#include <sys/sx.h>
#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/version.h>
#include <sys/sx.h>
SND_DECLARE_FILE("$FreeBSD$");
#define SS_TYPE_MODULE 0
@ -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);

163
sys/dev/sound/pcm/sndstat.h Normal file
View File

@ -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_ */

View File

@ -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 @@ pcm_chnalloc(struct snddev_info *d, struct pcm_channel **ch, int direction,
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,
@ -1079,7 +1092,6 @@ pcm_register(device_t dev, void *devinfo, int numplay, int numrec)
"clone_gc", CTLTYPE_INT | CTLFLAG_RW, d, sizeof(d),
sysctl_dev_pcm_clone_gc, "I",
"clone garbage collector");
#endif
#endif
if (numplay > 0 || numrec > 0) {
@ -1087,6 +1099,9 @@ pcm_register(device_t dev, void *devinfo, int numplay, int numrec)
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;

View File

@ -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.
*/
/*
* Automatically turn on 64bit arithmetic on suitable archs
* (amd64 64bit, ia64, etc..) for wider 32bit samples / integer processing.
*/
#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
#ifdef PCM_USE_64BIT_ARITH
typedef int64_t intpcm_t;
#else
typedef int32_t intpcm_t;
#endif
/* 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
/* Bytes-per-sample definition */
#define PCM_8_BPS 1
#define PCM_16_BPS 2
#define PCM_24_BPS 3
#define PCM_32_BPS 4
#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 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
/*
* 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)))
#else
#define PCM_CLAMP_S32(val) \
(((val) > PCM_S24_MAX) ? PCM_S32_MAX : \
(((val) < PCM_S24_MIN) ? PCM_S32_MIN : \
((val) << PCM_FXSHIFT)))
#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)
/* 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_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)
struct pcm_channel *fkchan_setup(device_t dev);
int fkchan_kill(struct pcm_channel *c);
#define AFMT_CONVERTIBLE (AFMT_8BIT | AFMT_16BIT | AFMT_24BIT | \
AFMT_32BIT)
/* Supported vchan mixing formats */
#define AFMT_VCHAN (AFMT_CONVERTIBLE & ~AFMT_G711)
#define AFMT_PASSTHROUGH AFMT_AC3
#define AFMT_PASSTHROUGH_RATE 48000
#define AFMT_PASSTHROUGH_CHANNEL 2
#define AFMT_PASSTHROUGH_EXTCHANNEL 0
/*
* We're simply using unused, contiguous bits from various AFMT_ definitions.
* ~(0xb00ff7ff)
*/
#define AFMT_ENCODING_MASK 0xf00fffff
#define AFMT_CHANNEL_MASK 0x01f00000
#define AFMT_CHANNEL_SHIFT 20
#define AFMT_EXTCHANNEL_MASK 0x0e000000
#define AFMT_EXTCHANNEL_SHIFT 25
#define AFMT_ENCODING(v) ((v) & AFMT_ENCODING_MASK)
#define AFMT_EXTCHANNEL(v) (((v) & AFMT_EXTCHANNEL_MASK) >> \
AFMT_EXTCHANNEL_SHIFT)
#define AFMT_CHANNEL(v) (((v) & AFMT_CHANNEL_MASK) >> \
AFMT_CHANNEL_SHIFT)
#define AFMT_BIT(v) (((v) & AFMT_32BIT) ? 32 : \
(((v) & AFMT_24BIT) ? 24 : \
((((v) & AFMT_16BIT) || \
((v) & AFMT_PASSTHROUGH)) ? 16 : 8)))
#define AFMT_BPS(v) (AFMT_BIT(v) >> 3)
#define AFMT_ALIGN(v) (AFMT_BPS(v) * AFMT_CHANNEL(v))
#define SND_FORMAT(f, c, e) (AFMT_ENCODING(f) | \
(((c) << AFMT_CHANNEL_SHIFT) & \
AFMT_CHANNEL_MASK) | \
(((e) << AFMT_EXTCHANNEL_SHIFT) & \
AFMT_EXTCHANNEL_MASK))
#define AFMT_U8_NE AFMT_U8
#define AFMT_S8_NE AFMT_S8
#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 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 AFMT_SIGNED_NE (AFMT_S8_NE | AFMT_S16_NE | AFMT_S24_NE | AFMT_S32_NE)
#define AFMT_NE (AFMT_SIGNED_NE | AFMT_U8_NE | AFMT_U16_NE | \
AFMT_U24_NE | AFMT_U32_NE)
/*
* Minor numbers for the sound driver.
@ -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)

File diff suppressed because it is too large Load Diff

View File

@ -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_ */

View File

@ -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" :

View File

@ -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!", \

View File

@ -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)
{

View File

@ -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);

View File

@ -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);

View File

@ -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_ */

View File

@ -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"

View File

@ -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
@ -1681,6 +1682,25 @@ typedef unsigned short oss_peaks_t[MAX_PEAK_CHANNELS];
#define SNDCTL_DSP_GETOPEAKS _IOR('P', 44, oss_peaks_t)
#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.
*/

View File

@ -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");
}

View File

@ -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");
}

142
sys/tools/snd_fxdiv_gen.awk Normal file
View File

@ -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");
}