Vchan/Sound/patch hard locks...
Ariff Abdullah
skywizard at MyBSD.org.my
Thu Sep 15 15:36:47 PDT 2005
On Thu, 15 Sep 2005 23:31:17 +0200
Michael Nottebrock <lofi at freebsd.org> wrote:
> On Thursday, 15. September 2005 22:00, Mike S. wrote:
>
> > Did not patch from /incoming, and also have rm'd the sound
> > sources, and
> > re-cvs'd a couple times to retry the patch since.
>
> I can confirm
> http://staff.mybsd.org.my/skywizard/FreeBSD/sound/snd_RELENG_5_20050915_041softvol.diff
>
> doesn't compile (I didn't try it before - 20050913_036softvol +
> channel.c.diff worked).
>
Hmmm strange enough.. How about this attached diff.
--
Ariff Abdullah
MyBSD
http://www.MyBSD.org.my (IPv6/IPv4)
http://staff.MyBSD.org.my (IPv6/IPv4)
http://tomoyo.MyBSD.org.my (IPv6/IPv4)
-------------- next part --------------
--- sys/conf/files.orig Fri Sep 16 06:20:38 2005
+++ sys/conf/files Fri Sep 16 06:32:00 2005
@@ -738,8 +738,10 @@
dev/sound/pcm/feeder_if.m optional sound
dev/sound/pcm/feeder_fmt.c optional sound
dev/sound/pcm/feeder_rate.c optional sound
+dev/sound/pcm/feeder_volume.c optional sound
dev/sound/pcm/mixer.c optional sound
dev/sound/pcm/mixer_if.m optional sound
+dev/sound/pcm/slavechan.c optional sound
dev/sound/pcm/sndstat.c optional sound
dev/sound/pcm/sound.c optional sound
dev/sound/pcm/vchan.c optional sound
--- sys/sys/soundcard.h.orig Fri Sep 16 06:21:09 2005
+++ sys/sys/soundcard.h Fri Sep 16 06:32:00 2005
@@ -180,6 +180,10 @@
#define AFMT_S32_BE 0x00002000 /* Big endian signed 32-bit */
#define AFMT_U32_LE 0x00004000 /* Little endian unsigned 32-bit */
#define AFMT_U32_BE 0x00008000 /* Big endian unsigned 32-bit */
+#define AFMT_S24_LE 0x00010000 /* Little endian signed 24-bit */
+#define AFMT_S24_BE 0x00020000 /* Big endian signed 24-bit */
+#define AFMT_U24_LE 0x00040000 /* Little endian unsigned 24-bit */
+#define AFMT_U24_BE 0x00080000 /* Big endian unsigned 24-bit */
#define AFMT_STEREO 0x10000000 /* can do/want stereo */
--- sys/modules/sound/sound/Makefile.orig Fri Sep 16 06:22:57 2005
+++ sys/modules/sound/sound/Makefile Fri Sep 16 06:32:00 2005
@@ -8,8 +8,8 @@
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+= ac97.c ac97_patch.c buffer.c channel.c dsp.c
-SRCS+= fake.c feeder.c feeder_fmt.c feeder_rate.c
-SRCS+= mixer.c sndstat.c sound.c vchan.c
+SRCS+= fake.c feeder.c feeder_fmt.c feeder_rate.c feeder_volume.c
+SRCS+= mixer.c slavechan.c sndstat.c sound.c vchan.c
SRCS+= sndbuf_dma.c
EXPORT_SYMS= YES # XXX evaluate
--- sys/dev/sound/isa/ad1816.c.orig Sun Jan 30 09:00:03 2005
+++ sys/dev/sound/isa/ad1816.c Fri Sep 16 06:32:00 2005
@@ -138,12 +138,16 @@
}
/* check for capture interupt */
if (sndbuf_runsz(ad1816->rch.buffer) && (c & AD1816_INTRCI)) {
+ ad1816_unlock(ad1816);
chn_intr(ad1816->rch.channel);
+ ad1816_lock(ad1816);
served |= AD1816_INTRCI; /* cp served */
}
/* check for playback interupt */
if (sndbuf_runsz(ad1816->pch.buffer) && (c & AD1816_INTRPI)) {
+ ad1816_unlock(ad1816);
chn_intr(ad1816->pch.channel);
+ ad1816_lock(ad1816);
served |= AD1816_INTRPI; /* pb served */
}
if (served == 0) {
--- sys/dev/sound/isa/ess.c.orig Sun Jan 30 09:00:03 2005
+++ sys/dev/sound/isa/ess.c Fri Sep 16 06:32:00 2005
@@ -361,8 +361,11 @@
rirq = (src & sc->rch.hwch)? 1 : 0;
if (pirq) {
- if (sc->pch.run)
+ if (sc->pch.run) {
+ ess_unlock(sc);
chn_intr(sc->pch.channel);
+ ess_lock(sc);
+ }
if (sc->pch.stopping) {
sc->pch.run = 0;
sndbuf_dma(sc->pch.buffer, PCMTRIG_STOP);
@@ -375,8 +378,11 @@
}
if (rirq) {
- if (sc->rch.run)
+ if (sc->rch.run) {
+ ess_unlock(sc);
chn_intr(sc->rch.channel);
+ ess_lock(sc);
+ }
if (sc->rch.stopping) {
sc->rch.run = 0;
sndbuf_dma(sc->rch.buffer, PCMTRIG_STOP);
--- sys/dev/sound/isa/mss.c.orig Mon Feb 28 07:32:21 2005
+++ sys/dev/sound/isa/mss.c Fri Sep 16 06:32:00 2005
@@ -795,11 +795,15 @@
c &= ~served;
if (sndbuf_runsz(mss->pch.buffer) && (c & 0x10)) {
served |= 0x10;
+ mss_unlock(mss);
chn_intr(mss->pch.channel);
+ mss_lock(mss);
}
if (sndbuf_runsz(mss->rch.buffer) && (c & 0x20)) {
served |= 0x20;
+ mss_unlock(mss);
chn_intr(mss->rch.channel);
+ mss_unlock(mss);
}
/* now ack the interrupt */
if (FULL_DUPLEX(mss)) ad_write(mss, 24, ~c); /* ack selectively */
@@ -1111,8 +1115,16 @@
return;
}
- if (sndbuf_runsz(mss->rch.buffer) && (mc11 & 8)) chn_intr(mss->rch.channel);
- if (sndbuf_runsz(mss->pch.buffer) && (mc11 & 4)) chn_intr(mss->pch.channel);
+ if (sndbuf_runsz(mss->rch.buffer) && (mc11 & 8)) {
+ mss_unlock(mss);
+ chn_intr(mss->rch.channel);
+ mss_lock(mss);
+ }
+ if (sndbuf_runsz(mss->pch.buffer) && (mc11 & 4)) {
+ mss_unlock(mss);
+ chn_intr(mss->pch.channel);
+ mss_lock(mss);
+ }
opti_wr(mss, 11, ~mc11); /* ack */
if (--loops) goto again;
mss_unlock(mss);
--- sys/dev/sound/isa/sb16.c.orig Sun Jan 30 09:00:03 2005
+++ sys/dev/sound/isa/sb16.c Fri Sep 16 06:32:00 2005
@@ -494,7 +494,7 @@
sb_intr(void *arg)
{
struct sb_info *sb = (struct sb_info *)arg;
- int reason = 3, c;
+ int reason, c;
/*
* The Vibra16X has separate flags for 8 and 16 bit transfers, but
@@ -570,8 +570,9 @@
sb_reset_dsp(sb);
if (sb->bd_flags & BD_F_SB16X) {
+ /* full-duplex doesn't work! */
pprio = sb->pch.run? 1 : 0;
- sndbuf_dmasetup(sb->pch.buffer, pprio? sb->drq1 : NULL);
+ sndbuf_dmasetup(sb->pch.buffer, pprio? sb->drq1 : sb->drq2);
sb->pch.dch = pprio? 1 : 0;
sndbuf_dmasetup(sb->rch.buffer, pprio? sb->drq2 : sb->drq1);
sb->rch.dch = pprio? 2 : 1;
--- sys/dev/sound/isa/sb8.c.orig Sun Jan 30 09:00:03 2005
+++ sys/dev/sound/isa/sb8.c Fri Sep 16 06:32:00 2005
@@ -475,11 +475,17 @@
struct sb_info *sb = (struct sb_info *)arg;
sb_lock(sb);
- if (sndbuf_runsz(sb->pch.buffer) > 0)
+ if (sndbuf_runsz(sb->pch.buffer) > 0) {
+ sb_unlock(sb);
chn_intr(sb->pch.channel);
+ sb_lock(sb);
+ }
- if (sndbuf_runsz(sb->rch.buffer) > 0)
+ if (sndbuf_runsz(sb->rch.buffer) > 0) {
+ sb_unlock(sb);
chn_intr(sb->rch.channel);
+ sb_lock(sb);
+ }
sb_rd(sb, DSP_DATA_AVAIL); /* int ack */
sb_unlock(sb);
@@ -564,8 +570,16 @@
sb_lock(sb);
if (sb->bd_flags & BD_F_HISPEED)
sb_reset_dsp(sb);
- else
+ else {
+#if 0
+ /*
+ * NOTE: DSP_CMD_DMAEXIT_8 does not work with old
+ * soundblaster.
+ */
sb_cmd(sb, DSP_CMD_DMAEXIT_8);
+#endif
+ sb_reset_dsp(sb);
+ }
if (play)
sb_cmd(sb, DSP_CMD_SPKOFF); /* speaker off */
--- sys/dev/sound/isa/sbc.c.orig Sun Jan 30 09:00:03 2005
+++ sys/dev/sound/isa/sbc.c Fri Sep 16 06:32:00 2005
@@ -259,6 +259,7 @@
{0x81167316, "ESS ES1681"}, /* ESS1681 */
{0x02017316, "ESS ES1688"}, /* ESS1688 */
+ {0x68097316, "ESS ES1688"}, /* ESS1688 */
{0x68187316, "ESS ES1868"}, /* ESS1868 */
{0x03007316, "ESS ES1869"}, /* ESS1869 */
{0x69187316, "ESS ES1869"}, /* ESS1869 */
--- sys/dev/sound/pci/als4000.c.orig Sun Jan 30 09:00:03 2005
+++ sys/dev/sound/pci/als4000.c Fri Sep 16 06:32:00 2005
@@ -75,6 +75,7 @@
struct resource *reg, *irq;
int regid, irqid;
void *ih;
+ struct mtx *lock;
unsigned int bufsz;
struct sc_chinfo pch, rch;
@@ -90,7 +91,11 @@
0
};
-static struct pcmchan_caps als_caps = { 4000, 48000, als_format, 0 };
+/*
+ * I don't believe this rotten soundcard can do 48k, really,
+ * trust me.
+ */
+static struct pcmchan_caps als_caps = { 4000, 44100, als_format, 0 };
/* ------------------------------------------------------------------------- */
/* Register Utilities */
@@ -199,6 +204,7 @@
struct sc_info *sc = devinfo;
struct sc_chinfo *ch;
+ snd_mtxlock(sc->lock);
if (dir == PCMDIR_PLAY) {
ch = &sc->pch;
ch->gcr_fifo_status = ALS_GCR_FIFO0_STATUS;
@@ -213,9 +219,11 @@
ch->format = AFMT_U8;
ch->speed = DSP_DEFAULT_SPEED;
ch->buffer = b;
- if (sndbuf_alloc(ch->buffer, sc->parent_dmat, sc->bufsz) != 0) {
+ snd_mtxunlock(sc->lock);
+
+ if (sndbuf_alloc(ch->buffer, sc->parent_dmat, sc->bufsz) != 0)
return NULL;
- }
+
return ch;
}
@@ -574,7 +582,7 @@
alsmix_setrecsrc(struct snd_mixer *m, u_int32_t src)
{
struct sc_info *sc = mix_getdevinfo(m);
- u_int32_t i, l, r;
+ u_int32_t i, l, r, mask;
for (i = l = r = 0; i < SOUND_MIXER_NRDEVICES; i++) {
if (src & (1 << i)) {
@@ -583,8 +591,24 @@
}
}
- als_mix_wr(sc, SB16_IMASK_L, l);
- als_mix_wr(sc, SB16_IMASK_R, r);
+ /* ALS mixer is really an SB16 mixer */
+
+ mask = 0;
+
+ if (src & SOUND_MASK_MIC)
+ mask |= 0x01;
+
+ if (src & SOUND_MASK_CD)
+ mask |= 0x06;
+
+ if (src & SOUND_MASK_LINE)
+ mask |= 0x18;
+
+ if (src & SOUND_MASK_SYNTH)
+ mask |= 0x60;
+
+ als_mix_wr(sc, SB16_IMASK_L, l|mask);
+ als_mix_wr(sc, SB16_IMASK_R, r|mask);
return src;
}
@@ -605,13 +629,20 @@
struct sc_info *sc = (struct sc_info *)p;
u_int8_t intr, sb_status;
+ snd_mtxlock(sc->lock);
intr = als_intr_rd(sc);
- if (intr & 0x80)
+ if (intr & 0x80) {
+ snd_mtxunlock(sc->lock);
chn_intr(sc->pch.channel);
+ snd_mtxlock(sc->lock);
+ }
- if (intr & 0x40)
+ if (intr & 0x40) {
+ snd_mtxunlock(sc->lock);
chn_intr(sc->rch.channel);
+ snd_mtxlock(sc->lock);
+ }
/* ACK interrupt in PCI core */
als_intr_wr(sc, intr);
@@ -627,6 +658,8 @@
als_ack_read(sc, ALS_MIDI_DATA);
if (sb_status & ALS_IRQ_CR1E)
als_ack_read(sc, ALS_CR1E_ACK_PORT);
+
+ snd_mtxunlock(sc->lock);
return;
}
@@ -708,6 +741,10 @@
bus_dma_tag_destroy(sc->parent_dmat);
sc->parent_dmat = 0;
}
+ if (sc->lock) {
+ snd_mtxfree(sc->lock);
+ sc->lock = NULL;
+ }
}
static int
@@ -730,7 +767,7 @@
goto bad;
}
- if (bus_setup_intr(dev, sc->irq, INTR_TYPE_AV, als_intr,
+ if (snd_setup_intr(dev, sc->irq, INTR_MPSAFE, als_intr,
sc, &sc->ih)) {
device_printf(dev, "unable to setup interrupt\n");
goto bad;
@@ -745,8 +782,8 @@
/*filter*/NULL, /*filterarg*/NULL,
/*maxsize*/sc->bufsz,
/*nsegments*/1, /*maxsegz*/0x3ffff,
- /*flags*/0, /*lockfunc*/busdma_lock_mutex,
- /*lockarg*/&Giant, &sc->parent_dmat) != 0) {
+ /*flags*/0, /*lockfunc*/NULL,
+ /*lockarg*/NULL, &sc->parent_dmat) != 0) {
device_printf(dev, "unable to create dma tag\n");
goto bad;
}
@@ -768,6 +805,7 @@
return ENXIO;
}
+ sc->lock = snd_mtxcreate(device_get_nameunit(dev), "sound softc");
sc->dev = dev;
data = pci_read_config(dev, PCIR_COMMAND, 2);
@@ -862,6 +900,7 @@
{
struct sc_info *sc = pcm_getdevinfo(dev);
+
if (als_init(sc) != 0) {
device_printf(dev, "unable to reinitialize the card\n");
return ENXIO;
@@ -879,6 +918,7 @@
if (sc->rch.dma_was_active) {
als_capture_start(&sc->rch);
}
+
return 0;
}
--- sys/dev/sound/pci/cmi.c.orig Sun Jan 30 09:00:03 2005
+++ sys/dev/sound/pci/cmi.c Fri Sep 16 06:32:00 2005
@@ -876,8 +876,8 @@
/*filter*/NULL, /*filterarg*/NULL,
/*maxsize*/sc->bufsz, /*nsegments*/1,
/*maxsegz*/0x3ffff, /*flags*/0,
- /*lockfunc*/busdma_lock_mutex,
- /*lockfunc*/&Giant,
+ /*lockfunc*/NULL,
+ /*lockfunc*/NULL,
&sc->parent_dmat) != 0) {
device_printf(dev, "cmi_attach: Unable to create dma tag\n");
goto bad;
--- sys/dev/sound/pci/es137x.c.orig Sun Jan 30 09:00:04 2005
+++ sys/dev/sound/pci/es137x.c Fri Sep 16 06:32:00 2005
@@ -61,9 +61,6 @@
SND_DECLARE_FILE("$FreeBSD: src/sys/dev/sound/pci/es137x.c,v 1.51.2.2 2005/01/30 01:00:04 imp Exp $");
-static int debug = 0;
-SYSCTL_INT(_debug, OID_AUTO, es_debug, CTLFLAG_RW, &debug, 0, "");
-
#define MEM_MAP_REG 0x14
/* PCI IDs of supported chips */
@@ -112,12 +109,14 @@
device_t dev;
int num;
+ int spdif_en;
unsigned int bufsz;
/* Contents of board's registers */
u_long ctrl;
u_long sctrl;
struct es_chinfo pch, rch;
+ struct mtx *lock;
};
/* -------------------------------------------------------------------- */
@@ -171,6 +170,38 @@
[SOUND_MIXER_OGAIN] = { 9, 0xf, 0x0, 0, 0x0000, 1 }
};
+static u_int32_t
+es_rd(struct es_info *es, int regno, int size)
+{
+ switch (size) {
+ case 1:
+ return bus_space_read_1(es->st, es->sh, regno);
+ case 2:
+ return bus_space_read_2(es->st, es->sh, regno);
+ case 4:
+ return bus_space_read_4(es->st, es->sh, regno);
+ default:
+ return 0xFFFFFFFF;
+ }
+}
+
+static void
+es_wr(struct es_info *es, int regno, u_int32_t data, int size)
+{
+
+ switch (size) {
+ case 1:
+ bus_space_write_1(es->st, es->sh, regno, data);
+ break;
+ case 2:
+ bus_space_write_2(es->st, es->sh, regno, data);
+ break;
+ case 4:
+ bus_space_write_4(es->st, es->sh, regno, data);
+ break;
+ }
+}
+
/* -------------------------------------------------------------------- */
/* The es1370 mixer interface */
@@ -244,17 +275,17 @@
static int
es1370_wrcodec(struct es_info *es, u_char i, u_char data)
{
- int wait = 100; /* 100 msec timeout */
+ u_int t;
- do {
- if ((bus_space_read_4(es->st, es->sh, ES1370_REG_STATUS) &
+ for (t = 0; t < 0x1000; t++) {
+ if ((es_rd(es, ES1370_REG_STATUS, 4) &
STAT_CSTAT) == 0) {
- bus_space_write_2(es->st, es->sh, ES1370_REG_CODEC,
- ((u_short)i << CODEC_INDEX_SHIFT) | data);
+ es_wr(es, ES1370_REG_CODEC,
+ ((u_short)i << CODEC_INDEX_SHIFT) | data, 2);
return 0;
}
- DELAY(1000);
- } while (--wait);
+ DELAY(1);
+ }
printf("pcm: es1370_wrcodec timed out\n");
return -1;
}
@@ -268,12 +299,14 @@
struct es_info *es = devinfo;
struct es_chinfo *ch = (dir == PCMDIR_PLAY)? &es->pch : &es->rch;
+ snd_mtxlock(es->lock);
ch->parent = es;
ch->channel = c;
ch->buffer = b;
ch->bufsz = es->bufsz;
ch->blksz = ch->bufsz / 2;
ch->num = ch->parent->num++;
+ snd_mtxunlock(es->lock);
if (sndbuf_alloc(ch->buffer, es->parent_dmat, ch->bufsz) != 0)
return NULL;
return ch;
@@ -286,13 +319,13 @@
struct es_info *es = ch->parent;
if (dir == PCMDIR_PLAY) {
- bus_space_write_1(es->st, es->sh, ES1370_REG_MEMPAGE, ES1370_REG_DAC2_FRAMEADR >> 8);
- bus_space_write_4(es->st, es->sh, ES1370_REG_DAC2_FRAMEADR & 0xff, sndbuf_getbufaddr(ch->buffer));
- bus_space_write_4(es->st, es->sh, ES1370_REG_DAC2_FRAMECNT & 0xff, (ch->bufsz >> 2) - 1);
+ es_wr(es, ES1370_REG_MEMPAGE, ES1370_REG_DAC2_FRAMEADR >> 8, 1);
+ es_wr(es, ES1370_REG_DAC2_FRAMEADR & 0xff, sndbuf_getbufaddr(ch->buffer), 4);
+ es_wr(es, ES1370_REG_DAC2_FRAMECNT & 0xff, (ch->bufsz >> 2) - 1, 4);
} else {
- bus_space_write_1(es->st, es->sh, ES1370_REG_MEMPAGE, ES1370_REG_ADC_FRAMEADR >> 8);
- bus_space_write_4(es->st, es->sh, ES1370_REG_ADC_FRAMEADR & 0xff, sndbuf_getbufaddr(ch->buffer));
- bus_space_write_4(es->st, es->sh, ES1370_REG_ADC_FRAMECNT & 0xff, (ch->bufsz >> 2) - 1);
+ es_wr(es, ES1370_REG_MEMPAGE, ES1370_REG_ADC_FRAMEADR >> 8, 1);
+ es_wr(es, ES1370_REG_ADC_FRAMEADR & 0xff, sndbuf_getbufaddr(ch->buffer), 4);
+ es_wr(es, ES1370_REG_ADC_FRAMECNT & 0xff, (ch->bufsz >> 2) - 1, 4);
}
ch->dir = dir;
return 0;
@@ -313,7 +346,7 @@
if (format & AFMT_S16_LE) es->sctrl |= SCTRL_R1SEB;
if (format & AFMT_STEREO) es->sctrl |= SCTRL_R1SMB;
}
- bus_space_write_4(es->st, es->sh, ES1370_REG_SERIAL_CONTROL, es->sctrl);
+ es_wr(es, ES1370_REG_SERIAL_CONTROL, es->sctrl, 4);
ch->fmt = format;
return 0;
}
@@ -326,7 +359,7 @@
es->ctrl &= ~CTRL_PCLKDIV;
es->ctrl |= DAC2_SRTODIV(speed) << CTRL_SH_PCLKDIV;
- bus_space_write_4(es->st, es->sh, ES1370_REG_CONTROL, es->ctrl);
+ es_wr(es, ES1370_REG_CONTROL, es->ctrl, 4);
/* rec/play speeds locked together - should indicate in flags */
return speed; /* XXX calc real speed */
}
@@ -336,12 +369,16 @@
{
struct es_chinfo *ch = data;
struct es_info *es = ch->parent;
+ int i, delta;
- if (ch->dir == PCMDIR_PLAY) {
- return es1371_dac_rate(es, speed, 3 - ch->num); /* play */
- } else {
- return es1371_adc_rate(es, speed, 1); /* record */
- }
+ if (ch->dir == PCMDIR_PLAY)
+ i = es1371_dac_rate(es, speed, 3 - ch->num); /* play */
+ else
+ i = es1371_adc_rate(es, speed, 1); /* record */
+ delta = (speed > i) ? speed - i : i - speed;
+ if (delta < 2)
+ return speed;
+ return i;
}
static int
@@ -352,7 +389,6 @@
ch->blksz = blocksize;
ch->bufsz = ch->blksz * 2;
sndbuf_resize(ch->buffer, 2, ch->blksz);
-
return ch->blksz;
}
@@ -374,24 +410,24 @@
es->ctrl |= CTRL_DAC2_EN;
es->sctrl &= ~(SCTRL_P2ENDINC | SCTRL_P2STINC | SCTRL_P2LOOPSEL | SCTRL_P2PAUSE | SCTRL_P2DACSEN);
es->sctrl |= SCTRL_P2INTEN | (b << SCTRL_SH_P2ENDINC);
- bus_space_write_4(es->st, es->sh, ES1370_REG_DAC2_SCOUNT, cnt);
+ es_wr(es, ES1370_REG_DAC2_SCOUNT, cnt, 4);
/* start at beginning of buffer */
- bus_space_write_4(es->st, es->sh, ES1370_REG_MEMPAGE, ES1370_REG_DAC2_FRAMECNT >> 8);
- bus_space_write_4(es->st, es->sh, ES1370_REG_DAC2_FRAMECNT & 0xff, (ch->bufsz >> 2) - 1);
+ es_wr(es, ES1370_REG_MEMPAGE, ES1370_REG_DAC2_FRAMECNT >> 8, 4);
+ es_wr(es, ES1370_REG_DAC2_FRAMECNT & 0xff, (ch->bufsz >> 2) - 1, 4);
} else es->ctrl &= ~CTRL_DAC2_EN;
} else {
if (go == PCMTRIG_START) {
es->ctrl |= CTRL_ADC_EN;
es->sctrl &= ~SCTRL_R1LOOPSEL;
es->sctrl |= SCTRL_R1INTEN;
- bus_space_write_4(es->st, es->sh, ES1370_REG_ADC_SCOUNT, cnt);
+ es_wr(es, ES1370_REG_ADC_SCOUNT, cnt, 4);
/* start at beginning of buffer */
- bus_space_write_4(es->st, es->sh, ES1370_REG_MEMPAGE, ES1370_REG_ADC_FRAMECNT >> 8);
- bus_space_write_4(es->st, es->sh, ES1370_REG_ADC_FRAMECNT & 0xff, (ch->bufsz >> 2) - 1);
+ es_wr(es, ES1370_REG_MEMPAGE, ES1370_REG_ADC_FRAMECNT >> 8, 4);
+ es_wr(es, ES1370_REG_ADC_FRAMECNT & 0xff, (ch->bufsz >> 2) - 1, 4);
} else es->ctrl &= ~CTRL_ADC_EN;
}
- bus_space_write_4(es->st, es->sh, ES1370_REG_SERIAL_CONTROL, es->sctrl);
- bus_space_write_4(es->st, es->sh, ES1370_REG_CONTROL, es->ctrl);
+ es_wr(es, ES1370_REG_SERIAL_CONTROL, es->sctrl, 4);
+ es_wr(es, ES1370_REG_CONTROL, es->ctrl, 4);
return 0;
}
@@ -406,9 +442,8 @@
reg = ES1370_REG_DAC2_FRAMECNT;
else
reg = ES1370_REG_ADC_FRAMECNT;
-
- bus_space_write_4(es->st, es->sh, ES1370_REG_MEMPAGE, reg >> 8);
- cnt = bus_space_read_4(es->st, es->sh, reg & 0x000000ff) >> 16;
+ es_wr(es, ES1370_REG_MEMPAGE, reg >> 8, 4);
+ cnt = es_rd(es, reg & 0x000000ff, 4) >> 16;
/* cnt is longwords */
return cnt << 2;
}
@@ -454,19 +489,25 @@
struct es_info *es = p;
unsigned intsrc, sctrl;
- intsrc = bus_space_read_4(es->st, es->sh, ES1370_REG_STATUS);
- if ((intsrc & STAT_INTR) == 0) return;
+ snd_mtxlock(es->lock);
+ intsrc = es_rd(es, ES1370_REG_STATUS, 4);
+ if ((intsrc & STAT_INTR) == 0) {
+ snd_mtxunlock(es->lock);
+ return;
+ }
sctrl = es->sctrl;
if (intsrc & STAT_ADC) sctrl &= ~SCTRL_R1INTEN;
if (intsrc & STAT_DAC1) sctrl &= ~SCTRL_P1INTEN;
if (intsrc & STAT_DAC2) sctrl &= ~SCTRL_P2INTEN;
- bus_space_write_4(es->st, es->sh, ES1370_REG_SERIAL_CONTROL, sctrl);
- bus_space_write_4(es->st, es->sh, ES1370_REG_SERIAL_CONTROL, es->sctrl);
+ es_wr(es, ES1370_REG_SERIAL_CONTROL, sctrl, 4);
+ es_wr(es, ES1370_REG_SERIAL_CONTROL, es->sctrl, 4);
+ snd_mtxunlock(es->lock);
if (intsrc & STAT_ADC) chn_intr(es->rch.channel);
- if (intsrc & STAT_DAC1);
+ if (intsrc & STAT_DAC1)
+ ; /* nothing */
if (intsrc & STAT_DAC2) chn_intr(es->pch.channel);
}
@@ -476,10 +517,10 @@
{
es->ctrl = CTRL_CDC_EN | CTRL_SERR_DIS |
(DAC2_SRTODIV(DSP_DEFAULT_SPEED) << CTRL_SH_PCLKDIV);
- bus_space_write_4(es->st, es->sh, ES1370_REG_CONTROL, es->ctrl);
+ es_wr(es, ES1370_REG_CONTROL, es->ctrl, 4);
es->sctrl = 0;
- bus_space_write_4(es->st, es->sh, ES1370_REG_SERIAL_CONTROL, es->sctrl);
+ es_wr(es, ES1370_REG_SERIAL_CONTROL, es->sctrl, 4);
es1370_wrcodec(es, CODEC_RES_PD, 3);/* No RST, PD */
es1370_wrcodec(es, CODEC_CSEL, 0); /* CODEC ADC and CODEC DAC use
@@ -495,37 +536,39 @@
int
es1371_init(struct es_info *es, device_t dev)
{
+ u_long cssr;
int idx;
int devid = pci_get_devid(dev);
int revid = pci_get_revid(dev);
- if (debug > 0) printf("es_init\n");
-
es->num = 0;
es->ctrl = 0;
es->sctrl = 0;
+ cssr = 0;
+ if (devid == CT4730_PCI_ID) {
+ /* XXX amplifier hack? */
+ es->ctrl |= (1 << 16);
+ }
/* initialize the chips */
+ es_wr(es, ES1370_REG_CONTROL, es->ctrl, 4);
+ es_wr(es, ES1370_REG_SERIAL_CONTROL, es->sctrl, 4);
+ es_wr(es, ES1371_REG_LEGACY, 0, 4);
if ((devid == ES1371_PCI_ID && revid == ES1371REV_ES1373_8) ||
(devid == ES1371_PCI_ID && revid == ES1371REV_CT5880_A) ||
(devid == CT5880_PCI_ID && revid == CT5880REV_CT5880_C) ||
(devid == CT5880_PCI_ID && revid == CT5880REV_CT5880_D) ||
- (devid == CT5880_PCI_ID && revid == CT5880REV_CT5880_E) ||
- (devid == CT4730_PCI_ID)) {
- bus_space_write_4(es->st, es->sh, ES1370_REG_STATUS, 0x20000000);
+ (devid == CT5880_PCI_ID && revid == CT5880REV_CT5880_E)) {
+ cssr = 1 << 29;
+ es_wr(es, ES1370_REG_STATUS, cssr, 4);
DELAY(20000);
- if (debug > 0) device_printf(dev, "ac97 2.1 enabled\n");
- } else { /* pre ac97 2.1 card */
- bus_space_write_4(es->st, es->sh, ES1370_REG_CONTROL, es->ctrl);
- if (debug > 0) device_printf(dev, "ac97 pre-2.1 enabled\n");
}
- bus_space_write_4(es->st, es->sh, ES1370_REG_SERIAL_CONTROL, es->sctrl);
- bus_space_write_4(es->st, es->sh, ES1371_REG_LEGACY, 0);
/* AC'97 warm reset to start the bitclk */
- bus_space_write_4(es->st, es->sh, ES1371_REG_LEGACY, es->ctrl | ES1371_SYNC_RES);
+ es_wr(es, ES1370_REG_CONTROL, es->ctrl | ES1371_SYNC_RES, 4);
DELAY(2000);
- bus_space_write_4(es->st, es->sh, ES1370_REG_SERIAL_CONTROL, es->ctrl);
+ es_wr(es, ES1370_REG_CONTROL, es->ctrl, 4);
+ es1371_wait_src_ready(es);
/* Init the sample rate converter */
- bus_space_write_4(es->st, es->sh, ES1371_REG_SMPRATE, ES1371_DIS_SRC);
+ es_wr(es, ES1371_REG_SMPRATE, ES1371_DIS_SRC, 4);
for (idx = 0; idx < 0x80; idx++)
es1371_src_write(es, idx, 0);
es1371_src_write(es, ES_SMPREG_DAC1 + ES_SMPREG_TRUNC_N, 16 << 4);
@@ -547,7 +590,11 @@
* be stuck high, and I've found no way to rectify this other than
* power cycle)
*/
- bus_space_write_4(es->st, es->sh, ES1371_REG_SMPRATE, 0);
+ es1371_wait_src_ready(es);
+ es_wr(es, ES1371_REG_SMPRATE, 0, 4);
+ /* try to reset codec directly */
+ es_wr(es, ES1371_REG_CODEC, 0, 4);
+ es_wr(es, ES1370_REG_STATUS, cssr, 4);
return (0);
}
@@ -557,43 +604,34 @@
static int
es1371_wrcd(kobj_t obj, void *s, int addr, u_int32_t data)
{
- int sl;
- unsigned t, x;
+ unsigned t, x, orig;
struct es_info *es = (struct es_info*)s;
- if (debug > 0) printf("wrcodec addr 0x%x data 0x%x\n", addr, data);
-
for (t = 0; t < 0x1000; t++)
- if (!(bus_space_read_4(es->st, es->sh,(ES1371_REG_CODEC & CODEC_WIP))))
+ if (!es_rd(es, ES1371_REG_CODEC & CODEC_WIP, 4))
break;
- sl = spltty();
/* save the current state for later */
- x = bus_space_read_4(es->st, es->sh, ES1371_REG_SMPRATE);
+ x = orig = es_rd(es, ES1371_REG_SMPRATE, 4);
/* enable SRC state data in SRC mux */
- bus_space_write_4(es->st, es->sh, ES1371_REG_SMPRATE,
- (es1371_wait_src_ready(s) &
- (ES1371_DIS_SRC | ES1371_DIS_P1 | ES1371_DIS_P2 | ES1371_DIS_R1)));
+ es_wr(es, ES1371_REG_SMPRATE,
+ (x &
+ (ES1371_DIS_SRC | ES1371_DIS_P1 | ES1371_DIS_P2 | ES1371_DIS_R1)) |
+ 0x00010000, 4);
+ /* busy wait */
+ for (t = 0; t < 0x1000; t++)
+ if ((es_rd(es, ES1371_REG_SMPRATE, 4) & 0x00870000) == 0x00000000)
+ break;
/* wait for a SAFE time to write addr/data and then do it, dammit */
for (t = 0; t < 0x1000; t++)
- if ((bus_space_read_4(es->st, es->sh, ES1371_REG_SMPRATE) & 0x00070000) == 0x00010000)
+ if ((es_rd(es, ES1371_REG_SMPRATE, 4) & 0x00870000) == 0x00010000)
break;
- if (debug > 2)
- printf("one b_s_w: 0x%lx 0x%x 0x%x\n",
- rman_get_start(es->reg), ES1371_REG_CODEC,
- ((addr << CODEC_POADD_SHIFT) & CODEC_POADD_MASK) |
- ((data << CODEC_PODAT_SHIFT) & CODEC_PODAT_MASK));
-
- bus_space_write_4(es->st, es->sh,ES1371_REG_CODEC,
+ es_wr(es, ES1371_REG_CODEC,
((addr << CODEC_POADD_SHIFT) & CODEC_POADD_MASK) |
- ((data << CODEC_PODAT_SHIFT) & CODEC_PODAT_MASK));
+ ((data << CODEC_PODAT_SHIFT) & CODEC_PODAT_MASK), 4);
/* restore SRC reg */
es1371_wait_src_ready(s);
- if (debug > 2)
- printf("two b_s_w: 0x%lx 0x%x 0x%x\n",
- rman_get_start(es->reg), ES1371_REG_SMPRATE, x);
- bus_space_write_4(es->st, es->sh, ES1371_REG_SMPRATE, x);
- splx(sl);
+ es_wr(es, ES1371_REG_SMPRATE, orig, 4);
return 0;
}
@@ -601,44 +639,42 @@
static int
es1371_rdcd(kobj_t obj, void *s, int addr)
{
- int sl;
- unsigned t, x = 0;
+ unsigned t, x = 0, orig;
struct es_info *es = (struct es_info *)s;
- if (debug > 0) printf("rdcodec addr 0x%x ... ", addr);
-
for (t = 0; t < 0x1000; t++)
- if (!(x = bus_space_read_4(es->st, es->sh, ES1371_REG_CODEC) & CODEC_WIP))
+ if (!(x = es_rd(es, ES1371_REG_CODEC, 4) & CODEC_WIP))
break;
- if (debug > 0) printf("loop 1 t 0x%x x 0x%x ", t, x);
-
- sl = spltty();
/* save the current state for later */
- x = bus_space_read_4(es->st, es->sh, ES1371_REG_SMPRATE);
+ x = orig = es_rd(es, ES1371_REG_SMPRATE, 4);
/* enable SRC state data in SRC mux */
- bus_space_write_4(es->st, es->sh, ES1371_REG_SMPRATE,
- (es1371_wait_src_ready(s) &
- (ES1371_DIS_SRC | ES1371_DIS_P1 | ES1371_DIS_P2 | ES1371_DIS_R1)));
+ es_wr(es, ES1371_REG_SMPRATE,
+ (x &
+ (ES1371_DIS_SRC | ES1371_DIS_P1 | ES1371_DIS_P2 | ES1371_DIS_R1)) |
+ 0x00010000, 4);
+ /* busy wait */
+ for (t = 0; t < 0x1000; t++)
+ if ((x = es_rd(es, ES1371_REG_SMPRATE, 4) & 0x00870000) == 0x00000000)
+ break;
/* wait for a SAFE time to write addr/data and then do it, dammit */
- for (t = 0; t < 0x5000; t++)
- if ((x = bus_space_read_4(es->st, es->sh, ES1371_REG_SMPRATE) & 0x00070000) == 0x00010000)
+ for (t = 0; t < 0x1000; t++)
+ if ((x = es_rd(es, ES1371_REG_SMPRATE, 4) & 0x00870000) == 0x00010000)
break;
- if (debug > 0) printf("loop 2 t 0x%x x 0x%x ", t, x);
- bus_space_write_4(es->st, es->sh, ES1371_REG_CODEC,
- ((addr << CODEC_POADD_SHIFT) & CODEC_POADD_MASK) | CODEC_PORD);
+
+ es_wr(es, ES1371_REG_CODEC,
+ ((addr << CODEC_POADD_SHIFT) & CODEC_POADD_MASK) |
+ CODEC_PORD, 4);
/* restore SRC reg */
es1371_wait_src_ready(s);
- bus_space_write_4(es->st, es->sh, ES1371_REG_SMPRATE, x);
-
- splx(sl);
+ es_wr(es, ES1371_REG_SMPRATE, orig, 4);
/* now wait for the stinkin' data (RDY) */
for (t = 0; t < 0x1000; t++)
- if ((x = bus_space_read_4(es->st, es->sh, ES1371_REG_CODEC)) & CODEC_RDY)
+ if ((x = es_rd(es, ES1371_REG_CODEC, 4)) & CODEC_RDY)
break;
- if (debug > 0) printf("loop 3 t 0x%x 0x%x ret 0x%x\n", t, x, ((x & CODEC_PIDAT_MASK) >> CODEC_PIDAT_SHIFT));
+
return ((x & CODEC_PIDAT_MASK) >> CODEC_PIDAT_SHIFT);
}
@@ -659,19 +695,19 @@
r = es1371_wait_src_ready(es) &
(ES1371_DIS_SRC | ES1371_DIS_P1 | ES1371_DIS_P2 | ES1371_DIS_R1);
r |= ES1371_SRC_RAM_ADDRO(reg);
- bus_space_write_4(es->st, es->sh, ES1371_REG_SMPRATE,r);
+ es_wr(es, ES1371_REG_SMPRATE, r, 4);
return ES1371_SRC_RAM_DATAI(es1371_wait_src_ready(es));
}
static void
-es1371_src_write(struct es_info *es, u_short reg, u_short data){
+es1371_src_write(struct es_info *es, u_short reg, u_short data)
+{
u_int r;
r = es1371_wait_src_ready(es) &
(ES1371_DIS_SRC | ES1371_DIS_P1 | ES1371_DIS_P2 | ES1371_DIS_R1);
r |= ES1371_SRC_RAM_ADDRO(reg) | ES1371_SRC_RAM_DATAO(data);
- /* printf("es1371_src_write 0x%x 0x%x\n",ES1371_REG_SMPRATE,r | ES1371_SRC_RAM_WE); */
- bus_space_write_4(es->st, es->sh, ES1371_REG_SMPRATE, r | ES1371_SRC_RAM_WE);
+ es_wr(es, ES1371_REG_SMPRATE, r | ES1371_SRC_RAM_WE, 4);
}
static u_int
@@ -721,12 +757,12 @@
dis = (set == 1)? ES1371_DIS_P2 : ES1371_DIS_P1;
r = (es1371_wait_src_ready(es) & (ES1371_DIS_SRC | ES1371_DIS_P1 | ES1371_DIS_P2 | ES1371_DIS_R1));
- bus_space_write_4(es->st, es->sh, ES1371_REG_SMPRATE, r);
+ es_wr(es, ES1371_REG_SMPRATE, r, 4);
es1371_src_write(es, dac + ES_SMPREG_INT_REGS,
(es1371_src_read(es, dac + ES_SMPREG_INT_REGS) & 0x00ff) | ((freq >> 5) & 0xfc00));
es1371_src_write(es, dac + ES_SMPREG_VFREQ_FRAC, freq & 0x7fff);
r = (es1371_wait_src_ready(es) & (ES1371_DIS_SRC | dis | ES1371_DIS_R1));
- bus_space_write_4(es->st, es->sh, ES1371_REG_SMPRATE, r);
+ es_wr(es, ES1371_REG_SMPRATE, r, 4);
}
return result;
}
@@ -736,10 +772,10 @@
{
u_int t, r;
- for (t = 0; t < 500; t++) {
- if (!((r = bus_space_read_4(es->st, es->sh, ES1371_REG_SMPRATE)) & ES1371_SRC_RAM_BUSY))
+ for (t = 0; t < 0x1000; t++) {
+ if (!((r = es_rd(es, ES1371_REG_SMPRATE, 4)) & ES1371_SRC_RAM_BUSY))
return r;
- DELAY(1000);
+ DELAY(1);
}
printf("es1371: wait src ready timeout 0x%x [0x%x]\n", ES1371_REG_SMPRATE, r);
return 0;
@@ -832,6 +868,107 @@
}
}
+#ifdef SND_DYNSYSCTL
+static int
+sysctl_es1371x_spdif_enable(SYSCTL_HANDLER_ARGS)
+{
+ struct es_info *es;
+ device_t dev;
+ int err, new_en, r;
+
+ dev = oidp->oid_arg1;
+ es = pcm_getdevinfo(dev);
+ snd_mtxlock(es->lock);
+ new_en = es->spdif_en;
+ snd_mtxunlock(es->lock);
+ err = sysctl_handle_int(oidp, &new_en, sizeof(new_en), req);
+
+ if (err || req->newptr == NULL)
+ return err;
+ if (new_en < 0 || new_en > 1)
+ return EINVAL;
+
+ snd_mtxlock(es->lock);
+ es->spdif_en = new_en;
+ r = es_rd(es, ES1370_REG_STATUS, 4);
+ if (new_en) {
+ r |= ENABLE_SPDIF;
+ es->ctrl |= SPDIFEN_B;
+ es->ctrl |= RECEN_B;
+ } else {
+ r &= ~ENABLE_SPDIF;
+ es->ctrl &= ~SPDIFEN_B;
+ es->ctrl &= ~RECEN_B;
+ }
+ es_wr(es, ES1370_REG_CONTROL, es->ctrl, 4);
+ es_wr(es, ES1370_REG_STATUS, r, 4);
+ snd_mtxunlock(es->lock);
+ return 0;
+}
+
+static int
+sysctl_es1371x_latency_timer(SYSCTL_HANDLER_ARGS)
+{
+ struct es_info *es;
+ device_t dev;
+ int err, val;
+
+ dev = oidp->oid_arg1;
+ es = pcm_getdevinfo(dev);
+ snd_mtxlock(es->lock);
+ val = pci_read_config(dev, PCIR_LATTIMER, 1);
+ snd_mtxunlock(es->lock);
+ err = sysctl_handle_int(oidp, &val, sizeof(val), req);
+
+ if (err || req->newptr == NULL)
+ return err;
+ if (val < 0 || val > 255)
+ return EINVAL;
+
+ snd_mtxlock(es->lock);
+ pci_write_config(dev, PCIR_LATTIMER, val, 1);
+ snd_mtxunlock(es->lock);
+ return 0;
+}
+#endif /* SND_DYNSYSCTL */
+
+static void
+es_init_sysctls(device_t dev)
+{
+#ifdef SND_DYNSYSCTL
+ struct es_info *es;
+ int r, devid, revid;
+
+ devid = pci_get_devid(dev);
+ revid = pci_get_revid(dev);
+ es = pcm_getdevinfo(dev);
+ if ((devid == ES1371_PCI_ID && revid == ES1371REV_ES1373_8) ||
+ (devid == ES1371_PCI_ID && revid == ES1371REV_CT5880_A) ||
+ (devid == CT5880_PCI_ID && revid == CT5880REV_CT5880_C) ||
+ (devid == CT5880_PCI_ID && revid == CT5880REV_CT5880_D) ||
+ (devid == CT5880_PCI_ID && revid == CT5880REV_CT5880_E)) {
+ r = es_rd(es, ES1370_REG_STATUS, 4);
+ es->spdif_en = (r & ENABLE_SPDIF) ? 1 : 0;
+ SYSCTL_ADD_PROC(snd_sysctl_tree(dev),
+ SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)),
+ OID_AUTO, "spdif_enabled",
+ CTLTYPE_INT | CTLFLAG_RW, dev, sizeof(dev),
+ sysctl_es1371x_spdif_enable, "I",
+ "Enable S/PDIF output on primary playback channel");
+ }
+ if (resource_int_value(device_get_name(dev),
+ device_get_unit(dev), "latency_timer", &r) == 0 &&
+ !(r < 0 || r > 255))
+ pci_write_config(dev, PCIR_LATTIMER, r, 1);
+ SYSCTL_ADD_PROC(snd_sysctl_tree(dev),
+ SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)),
+ OID_AUTO, "latency_timer",
+ CTLTYPE_INT | CTLFLAG_RW, dev, sizeof(dev),
+ sysctl_es1371x_latency_timer, "I",
+ "PCI Latency Timer configuration");
+#endif /* SND_DYNSYSCTL */
+}
+
static int
es_pci_attach(device_t dev)
{
@@ -841,12 +978,13 @@
char status[SND_STATUSLEN];
struct ac97_info *codec = 0;
kobj_class_t ct = NULL;
+ int devid, revid;
if ((es = malloc(sizeof *es, M_DEVBUF, M_NOWAIT | M_ZERO)) == NULL) {
device_printf(dev, "cannot allocate softc\n");
return ENXIO;
}
-
+ es->lock = snd_mtxcreate(device_get_nameunit(dev), "sound softc");
es->dev = dev;
mapped = 0;
data = pci_read_config(dev, PCIR_COMMAND, 2);
@@ -882,10 +1020,11 @@
es->bufsz = pcm_getbuffersize(dev, 4096, ES_DEFAULT_BUFSZ, 65536);
- if (pci_get_devid(dev) == ES1371_PCI_ID ||
- pci_get_devid(dev) == ES1371_PCI_ID2 ||
- pci_get_devid(dev) == CT5880_PCI_ID ||
- pci_get_devid(dev) == CT4730_PCI_ID) {
+ devid = pci_get_devid(dev);
+ revid = pci_get_revid(dev);
+
+ if (devid == ES1371_PCI_ID || devid == ES1371_PCI_ID2 ||
+ devid == CT5880_PCI_ID || devid == CT4730_PCI_ID) {
if(-1 == es1371_init(es, dev)) {
device_printf(dev, "unable to initialize the card\n");
goto bad;
@@ -897,7 +1036,7 @@
/* ac97_mixer.init = NULL; */
if (mixer_init(dev, ac97_getmixerclass(), codec)) goto bad;
ct = &eschan1371_class;
- } else if (pci_get_devid(dev) == ES1370_PCI_ID) {
+ } else if (devid == ES1370_PCI_ID) {
if (-1 == es1370_init(es)) {
device_printf(dev, "unable to initialize the card\n");
goto bad;
@@ -909,7 +1048,7 @@
es->irqid = 0;
es->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &es->irqid,
RF_ACTIVE | RF_SHAREABLE);
- if (!es->irq || snd_setup_intr(dev, es->irq, 0, es_intr, es, &es->ih)) {
+ if (!es->irq || snd_setup_intr(dev, es->irq, INTR_MPSAFE, es_intr, es, &es->ih)) {
device_printf(dev, "unable to map interrupt\n");
goto bad;
}
@@ -919,8 +1058,8 @@
/*highaddr*/BUS_SPACE_MAXADDR,
/*filter*/NULL, /*filterarg*/NULL,
/*maxsize*/es->bufsz, /*nsegments*/1, /*maxsegz*/0x3ffff,
- /*flags*/0, /*lockfunc*/busdma_lock_mutex,
- /*lockarg*/&Giant, &es->parent_dmat) != 0) {
+ /*flags*/0, /*lockfunc*/NULL,
+ /*lockarg*/NULL, &es->parent_dmat) != 0) {
device_printf(dev, "unable to create dma tag\n");
goto bad;
}
@@ -932,16 +1071,18 @@
if (pcm_register(dev, es, 1, 1)) goto bad;
pcm_addchan(dev, PCMDIR_REC, ct, es);
pcm_addchan(dev, PCMDIR_PLAY, ct, es);
+ es_init_sysctls(dev);
pcm_setstatus(dev, status);
return 0;
bad:
- if (codec) ac97_destroy(codec);
- if (es->reg) bus_release_resource(dev, es->regtype, es->regid, es->reg);
+ if (es->parent_dmat) bus_dma_tag_destroy(es->parent_dmat);
if (es->ih) bus_teardown_intr(dev, es->irq, es->ih);
if (es->irq) bus_release_resource(dev, SYS_RES_IRQ, es->irqid, es->irq);
- if (es->parent_dmat) bus_dma_tag_destroy(es->parent_dmat);
+ if (codec) ac97_destroy(codec);
+ if (es->reg) bus_release_resource(dev, es->regtype, es->regid, es->reg);
+ if (es->lock) snd_mtxfree(es->lock);
if (es) free(es, M_DEVBUF);
return ENXIO;
}
@@ -953,14 +1094,14 @@
struct es_info *es;
r = pcm_unregister(dev);
- if (r)
- return r;
+ if (r) return r;
es = pcm_getdevinfo(dev);
- bus_release_resource(dev, es->regtype, es->regid, es->reg);
+ bus_dma_tag_destroy(es->parent_dmat);
bus_teardown_intr(dev, es->irq, es->ih);
bus_release_resource(dev, SYS_RES_IRQ, es->irqid, es->irq);
- bus_dma_tag_destroy(es->parent_dmat);
+ bus_release_resource(dev, es->regtype, es->regid, es->reg);
+ snd_mtxfree(es->lock);
free(es, M_DEVBUF);
return 0;
--- sys/dev/sound/pci/es137x.h.orig Sun Jan 30 09:00:04 2005
+++ sys/dev/sound/pci/es137x.h Fri Sep 16 06:32:00 2005
@@ -167,6 +167,17 @@
#define ES1371_SRC_RAM_DATAI(i) (((i)>>0)&0xffff) /* current value of the sample rate converter */
/*
+ * S/PDIF specific
+ */
+
+/* Use ES1370_REG_CONTROL */
+#define RECEN_B 0x08000000 /* Used to control mixing of analog with digital data */
+#define SPDIFEN_B 0x04000000 /* Reset to switch digital output mux to "THRU" mode */
+/* Use ES1370_REG_STATUS */
+#define ENABLE_SPDIF 0x00040000 /* Used to enable the S/PDIF circuitry */
+#define TEST_SPDIF 0x00020000 /* Used to put the S/PDIF module in "test mode" */
+
+/*
* Sample rate converter addresses
*/
--- sys/dev/sound/pci/via8233.c.orig Sun Jan 30 09:00:04 2005
+++ sys/dev/sound/pci/via8233.c Fri Sep 16 06:32:00 2005
@@ -100,12 +100,14 @@
struct ac97_info *codec;
unsigned int bufsz;
+ int spdif_en, dxs_src;
struct via_chinfo pch[NDXSCHANS + NMSGDCHANS];
struct via_chinfo rch[NWRCHANS];
struct via_dma_op *sgd_table;
u_int16_t codec_caps;
u_int16_t n_dxs_registered;
+ struct mtx *lock;
};
static u_int32_t via_fmt[] = {
@@ -119,6 +121,91 @@
static struct pcmchan_caps via_vracaps = { 4000, 48000, via_fmt, 0 };
static struct pcmchan_caps via_caps = { 48000, 48000, via_fmt, 0 };
+#ifdef SND_DYNSYSCTL
+static int
+sysctl_via8233_spdif_enable(SYSCTL_HANDLER_ARGS)
+{
+ struct via_info *via;
+ device_t dev;
+ int err, new_en, r;
+
+ dev = oidp->oid_arg1;
+ via = pcm_getdevinfo(dev);
+ snd_mtxlock(via->lock);
+ new_en = via->spdif_en;
+ snd_mtxunlock(via->lock);
+ err = sysctl_handle_int(oidp, &new_en, sizeof(new_en), req);
+
+ if (err || req->newptr == NULL)
+ return err;
+ if (new_en < 0 || new_en > 1)
+ return EINVAL;
+
+ snd_mtxlock(via->lock);
+ via->spdif_en = new_en;
+
+ r = pci_read_config(dev, VIA_PCI_SPDIF, 1) & ~VIA_SPDIF_EN;
+ if (new_en)
+ r |= VIA_SPDIF_EN;
+ pci_write_config(dev, VIA_PCI_SPDIF, r, 1);
+ snd_mtxunlock(via->lock);
+
+ return 0;
+}
+
+static int
+sysctl_via8233_dxs_src(SYSCTL_HANDLER_ARGS)
+{
+ struct via_info *via;
+ device_t dev;
+ int err, val;
+
+ dev = oidp->oid_arg1;
+ via = pcm_getdevinfo(dev);
+ snd_mtxlock(via->lock);
+ val = via->dxs_src;
+ snd_mtxunlock(via->lock);
+ err = sysctl_handle_int(oidp, &val, sizeof(val), req);
+
+ if (err || req->newptr == NULL)
+ return err;
+ if (val < 0 || val > 1)
+ return EINVAL;
+
+ snd_mtxlock(via->lock);
+ via->dxs_src = val;
+ snd_mtxunlock(via->lock);
+
+ return 0;
+}
+#endif /* SND_DYNSYSCTL */
+
+static void
+via_init_sysctls(device_t dev)
+{
+#ifdef SND_DYNSYSCTL
+ struct via_info *via;
+ int r;
+
+ via = pcm_getdevinfo(dev);
+ r = pci_read_config(dev, VIA_PCI_SPDIF, 1);
+ via->spdif_en = (r & VIA_SPDIF_EN) ? 1 : 0;
+
+ SYSCTL_ADD_PROC(snd_sysctl_tree(dev),
+ SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)),
+ OID_AUTO, "spdif_enabled",
+ CTLTYPE_INT | CTLFLAG_RW, dev, sizeof(dev),
+ sysctl_via8233_spdif_enable, "I",
+ "Enable S/PDIF output on primary playback channel");
+ SYSCTL_ADD_PROC(snd_sysctl_tree(dev),
+ SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)),
+ OID_AUTO, "via_dxs_src",
+ CTLTYPE_INT | CTLFLAG_RW, dev, sizeof(dev),
+ sysctl_via8233_dxs_src, "I",
+ "Enable VIA DXS Sample Rate Converter");
+#endif
+}
+
static u_int32_t
via_rd(struct via_info *via, int regno, int size)
{
@@ -253,7 +340,7 @@
{
struct via_chinfo *ch = data;
struct via_info *via = ch->parent;
-
+
u_int32_t f = WR_FORMAT_STOP_INDEX;
if (format & AFMT_STEREO)
@@ -270,9 +357,10 @@
{
struct via_chinfo *ch = data;
struct via_info *via = ch->parent;
+ u_int32_t r, v;
- u_int32_t r = ch->rbase + VIA8233_RP_DXS_RATEFMT;
- u_int32_t v = via_rd(via, r, 4);
+ r = ch->rbase + VIA8233_RP_DXS_RATEFMT;
+ v = via_rd(via, r, 4);
v &= ~(VIA8233_DXS_RATEFMT_STEREO | VIA8233_DXS_RATEFMT_16BIT);
if (format & AFMT_STEREO)
@@ -316,11 +404,10 @@
struct via_chinfo *ch = data;
struct via_info *via = ch->parent;
- u_int32_t spd = 48000;
- if (via->codec_caps & AC97_EXTCAP_VRA) {
- spd = ac97_setrate(via->codec, AC97_REGEXT_LADCRATE, speed);
- }
- return spd;
+ if (via->codec_caps & AC97_EXTCAP_VRA)
+ return ac97_setrate(via->codec, AC97_REGEXT_LADCRATE, speed);
+
+ return 48000;
}
static int
@@ -328,9 +415,10 @@
{
struct via_chinfo *ch = data;
struct via_info *via = ch->parent;
+ u_int32_t r, v;
- u_int32_t r = ch->rbase + VIA8233_RP_DXS_RATEFMT;
- u_int32_t v = via_rd(via, r, 4) & ~VIA8233_DXS_RATEFMT_48K;
+ r = ch->rbase + VIA8233_RP_DXS_RATEFMT;
+ v = via_rd(via, r, 4) & ~VIA8233_DXS_RATEFMT_48K;
/* Careful to avoid overflow (divide by 48 per vt8233c docs) */
@@ -362,7 +450,7 @@
struct via_info *via = ch->parent;
/* Controlled by ac97 registers */
- if (via->codec_caps & AC97_EXTCAP_VRA)
+ if (via->codec_caps & AC97_EXTCAP_VRA)
return &via_vracaps;
return &via_caps;
}
@@ -370,7 +458,17 @@
static struct pcmchan_caps *
via8233dxs_getcaps(kobj_t obj, void *data)
{
- /* Controlled by onboard registers */
+ struct via_chinfo *ch = data;
+ struct via_info *via = ch->parent;
+
+ /*
+ * Controlled by onboard registers
+ *
+ * Apparently, few boards can do DXS sample rate
+ * conversion.
+ */
+ if (via->dxs_src)
+ return &via_vracaps;
return &via_caps;
}
@@ -381,7 +479,7 @@
struct via_info *via = ch->parent;
/* Controlled by ac97 registers */
- if (via->codec_caps & AC97_EXTCAP_VRA)
+ if (via->codec_caps & AC97_EXTCAP_VRA)
return &via_vracaps;
return &via_caps;
}
@@ -393,6 +491,7 @@
via8233chan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize)
{
struct via_chinfo *ch = data;
+
sndbuf_resize(ch->buffer, SEGS_PER_CHAN, blocksize);
ch->blksz = sndbuf_getblksz(ch->buffer);
return ch->blksz;
@@ -403,11 +502,13 @@
{
struct via_chinfo *ch = data;
struct via_info *via = ch->parent;
+ u_int32_t v, index, count;
+ int ptr;
- u_int32_t v = via_rd(via, ch->rbase + VIA_RP_CURRENT_COUNT, 4);
- u_int32_t index = v >> 24; /* Last completed buffer */
- u_int32_t count = v & 0x00ffffff; /* Bytes remaining */
- int ptr = (index + 1) * ch->blksz - count;
+ v = via_rd(via, ch->rbase + VIA_RP_CURRENT_COUNT, 4);
+ index = v >> 24; /* Last completed buffer */
+ count = v & 0x00ffffff; /* Bytes remaining */
+ ptr = (index + 1) * ch->blksz - count;
ptr %= SEGS_PER_CHAN * ch->blksz; /* Wrap to available space */
return ptr;
@@ -439,6 +540,7 @@
struct via_info *via = devinfo;
struct via_chinfo *ch = &via->rch[c->num];
+ snd_mtxlock(via->lock);
ch->parent = via;
ch->channel = c;
ch->buffer = b;
@@ -446,11 +548,15 @@
ch->rbase = VIA_WR_BASE(c->num);
via_wr(via, ch->rbase + VIA_WR_RP_SGD_FORMAT, WR_FIFO_ENABLE, 1);
+ snd_mtxunlock(via->lock);
if (sndbuf_alloc(ch->buffer, via->parent_dmat, via->bufsz) != 0)
return NULL;
+
+ snd_mtxlock(via->lock);
via8233chan_sgdinit(via, ch, c->num);
via8233chan_reset(via, ch);
+ snd_mtxunlock(via->lock);
return ch;
}
@@ -462,6 +568,7 @@
struct via_info *via = devinfo;
struct via_chinfo *ch = &via->pch[c->num];
+ snd_mtxlock(via->lock);
ch->parent = via;
ch->channel = c;
ch->buffer = b;
@@ -474,11 +581,15 @@
*/
ch->rbase = VIA_DXS_BASE(NDXSCHANS - 1 - via->n_dxs_registered);
via->n_dxs_registered++;
+ snd_mtxunlock(via->lock);
if (sndbuf_alloc(ch->buffer, via->parent_dmat, via->bufsz) != 0)
return NULL;
+
+ snd_mtxlock(via->lock);
via8233chan_sgdinit(via, ch, NWRCHANS + c->num);
via8233chan_reset(via, ch);
+ snd_mtxunlock(via->lock);
return ch;
}
@@ -490,16 +601,21 @@
struct via_info *via = devinfo;
struct via_chinfo *ch = &via->pch[c->num];
+ snd_mtxlock(via->lock);
ch->parent = via;
ch->channel = c;
ch->buffer = b;
ch->dir = dir;
ch->rbase = VIA_MC_SGD_STATUS;
+ snd_mtxunlock(via->lock);
if (sndbuf_alloc(ch->buffer, via->parent_dmat, via->bufsz) != 0)
return NULL;
+
+ snd_mtxlock(via->lock);
via8233chan_sgdinit(via, ch, NWRCHANS + c->num);
via8233chan_reset(via, ch);
+ snd_mtxunlock(via->lock);
return ch;
}
@@ -590,16 +706,19 @@
int i, stat;
/* Poll playback channels */
+ snd_mtxlock(via->lock);
for (i = 0; i < NDXSCHANS + NMSGDCHANS; i++) {
if (via->pch[i].rbase == 0)
continue;
stat = via->pch[i].rbase + VIA_RP_STATUS;
if (via_rd(via, stat, 1) & SGD_STATUS_INTR) {
via_wr(via, stat, SGD_STATUS_INTR, 1);
+ snd_mtxunlock(via->lock);
chn_intr(via->pch[i].channel);
+ snd_mtxlock(via->lock);
}
}
-
+
/* Poll record channels */
for (i = 0; i < NWRCHANS; i++) {
if (via->rch[i].rbase == 0)
@@ -607,9 +726,12 @@
stat = via->rch[i].rbase + VIA_RP_STATUS;
if (via_rd(via, stat, 1) & SGD_STATUS_INTR) {
via_wr(via, stat, SGD_STATUS_INTR, 1);
+ snd_mtxunlock(via->lock);
chn_intr(via->rch[i].channel);
+ snd_mtxlock(via->lock);
}
}
+ snd_mtxunlock(via->lock);
}
/*
@@ -710,65 +832,22 @@
return ENXIO;
}
-#ifdef SND_DYNSYSCTL
-static int via8233_spdif_en;
-
-static int
-sysctl_via8233_spdif_enable(SYSCTL_HANDLER_ARGS)
-{
- device_t dev;
- int err, new_en, r;
-
- new_en = via8233_spdif_en;
- err = sysctl_handle_int(oidp, &new_en, sizeof(new_en), req);
- if (err || req->newptr == NULL)
- return err;
-
- if (new_en < 0 || new_en > 1)
- return EINVAL;
- via8233_spdif_en = new_en;
-
- dev = oidp->oid_arg1;
- r = pci_read_config(dev, VIA_PCI_SPDIF, 1) & ~VIA_SPDIF_EN;
- if (new_en)
- r |= VIA_SPDIF_EN;
- pci_write_config(dev, VIA_PCI_SPDIF, r, 1);
- return 0;
-}
-#endif /* SND_DYNSYSCTL */
-
-static void
-via_init_sysctls(device_t dev)
-{
-#ifdef SND_DYNSYSCTL
- int r;
-
- r = pci_read_config(dev, VIA_PCI_SPDIF, 1);
- via8233_spdif_en = (r & VIA_SPDIF_EN) ? 1 : 0;
-
- SYSCTL_ADD_PROC(snd_sysctl_tree(dev),
- SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)),
- OID_AUTO, "spdif_enabled",
- CTLTYPE_INT | CTLFLAG_RW, dev, sizeof(dev),
- sysctl_via8233_spdif_enable, "I",
- "Enable S/PDIF output on primary playback channel");
-#endif
-}
-
static int
via_attach(device_t dev)
{
struct via_info *via = 0;
char status[SND_STATUSLEN];
+ int i, via_dxs_disabled, via_dxs_src, via_dxs_chnum, via_sgd_chnum;
if ((via = malloc(sizeof *via, M_DEVBUF, M_NOWAIT | M_ZERO)) == NULL) {
device_printf(dev, "cannot allocate softc\n");
return ENXIO;
}
+ via->lock = snd_mtxcreate(device_get_nameunit(dev), "sound softc");
pci_set_powerstate(dev, PCI_POWERSTATE_D0);
pci_enable_busmaster(dev);
-
+
via->regid = PCIR_BAR(0);
via->reg = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &via->regid,
RF_ACTIVE);
@@ -785,7 +864,7 @@
via->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &via->irqid,
RF_ACTIVE | RF_SHAREABLE);
if (!via->irq ||
- snd_setup_intr(dev, via->irq, 0, via_intr, via, &via->ih)) {
+ snd_setup_intr(dev, via->irq, INTR_MPSAFE, via_intr, via, &via->ih)) {
device_printf(dev, "unable to map interrupt\n");
goto bad;
}
@@ -796,8 +875,8 @@
/*highaddr*/BUS_SPACE_MAXADDR,
/*filter*/NULL, /*filterarg*/NULL,
/*maxsize*/via->bufsz, /*nsegments*/1, /*maxsegz*/0x3ffff,
- /*flags*/0, /*lockfunc*/busdma_lock_mutex,
- /*lockarg*/&Giant, &via->parent_dmat) != 0) {
+ /*flags*/0, /*lockfunc*/NULL,
+ /*lockarg*/NULL, &via->parent_dmat) != 0) {
device_printf(dev, "unable to create dma tag\n");
goto bad;
}
@@ -813,8 +892,8 @@
/*filter*/NULL, /*filterarg*/NULL,
/*maxsize*/NSEGS * sizeof(struct via_dma_op),
/*nsegments*/1, /*maxsegz*/0x3ffff,
- /*flags*/0, /*lockfunc*/busdma_lock_mutex,
- /*lockarg*/&Giant, &via->sgd_dmat) != 0) {
+ /*flags*/0, /*lockfunc*/NULL,
+ /*lockarg*/NULL, &via->sgd_dmat) != 0) {
device_printf(dev, "unable to create dma tag\n");
goto bad;
}
@@ -850,28 +929,71 @@
snprintf(status, SND_STATUSLEN, "at io 0x%lx irq %ld %s",
rman_get_start(via->reg), rman_get_start(via->irq),PCM_KLDSTRING(snd_via8233));
- /* Register */
+ /*
+ * Decide whether DXS had to be disabled or not
+ */
if (pci_get_revid(dev) == VIA8233_REV_ID_8233A) {
- if (pcm_register(dev, via, NMSGDCHANS, 1)) goto bad;
/*
* DXS channel is disabled. Reports from multiple users
* that it plays at half-speed. Do not see this behaviour
* on available 8233C or when emulating 8233A register set
* on 8233C (either with or without ac97 VRA).
- pcm_addchan(dev, PCMDIR_PLAY, &via8233dxs_class, via);
*/
- pcm_addchan(dev, PCMDIR_PLAY, &via8233msgd_class, via);
- pcm_addchan(dev, PCMDIR_REC, &via8233wr_class, via);
+ via_dxs_disabled = 1;
+ } else if (resource_int_value(device_get_name(dev),
+ device_get_unit(dev), "via_dxs_disabled",
+ &via_dxs_disabled) == 0)
+ via_dxs_disabled = (via_dxs_disabled > 0) ? 1 : 0;
+ else
+ via_dxs_disabled = 0;
+
+ if (via_dxs_disabled) {
+ via_dxs_chnum = 0;
+ via_sgd_chnum = 1;
} else {
- int i;
- if (pcm_register(dev, via, NMSGDCHANS + NDXSCHANS, NWRCHANS)) goto bad;
- for (i = 0; i < NDXSCHANS; i++)
- pcm_addchan(dev, PCMDIR_PLAY, &via8233dxs_class, via);
- pcm_addchan(dev, PCMDIR_PLAY, &via8233msgd_class, via);
- for (i = 0; i < NWRCHANS; i++)
- pcm_addchan(dev, PCMDIR_REC, &via8233wr_class, via);
+ if (resource_int_value(device_get_name(dev),
+ device_get_unit(dev), "via_dxs_channels",
+ &via_dxs_chnum) != 0)
+ via_dxs_chnum = NDXSCHANS;
+ if (resource_int_value(device_get_name(dev),
+ device_get_unit(dev), "via_sgd_channels",
+ &via_sgd_chnum) != 0)
+ via_sgd_chnum = NMSGDCHANS;
+ }
+ if (via_dxs_chnum > NDXSCHANS)
+ via_dxs_chnum = NDXSCHANS;
+ else if (via_dxs_chnum < 0)
+ via_dxs_chnum = 0;
+ if (via_sgd_chnum > NMSGDCHANS)
+ via_sgd_chnum = NMSGDCHANS;
+ else if (via_sgd_chnum < 0)
+ via_sgd_chnum = 0;
+ if (via_dxs_chnum + via_sgd_chnum < 1) {
+ /* Minimalist ? */
+ via_dxs_chnum = 1;
+ via_sgd_chnum = 0;
+ }
+ if (via_dxs_chnum > 0 && resource_int_value(device_get_name(dev),
+ device_get_unit(dev), "via_dxs_src",
+ &via_dxs_src) == 0)
+ via->dxs_src = (via_dxs_src > 0) ? 1 : 0;
+ else
+ via->dxs_src = 0;
+ /* Register */
+ if (pcm_register(dev, via, via_dxs_chnum + via_sgd_chnum, NWRCHANS))
+ goto bad;
+ for (i = 0; i < via_dxs_chnum; i++)
+ pcm_addchan(dev, PCMDIR_PLAY, &via8233dxs_class, via);
+ for (i = 0; i < via_sgd_chnum; i++)
+ pcm_addchan(dev, PCMDIR_PLAY, &via8233msgd_class, via);
+ for (i = 0; i < NWRCHANS; i++)
+ pcm_addchan(dev, PCMDIR_REC, &via8233wr_class, via);
+ if (via_dxs_chnum > 0)
via_init_sysctls(dev);
- }
+ device_printf(dev, "<VIA DXS %sabled: DXS%s %d / SGD %d / REC %d>\n",
+ (via_dxs_chnum > 0) ? "En" : "Dis",
+ (via->dxs_src) ? "(SRC)" : "",
+ via_dxs_chnum, via_sgd_chnum, NWRCHANS);
pcm_setstatus(dev, status);
@@ -884,6 +1006,7 @@
if (via->parent_dmat) bus_dma_tag_destroy(via->parent_dmat);
if (via->sgd_dmamap) bus_dmamap_unload(via->sgd_dmat, via->sgd_dmamap);
if (via->sgd_dmat) bus_dma_tag_destroy(via->sgd_dmat);
+ if (via->lock) snd_mtxfree(via->lock);
if (via) free(via, M_DEVBUF);
return ENXIO;
}
@@ -904,6 +1027,7 @@
bus_dma_tag_destroy(via->parent_dmat);
bus_dmamap_unload(via->sgd_dmat, via->sgd_dmamap);
bus_dma_tag_destroy(via->sgd_dmat);
+ snd_mtxfree(via->lock);
free(via, M_DEVBUF);
return 0;
}
--- sys/dev/sound/pci/via82c686.c.orig Sun Jan 30 09:00:04 2005
+++ sys/dev/sound/pci/via82c686.c Fri Sep 16 06:32:00 2005
@@ -86,6 +86,7 @@
struct via_chinfo pch, rch;
struct via_dma_op *sgd_table;
u_int16_t codec_caps;
+ struct mtx *lock;
};
static u_int32_t via_fmt[] = {
@@ -244,6 +245,7 @@
struct via_info *via = devinfo;
struct via_chinfo *ch;
+ snd_mtxlock(via->lock);
if (dir == PCMDIR_PLAY) {
ch = &via->pch;
ch->base = VIA_PLAY_DMAOPS_BASE;
@@ -266,9 +268,11 @@
ch->channel = c;
ch->buffer = b;
ch->dir = dir;
+ snd_mtxunlock(via->lock);
if (sndbuf_alloc(ch->buffer, via->parent_dmat, via->bufsz) != 0)
return NULL;
+
return ch;
}
@@ -417,22 +421,25 @@
via_intr(void *p)
{
struct via_info *via = p;
- int st;
/* DEB(printf("viachan_intr\n")); */
/* Read channel */
- st = via_rd(via, VIA_PLAY_STAT, 1);
- if (st & VIA_RPSTAT_INTR) {
+ snd_mtxlock(via->lock);
+ if (via_rd(via, VIA_PLAY_STAT, 1) & VIA_RPSTAT_INTR) {
via_wr(via, VIA_PLAY_STAT, VIA_RPSTAT_INTR, 1);
+ snd_mtxunlock(via->lock);
chn_intr(via->pch.channel);
+ snd_mtxlock(via->lock);
}
/* Write channel */
- st = via_rd(via, VIA_RECORD_STAT, 1);
- if (st & VIA_RPSTAT_INTR) {
+ if (via_rd(via, VIA_RECORD_STAT, 1) & VIA_RPSTAT_INTR) {
via_wr(via, VIA_RECORD_STAT, VIA_RPSTAT_INTR, 1);
+ snd_mtxunlock(via->lock);
chn_intr(via->rch.channel);
+ return;
}
+ snd_mtxunlock(via->lock);
}
/*
@@ -468,6 +475,7 @@
device_printf(dev, "cannot allocate softc\n");
return ENXIO;
}
+ via->lock = snd_mtxcreate(device_get_nameunit(dev), "sound softc");
/* Get resources */
data = pci_read_config(dev, PCIR_COMMAND, 2);
@@ -521,7 +529,7 @@
via->irqid = 0;
via->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &via->irqid,
RF_ACTIVE | RF_SHAREABLE);
- if (!via->irq || snd_setup_intr(dev, via->irq, 0, via_intr, via, &via->ih)) {
+ if (!via->irq || snd_setup_intr(dev, via->irq, INTR_MPSAFE, via_intr, via, &via->ih)) {
device_printf(dev, "unable to map interrupt\n");
goto bad;
}
@@ -546,8 +554,8 @@
/*highaddr*/BUS_SPACE_MAXADDR,
/*filter*/NULL, /*filterarg*/NULL,
/*maxsize*/via->bufsz, /*nsegments*/1, /*maxsegz*/0x3ffff,
- /*flags*/0, /*lockfunc*/busdma_lock_mutex,
- /*lockarg*/&Giant, &via->parent_dmat) != 0) {
+ /*flags*/0, /*lockfunc*/NULL,
+ /*lockarg*/NULL, &via->parent_dmat) != 0) {
device_printf(dev, "unable to create dma tag\n");
goto bad;
}
@@ -563,8 +571,8 @@
/*filter*/NULL, /*filterarg*/NULL,
/*maxsize*/NSEGS * sizeof(struct via_dma_op),
/*nsegments*/1, /*maxsegz*/0x3ffff,
- /*flags*/0, /*lockfunc*/busdma_lock_mutex,
- /*lockarg*/&Giant, &via->sgd_dmat) != 0) {
+ /*flags*/0, /*lockfunc*/NULL,
+ /*lockarg*/NULL, &via->sgd_dmat) != 0) {
device_printf(dev, "unable to create dma tag\n");
goto bad;
}
@@ -594,6 +602,7 @@
if (via->parent_dmat) bus_dma_tag_destroy(via->parent_dmat);
if (via->sgd_dmamap) bus_dmamap_unload(via->sgd_dmat, via->sgd_dmamap);
if (via->sgd_dmat) bus_dma_tag_destroy(via->sgd_dmat);
+ if (via->lock) snd_mtxfree(via->lock);
if (via) free(via, M_DEVBUF);
return ENXIO;
}
@@ -615,6 +624,7 @@
bus_dma_tag_destroy(via->parent_dmat);
bus_dmamap_unload(via->sgd_dmat, via->sgd_dmamap);
bus_dma_tag_destroy(via->sgd_dmat);
+ snd_mtxfree(via->lock);
free(via, M_DEVBUF);
return 0;
}
--- sys/dev/sound/pcm/ac97.c.orig Sun May 1 22:31:06 2005
+++ sys/dev/sound/pcm/ac97.c Fri Sep 16 06:32:00 2005
@@ -118,6 +118,11 @@
{ 0x57454300, "Winbond" },
{ 0x574d4c00, "Wolfson" },
{ 0x594d4800, "Yamaha" },
+ /*
+ * XXX This is a fluke, really! The real vendor
+ * should be SigmaTel, not this! This should be
+ * removed someday!
+ */
{ 0x01408300, "Creative" },
{ 0x00000000, NULL }
};
@@ -158,6 +163,7 @@
{ 0x43525960, 0x07, 0, "CS4291A", 0 },
{ 0x434d4961, 0x00, 0, "CMI9739", 0 },
{ 0x434d4941, 0x00, 0, "CMI9738", 0 },
+ { 0x434d4983, 0x00, 0, "CMI9761", 0 },
{ 0x43585421, 0x00, 0, "HSD11246", 0 },
{ 0x43585428, 0x07, 0, "CX20468", 0 },
{ 0x44543000, 0x00, 0, "DT0398", 0 },
@@ -211,6 +217,11 @@
{ 0x594d4800, 0x00, 0, "YMF743", 0 },
{ 0x594d4802, 0x00, 0, "YMF752", 0 },
{ 0x594d4803, 0x00, 0, "YMF753", 0 },
+ /*
+ * XXX This is a fluke, really! The real codec
+ * should be STAC9704, not this! This should be
+ * removed someday!
+ */
{ 0x01408384, 0x00, 0, "EV1938", 0 },
{ 0, 0, 0, NULL, 0 }
};
@@ -283,6 +294,21 @@
u_int16_t
ac97_rdcd(struct ac97_info *codec, int reg)
{
+ if (codec->flags & AC97_F_RDCD_BUG) {
+ u_int16_t i[2], j = 100;
+
+ i[0] = AC97_READ(codec->methods, codec->devinfo, reg);
+ i[1] = AC97_READ(codec->methods, codec->devinfo, reg);
+ while (i[0] != i[1] && j)
+ i[j-- & 1] = AC97_READ(codec->methods, codec->devinfo, reg);
+#if 0
+ if (j < 100) {
+ device_printf(codec->dev, "%s(): Inconsistent register value at"
+ " 0x%08x (retry: %d)\n", __func__, reg, 100 - j);
+ }
+#endif
+ return i[!(j & 1)];
+ }
return AC97_READ(codec->methods, codec->devinfo, reg);
}
@@ -452,14 +478,16 @@
*/
snd_mtxlock(codec->lock);
if (e->mask) {
- int cur = ac97_rdcd(codec, e->reg);
+ int cur = ac97_rdcd(codec, reg);
val |= cur & ~(mask);
}
ac97_wrcd(codec, reg, val);
snd_mtxunlock(codec->lock);
return left | (right << 8);
} else {
- /* printf("ac97_setmixer: reg=%d, bits=%d, enable=%d\n", e->reg, e->bits, e->enable); */
+#if 0
+ printf("ac97_setmixer: reg=%d, bits=%d, enable=%d\n", e->reg, e->bits, e->enable);
+#endif
return -1;
}
}
@@ -511,6 +539,38 @@
}
}
+static void
+ac97_fix_volume(struct ac97_info *codec)
+{
+ struct snddev_info *d = device_get_softc(codec->dev);
+
+#if 0
+ /* XXX For the sake of debugging purposes */
+ ac97_wrcd(codec, AC97_MIX_PCM, 0);
+ bzero(&codec->mix[SOUND_MIXER_PCM],
+ sizeof(codec->mix[SOUND_MIXER_PCM]));
+ codec->flags |= AC97_F_SOFTVOL;
+ if (d)
+ d->flags |= SD_F_SOFTVOL;
+ return;
+#endif
+ switch (codec->id) {
+ case 0x434d4941: /* CMI9738 */
+ case 0x434d4961: /* CMI9739 */
+ case 0x434d4983: /* CMI9761 */
+ ac97_wrcd(codec, AC97_MIX_PCM, 0);
+ bzero(&codec->mix[SOUND_MIXER_PCM],
+ sizeof(codec->mix[SOUND_MIXER_PCM]));
+ break;
+ default:
+ return;
+ break;
+ }
+ codec->flags |= AC97_F_SOFTVOL;
+ if (d)
+ d->flags |= SD_F_SOFTVOL;
+}
+
static const char*
ac97_hw_desc(u_int32_t id, const char* vname, const char* cname, char* buf)
{
@@ -536,8 +596,9 @@
const char *cname, *vname;
char desc[80];
u_int8_t model, step;
- unsigned i, j, k, old;
+ unsigned i, j, k, bit, old;
u_int32_t id;
+ int reg;
snd_mtxlock(codec->lock);
codec->count = AC97_INIT(codec->methods, codec->devinfo);
@@ -552,6 +613,16 @@
ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000);
i = ac97_rdcd(codec, AC97_REG_RESET);
+ j = ac97_rdcd(codec, AC97_REG_RESET);
+ /*
+ * Let see if this codec can return consistent value.
+ * If not, turn on aggressive read workaround
+ * (STAC9704 comes in mind).
+ */
+ if (i != j) {
+ codec->flags |= AC97_F_RDCD_BUG;
+ i = ac97_rdcd(codec, AC97_REG_RESET);
+ }
codec->caps = i & 0x03ff;
codec->se = (i & 0x7c00) >> 10;
@@ -605,27 +676,75 @@
}
ac97_fix_auxout(codec);
ac97_fix_tone(codec);
+ ac97_fix_volume(codec);
if (codec_patch)
codec_patch(codec);
for (i = 0; i < 32; i++) {
k = codec->noext? codec->mix[i].enable : 1;
- if (k && (codec->mix[i].reg > 0)) {
- old = ac97_rdcd(codec, codec->mix[i].reg);
- ac97_wrcd(codec, codec->mix[i].reg, 0x3f);
- j = ac97_rdcd(codec, codec->mix[i].reg);
- ac97_wrcd(codec, codec->mix[i].reg, old);
- codec->mix[i].enable = (j != 0 && j != old)? 1 : 0;
- for (k = 1; j & (1 << k); k++);
- codec->mix[i].bits = j? k - codec->mix[i].ofs : 0;
+ reg = codec->mix[i].reg;
+ if (reg < 0)
+ reg = -reg;
+ if (k && reg) {
+ j = old = ac97_rdcd(codec, reg);
+ /*
+ * Test for mute bit (except for AC97_MIX_TONE,
+ * where we simply assume it as available).
+ */
+ if (codec->mix[i].mute) {
+ ac97_wrcd(codec, reg, j | 0x8000);
+ j = ac97_rdcd(codec, reg);
+ } else
+ j |= 0x8000;
+ if ((j & 0x8000)) {
+ /*
+ * Test whether the control width should be
+ * 4, 5 or 6 bit. For 5bit register, we should
+ * test it whether it's really 5 or 6bit. Leave
+ * 4bit register alone, because sometimes an
+ * attempt to write past 4th bit may cause
+ * incorrect result especially for AC97_MIX_BEEP
+ * (ac97 2.3).
+ */
+ bit = codec->mix[i].bits;
+ if (bit == 5)
+ bit++;
+ j = ((1 << bit) - 1) << codec->mix[i].ofs;
+ ac97_wrcd(codec, reg,
+ j | (codec->mix[i].mute ? 0x8000 : 0));
+ k = ac97_rdcd(codec, reg) & j;
+ k >>= codec->mix[i].ofs;
+ if (reg == AC97_MIX_TONE &&
+ ((k & 0x0001) == 0x0000))
+ k >>= 1;
+ for (j = 0; k >> j; j++)
+ ;
+ if (j != 0) {
+#if 0
+ device_printf(codec->dev, "%2d: [ac97_rdcd() = %d] [Testbit = %d] %d -> %d\n",
+ i, k, bit, codec->mix[i].bits, j);
+#endif
+ codec->mix[i].enable = 1;
+ codec->mix[i].bits = j;
+ } else
+ codec->mix[i].enable = 0;
+ } else
+ codec->mix[i].enable = 0;
+ ac97_wrcd(codec, reg, old);
}
- /* printf("mixch %d, en=%d, b=%d\n", i, codec->mix[i].enable, codec->mix[i].bits); */
+#if 0
+ printf("mixch %d, en=%d, b=%d\n", i, codec->mix[i].enable, codec->mix[i].bits);
+#endif
}
device_printf(codec->dev, "<%s>\n",
ac97_hw_desc(codec->id, vname, cname, desc));
if (bootverbose) {
+ if (codec->flags & AC97_F_RDCD_BUG)
+ device_printf(codec->dev, "Buggy AC97 Codec: aggressive ac97_rdcd() workaround enabled\n");
+ if (codec->flags & AC97_F_SOFTVOL)
+ device_printf(codec->dev, "Soft PCM volume\n");
device_printf(codec->dev, "Codec features ");
for (i = j = 0; i < 10; i++)
if (codec->caps & (1 << i))
@@ -645,8 +764,16 @@
}
}
- if ((ac97_rdcd(codec, AC97_REG_POWER) & 2) == 0)
- device_printf(codec->dev, "ac97 codec reports dac not ready\n");
+ i = 0;
+ while ((ac97_rdcd(codec, AC97_REG_POWER) & 2) == 0) {
+ if (++i == 100) {
+ device_printf(codec->dev, "ac97 codec reports dac not ready\n");
+ break;
+ }
+ DELAY(1000);
+ }
+ if (bootverbose)
+ device_printf(codec->dev, "ac97 codec dac ready count: %d\n", i);
snd_mtxunlock(codec->lock);
return 0;
}
--- sys/dev/sound/pcm/ac97.h.orig Sun Jan 30 09:00:04 2005
+++ sys/dev/sound/pcm/ac97.h Fri Sep 16 06:32:00 2005
@@ -81,6 +81,8 @@
#define AC97_REG_ID2 0x7e
#define AC97_F_EAPD_INV 0x00000001
+#define AC97_F_RDCD_BUG 0x00000002
+#define AC97_F_SOFTVOL 0x00000004
#define AC97_DECLARE(name) static DEFINE_CLASS(name, name ## _methods, sizeof(struct kobj))
#define AC97_CREATE(dev, devinfo, cls) ac97_create(dev, devinfo, &cls ## _class)
--- sys/dev/sound/pcm/buffer.c.orig Sun Jan 30 09:00:04 2005
+++ sys/dev/sound/pcm/buffer.c Fri Sep 16 06:32:00 2005
@@ -286,8 +286,12 @@
b->fmt = fmt;
b->bps = 1;
b->bps <<= (b->fmt & AFMT_STEREO)? 1 : 0;
- b->bps <<= (b->fmt & AFMT_16BIT)? 1 : 0;
- b->bps <<= (b->fmt & AFMT_32BIT)? 2 : 0;
+ 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;
return 0;
}
--- sys/dev/sound/pcm/channel.c.orig Sun Jan 30 09:00:04 2005
+++ sys/dev/sound/pcm/channel.c Fri Sep 16 06:32:00 2005
@@ -79,6 +79,9 @@
case PCMDIR_VIRTUAL:
c->lock = snd_mtxcreate(c->name, "pcm virtual play channel");
break;
+ case PCMDIR_SLAVE:
+ c->lock = snd_mtxcreate(c->name, "pcm slave play channel");
+ break;
case 0:
c->lock = snd_mtxcreate(c->name, "pcm fake channel");
break;
@@ -105,7 +108,9 @@
return (sndbuf_getblocks(bs) > sndbuf_getprevblocks(bs))? 1 : 0;
} else {
amt = (c->direction == PCMDIR_PLAY)? sndbuf_getfree(bs) : sndbuf_getready(bs);
+#if 0
lim = (c->flags & CHN_F_HAS_SIZE)? sndbuf_getblksz(bs) : 1;
+#endif
lim = 1;
return (amt >= lim)? 1 : 0;
}
@@ -206,7 +211,8 @@
CHN_LOCKASSERT(c);
KASSERT(c->direction == PCMDIR_PLAY, ("chn_wrupdate on bad channel"));
- if ((c->flags & (CHN_F_MAPPED | CHN_F_VIRTUAL)) || !(c->flags & CHN_F_TRIGGERED))
+ if ((c->flags & (CHN_F_MAPPED | CHN_F_VIRTUAL | CHN_F_SLAVE)) ||
+ !(c->flags & CHN_F_TRIGGERED))
return;
chn_dmaupdate(c);
ret = chn_wrfeed(c);
@@ -225,11 +231,13 @@
unsigned int ret, amt;
CHN_LOCKASSERT(c);
-/* DEB(
+#if 0
+ DEB(
if (c->flags & CHN_F_CLOSING) {
sndbuf_dump(b, "b", 0x02);
sndbuf_dump(bs, "bs", 0x02);
- }) */
+ })
+#endif
if (c->flags & CHN_F_MAPPED)
sndbuf_acquire(bs, NULL, sndbuf_getfree(bs));
@@ -238,10 +246,30 @@
KASSERT(amt <= sndbuf_getsize(bs),
("%s(%s): amt %d > source size %d, flags 0x%x", __func__, c->name,
amt, sndbuf_getsize(bs), c->flags));
- if (sndbuf_getready(bs) < amt)
- c->xruns++;
- ret = (amt > 0)? sndbuf_feed(bs, b, c, c->feeder, amt) : ENOSPC;
+ if (SLIST_EMPTY(&c->children)) {
+ /*
+ * Hardware channel
+ */
+ if (sndbuf_getready(bs) < amt)
+ c->xruns++;
+ ret = (amt > 0) ? sndbuf_feed(bs, b, c, c->feeder, amt) : ENOSPC;
+ } else {
+ /*
+ * vchan / slave
+ */
+ if (amt > 0) {
+ ret = sndbuf_feed(bs, b, c, c->feeder, amt);
+ /*
+ * Possible vchan / slave xruns. There should be no empty
+ * space left in buffer.
+ */
+ if (sndbuf_getfree(b) > 0)
+ c->xruns++;
+ } else
+ ret = ENOSPC;
+ }
+
if (ret == 0 && sndbuf_getfree(b) < amt)
chn_wakeup(c);
@@ -357,6 +385,11 @@
struct snd_dbuf *b = c->bufhard;
CHN_LOCKASSERT(c);
+#if 0
+ static uint32_t kk = 0;
+ printf("%u: dumping %d bytes\n", ++kk, cnt);
+#endif
+ c->xruns++;
sndbuf_setxrun(b, sndbuf_getxrun(b) + cnt);
return sndbuf_dispose(b, NULL, cnt);
}
@@ -379,11 +412,16 @@
sndbuf_dump(bs, "bs", 0x02);
})
+#if 0
amt = sndbuf_getready(b);
if (sndbuf_getfree(bs) < amt) {
c->xruns++;
amt = sndbuf_getfree(bs);
}
+#endif
+ amt = sndbuf_getfree(bs);
+ if (amt < sndbuf_getready(b))
+ c->xruns++;
ret = (amt > 0)? sndbuf_feed(b, bs, c, c->feeder, amt) : 0;
amt = sndbuf_getready(b);
@@ -533,10 +571,12 @@
* fed at the first irq.
*/
if (c->direction == PCMDIR_PLAY) {
+ /*
+ * Reduce pops during playback startup.
+ */
+ sndbuf_fillsilence(b);
if (SLIST_EMPTY(&c->children))
chn_wrfeed(c);
- else
- sndbuf_fillsilence(b);
}
sndbuf_setrun(b, 1);
c->xruns = 0;
@@ -637,7 +677,7 @@
/* kill the channel */
chn_trigger(c, PCMTRIG_ABORT);
sndbuf_setrun(b, 0);
- if (!(c->flags & CHN_F_VIRTUAL))
+ if (!(c->flags & (CHN_F_VIRTUAL | CHN_F_SLAVE)))
chn_dmaupdate(c);
missing = sndbuf_getready(bs) + sndbuf_getready(b);
@@ -733,18 +773,24 @@
r = CHANNEL_RESET(c->methods, c->devinfo);
if (fmt != 0) {
+#if 0
hwspd = DSP_DEFAULT_SPEED;
/* only do this on a record channel until feederbuilder works */
if (c->direction == PCMDIR_REC)
RANGE(hwspd, chn_getcaps(c)->minspeed, chn_getcaps(c)->maxspeed);
c->speed = hwspd;
+#endif
+ hwspd = chn_getcaps(c)->minspeed;
+ c->speed = hwspd;
if (r == 0)
r = chn_setformat(c, fmt);
if (r == 0)
r = chn_setspeed(c, hwspd);
+#if 0
if (r == 0)
r = chn_setvolume(c, 100, 100);
+#endif
}
if (r == 0)
r = chn_setblocksize(c, 0, 0);
@@ -807,7 +853,7 @@
goto out;
ret = ENOMEM;
- if ((sndbuf_getsize(b) == 0) && ((c->flags & CHN_F_VIRTUAL) == 0))
+ if ((sndbuf_getsize(b) == 0) && ((c->flags & (CHN_F_VIRTUAL | CHN_F_SLAVE)) == 0))
goto out;
ret = chn_setdir(c, direction);
@@ -880,7 +926,15 @@
{
CHN_LOCKASSERT(c);
/* should add a feeder for volume changing if channel returns -1 */
- c->volume = (left << 8) | right;
+ if (left > 100)
+ left = 100;
+ if (left < 0)
+ left = 0;
+ if (right > 100)
+ right = 100;
+ if (right < 0)
+ right = 0;
+ c->volume = left | (right << 8);
return 0;
}
@@ -912,7 +966,10 @@
delta = -delta;
c->feederflags &= ~(1 << FEEDER_RATE);
- if (delta > 500)
+ /*
+ * Used to be 500. It was too big!
+ */
+ if (delta > 25)
c->feederflags |= 1 << FEEDER_RATE;
else
sndbuf_setspd(bs, sndbuf_getspd(b));
@@ -945,6 +1002,11 @@
r = FEEDER_SET(f, FEEDRATE_DST, sndbuf_getspd(x));
DEB(printf("feeder_set(FEEDRATE_DST, %d) = %d\n", sndbuf_getspd(x), r));
out:
+ if (!r)
+ r = CHANNEL_SETFORMAT(c->methods, c->devinfo,
+ sndbuf_getfmt(b));
+ if (!r)
+ sndbuf_setfmt(bs, c->format);
DEB(printf("setspeed done, r = %d\n", r));
return r;
} else
@@ -1050,6 +1112,10 @@
}
reqblksz = blksz;
+ if (reqblksz < sndbuf_getbps(bs))
+ reqblksz = sndbuf_getbps(bs);
+ if (reqblksz % sndbuf_getbps(bs))
+ reqblksz -= reqblksz % sndbuf_getbps(bs);
/* adjust for different hw format/speed */
irqhz = (sndbuf_getbps(bs) * sndbuf_getspd(bs)) / blksz;
@@ -1113,6 +1179,24 @@
blksz, maxsize, blkcnt));
out:
c->flags &= ~CHN_F_SETBLOCKSIZE;
+#if 0
+ if (1) {
+ static uint32_t kk = 0;
+ printf("%u: b %d/%d/%d : (%d)%d/0x%0x | bs %d/%d/%d : (%d)%d/0x%0x\n", ++kk,
+ sndbuf_getsize(b), sndbuf_getblksz(b), sndbuf_getblkcnt(b),
+ sndbuf_getbps(b),
+ sndbuf_getspd(b), sndbuf_getfmt(b),
+ sndbuf_getsize(bs), sndbuf_getblksz(bs), sndbuf_getblkcnt(bs),
+ sndbuf_getbps(bs),
+ sndbuf_getspd(bs), sndbuf_getfmt(bs));
+ if (sndbuf_getsize(b) % sndbuf_getbps(b) ||
+ sndbuf_getblksz(b) % sndbuf_getbps(b) ||
+ sndbuf_getsize(bs) % sndbuf_getbps(bs) ||
+ sndbuf_getblksz(b) % sndbuf_getbps(b)) {
+ printf("%u: bps/blksz alignment screwed!\n", kk);
+ }
+ }
+#endif
return ret;
}
@@ -1166,7 +1250,9 @@
/* report software-supported formats */
if (report_soft_formats)
- fmts |= AFMT_MU_LAW|AFMT_A_LAW|AFMT_U16_LE|AFMT_U16_BE|
+ fmts |= AFMT_MU_LAW|AFMT_A_LAW|AFMT_U32_LE|AFMT_U32_BE|
+ AFMT_S32_LE|AFMT_S32_BE|AFMT_U24_LE|AFMT_U24_BE|
+ AFMT_S24_LE|AFMT_S24_BE|AFMT_U16_LE|AFMT_U16_BE|
AFMT_S16_LE|AFMT_S16_BE|AFMT_U8|AFMT_S8;
return fmts;
@@ -1177,7 +1263,7 @@
{
struct feeder_class *fc;
struct pcm_feederdesc desc;
- u_int32_t tmp[2], type, flags, hwfmt;
+ u_int32_t tmp[2], type, flags, hwfmt, *fmtlist;
int err;
CHN_LOCKASSERT(c);
@@ -1198,25 +1284,43 @@
}
c->feeder->desc->out = c->format;
} else {
- desc.type = FEEDER_MIXER;
- desc.in = 0;
+ if (c->flags & CHN_F_HAS_SLAVE) {
+ desc.type = FEEDER_SLAVE;
+ desc.in = c->format;
+ } else if (c->flags & CHN_F_HAS_VCHAN) {
+ desc.type = FEEDER_MIXER;
+ desc.in = 0;
+ } else {
+ DEB(printf("can't decide which feeder type to use!\n"));
+ return EOPNOTSUPP;
+ }
desc.out = c->format;
desc.flags = 0;
fc = feeder_getclass(&desc);
if (fc == NULL) {
- DEB(printf("can't find vchan feeder\n"));
+ DEB(printf("can't find %s feeder\n",
+ desc.type == FEEDER_MIXER ? "vchan" : "slave"));
return EOPNOTSUPP;
}
err = chn_addfeeder(c, fc, &desc);
if (err) {
- DEB(printf("can't add vchan feeder, err %d\n", err));
+ DEB(printf("can't add %s feeder, err %d\n",
+ desc.type == FEEDER_MIXER ? "vchan" : "slave",
+ err));
return err;
}
}
+ c->feederflags &= ~(1 << FEEDER_VOLUME);
+ if (c->direction == PCMDIR_PLAY &&
+ !(c->flags & (CHN_F_VIRTUAL|CHN_F_HAS_SLAVE)) &&
+ c->parentsnddev && (c->parentsnddev->flags & SD_F_SOFTVOL) &&
+ c->parentsnddev->mixer_dev)
+ c->feederflags |= 1 << FEEDER_VOLUME;
flags = c->feederflags;
+ fmtlist = chn_getcaps(c)->fmtlist;
DEB(printf("feederflags %x\n", flags));
@@ -1235,7 +1339,9 @@
return EOPNOTSUPP;
}
- if (c->feeder->desc->out != fc->desc->in) {
+ if ((type == FEEDER_RATE &&
+ !fmtvalid(fc->desc->in, fmtlist))
+ || c->feeder->desc->out != fc->desc->in) {
DEB(printf("build fmtchain from 0x%x to 0x%x: ", c->feeder->desc->out, fc->desc->in));
tmp[0] = fc->desc->in;
tmp[1] = 0;
@@ -1257,30 +1363,41 @@
}
}
- if (fmtvalid(c->feeder->desc->out, chn_getcaps(c)->fmtlist)) {
+ if (fmtvalid(c->feeder->desc->out, fmtlist)
+ && !(c->direction == PCMDIR_REC &&
+ c->format != c->feeder->desc->out))
hwfmt = c->feeder->desc->out;
- } else {
+ else {
if (c->direction == PCMDIR_REC) {
tmp[0] = c->format;
tmp[1] = 0;
hwfmt = chn_fmtchain(c, tmp);
- } else {
-#if 0
- u_int32_t *x = chn_getcaps(c)->fmtlist;
- printf("acceptable formats for %s:\n", c->name);
- while (*x) {
- printf("[0x%8x] ", *x);
- x++;
- }
-#endif
- hwfmt = chn_fmtchain(c, chn_getcaps(c)->fmtlist);
- }
+ } else
+ hwfmt = chn_fmtchain(c, fmtlist);
}
- if (hwfmt == 0)
+ if (hwfmt == 0 || !fmtvalid(hwfmt, fmtlist)) {
+ DEB(printf("Invalid hardware format: 0x%x\n", hwfmt));
return ENODEV;
+ }
sndbuf_setfmt(c->bufhard, hwfmt);
+
+ if ((flags & (1 << FEEDER_VOLUME))) {
+ int vol = 100 | (100 << 8);
+
+ CHN_UNLOCK(c);
+ /*
+ * XXX This is ugly! The way mixer subs being so secretive
+ * about its own internals force us to use this silly
+ * monkey trick.
+ */
+ if (mixer_ioctl(c->parentsnddev->mixer_dev,
+ MIXER_READ(SOUND_MIXER_PCM), (caddr_t)&vol, -1, NULL) != 0)
+ device_printf(c->dev, "Soft Volume: Failed to read default value\n");
+ CHN_LOCK(c);
+ chn_setvolume(c, vol & 0x7f, (vol >> 8) & 0x7f);
+ }
return 0;
}
--- sys/dev/sound/pcm/channel.h.orig Sun Jan 30 09:00:04 2005
+++ sys/dev/sound/pcm/channel.h Fri Sep 16 06:32:00 2005
@@ -114,6 +114,7 @@
int fmtvalid(u_int32_t fmt, u_int32_t *fmtlist);
+#define PCMDIR_SLAVE 3
#define PCMDIR_VIRTUAL 2
#define PCMDIR_PLAY 1
#define PCMDIR_REC -1
@@ -137,10 +138,15 @@
#define CHN_F_DEAD 0x00020000
#define CHN_F_BADSETTING 0x00040000
#define CHN_F_SETBLOCKSIZE 0x00080000
+#define CHN_F_HAS_VCHAN 0x00100000
+#define CHN_F_HAS_SLAVE 0x00200000
#define CHN_F_VIRTUAL 0x10000000 /* not backed by hardware */
+#define CHN_F_SLAVE 0x20000000 /* not backed by hardware */
-#define CHN_F_RESET (CHN_F_BUSY | CHN_F_DEAD | CHN_F_VIRTUAL)
+#define CHN_F_RESET (CHN_F_BUSY | CHN_F_DEAD | \
+ CHN_F_HAS_VCHAN | CHN_F_HAS_SLAVE | \
+ CHN_F_VIRTUAL | CHN_F_SLAVE)
#define CHN_N_RATE 0x00000001
#define CHN_N_FORMAT 0x00000002
--- sys/dev/sound/pcm/dsp.c.orig Sun Jan 30 09:00:04 2005
+++ sys/dev/sound/pcm/dsp.c Fri Sep 16 06:32:00 2005
@@ -242,13 +242,13 @@
*/
if (flags & FREAD) {
/* open for read */
+ pcm_unlock(d);
if (devtype == SND_DEV_DSPREC)
rdch = pcm_chnalloc(d, PCMDIR_REC, td->td_proc->p_pid, PCMCHAN(i_dev));
else
rdch = pcm_chnalloc(d, PCMDIR_REC, td->td_proc->p_pid, -1);
if (!rdch) {
/* no channel available, exit */
- pcm_unlock(d);
splx(s);
return EBUSY;
}
@@ -256,11 +256,11 @@
if (chn_reset(rdch, fmt)) {
pcm_chnrelease(rdch);
i_dev->si_drv1 = NULL;
- pcm_unlock(d);
splx(s);
return ENODEV;
}
+ pcm_lock(d);
if (flags & O_NONBLOCK)
rdch->flags |= CHN_F_NBIO;
pcm_chnref(rdch, 1);
@@ -273,6 +273,7 @@
if (flags & FWRITE) {
/* open for write */
+ pcm_unlock(d);
wrch = pcm_chnalloc(d, PCMDIR_PLAY, td->td_proc->p_pid, -1);
error = 0;
@@ -281,6 +282,7 @@
else if (chn_reset(wrch, fmt))
error = ENODEV;
+ pcm_lock(d);
if (error != 0) {
if (wrch) {
/*
@@ -328,7 +330,6 @@
s = spltty();
d = dsp_get_info(i_dev);
- pcm_lock(d);
rdch = i_dev->si_drv1;
wrch = i_dev->si_drv2;
@@ -350,6 +351,8 @@
*/
if ((rdch || wrch) && refs == 0) {
+ pcm_lock(d);
+
if (pcm_getfakechan(d))
pcm_getfakechan(d)->flags = 0;
@@ -381,8 +384,7 @@
chn_reset(wrch, 0);
pcm_chnrelease(wrch);
}
- } else
- pcm_unlock(d);
+ }
splx(s);
return 0;
}
@@ -943,7 +945,16 @@
case SOUND_PCM_READ_BITS:
chn = wrch ? wrch : rdch;
CHN_LOCK(chn);
- *arg_i = (chn->format & AFMT_16BIT) ? 16 : 8;
+ if (chn->format & AFMT_8BIT)
+ *arg_i = 8;
+ else if (chn->format & AFMT_16BIT)
+ *arg_i = 16;
+ else if (chn->format & AFMT_24BIT)
+ *arg_i = 24;
+ else if (chn->format & AFMT_32BIT)
+ *arg_i = 32;
+ else
+ ret = EINVAL;
CHN_UNLOCK(chn);
break;
@@ -1175,6 +1186,7 @@
if ((pdev->si_drv1 == NULL) && (pdev->si_drv2 == NULL)) {
*dev = pdev;
+ dev_ref(*dev);
return;
}
}
--- sys/dev/sound/pcm/fake.c.orig Sun Jan 30 09:00:05 2005
+++ sys/dev/sound/pcm/fake.c Fri Sep 16 06:32:00 2005
@@ -29,6 +29,10 @@
SND_DECLARE_FILE("$FreeBSD: src/sys/dev/sound/pcm/fake.c,v 1.13.4.1 2005/01/30 01:00:05 imp Exp $");
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,
@@ -41,6 +45,22 @@
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};
@@ -120,6 +140,12 @@
c = malloc(sizeof(*c), M_DEVBUF, M_WAITOK);
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 without
+ * querying kernel hint repetitively (see vchan_create / vchan.c).
+ */
+ c->speed = 0;
snprintf(c->name, CHN_NAMELEN, "%s:fake", device_get_nameunit(dev));
return c;
--- sys/dev/sound/pcm/feeder.c.orig Sun Jan 30 09:00:05 2005
+++ sys/dev/sound/pcm/feeder.c Fri Sep 16 06:32:00 2005
@@ -320,7 +320,10 @@
i = 0;
best = 0;
bestmax = 100;
- while (from[i] != 0) {
+ while (from[i] != 0)
+ i++;
+ while (i > 0) {
+ i--;
c->feeder->desc->out = from[i];
try = NULL;
max = 0;
@@ -338,7 +341,6 @@
try = try->source;
feeder_destroy(del);
}
- i++;
}
if (best == 0)
return 0;
@@ -371,7 +373,16 @@
printf("%s [%d]\n", try->class->name, try->desc->idx);
#endif
- return (c->direction == PCMDIR_REC)? best : c->feeder->desc->out;
+ 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
--- sys/dev/sound/pcm/feeder.h.orig Sun Jan 30 09:00:05 2005
+++ sys/dev/sound/pcm/feeder.h Fri Sep 16 06:32:00 2005
@@ -72,10 +72,11 @@
#define FEEDER_ROOT 1
#define FEEDER_FMT 2
-#define FEEDER_MIXER 3
-#define FEEDER_RATE 4
-#define FEEDER_FILTER 5
-#define FEEDER_VOLUME 6
+#define FEEDER_SLAVE 3
+#define FEEDER_MIXER 4
+#define FEEDER_RATE 5
+#define FEEDER_FILTER 6
+#define FEEDER_VOLUME 7
#define FEEDER_LAST FEEDER_VOLUME
#define FEEDRATE_SRC 1
--- sys/dev/sound/pcm/feeder_fmt.c.orig Sun Jan 30 09:00:05 2005
+++ sys/dev/sound/pcm/feeder_fmt.c Fri Sep 16 06:32:00 2005
@@ -1,5 +1,6 @@
/*-
* Copyright (c) 1999 Cameron Grant <cg at freebsd.org>
+ * Copyright (c) 2005 Ariff Abdullah <skywizard at MyBSD.org.my>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -22,54 +23,107 @@
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
+ *
+ * *New* and rewritten soft format converter, supporting 24/32bit pcm data,
+ * simplified and optimized.
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * *
+ * This new implementation is fully dedicated in memory of Cameron Grant, *
+ * the creator of magnificent, highly addictive feeder infrastructure. *
+ * *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
*/
#include <dev/sound/pcm/sound.h>
-
#include "feeder_if.h"
-SND_DECLARE_FILE("$FreeBSD: src/sys/dev/sound/pcm/feeder_fmt.c,v 1.13.4.1 2005/01/30 01:00:05 imp Exp $");
+SND_DECLARE_FILE("$FreeBSD$");
MALLOC_DEFINE(M_FMTFEEDER, "fmtfeed", "pcm format feeder");
#define FEEDBUFSZ 8192
+#define FEEDBUF24SZ 8190
-static unsigned char 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 FMT_TRACE(x...) /* printf(x) */
+#define FMT_TEST(x, y...) /* if (x) FMT_TRACE(y) */
+#define FMT_ALIGNBYTE(x) /* x */
+
+/*
+ * Sign inverted ulaw/alaw -> 8 table
+ */
+static uint8_t ulaw_to_s8_tbl[] = {
+ 131, 135, 139, 143, 147, 151, 155, 159,
+ 163, 167, 171, 175, 179, 183, 187, 191,
+ 194, 196, 198, 200, 202, 204, 206, 208,
+ 210, 212, 214, 216, 218, 220, 222, 224,
+ 226, 227, 228, 229, 230, 231, 232, 233,
+ 234, 235, 236, 237, 238, 239, 240, 241,
+ 241, 242, 242, 243, 243, 244, 244, 245,
+ 245, 246, 246, 247, 247, 248, 248, 249,
+ 249, 249, 250, 250, 250, 250, 251, 251,
+ 251, 251, 252, 252, 252, 252, 253, 253,
+ 253, 253, 253, 253, 254, 254, 254, 254,
+ 254, 254, 254, 254, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 125, 121, 117, 113, 109, 105, 101, 97,
+ 93, 89, 85, 81, 77, 73, 69, 65,
+ 62, 60, 58, 56, 54, 52, 50, 48,
+ 46, 44, 42, 40, 38, 36, 34, 32,
+ 30, 29, 28, 27, 26, 25, 24, 23,
+ 22, 21, 20, 19, 18, 17, 16, 15,
+ 15, 14, 14, 13, 13, 12, 12, 11,
+ 11, 10, 10, 9, 9, 8, 8, 7,
+ 7, 7, 6, 6, 6, 6, 5, 5,
+ 5, 5, 4, 4, 4, 4, 3, 3,
+ 3, 3, 3, 3, 2, 2, 2, 2,
+ 2, 2, 2, 2, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+};
+
+static uint8_t alaw_to_s8_tbl[] = {
+ 236, 237, 234, 235, 240, 241, 238, 239,
+ 228, 229, 226, 227, 232, 233, 230, 231,
+ 246, 246, 245, 245, 248, 248, 247, 247,
+ 242, 242, 241, 241, 244, 244, 243, 243,
+ 171, 175, 163, 167, 187, 191, 179, 183,
+ 139, 143, 131, 135, 155, 159, 147, 151,
+ 214, 216, 210, 212, 222, 224, 218, 220,
+ 198, 200, 194, 196, 206, 208, 202, 204,
+ 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 251, 251, 251, 251, 252, 252, 252, 252,
+ 249, 249, 249, 249, 250, 250, 250, 250,
+ 254, 254, 254, 254, 254, 254, 254, 254,
+ 253, 253, 253, 253, 253, 253, 253, 253,
+ 20, 19, 22, 21, 16, 15, 18, 17,
+ 28, 27, 30, 29, 24, 23, 26, 25,
+ 10, 10, 11, 11, 8, 8, 9, 9,
+ 14, 14, 15, 15, 12, 12, 13, 13,
+ 85, 81, 93, 89, 69, 65, 77, 73,
+ 117, 113, 125, 121, 101, 97, 109, 105,
+ 42, 40, 46, 44, 34, 32, 38, 36,
+ 58, 56, 62, 60, 50, 48, 54, 52,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 5, 5, 5, 5, 4, 4, 4, 4,
+ 7, 7, 7, 7, 6, 6, 6, 6,
+ 2, 2, 2, 2, 2, 2, 2, 2,
+ 3, 3, 3, 3, 3, 3, 3, 3,
};
-static unsigned char u8_to_ulaw[] = {
+static uint8_t u8_to_ulaw_tbl[] = {
0, 0, 0, 0, 0, 1, 1, 1,
1, 2, 2, 2, 2, 3, 3, 3,
3, 4, 4, 4, 4, 5, 5, 5,
@@ -104,119 +158,176 @@
129, 129, 129, 129, 128, 128, 128, 128,
};
-static unsigned char alaw_to_ulaw[] = {
- 42, 43, 40, 41, 46, 47, 44, 45,
- 34, 35, 32, 33, 38, 39, 36, 37,
- 57, 58, 55, 56, 61, 62, 59, 60,
- 49, 50, 48, 48, 53, 54, 51, 52,
- 10, 11, 8, 9, 14, 15, 12, 13,
- 2, 3, 0, 1, 6, 7, 4, 5,
- 26, 27, 24, 25, 30, 31, 28, 29,
- 18, 19, 16, 17, 22, 23, 20, 21,
- 98, 99, 96, 97, 102, 103, 100, 101,
- 93, 93, 92, 92, 95, 95, 94, 94,
- 116, 118, 112, 114, 124, 126, 120, 122,
- 106, 107, 104, 105, 110, 111, 108, 109,
- 72, 73, 70, 71, 76, 77, 74, 75,
- 64, 65, 63, 63, 68, 69, 66, 67,
- 86, 87, 84, 85, 90, 91, 88, 89,
- 79, 79, 78, 78, 82, 83, 80, 81,
- 170, 171, 168, 169, 174, 175, 172, 173,
- 162, 163, 160, 161, 166, 167, 164, 165,
- 185, 186, 183, 184, 189, 190, 187, 188,
- 177, 178, 176, 176, 181, 182, 179, 180,
- 138, 139, 136, 137, 142, 143, 140, 141,
- 130, 131, 128, 129, 134, 135, 132, 133,
- 154, 155, 152, 153, 158, 159, 156, 157,
- 146, 147, 144, 145, 150, 151, 148, 149,
- 226, 227, 224, 225, 230, 231, 228, 229,
- 221, 221, 220, 220, 223, 223, 222, 222,
- 244, 246, 240, 242, 252, 254, 248, 250,
- 234, 235, 232, 233, 238, 239, 236, 237,
- 200, 201, 198, 199, 204, 205, 202, 203,
- 192, 193, 191, 191, 196, 197, 194, 195,
- 214, 215, 212, 213, 218, 219, 216, 217,
- 207, 207, 206, 206, 210, 211, 208, 209,
-};
-
-static unsigned char ulaw_to_alaw[] = {
- 42, 43, 40, 41, 46, 47, 44, 45,
- 34, 35, 32, 33, 38, 39, 36, 37,
- 58, 59, 56, 57, 62, 63, 60, 61,
- 50, 51, 48, 49, 54, 55, 52, 53,
- 10, 11, 8, 9, 14, 15, 12, 13,
- 2, 3, 0, 1, 6, 7, 4, 5,
- 27, 24, 25, 30, 31, 28, 29, 18,
- 19, 16, 17, 22, 23, 20, 21, 106,
- 104, 105, 110, 111, 108, 109, 98, 99,
- 96, 97, 102, 103, 100, 101, 122, 120,
- 126, 127, 124, 125, 114, 115, 112, 113,
- 118, 119, 116, 117, 75, 73, 79, 77,
- 66, 67, 64, 65, 70, 71, 68, 69,
- 90, 91, 88, 89, 94, 95, 92, 93,
- 82, 82, 83, 83, 80, 80, 81, 81,
- 86, 86, 87, 87, 84, 84, 85, 85,
- 170, 171, 168, 169, 174, 175, 172, 173,
- 162, 163, 160, 161, 166, 167, 164, 165,
- 186, 187, 184, 185, 190, 191, 188, 189,
- 178, 179, 176, 177, 182, 183, 180, 181,
- 138, 139, 136, 137, 142, 143, 140, 141,
- 130, 131, 128, 129, 134, 135, 132, 133,
- 155, 152, 153, 158, 159, 156, 157, 146,
- 147, 144, 145, 150, 151, 148, 149, 234,
- 232, 233, 238, 239, 236, 237, 226, 227,
- 224, 225, 230, 231, 228, 229, 250, 248,
- 254, 255, 252, 253, 242, 243, 240, 241,
- 246, 247, 244, 245, 203, 201, 207, 205,
- 194, 195, 192, 193, 198, 199, 196, 197,
- 218, 219, 216, 217, 222, 223, 220, 221,
- 210, 210, 211, 211, 208, 208, 209, 209,
- 214, 214, 215, 215, 212, 212, 213, 213,
+static uint8_t u8_to_alaw_tbl[] = {
+ 42, 42, 42, 42, 42, 43, 43, 43,
+ 43, 40, 40, 40, 40, 41, 41, 41,
+ 41, 46, 46, 46, 46, 47, 47, 47,
+ 47, 44, 44, 44, 44, 45, 45, 45,
+ 45, 34, 34, 34, 34, 35, 35, 35,
+ 35, 32, 32, 32, 32, 33, 33, 33,
+ 33, 38, 38, 38, 38, 39, 39, 39,
+ 39, 36, 36, 36, 36, 37, 37, 37,
+ 37, 58, 58, 59, 59, 56, 56, 57,
+ 57, 62, 62, 63, 63, 60, 60, 61,
+ 61, 50, 50, 51, 51, 48, 48, 49,
+ 49, 54, 54, 55, 55, 52, 52, 53,
+ 53, 10, 11, 8, 9, 14, 15, 12,
+ 13, 2, 3, 0, 1, 6, 7, 4,
+ 5, 24, 30, 28, 18, 16, 22, 20,
+ 106, 110, 98, 102, 122, 114, 75, 90,
+ 213, 197, 245, 253, 229, 225, 237, 233,
+ 149, 151, 145, 147, 157, 159, 153, 155,
+ 133, 132, 135, 134, 129, 128, 131, 130,
+ 141, 140, 143, 142, 137, 136, 139, 138,
+ 181, 181, 180, 180, 183, 183, 182, 182,
+ 177, 177, 176, 176, 179, 179, 178, 178,
+ 189, 189, 188, 188, 191, 191, 190, 190,
+ 185, 185, 184, 184, 187, 187, 186, 186,
+ 165, 165, 165, 165, 164, 164, 164, 164,
+ 167, 167, 167, 167, 166, 166, 166, 166,
+ 161, 161, 161, 161, 160, 160, 160, 160,
+ 163, 163, 163, 163, 162, 162, 162, 162,
+ 173, 173, 173, 173, 172, 172, 172, 172,
+ 175, 175, 175, 175, 174, 174, 174, 174,
+ 169, 169, 169, 169, 168, 168, 168, 168,
+ 171, 171, 171, 171, 170, 170, 170, 170,
};
-/*****************************************************************************/
+static int
+feed_table_u8(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
+ uint32_t count, void *source)
+{
+ int j, k = FEEDER_FEED(f->source, c, b, count, source);
+ uint8_t *tbl = (uint8_t *)f->data;
+
+ j = k;
+ while (j > 0) {
+ j--;
+ b[j] = tbl[b[j]] ^ 0x80;
+ }
+ return k;
+}
static int
-feed_8to16le(struct pcm_feeder *f, struct pcm_channel *c, u_int8_t *b, u_int32_t count, void *source)
+feed_table_s16le(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
+ uint32_t count, void *source)
{
- int i, j, k;
+ int i, j, k = FEEDER_FEED(f->source, c, b, count >> 1, source);
+ uint8_t *tbl = (uint8_t *)f->data;
+
+ i = k;
+ k <<= 1;
+ j = k;
+ while (i > 0) {
+ b[--j] = tbl[b[--i]];
+ b[--j] = 0;
+ }
+ return k;
+}
+
+static int
+feed_table_xlaw(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
+ uint32_t count, void *source)
+{
+ int j, k = FEEDER_FEED(f->source, c, b, count, source);
+ uint8_t *tbl = (uint8_t *)f->data;
- k = FEEDER_FEED(f->source, c, b, count / 2, source);
- j = k - 1;
- i = j * 2 + 1;
- while (i > 0 && j >= 0) {
- b[i--] = b[j--];
- b[i--] = 0;
+ j = k;
+ while (j > 0) {
+ j--;
+ b[j] = tbl[b[j]];
}
- return k * 2;
+ return k;
}
-static struct pcm_feederdesc feeder_8to16le_desc[] = {
- {FEEDER_FMT, AFMT_U8, AFMT_U16_LE, 0},
- {FEEDER_FMT, AFMT_U8 | AFMT_STEREO, AFMT_U16_LE | AFMT_STEREO, 0},
- {FEEDER_FMT, AFMT_S8, AFMT_S16_LE, 0},
- {FEEDER_FMT, AFMT_S8 | AFMT_STEREO, AFMT_S16_LE | AFMT_STEREO, 0},
- {0},
+static struct pcm_feederdesc feeder_ulawtou8_desc[] = {
+ {FEEDER_FMT, AFMT_MU_LAW, AFMT_U8, 0},
+ {FEEDER_FMT, AFMT_MU_LAW|AFMT_STEREO, AFMT_U8|AFMT_STEREO, 0},
+ {0, 0, 0, 0},
};
-static kobj_method_t feeder_8to16le_methods[] = {
- KOBJMETHOD(feeder_feed, feed_8to16le),
- { 0, 0 }
+static kobj_method_t feeder_ulawtou8_methods[] = {
+ KOBJMETHOD(feeder_feed, feed_table_u8),
+ {0, 0}
};
-FEEDER_DECLARE(feeder_8to16le, 0, NULL);
+FEEDER_DECLARE(feeder_ulawtou8, 0, ulaw_to_s8_tbl);
+
+static struct pcm_feederdesc feeder_alawtou8_desc[] = {
+ {FEEDER_FMT, AFMT_A_LAW, AFMT_U8, 0},
+ {FEEDER_FMT, AFMT_A_LAW|AFMT_STEREO, AFMT_U8|AFMT_STEREO, 0},
+ {0, 0, 0, 0},
+};
+static kobj_method_t feeder_alawtou8_methods[] = {
+ KOBJMETHOD(feeder_feed, feed_table_u8),
+ {0, 0}
+};
+FEEDER_DECLARE(feeder_alawtou8, 0, alaw_to_s8_tbl);
+
+static struct pcm_feederdesc feeder_ulawtos16le_desc[] = {
+ {FEEDER_FMT, AFMT_MU_LAW, AFMT_S16_LE, 0},
+ {FEEDER_FMT, AFMT_MU_LAW|AFMT_STEREO, AFMT_S16_LE|AFMT_STEREO, 0},
+ {0, 0, 0, 0},
+};
+static kobj_method_t feeder_ulawtos16le_methods[] = {
+ KOBJMETHOD(feeder_feed, feed_table_s16le),
+ {0, 0}
+};
+FEEDER_DECLARE(feeder_ulawtos16le, 0, ulaw_to_s8_tbl);
-/*****************************************************************************/
+static struct pcm_feederdesc feeder_alawtos16le_desc[] = {
+ {FEEDER_FMT, AFMT_A_LAW, AFMT_S16_LE, 0},
+ {FEEDER_FMT, AFMT_A_LAW|AFMT_STEREO, AFMT_S16_LE|AFMT_STEREO, 0},
+ {0, 0, 0, 0},
+};
+static kobj_method_t feeder_alawtos16le_methods[] = {
+ KOBJMETHOD(feeder_feed, feed_table_s16le),
+ {0, 0}
+};
+FEEDER_DECLARE(feeder_alawtos16le, 0, alaw_to_s8_tbl);
+
+static struct pcm_feederdesc feeder_u8toulaw_desc[] = {
+ {FEEDER_FMT, AFMT_U8, AFMT_MU_LAW, 0},
+ {FEEDER_FMT, AFMT_U8|AFMT_STEREO, AFMT_MU_LAW|AFMT_STEREO, 0},
+ {0, 0, 0, 0},
+};
+static kobj_method_t feeder_u8toulaw_methods[] = {
+ KOBJMETHOD(feeder_feed, feed_table_xlaw),
+ {0, 0}
+};
+FEEDER_DECLARE(feeder_u8toulaw, 0, u8_to_ulaw_tbl);
+
+static struct pcm_feederdesc feeder_u8toalaw_desc[] = {
+ {FEEDER_FMT, AFMT_U8, AFMT_A_LAW, 0},
+ {FEEDER_FMT, AFMT_U8|AFMT_STEREO, AFMT_A_LAW|AFMT_STEREO, 0},
+ {0, 0, 0, 0},
+};
+static kobj_method_t feeder_u8toalaw_methods[] = {
+ KOBJMETHOD(feeder_feed, feed_table_xlaw),
+ {0, 0}
+};
+FEEDER_DECLARE(feeder_u8toalaw, 0, u8_to_alaw_tbl);
+
+/*
+ * Conversion rules:-
+ * 1. BE -> LE
+ * 2. if fmt == u8 , u8 -> s8 (economical)
+ * 3. Xle -> 16le
+ * 4. if fmt != u8 && fmt == u16le , u16le -> s16le
+ * 4. s16le mono -> s16le stereo
+ *
+ * All conversion done in byte level to preserve endianess.
+ */
static int
-feed_16to8_init(struct pcm_feeder *f)
+feed_common_init(struct pcm_feeder *f)
{
- f->data = malloc(FEEDBUFSZ, M_FMTFEEDER, M_NOWAIT | M_ZERO);
+ f->data = malloc(FEEDBUFSZ, M_FMTFEEDER, M_NOWAIT|M_ZERO);
if (f->data == NULL)
return ENOMEM;
return 0;
}
static int
-feed_16to8_free(struct pcm_feeder *f)
+feed_common_free(struct pcm_feeder *f)
{
if (f->data)
free(f->data, M_FMTFEEDER);
@@ -224,326 +335,572 @@
return 0;
}
+/*
+ * Bit conversion
+ */
static int
-feed_16leto8(struct pcm_feeder *f, struct pcm_channel *c, u_int8_t *b, u_int32_t count, void *source)
+feed_8to16le(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
+ uint32_t count, void *source)
{
- u_int32_t i = 0, toget = count * 2;
- int j = 1, k;
-
- k = FEEDER_FEED(f->source, c, f->data, min(toget, FEEDBUFSZ), source);
- while (j < k) {
- b[i++] = ((u_int8_t *)f->data)[j];
- j += 2;
+ int i, j, k = FEEDER_FEED(f->source, c, b, count >> 1, source);
+
+ i = k;
+ k <<= 1;
+ j = k;
+ while (i > 0) {
+ b[--j] = b[--i];
+ b[--j] = 0;
}
- return i;
+ return k;
}
+static struct pcm_feederdesc feeder_8to16le_desc[] = {
+ {FEEDER_FMT, AFMT_U8, AFMT_U16_LE, 0},
+ {FEEDER_FMT, AFMT_U8|AFMT_STEREO, AFMT_U16_LE|AFMT_STEREO, 0},
+ {FEEDER_FMT, AFMT_S8, AFMT_S16_LE, 0},
+ {FEEDER_FMT, AFMT_S8|AFMT_STEREO, AFMT_S16_LE|AFMT_STEREO, 0},
+ {0, 0, 0, 0},
+};
+static kobj_method_t feeder_8to16le_methods[] = {
+ KOBJMETHOD(feeder_feed, feed_8to16le),
+ {0, 0}
+};
+FEEDER_DECLARE(feeder_8to16le, 0, NULL);
+static int
+feed_16leto8(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
+ uint32_t count, void *source)
+{
+ int i, j, k;
+ uint8_t *src = (uint8_t *)f->data;
+
+ k = count << 1;
+ k = FEEDER_FEED(f->source, c, src, min(k, FEEDBUFSZ), source);
+ if (k < 2) {
+ FMT_TRACE("%s: Not enough data (Got: %d bytes)\n",
+ __func__, k);
+ return 0;
+ }
+ FMT_TEST(k & 1, "%s: Bytes not 16bit aligned.\n", __func__);
+ FMT_ALIGNBYTE(k &= ~1);
+ i = k;
+ j = k >> 1;
+ while (i > 0) {
+ b[--j] = src[--i];
+ i--;
+ }
+ return k >> 1;
+}
static struct pcm_feederdesc feeder_16leto8_desc[] = {
{FEEDER_FMT, AFMT_U16_LE, AFMT_U8, 0},
- {FEEDER_FMT, AFMT_U16_LE | AFMT_STEREO, AFMT_U8 | AFMT_STEREO, 0},
+ {FEEDER_FMT, AFMT_U16_LE|AFMT_STEREO, AFMT_U8|AFMT_STEREO, 0},
{FEEDER_FMT, AFMT_S16_LE, AFMT_S8, 0},
- {FEEDER_FMT, AFMT_S16_LE | AFMT_STEREO, AFMT_S8 | AFMT_STEREO, 0},
- {0},
+ {FEEDER_FMT, AFMT_S16_LE|AFMT_STEREO, AFMT_S8|AFMT_STEREO, 0},
+ {0, 0, 0, 0},
};
static kobj_method_t feeder_16leto8_methods[] = {
- KOBJMETHOD(feeder_init, feed_16to8_init),
- KOBJMETHOD(feeder_free, feed_16to8_free),
- KOBJMETHOD(feeder_feed, feed_16leto8),
- { 0, 0 }
+ KOBJMETHOD(feeder_init, feed_common_init),
+ KOBJMETHOD(feeder_free, feed_common_free),
+ KOBJMETHOD(feeder_feed, feed_16leto8),
+ {0, 0}
};
-FEEDER_DECLARE(feeder_16leto8, 1, NULL);
-
-/*****************************************************************************/
+FEEDER_DECLARE(feeder_16leto8, 0, NULL);
static int
-feed_monotostereo8(struct pcm_feeder *f, struct pcm_channel *c, u_int8_t *b, u_int32_t count, void *source)
+feed_16leto24le(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
+ uint32_t count, void *source)
{
- int i, j, k = FEEDER_FEED(f->source, c, b, count / 2, source);
+ int i, j, k;
- j = k - 1;
- i = j * 2 + 1;
- while (i > 0 && j >= 0) {
- b[i--] = b[j];
- b[i--] = b[j];
- j--;
+ k = (count / 3) << 1;
+ k = FEEDER_FEED(f->source, c, b, k, source);
+ if (k < 2) {
+ FMT_TRACE("%s: Not enough data (Got: %d bytes)\n",
+ __func__, k);
+ return 0;
+ }
+ FMT_TEST(k & 1, "%s: Bytes not 16bit aligned.\n", __func__);
+ FMT_ALIGNBYTE(k &= ~1);
+ i = k;
+ j = (k >> 1) * 3;
+ k = j;
+ while (i > 0) {
+ b[--j] = b[--i];
+ b[--j] = b[--i];
+ b[--j] = 0;
}
- return k * 2;
+ return k;
}
-
-static struct pcm_feederdesc feeder_monotostereo8_desc[] = {
- {FEEDER_FMT, AFMT_U8, AFMT_U8 | AFMT_STEREO, 0},
- {FEEDER_FMT, AFMT_S8, AFMT_S8 | AFMT_STEREO, 0},
- {0},
+static struct pcm_feederdesc feeder_16leto24le_desc[] = {
+ {FEEDER_FMT, AFMT_U16_LE, AFMT_U24_LE, 0},
+ {FEEDER_FMT, AFMT_U16_LE|AFMT_STEREO, AFMT_U24_LE|AFMT_STEREO, 0},
+ {FEEDER_FMT, AFMT_S16_LE, AFMT_S24_LE, 0},
+ {FEEDER_FMT, AFMT_S16_LE|AFMT_STEREO, AFMT_S24_LE|AFMT_STEREO, 0},
+ {0, 0, 0, 0},
};
-static kobj_method_t feeder_monotostereo8_methods[] = {
- KOBJMETHOD(feeder_feed, feed_monotostereo8),
- { 0, 0 }
+static kobj_method_t feeder_16leto24le_methods[] = {
+ KOBJMETHOD(feeder_feed, feed_16leto24le),
+ {0, 0}
};
-FEEDER_DECLARE(feeder_monotostereo8, 0, NULL);
-
-/*****************************************************************************/
+FEEDER_DECLARE(feeder_16leto24le, 0, NULL);
static int
-feed_monotostereo16(struct pcm_feeder *f, struct pcm_channel *c, u_int8_t *b, u_int32_t count, void *source)
+feed_24leto16le(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
+ uint32_t count, void *source)
{
- int i, j, k = FEEDER_FEED(f->source, c, b, count / 2, source);
- u_int8_t x, y;
+ int i, j, k;
+ uint8_t *src = (uint8_t *)f->data;
- j = k - 1;
- i = j * 2 + 1;
- while (i >= 3 && j >= 1) {
- x = b[j--];
- y = b[j--];
- b[i--] = x;
- b[i--] = y;
- b[i--] = x;
- b[i--] = y;
+ k = (count * 3) >> 1;
+ k = FEEDER_FEED(f->source, c, src, min(k, FEEDBUF24SZ), source);
+ if (k < 3) {
+ FMT_TRACE("%s: Not enough data (Got: %d bytes)\n",
+ __func__, k);
+ return 0;
}
- return k * 2;
+ FMT_TEST(k % 3, "%s: Bytes not 24bit aligned.\n", __func__);
+ FMT_ALIGNBYTE(k -= k % 3);
+ i = (k / 3) << 1;
+ j = i;
+ while (i > 0) {
+ b[--i] = src[--k];
+ b[--i] = src[--k];
+ k--;
+ }
+ return j;
}
-
-static struct pcm_feederdesc feeder_monotostereo16_desc[] = {
- {FEEDER_FMT, AFMT_U16_LE, AFMT_U16_LE | AFMT_STEREO, 0},
- {FEEDER_FMT, AFMT_S16_LE, AFMT_S16_LE | AFMT_STEREO, 0},
- {FEEDER_FMT, AFMT_U16_BE, AFMT_U16_BE | AFMT_STEREO, 0},
- {FEEDER_FMT, AFMT_S16_BE, AFMT_S16_BE | AFMT_STEREO, 0},
- {0},
+static struct pcm_feederdesc feeder_24leto16le_desc[] = {
+ {FEEDER_FMT, AFMT_U24_LE, AFMT_U16_LE, 0},
+ {FEEDER_FMT, AFMT_U24_LE|AFMT_STEREO, AFMT_U16_LE|AFMT_STEREO, 0},
+ {FEEDER_FMT, AFMT_S24_LE, AFMT_S16_LE, 0},
+ {FEEDER_FMT, AFMT_S24_LE|AFMT_STEREO, AFMT_S16_LE|AFMT_STEREO, 0},
+ {0, 0, 0, 0},
+};
+static kobj_method_t feeder_24leto16le_methods[] = {
+ KOBJMETHOD(feeder_init, feed_common_init),
+ KOBJMETHOD(feeder_free, feed_common_free),
+ KOBJMETHOD(feeder_feed, feed_24leto16le),
+ {0, 0}
+};
+FEEDER_DECLARE(feeder_24leto16le, 1, NULL);
+
+static int
+feed_16leto32le(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
+ uint32_t count, void *source)
+{
+ int i, j, k = FEEDER_FEED(f->source, c, b, count >> 1, source);
+ if (k < 2) {
+ FMT_TRACE("%s: Not enough data (Got: %d bytes)\n",
+ __func__, k);
+ return 0;
+ }
+ FMT_TEST(k & 1, "%s: Bytes not 16bit aligned.\n", __func__);
+ FMT_ALIGNBYTE(k &= ~1);
+ i = k;
+ j = k << 1;
+ k = j;
+ while (i > 0) {
+ b[--j] = b[--i];
+ b[--j] = b[--i];
+ b[--j] = 0;
+ b[--j] = 0;
+ }
+ return k;
+}
+static struct pcm_feederdesc feeder_16leto32le_desc[] = {
+ {FEEDER_FMT, AFMT_U16_LE, AFMT_U32_LE, 0},
+ {FEEDER_FMT, AFMT_U16_LE|AFMT_STEREO, AFMT_U32_LE|AFMT_STEREO, 0},
+ {FEEDER_FMT, AFMT_S16_LE, AFMT_S32_LE, 0},
+ {FEEDER_FMT, AFMT_S16_LE|AFMT_STEREO, AFMT_S32_LE|AFMT_STEREO, 0},
+ {0, 0, 0, 0},
};
-static kobj_method_t feeder_monotostereo16_methods[] = {
- KOBJMETHOD(feeder_feed, feed_monotostereo16),
- { 0, 0 }
+static kobj_method_t feeder_16leto32le_methods[] = {
+ KOBJMETHOD(feeder_feed, feed_16leto32le),
+ {0, 0}
};
-FEEDER_DECLARE(feeder_monotostereo16, 0, NULL);
-
-/*****************************************************************************/
+FEEDER_DECLARE(feeder_16leto32le, 0, NULL);
static int
-feed_stereotomono8_init(struct pcm_feeder *f)
+feed_32leto16le(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
+ uint32_t count, void *source)
{
- f->data = malloc(FEEDBUFSZ, M_FMTFEEDER, M_NOWAIT | M_ZERO);
- if (f->data == NULL)
- return ENOMEM;
- return 0;
-}
+ int i, j, k;
+ uint8_t *src = (uint8_t *)f->data;
-static int
-feed_stereotomono8_free(struct pcm_feeder *f)
-{
- if (f->data)
- free(f->data, M_FMTFEEDER);
- f->data = NULL;
- return 0;
+ k = count << 1;
+ k = FEEDER_FEED(f->source, c, src, min(k, FEEDBUFSZ), source);
+ if (k < 4) {
+ FMT_TRACE("%s: Not enough data (Got: %d bytes)\n",
+ __func__, k);
+ return 0;
+ }
+ FMT_TEST(k & 3, "%s: Bytes not 32bit aligned.\n", __func__);
+ FMT_ALIGNBYTE(k &= ~3);
+ i = k;
+ k >>= 1;
+ j = k;
+ while (i > 0) {
+ b[--j] = src[--i];
+ b[--j] = src[--i];
+ i -= 2;
+ }
+ return k;
}
+static struct pcm_feederdesc feeder_32leto16le_desc[] = {
+ {FEEDER_FMT, AFMT_U32_LE, AFMT_U16_LE, 0},
+ {FEEDER_FMT, AFMT_U32_LE|AFMT_STEREO, AFMT_U16_LE|AFMT_STEREO, 0},
+ {FEEDER_FMT, AFMT_S32_LE, AFMT_S16_LE, 0},
+ {FEEDER_FMT, AFMT_S32_LE|AFMT_STEREO, AFMT_S16_LE|AFMT_STEREO, 0},
+ {0, 0, 0, 0},
+};
+static kobj_method_t feeder_32leto16le_methods[] = {
+ KOBJMETHOD(feeder_init, feed_common_init),
+ KOBJMETHOD(feeder_free, feed_common_free),
+ KOBJMETHOD(feeder_feed, feed_32leto16le),
+ {0, 0}
+};
+FEEDER_DECLARE(feeder_32leto16le, 1, NULL);
+/*
+ * Bit conversion end
+ */
+/*
+ * Channel conversion (mono -> stereo)
+ */
static int
-feed_stereotomono8(struct pcm_feeder *f, struct pcm_channel *c, u_int8_t *b, u_int32_t count, void *source)
+feed_monotostereo8(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
+ uint32_t count, void *source)
{
- u_int32_t i = 0, toget = count * 2;
- int j = 0, k;
+ int i, j, k = FEEDER_FEED(f->source, c, b, count >> 1, source);
- k = FEEDER_FEED(f->source, c, f->data, min(toget, FEEDBUFSZ), source);
- while (j < k) {
- b[i++] = ((u_int8_t *)f->data)[j];
- j += 2;
+ i = k;
+ j = k << 1;
+ while (i > 0) {
+ b[--j] = b[--i];
+ b[--j] = b[i];
}
- return i;
+ return k << 1;
}
-
-static struct pcm_feederdesc feeder_stereotomono8_desc[] = {
- {FEEDER_FMT, AFMT_U8 | AFMT_STEREO, AFMT_U8, 0},
- {FEEDER_FMT, AFMT_S8 | AFMT_STEREO, AFMT_S8, 0},
- {0},
+static struct pcm_feederdesc feeder_monotostereo8_desc[] = {
+ {FEEDER_FMT, AFMT_U8, AFMT_U8|AFMT_STEREO, 0},
+ {FEEDER_FMT, AFMT_S8, AFMT_S8|AFMT_STEREO, 0},
+ {0, 0, 0, 0},
};
-static kobj_method_t feeder_stereotomono8_methods[] = {
- KOBJMETHOD(feeder_init, feed_stereotomono8_init),
- KOBJMETHOD(feeder_free, feed_stereotomono8_free),
- KOBJMETHOD(feeder_feed, feed_stereotomono8),
- { 0, 0 }
+static kobj_method_t feeder_monotostereo8_methods[] = {
+ KOBJMETHOD(feeder_feed, feed_monotostereo8),
+ {0, 0}
};
-FEEDER_DECLARE(feeder_stereotomono8, 1, NULL);
-
-/*****************************************************************************/
+FEEDER_DECLARE(feeder_monotostereo8, 0, NULL);
static int
-feed_stereotomono16_init(struct pcm_feeder *f)
+feed_monotostereo16(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
+ uint32_t count, void *source)
{
- f->data = malloc(FEEDBUFSZ, M_FMTFEEDER, M_NOWAIT | M_ZERO);
- if (f->data == NULL)
- return ENOMEM;
- return 0;
-}
+ int i, j, k = FEEDER_FEED(f->source, c, b, count >> 1, source);
+ uint8_t l, m;
-static int
-feed_stereotomono16_free(struct pcm_feeder *f)
-{
- if (f->data)
- free(f->data, M_FMTFEEDER);
- f->data = NULL;
- return 0;
+ if (k < 2) {
+ FMT_TRACE("%s: Not enough data (Got: %d bytes)\n",
+ __func__, k);
+ return 0;
+ }
+ FMT_TEST(k & 1, "%s: Bytes not 16bit aligned.\n", __func__);
+ FMT_ALIGNBYTE(k &= ~1);
+ i = k;
+ j = k << 1;
+ while (i > 0) {
+ l = b[--i];
+ m = b[--i];
+ b[--j] = l;
+ b[--j] = m;
+ b[--j] = l;
+ b[--j] = m;
+ }
+ return k << 1;
}
+static struct pcm_feederdesc feeder_monotostereo16_desc[] = {
+ {FEEDER_FMT, AFMT_U16_LE, AFMT_U16_LE|AFMT_STEREO, 0},
+ {FEEDER_FMT, AFMT_S16_LE, AFMT_S16_LE|AFMT_STEREO, 0},
+ {FEEDER_FMT, AFMT_U16_BE, AFMT_U16_BE|AFMT_STEREO, 0},
+ {FEEDER_FMT, AFMT_S16_BE, AFMT_S16_BE|AFMT_STEREO, 0},
+ {0, 0, 0, 0},
+};
+static kobj_method_t feeder_monotostereo16_methods[] = {
+ KOBJMETHOD(feeder_feed, feed_monotostereo16),
+ {0, 0}
+};
+FEEDER_DECLARE(feeder_monotostereo16, 0, NULL);
+/*
+ * Channel conversion (mono -> stereo) end
+ */
+/*
+ * Channel conversion (stereo -> mono)
+ */
static int
-feed_stereotomono16(struct pcm_feeder *f, struct pcm_channel *c, u_int8_t *b, u_int32_t count, void *source)
+feed_stereotomono8(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
+ uint32_t count, void *source)
{
- u_int32_t i = 0, toget = count * 2;
- int j = 0, k;
+ int i, j, k;
+ uint8_t *src = (uint8_t *)f->data;
- k = FEEDER_FEED(f->source, c, f->data, min(toget, FEEDBUFSZ), source);
- while (j < k) {
- b[i++] = ((u_int8_t *)f->data)[j];
- b[i++] = ((u_int8_t *)f->data)[j + 1];
- j += 4;
+ k = count << 1;
+ k = FEEDER_FEED(f->source, c, src, min(k, FEEDBUFSZ), source);
+ if (k < 2) {
+ FMT_TRACE("%s: Not enough data (Got: %d bytes)\n",
+ __func__, k);
+ return 0;
}
- return i;
+ FMT_TEST(k & 1, "%s: Bytes not 8bit (stereo) aligned.\n", __func__);
+ FMT_ALIGNBYTE(k &= ~1);
+ i = k >> 1;
+ j = i;
+ while (i > 0) {
+ k--;
+ b[--i] = src[--k];
+ }
+ return j;
}
-
-static struct pcm_feederdesc feeder_stereotomono16_desc[] = {
- {FEEDER_FMT, AFMT_U16_LE | AFMT_STEREO, AFMT_U16_LE, 0},
- {FEEDER_FMT, AFMT_S16_LE | AFMT_STEREO, AFMT_S16_LE, 0},
- {FEEDER_FMT, AFMT_U16_BE | AFMT_STEREO, AFMT_U16_BE, 0},
- {FEEDER_FMT, AFMT_S16_BE | AFMT_STEREO, AFMT_S16_BE, 0},
- {0},
+static struct pcm_feederdesc feeder_stereotomono8_desc[] = {
+ {FEEDER_FMT, AFMT_U8|AFMT_STEREO, AFMT_U8, 0},
+ {FEEDER_FMT, AFMT_S8|AFMT_STEREO, AFMT_S8, 0},
+ {0, 0, 0, 0},
};
-static kobj_method_t feeder_stereotomono16_methods[] = {
- KOBJMETHOD(feeder_init, feed_stereotomono16_init),
- KOBJMETHOD(feeder_free, feed_stereotomono16_free),
- KOBJMETHOD(feeder_feed, feed_stereotomono16),
- { 0, 0 }
+static kobj_method_t feeder_stereotomono8_methods[] = {
+ KOBJMETHOD(feeder_init, feed_common_init),
+ KOBJMETHOD(feeder_free, feed_common_free),
+ KOBJMETHOD(feeder_feed, feed_stereotomono8),
+ {0, 0}
};
-FEEDER_DECLARE(feeder_stereotomono16, 1, NULL);
-
-/*****************************************************************************/
+FEEDER_DECLARE(feeder_stereotomono8, 0, NULL);
static int
-feed_endian(struct pcm_feeder *f, struct pcm_channel *c, u_int8_t *b, u_int32_t count, void *source)
+feed_stereotomono16(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
+ uint32_t count, void *source)
{
- u_int8_t t;
- int i = 0, j = FEEDER_FEED(f->source, c, b, count, source);
+ int i, j, k;
+ uint8_t *src = (uint8_t *)f->data;
- while (i < j) {
- t = b[i];
- b[i] = b[i + 1];
- b[i + 1] = t;
- i += 2;
+ k = count << 1;
+ k = FEEDER_FEED(f->source, c, src, min(k, FEEDBUFSZ), source);
+ if (k < 4) {
+ FMT_TRACE("%s: Not enough data (Got: %d bytes)\n",
+ __func__, k);
+ return 0;
+ }
+ FMT_TEST(k & 3, "%s: Bytes not 16bit (stereo) aligned.\n", __func__);
+ FMT_ALIGNBYTE(k &= ~3);
+ i = k >> 1;
+ j = i;
+ while (i > 0) {
+ k -= 2;
+ b[--i] = src[--k];
+ b[--i] = src[--k];
}
- return i;
+ return j;
}
-
-static struct pcm_feederdesc feeder_endian_desc[] = {
- {FEEDER_FMT, AFMT_U16_LE, AFMT_U16_BE, 0},
- {FEEDER_FMT, AFMT_U16_LE | AFMT_STEREO, AFMT_U16_BE | AFMT_STEREO, 0},
- {FEEDER_FMT, AFMT_S16_LE, AFMT_S16_BE, 0},
- {FEEDER_FMT, AFMT_S16_LE | AFMT_STEREO, AFMT_S16_BE | AFMT_STEREO, 0},
- {FEEDER_FMT, AFMT_U16_BE, AFMT_U16_LE, 0},
- {FEEDER_FMT, AFMT_U16_BE | AFMT_STEREO, AFMT_U16_LE | AFMT_STEREO, 0},
- {FEEDER_FMT, AFMT_S16_BE, AFMT_S16_LE, 0},
- {FEEDER_FMT, AFMT_S16_BE | AFMT_STEREO, AFMT_S16_LE | AFMT_STEREO, 0},
- {0},
-};
-static kobj_method_t feeder_endian_methods[] = {
- KOBJMETHOD(feeder_feed, feed_endian),
- { 0, 0 }
+static struct pcm_feederdesc feeder_stereotomono16_desc[] = {
+ {FEEDER_FMT, AFMT_U16_LE|AFMT_STEREO, AFMT_U16_LE, 0},
+ {FEEDER_FMT, AFMT_S16_LE|AFMT_STEREO, AFMT_S16_LE, 0},
+ {FEEDER_FMT, AFMT_U16_BE|AFMT_STEREO, AFMT_U16_BE, 0},
+ {FEEDER_FMT, AFMT_S16_BE|AFMT_STEREO, AFMT_S16_BE, 0},
+ {0, 0, 0, 0},
};
-FEEDER_DECLARE(feeder_endian, 0, NULL);
-
-/*****************************************************************************/
+static kobj_method_t feeder_stereotomono16_methods[] = {
+ KOBJMETHOD(feeder_init, feed_common_init),
+ KOBJMETHOD(feeder_free, feed_common_free),
+ KOBJMETHOD(feeder_feed, feed_stereotomono16),
+ {0, 0}
+};
+FEEDER_DECLARE(feeder_stereotomono16, 0, NULL);
+/*
+ * Channel conversion (stereo -> mono) end
+ */
+/*
+ * Sign conversion
+ */
static int
-feed_sign(struct pcm_feeder *f, struct pcm_channel *c, u_int8_t *b, u_int32_t count, void *source)
+feed_sign8(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
+ uint32_t count, void *source)
{
- int i = 0, j = FEEDER_FEED(f->source, c, b, count, source);
- intptr_t ssz = (intptr_t)f->data, ofs = ssz - 1;
+ int i, j = FEEDER_FEED(f->source, c, b, count, source);
- while (i < j) {
- b[i + ofs] ^= 0x80;
- i += ssz;
- }
- return i;
+ i = j;
+ while (i > 0)
+ b[--i] ^= 0x80;
+ return j;
}
-
static struct pcm_feederdesc feeder_sign8_desc[] = {
{FEEDER_FMT, AFMT_U8, AFMT_S8, 0},
- {FEEDER_FMT, AFMT_U8 | AFMT_STEREO, AFMT_S8 | AFMT_STEREO, 0},
+ {FEEDER_FMT, AFMT_U8|AFMT_STEREO, AFMT_S8|AFMT_STEREO, 0},
{FEEDER_FMT, AFMT_S8, AFMT_U8, 0},
- {FEEDER_FMT, AFMT_S8 | AFMT_STEREO, AFMT_U8 | AFMT_STEREO, 0},
- {0},
+ {FEEDER_FMT, AFMT_S8|AFMT_STEREO, AFMT_U8|AFMT_STEREO, 0},
+ {0, 0, 0, 0},
};
static kobj_method_t feeder_sign8_methods[] = {
- KOBJMETHOD(feeder_feed, feed_sign),
- { 0, 0 }
+ KOBJMETHOD(feeder_feed, feed_sign8),
+ {0, 0}
};
-FEEDER_DECLARE(feeder_sign8, 0, (void *)1);
+FEEDER_DECLARE(feeder_sign8, 0, NULL);
+
+static int
+feed_sign16le(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
+ uint32_t count, void *source)
+{
+ int i, j = FEEDER_FEED(f->source, c, b, count, source);
+ if (j < 2) {
+ FMT_TRACE("%s: Not enough data (Got: %d bytes)\n",
+ __func__, j);
+ return 0;
+ }
+ FMT_TEST(j & 1, "%s: Bytes not 16bit aligned.\n", __func__);
+ FMT_ALIGNBYTE(j &= ~1);
+ i = j;
+ while (i > 0) {
+ b[--i] ^= 0x80;
+ i--;
+ }
+ return j;
+}
static struct pcm_feederdesc feeder_sign16le_desc[] = {
{FEEDER_FMT, AFMT_U16_LE, AFMT_S16_LE, 0},
- {FEEDER_FMT, AFMT_U16_LE | AFMT_STEREO, AFMT_S16_LE | AFMT_STEREO, 0},
+ {FEEDER_FMT, AFMT_U16_LE|AFMT_STEREO, AFMT_S16_LE|AFMT_STEREO, 0},
{FEEDER_FMT, AFMT_S16_LE, AFMT_U16_LE, 0},
- {FEEDER_FMT, AFMT_S16_LE | AFMT_STEREO, AFMT_U16_LE | AFMT_STEREO, 0},
- {0},
+ {FEEDER_FMT, AFMT_S16_LE|AFMT_STEREO, AFMT_U16_LE|AFMT_STEREO, 0},
+ {0, 0, 0, 0},
};
static kobj_method_t feeder_sign16le_methods[] = {
- KOBJMETHOD(feeder_feed, feed_sign),
- { 0, 0 }
+ KOBJMETHOD(feeder_feed, feed_sign16le),
+ {0, 0}
};
-FEEDER_DECLARE(feeder_sign16le, -1, (void *)2);
-
-/*****************************************************************************/
+FEEDER_DECLARE(feeder_sign16le, 0, NULL);
+/*
+ * Sign conversion end.
+ */
+/*
+ * Endian conversion.
+ */
static int
-feed_table(struct pcm_feeder *f, struct pcm_channel *c, u_int8_t *b, u_int32_t count, void *source)
+feed_endian16(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
+ uint32_t count, void *source)
{
- int i = 0, j = FEEDER_FEED(f->source, c, b, count, source);
+ int i, j = FEEDER_FEED(f->source, c, b, count, source);
+ uint8_t v;
- while (i < j) {
- b[i] = ((u_int8_t *)f->data)[b[i]];
- i++;
+ if (j < 2) {
+ FMT_TRACE("%s: Not enough data (Got: %d bytes)\n",
+ __func__, j);
+ return 0;
+ }
+ FMT_TEST(j & 1, "%s: Bytes not 16bit aligned.\n", __func__);
+ FMT_ALIGNBYTE(j &= ~1);
+ i = j;
+ while (i > 0) {
+ v = b[--i];
+ b[i] = b[i - 1];
+ b[--i] = v;
}
- return i;
+ return j;
}
-
-static struct pcm_feederdesc feeder_ulawtou8_desc[] = {
- {FEEDER_FMT, AFMT_MU_LAW, AFMT_U8, 0},
- {FEEDER_FMT, AFMT_MU_LAW | AFMT_STEREO, AFMT_U8 | AFMT_STEREO, 0},
- {0},
-};
-static kobj_method_t feeder_ulawtou8_methods[] = {
- KOBJMETHOD(feeder_feed, feed_table),
- { 0, 0 }
-};
-FEEDER_DECLARE(feeder_ulawtou8, 0, ulaw_to_u8);
-
-static struct pcm_feederdesc feeder_u8toulaw_desc[] = {
- {FEEDER_FMT, AFMT_U8, AFMT_MU_LAW, 0},
- {FEEDER_FMT, AFMT_U8 | AFMT_STEREO, AFMT_MU_LAW | AFMT_STEREO, 0},
- {0},
-};
-static kobj_method_t feeder_u8toulaw_methods[] = {
- KOBJMETHOD(feeder_feed, feed_table),
- { 0, 0 }
-};
-FEEDER_DECLARE(feeder_u8toulaw, 0, u8_to_ulaw);
-
-static struct pcm_feederdesc feeder_alawtoulaw_desc[] = {
- {FEEDER_FMT, AFMT_A_LAW, AFMT_MU_LAW, 0},
- {FEEDER_FMT, AFMT_A_LAW | AFMT_STEREO, AFMT_MU_LAW | AFMT_STEREO, 0},
- {0},
-};
-static kobj_method_t feeder_alawtoulaw_methods[] = {
- KOBJMETHOD(feeder_feed, feed_table),
- { 0, 0 }
-};
-FEEDER_DECLARE(feeder_alawtoulaw, 0, alaw_to_ulaw);
-
-static struct pcm_feederdesc feeder_ulawtoalaw_desc[] = {
- {FEEDER_FMT, AFMT_MU_LAW, AFMT_A_LAW, 0},
- {FEEDER_FMT, AFMT_MU_LAW | AFMT_STEREO, AFMT_A_LAW | AFMT_STEREO, 0},
- {0},
+static struct pcm_feederdesc feeder_endian16_desc[] = {
+ {FEEDER_FMT, AFMT_U16_LE, AFMT_U16_BE, 0},
+ {FEEDER_FMT, AFMT_U16_LE|AFMT_STEREO, AFMT_U16_BE|AFMT_STEREO, 0},
+ {FEEDER_FMT, AFMT_S16_LE, AFMT_S16_BE, 0},
+ {FEEDER_FMT, AFMT_S16_LE|AFMT_STEREO, AFMT_S16_BE|AFMT_STEREO, 0},
+ {FEEDER_FMT, AFMT_U16_BE, AFMT_U16_LE, 0},
+ {FEEDER_FMT, AFMT_U16_BE|AFMT_STEREO, AFMT_U16_LE|AFMT_STEREO, 0},
+ {FEEDER_FMT, AFMT_S16_BE, AFMT_S16_LE, 0},
+ {FEEDER_FMT, AFMT_S16_BE|AFMT_STEREO, AFMT_S16_LE|AFMT_STEREO, 0},
+ {0, 0, 0, 0},
};
-static kobj_method_t feeder_ulawtoalaw_methods[] = {
- KOBJMETHOD(feeder_feed, feed_table),
- { 0, 0 }
+static kobj_method_t feeder_endian16_methods[] = {
+ KOBJMETHOD(feeder_feed, feed_endian16),
+ {0, 0}
};
-FEEDER_DECLARE(feeder_ulawtoalaw, 0, ulaw_to_alaw);
-
+FEEDER_DECLARE(feeder_endian16, 0, NULL);
+static int
+feed_endian24(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
+ uint32_t count, void *source)
+{
+ int i, j = FEEDER_FEED(f->source, c, b, count, source);
+ uint8_t v;
+ if (j < 3) {
+ FMT_TRACE("%s: Not enough data (Got: %d bytes)\n",
+ __func__, j);
+ return 0;
+ }
+ FMT_TEST(j % 3, "%s: Bytes not 24bit aligned.\n", __func__);
+ FMT_ALIGNBYTE(j -= j % 3);
+ i = j;
+ while (i > 0) {
+ v = b[--i];
+ b[i] = b[i - 2];
+ b[i -= 2] = v;
+ }
+ return j;
+}
+static struct pcm_feederdesc feeder_endian24_desc[] = {
+ {FEEDER_FMT, AFMT_U24_LE, AFMT_U24_BE, 0},
+ {FEEDER_FMT, AFMT_U24_LE|AFMT_STEREO, AFMT_U24_BE|AFMT_STEREO, 0},
+ {FEEDER_FMT, AFMT_S24_LE, AFMT_S24_BE, 0},
+ {FEEDER_FMT, AFMT_S24_LE|AFMT_STEREO, AFMT_S24_BE|AFMT_STEREO, 0},
+ {FEEDER_FMT, AFMT_U24_BE, AFMT_U24_LE, 0},
+ {FEEDER_FMT, AFMT_U24_BE|AFMT_STEREO, AFMT_U24_LE|AFMT_STEREO, 0},
+ {FEEDER_FMT, AFMT_S24_BE, AFMT_S24_LE, 0},
+ {FEEDER_FMT, AFMT_S24_BE|AFMT_STEREO, AFMT_S24_LE|AFMT_STEREO, 0},
+ {0, 0, 0, 0},
+};
+static kobj_method_t feeder_endian24_methods[] = {
+ KOBJMETHOD(feeder_feed, feed_endian24),
+ {0, 0}
+};
+FEEDER_DECLARE(feeder_endian24, 0, NULL);
+
+static int
+feed_endian32(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
+ uint32_t count, void *source)
+{
+ int i, j = FEEDER_FEED(f->source, c, b, count, source);
+ uint8_t l, m;
+
+ if (j < 4) {
+ FMT_TRACE("%s: Not enough data (Got: %d bytes)\n",
+ __func__, j);
+ return 0;
+ }
+ FMT_TEST(j & 3, "%s: Bytes not 32bit aligned.\n", __func__);
+ FMT_ALIGNBYTE(j &= ~3);
+ i = j;
+ while (i > 0) {
+ l = b[--i];
+ m = b[--i];
+ b[i] = b[i - 1];
+ b[i + 1] = b[i - 2];
+ b[--i] = m;
+ b[--i] = l;
+ }
+ return j;
+}
+static struct pcm_feederdesc feeder_endian32_desc[] = {
+ {FEEDER_FMT, AFMT_U32_LE, AFMT_U32_BE, 0},
+ {FEEDER_FMT, AFMT_U32_LE|AFMT_STEREO, AFMT_U32_BE|AFMT_STEREO, 0},
+ {FEEDER_FMT, AFMT_S32_LE, AFMT_S32_BE, 0},
+ {FEEDER_FMT, AFMT_S32_LE|AFMT_STEREO, AFMT_S32_BE|AFMT_STEREO, 0},
+ {FEEDER_FMT, AFMT_U32_BE, AFMT_U32_LE, 0},
+ {FEEDER_FMT, AFMT_U32_BE|AFMT_STEREO, AFMT_U32_LE|AFMT_STEREO, 0},
+ {FEEDER_FMT, AFMT_S32_BE, AFMT_S32_LE, 0},
+ {FEEDER_FMT, AFMT_S32_BE|AFMT_STEREO, AFMT_S32_LE|AFMT_STEREO, 0},
+ {0, 0, 0, 0},
+};
+static kobj_method_t feeder_endian32_methods[] = {
+ KOBJMETHOD(feeder_feed, feed_endian32),
+ {0, 0}
+};
+FEEDER_DECLARE(feeder_endian32, 0, NULL);
+/*
+ * Endian conversion end
+ */
--- sys/dev/sound/pcm/feeder_rate.c.orig Sun Jan 30 09:00:05 2005
+++ sys/dev/sound/pcm/feeder_rate.c Fri Sep 16 06:32:00 2005
@@ -1,5 +1,7 @@
/*-
+ * Copyright (c) 1999 Cameron Grant <gandalf at vilnya.demon.co.uk>
* Copyright (c) 2003 Orion Hodson <orion at freebsd.org>
+ * Copyright (c) 2005 Ariff Abdullah <skywizard at MyBSD.org.my>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -23,7 +25,25 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * MAINTAINER: Orion Hodson <orion at freebsd.org>
+ * 2005-06-11:
+ * ==========
+ *
+ * *New* and rewritten soft sample rate converter supporting arbitary sample
+ * rate, fine grained scalling/coefficients and unified up/down stereo
+ * converter. Most of disclaimers from orion's previous version also applied
+ * here, regarding with linear interpolation deficiencies, pre/post
+ * anti-aliasing filtering issues. This version comes with much simpler and
+ * tighter interface, although it works almost exactly like the older one.
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * *
+ * This new implementation is fully dedicated in memory of Cameron Grant, *
+ * the creator of magnificent, highly addictive feeder infrastructure. *
+ * *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
+ * Orion's notes:
+ * =============
*
* This rate conversion code uses linear interpolation without any
* pre- or post- interpolation filtering to combat aliasing. This
@@ -37,194 +57,423 @@
* with several portions based on whether _KERNEL is defined. It's a
* little ugly, but exceedingly useful. The testsuite and its
* revisions can be found at:
- * http://people.freebsd.org/~orion/feedrate/
+ * http://people.freebsd.org/~orion/files/feedrate/
*
* Special thanks to Ken Marx for exposing flaws in the code and for
* testing revisions.
*/
-#ifdef _KERNEL
-
#include <dev/sound/pcm/sound.h>
#include "feeder_if.h"
-SND_DECLARE_FILE("$FreeBSD: src/sys/dev/sound/pcm/feeder_rate.c,v 1.10.6.1 2005/01/30 01:00:05 imp Exp $");
+SND_DECLARE_FILE("$FreeBSD$");
-#endif /* _KERNEL */
+#define RATE_ASSERT(x, y) /* KASSERT(x,y) */
+#define RATE_TRACE(x...) /* printf(x) */
MALLOC_DEFINE(M_RATEFEEDER, "ratefeed", "pcm rate feeder");
-#ifndef RATE_ASSERT
-#define RATE_ASSERT(x, y) /* KASSERT(x) */
-#endif /* RATE_ASSERT */
-
-#ifndef RATE_TRACE
-#define RATE_TRACE(x...) /* printf(x) */
-#endif
-
-/*****************************************************************************/
-
-/* The following coefficients are coupled. They are chosen to be
- * guarantee calculable factors for the interpolation routine. They
- * have been tested over the range of RATEMIN-RATEMAX Hz. Decreasing
- * the granularity increases the required buffer size and affects the
- * gain values at different points in the space. These values were
- * found by running the test program with -p (probe) and some trial
- * and error.
- *
- * ROUNDHZ the granularity of sample rates (fits n*11025 and n*8000).
- * FEEDBUFSZ the amount of buffer space.
- * MINGAIN the minimum acceptable gain in coefficients search.
+#define FEEDBUFSZ 8192
+#define ROUNDHZ 25
+#define RATEMIN 4000
+/* 8000 * 138 or 11025 * 100 . This is insane, indeed! */
+#define RATEMAX 1102500
+#define MINGAIN 92
+#define MAXGAIN 96
+
+#define FEEDRATE_CONVERT_64 0
+#define FEEDRATE_CONVERT_SCALE64 1
+#define FEEDRATE_CONVERT_SCALE32 2
+#define FEEDRATE_CONVERT_PLAIN 3
+#define FEEDRATE_CONVERT_FIXED 4
+#define FEEDRATE_CONVERT_OPTIMAL 5
+#define FEEDRATE_CONVERT_WORST 6
+
+#define FEEDRATE_64_MAXROLL 32
+#define FEEDRATE_32_MAXROLL 16
+
+struct feed_rate_info {
+ uint32_t src, dst; /* rounded source / destination rates */
+ uint32_t rsrc, rdst; /* original source / destination rates */
+ uint32_t gx, gy; /* interpolation / decimation ratio */
+ uint32_t alpha; /* interpolation distance */
+ uint32_t pos, bpos; /* current sample / buffer positions */
+ uint32_t bufsz; /* total buffer size */
+ int32_t scale, roll; /* scale / roll factor */
+ int16_t *buffer;
+ uint32_t (*convert)(struct feed_rate_info *, int16_t *, uint32_t);
+};
+
+static uint32_t
+feed_convert_64(struct feed_rate_info *, int16_t *, uint32_t);
+static uint32_t
+feed_convert_scale64(struct feed_rate_info *, int16_t *, uint32_t);
+static uint32_t
+feed_convert_scale32(struct feed_rate_info *, int16_t *, uint32_t);
+static uint32_t
+feed_convert_plain(struct feed_rate_info *, int16_t *, uint32_t);
+
+int feeder_rate_ratemin = RATEMIN;
+int feeder_rate_ratemax = RATEMAX;
+/*
+ * See 'Feeder Scaling Type' below..
*/
-#define ROUNDHZ 25
-#define FEEDBUFSZ 8192
-#define MINGAIN 92
+static int feeder_rate_scaling = FEEDRATE_CONVERT_OPTIMAL;
+static int feeder_rate_buffersize = FEEDBUFSZ & ~1;
-#define RATEMIN 4000
-#define RATEMAX 48000
+/*
+ * sysctls.. I love sysctls..
+ */
+TUNABLE_INT("hw.snd.feeder_rate_ratemin", &feeder_rate_ratemin);
+TUNABLE_INT("hw.snd.feeder_rate_ratemax", &feeder_rate_ratemin);
+TUNABLE_INT("hw.snd.feeder_rate_scaling", &feeder_rate_scaling);
+TUNABLE_INT("hw.snd.feeder_rate_buffersize", &feeder_rate_buffersize);
-struct feed_rate_info;
+static int
+sysctl_hw_snd_feeder_rate_ratemin(SYSCTL_HANDLER_ARGS)
+{
+ int err, val;
-typedef int (*rate_convert_method)(struct feed_rate_info *,
- uint32_t, uint32_t, int16_t *);
+ val = feeder_rate_ratemin;
+ err = sysctl_handle_int(oidp, &val, sizeof(val), req);
+ if (val < 1 || val >= feeder_rate_ratemax)
+ err = EINVAL;
+ else
+ feeder_rate_ratemin = val;
+ return err;
+}
+SYSCTL_PROC(_hw_snd, OID_AUTO, feeder_rate_ratemin, CTLTYPE_INT | CTLFLAG_RW,
+ 0, sizeof(int), sysctl_hw_snd_feeder_rate_ratemin, "I", "");
-static int
-convert_stereo_up(struct feed_rate_info *info,
- uint32_t src_ticks, uint32_t dst_ticks, int16_t *dst);
+static int
+sysctl_hw_snd_feeder_rate_ratemax(SYSCTL_HANDLER_ARGS)
+{
+ int err, val;
+
+ val = feeder_rate_ratemax;
+ err = sysctl_handle_int(oidp, &val, sizeof(val), req);
+ if (val <= feeder_rate_ratemin || val > 0x7fffff)
+ err = EINVAL;
+ else
+ feeder_rate_ratemax = val;
+ return err;
+}
+SYSCTL_PROC(_hw_snd, OID_AUTO, feeder_rate_ratemax, CTLTYPE_INT | CTLFLAG_RW,
+ 0, sizeof(int), sysctl_hw_snd_feeder_rate_ratemax, "I", "");
static int
-convert_stereo_down(struct feed_rate_info *info,
- uint32_t src_ticks, uint32_t dst_ticks, int16_t *dst);
+sysctl_hw_snd_feeder_rate_scaling(SYSCTL_HANDLER_ARGS)
+{
+ int err, val;
-struct feed_rate_info {
- uint32_t src, dst; /* source and destination rates */
- uint16_t buffer_ticks; /* number of available samples in buffer */
- uint16_t buffer_pos; /* next available sample in buffer */
- uint16_t rounds; /* maximum number of cycle rounds w buffer */
- uint16_t alpha; /* interpolation distance */
- uint16_t sscale; /* src clock scale */
- uint16_t dscale; /* dst clock scale */
- uint16_t mscale; /* scale factor to avoid divide per sample */
- uint16_t mroll; /* roll to again avoid divide per sample */
- uint16_t channels; /* 1 = mono, 2 = stereo */
+ val = feeder_rate_scaling;
+ err = sysctl_handle_int(oidp, &val, sizeof(val), req);
+ /*
+ * Feeder Scaling Type
+ * ===================
+ *
+ * 1. Plain 64bit (high precision)
+ * 2. 64bit scaling (high precision, CPU friendly, but can
+ * cause gain up/down).
+ * 3. 32bit scaling (somehow can cause hz roundup, gain
+ * up/down).
+ * 4. Plain copy (default if src == dst. Except if src == dst,
+ * this is the worst / silly conversion method!).
+ *
+ * Sysctl options:-
+ *
+ * 0 - Plain 64bit - no fallback.
+ * 1 - 64bit scaling - no fallback.
+ * 2 - 32bit scaling - no fallback.
+ * 3 - Plain copy - no fallback.
+ * 4 - Fixed rate. Means that, choose optimal conversion method
+ * without causing hz roundup.
+ * 32bit scaling (as long as hz roundup does not occur),
+ * 64bit scaling, Plain 64bit.
+ * 5 - Optimal / CPU friendly (DEFAULT).
+ * 32bit scaling, 64bit scaling, Plain 64bit
+ * 6 - Optimal to worst, no 64bit arithmetic involved.
+ * 32bit scaling, Plain copy.
+ */
+ if (val < FEEDRATE_CONVERT_64 || val > FEEDRATE_CONVERT_WORST)
+ err = EINVAL;
+ else
+ feeder_rate_scaling = val;
+ return err;
+}
+SYSCTL_PROC(_hw_snd, OID_AUTO, feeder_rate_scaling, CTLTYPE_INT | CTLFLAG_RW,
+ 0, sizeof(int), sysctl_hw_snd_feeder_rate_scaling, "I", "");
- rate_convert_method convert;
- int16_t buffer[FEEDBUFSZ];
-};
+static int
+sysctl_hw_snd_feeder_rate_buffersize(SYSCTL_HANDLER_ARGS)
+{
+ int err, val;
-#define bytes_per_sample 2
-#define src_ticks_per_cycle(info) (info->dscale * info->rounds)
-#define dst_ticks_per_cycle(info) (info->sscale * info->rounds)
-#define bytes_per_tick(info) (info->channels * bytes_per_sample)
-#define src_bytes_per_cycle(info) \
- (src_ticks_per_cycle(info) * bytes_per_tick(info))
-#define dst_bytes_per_cycle(info) \
- (dst_ticks_per_cycle(info) * bytes_per_tick(info))
+ val = feeder_rate_buffersize;
+ err = sysctl_handle_int(oidp, &val, sizeof(val), req);
+ /*
+ * Don't waste too much kernel space
+ */
+ if (val < 2 || val > 65536)
+ err = EINVAL;
+ else
+ feeder_rate_buffersize = val & ~1;
+ return err;
+}
+SYSCTL_PROC(_hw_snd, OID_AUTO, feeder_rate_buffersize, CTLTYPE_INT | CTLFLAG_RW,
+ 0, sizeof(int), sysctl_hw_snd_feeder_rate_buffersize, "I", "");
-static uint32_t
-gcd(uint32_t x, uint32_t y)
+static void
+feed_speed_ratio(uint32_t x, uint32_t y, uint32_t *gx, uint32_t *gy)
{
- uint32_t w;
+ uint32_t w, src = x, dst = y;
+
while (y != 0) {
w = x % y;
x = y;
y = w;
}
- return x;
+ *gx = src / x;
+ *gy = dst / x;
+}
+
+static void
+feed_scale_roll(uint32_t dst, int32_t *scale, int32_t *roll, int32_t max)
+{
+ int64_t k, tscale;
+ int32_t j, troll;
+
+ *scale = *roll = -1;
+ for (j = MAXGAIN; j >= MINGAIN; j -= 3) {
+ for (troll = 0; troll < max; troll++) {
+ tscale = (1 << troll) / dst;
+ k = (tscale * dst * 100) >> troll;
+ if (k > j && k <= 100) {
+ *scale = tscale;
+ *roll = troll;
+ return;
+ }
+ }
+ }
+}
+
+static int
+feed_get_best_coef(uint32_t *src, uint32_t *dst, uint32_t *gx, uint32_t *gy,
+ int32_t *scale, int32_t *roll)
+{
+ uint32_t tsrc, tdst, sscale, dscale;
+ int32_t tscale, troll;
+ int i, j, hzmin, hzmax;
+
+ *scale = *roll = -1;
+ for (i = 0; i < 2; i++) {
+ hzmin = (ROUNDHZ * i) + 1;
+ hzmax = hzmin + ROUNDHZ;
+ for (j = hzmin; j < hzmax; j++) {
+ tsrc = *src - (*src % j);
+ tdst = *dst;
+ if (tsrc < 1 || tdst < 1)
+ goto coef_failed;
+ feed_speed_ratio(tsrc, tdst, &sscale, &dscale);
+ feed_scale_roll(dscale, &tscale, &troll,
+ FEEDRATE_32_MAXROLL);
+ if (tscale != -1 && troll != -1) {
+ *src = tsrc;
+ *gx = sscale;
+ *gy = dscale;
+ *scale = tscale;
+ *roll = troll;
+ return j;
+ }
+ }
+ for (j = hzmin; j < hzmax; j++) {
+ tsrc = *src - (*src % j);
+ tdst = *dst - (*dst % j);
+ if (tsrc < 1 || tdst < 1)
+ goto coef_failed;
+ feed_speed_ratio(tsrc, tdst, &sscale, &dscale);
+ feed_scale_roll(dscale, &tscale, &troll,
+ FEEDRATE_32_MAXROLL);
+ if (tscale != -1 && troll != -1) {
+ *src = tsrc;
+ *dst = tdst;
+ *gx = sscale;
+ *gy = dscale;
+ *scale = tscale;
+ *roll = troll;
+ return j;
+ }
+ }
+ for (j = hzmin; j < hzmax; j++) {
+ tsrc = *src;
+ tdst = *dst - (*dst % j);
+ if (tsrc < 1 || tdst < 1)
+ goto coef_failed;
+ feed_speed_ratio(tsrc, tdst, &sscale, &dscale);
+ feed_scale_roll(dscale, &tscale, &troll,
+ FEEDRATE_32_MAXROLL);
+ if (tscale != -1 && troll != -1) {
+ *src = tsrc;
+ *dst = tdst;
+ *gx = sscale;
+ *gy = dscale;
+ *scale = tscale;
+ *roll = troll;
+ return j;
+ }
+ }
+ }
+coef_failed:
+ feed_speed_ratio(*src, *dst, gx, gy);
+ feed_scale_roll(*gy, scale, roll, FEEDRATE_32_MAXROLL);
+ return 0;
+}
+
+static void
+feed_rate_reset(struct feed_rate_info *info)
+{
+ info->scale = -1;
+ info->roll = -1;
+ info->src = info->rsrc;
+ info->dst = info->rdst;
+ info->gx = 0;
+ info->gy = 0;
}
static int
feed_rate_setup(struct pcm_feeder *f)
{
struct feed_rate_info *info = f->data;
- uint32_t mscale, mroll, l, r, g;
-
- /* Beat sample rates down by greatest common divisor */
- g = gcd(info->src, info->dst);
- info->sscale = info->dst / g;
- info->dscale = info->src / g;
+ int r = 0;
+ info->pos = 2;
+ info->bpos = 4;
info->alpha = 0;
- info->buffer_ticks = 0;
- info->buffer_pos = 0;
-
- /* Pick suitable conversion routine */
- if (info->src > info->dst) {
- info->convert = convert_stereo_down;
+ feed_rate_reset(info);
+ if (info->src == info->dst) {
+ /*
+ * No conversion ever needed. Just do plain copy.
+ */
+ info->convert = feed_convert_plain;
+ info->gx = 1;
+ info->gy = 1;
} else {
- info->convert = convert_stereo_up;
- }
-
- /*
- * Determine number of conversion rounds that will fit into
- * buffer. NB Must set info->rounds to one before using
- * src_ticks_per_cycle here since it used by src_ticks_per_cycle.
- */
- info->rounds = 1;
- r = (FEEDBUFSZ - bytes_per_tick(info)) /
- (src_ticks_per_cycle(info) * bytes_per_tick(info));
- if (r == 0) {
- RATE_TRACE("Insufficient buffer space for conversion %d -> %d "
- "(%d < %d)\n", info->src, info->dst, FEEDBUFSZ,
- src_ticks_per_cycle(info) * bytes_per_tick(info));
- return -1;
+ switch (feeder_rate_scaling) {
+ case FEEDRATE_CONVERT_64:
+ feed_speed_ratio(info->src, info->dst,
+ &info->gx, &info->gy);
+ info->convert = feed_convert_64;
+ break;
+ case FEEDRATE_CONVERT_SCALE64:
+ feed_speed_ratio(info->src, info->dst,
+ &info->gx, &info->gy);
+ feed_scale_roll(info->gy, &info->scale,
+ &info->roll, FEEDRATE_64_MAXROLL);
+ if (info->scale == -1 || info->roll == -1)
+ return -1;
+ info->convert = feed_convert_scale64;
+ break;
+ case FEEDRATE_CONVERT_SCALE32:
+ r = feed_get_best_coef(&info->src, &info->dst,
+ &info->gx, &info->gy, &info->scale,
+ &info->roll);
+ if (r == 0)
+ return -1;
+ info->convert = feed_convert_scale32;
+ break;
+ case FEEDRATE_CONVERT_PLAIN:
+ feed_speed_ratio(info->src, info->dst,
+ &info->gx, &info->gy);
+ info->convert = feed_convert_plain;
+ break;
+ case FEEDRATE_CONVERT_FIXED:
+ r = feed_get_best_coef(&info->src, &info->dst,
+ &info->gx, &info->gy, &info->scale,
+ &info->roll);
+ if (r != 0 && info->src == info->rsrc &&
+ info->dst == info->rdst)
+ info->convert = feed_convert_scale32;
+ else {
+ /* Fallback */
+ feed_rate_reset(info);
+ feed_speed_ratio(info->src, info->dst,
+ &info->gx, &info->gy);
+ feed_scale_roll(info->gy, &info->scale,
+ &info->roll, FEEDRATE_64_MAXROLL);
+ if (info->scale != -1 && info->roll != -1)
+ info->convert = feed_convert_scale64;
+ else
+ info->convert = feed_convert_64;
+ }
+ break;
+ case FEEDRATE_CONVERT_OPTIMAL:
+ r = feed_get_best_coef(&info->src, &info->dst,
+ &info->gx, &info->gy, &info->scale,
+ &info->roll);
+ if (r != 0)
+ info->convert = feed_convert_scale32;
+ else {
+ /* Fallback */
+ feed_rate_reset(info);
+ feed_speed_ratio(info->src, info->dst,
+ &info->gx, &info->gy);
+ feed_scale_roll(info->gy, &info->scale,
+ &info->roll, FEEDRATE_64_MAXROLL);
+ if (info->scale != -1 && info->roll != -1)
+ info->convert = feed_convert_scale64;
+ else
+ info->convert = feed_convert_64;
+ }
+ break;
+ case FEEDRATE_CONVERT_WORST:
+ r = feed_get_best_coef(&info->src, &info->dst,
+ &info->gx, &info->gy, &info->scale,
+ &info->roll);
+ if (r != 0)
+ info->convert = feed_convert_scale32;
+ else {
+ /* Fallback */
+ feed_rate_reset(info);
+ feed_speed_ratio(info->src, info->dst,
+ &info->gx, &info->gy);
+ info->convert = feed_convert_plain;
+ }
+ break;
+ default:
+ return -1;
+ break;
+ }
+ /* No way! */
+ if (info->gx == 0 || info->gy == 0)
+ return -1;
+ /*
+ * No need to interpolate/decimate, just do plain copy.
+ * This probably caused by Hz roundup.
+ */
+ if (info->gx == info->gy)
+ info->convert = feed_convert_plain;
}
- info->rounds = r;
-
- /*
- * Find scale and roll combination that allows us to trade
- * costly divide operations in the main loop for multiply-rolls.
- */
- for (l = 96; l >= MINGAIN; l -= 3) {
- for (mroll = 0; mroll < 16; mroll ++) {
- mscale = (1 << mroll) / info->sscale;
-
- r = (mscale * info->sscale * 100) >> mroll;
- if (r > l && r <= 100) {
- info->mscale = mscale;
- info->mroll = mroll;
- RATE_TRACE("Converting %d to %d with "
- "mscale = %d and mroll = %d "
- "(gain = %d / 100)\n",
- info->src, info->dst,
- info->mscale, info->mroll, r);
- return 0;
- }
- }
- }
-
- RATE_TRACE("Failed to find a converter within %d%% gain for "
- "%d to %d.\n", l, info->src, info->dst);
-
- return -2;
+ return 0;
}
static int
feed_rate_set(struct pcm_feeder *f, int what, int value)
{
struct feed_rate_info *info = f->data;
- int rvalue;
-
- if (value < RATEMIN || value > RATEMAX) {
+
+ if (value < feeder_rate_ratemin || value > feeder_rate_ratemax)
return -1;
- }
- rvalue = (value / ROUNDHZ) * ROUNDHZ;
- if (value - rvalue > ROUNDHZ / 2) {
- rvalue += ROUNDHZ;
- }
-
- switch(what) {
- case FEEDRATE_SRC:
- info->src = rvalue;
- break;
- case FEEDRATE_DST:
- info->dst = rvalue;
- break;
- default:
- return -1;
+ switch (what) {
+ case FEEDRATE_SRC:
+ info->rsrc = value;
+ break;
+ case FEEDRATE_DST:
+ info->rdst = value;
+ break;
+ default:
+ return -1;
}
-
return feed_rate_setup(f);
}
@@ -233,13 +482,16 @@
{
struct feed_rate_info *info = f->data;
- switch(what) {
- case FEEDRATE_SRC:
- return info->src;
- case FEEDRATE_DST:
- return info->dst;
- default:
- return -1;
+ /*
+ * Return *real* src/dst rate.
+ */
+ switch (what) {
+ case FEEDRATE_SRC:
+ return info->rsrc;
+ case FEEDRATE_DST:
+ return info->rdst;
+ default:
+ return -1;
}
return -1;
}
@@ -252,12 +504,20 @@
info = malloc(sizeof(*info), M_RATEFEEDER, M_NOWAIT | M_ZERO);
if (info == NULL)
return ENOMEM;
- info->src = DSP_DEFAULT_SPEED;
- info->dst = DSP_DEFAULT_SPEED;
- info->channels = 2;
-
+ /*
+ * bufsz = sample from last cycle + conversion space
+ */
+ info->bufsz = 2 + feeder_rate_buffersize;
+ info->buffer = malloc(sizeof(*info->buffer) * info->bufsz,
+ M_RATEFEEDER, M_NOWAIT | M_ZERO);
+ if (info->buffer == NULL) {
+ free(info, M_RATEFEEDER);
+ return ENOMEM;
+ }
+ info->rsrc = DSP_DEFAULT_SPEED;
+ info->rdst = DSP_DEFAULT_SPEED;
f->data = info;
- return 0;
+ return feed_rate_setup(f);
}
static int
@@ -266,211 +526,269 @@
struct feed_rate_info *info = f->data;
if (info) {
+ if (info->buffer)
+ free(info->buffer, M_RATEFEEDER);
free(info, M_RATEFEEDER);
}
f->data = NULL;
return 0;
}
-static int
-convert_stereo_up(struct feed_rate_info *info,
- uint32_t src_ticks,
- uint32_t dst_ticks,
- int16_t *dst)
+static uint32_t
+feed_convert_64(struct feed_rate_info *info, int16_t *dst, uint32_t max)
{
- uint32_t max_dst_ticks;
- int32_t alpha, dalpha, malpha, mroll, sp, dp, se, de, x, o;
+ int64_t x, alpha, distance;
+ uint32_t ret;
+ int32_t pos, bpos, gx, gy;
int16_t *src;
-
- sp = info->buffer_pos * 2;
- se = sp + src_ticks * 2;
-
- src = info->buffer;
- alpha = info->alpha * info->mscale;
- dalpha = info->dscale * info->mscale; /* Alpha increment */
- malpha = info->sscale * info->mscale; /* Maximum allowed alpha value */
- mroll = info->mroll;
-
/*
- * For efficiency the main conversion loop should only depend on
- * one variable. We use the state to work out the maximum number
- * of output samples that are available and eliminate the checking of
- * sp from the loop.
+ * Plain, straight forward 64bit arith. No bit-magic applied here.
*/
- max_dst_ticks = src_ticks * info->dst / info->src - alpha / dalpha;
- if (max_dst_ticks < dst_ticks) {
- dst_ticks = max_dst_ticks;
+ ret = 0;
+ alpha = info->alpha;
+ gx = info->gx;
+ gy = info->gy;
+ pos = info->pos;
+ bpos = info->bpos;
+ src = info->buffer;
+ for (;;) {
+ if (alpha < gx) {
+ alpha += gy;
+ pos += 2;
+ if (pos == bpos)
+ break;
+ } else {
+ alpha -= gx;
+ distance = gy - alpha;
+ x = (alpha * src[pos - 2]) + (distance * src[pos]);
+ dst[ret++] = x / gy;
+ x = (alpha * src[pos - 1]) + (distance * src[pos + 1]);
+ dst[ret++] = x / gy;
+ if (ret == max)
+ break;
+ }
}
+ info->alpha = alpha;
+ info->pos = pos;
+ return ret;
+}
- dp = 0;
- de = dst_ticks * 2;
+static uint32_t
+feed_convert_scale64(struct feed_rate_info *info, int16_t *dst, uint32_t max)
+{
+ int64_t x, alpha, distance;
+ uint32_t ret;
+ int32_t pos, bpos, gx, gy, roll;
+ int16_t *src;
/*
- * Unrolling this loop manually does not help much here because
- * of the alpha, malpha comparison.
+ * 64bit scaling.
*/
- while (dp < de) {
- o = malpha - alpha;
- x = alpha * src[sp + 2] + o * src[sp];
- dst[dp++] = x >> mroll;
- x = alpha * src[sp + 3] + o * src[sp + 1];
- dst[dp++] = x >> mroll;
- alpha += dalpha;
- if (alpha >= malpha) {
- alpha -= malpha;
- sp += 2;
+ ret = 0;
+ roll = info->roll;
+ alpha = info->alpha * info->scale;
+ gx = info->gx * info->scale;
+ gy = info->gy * info->scale;
+ pos = info->pos;
+ bpos = info->bpos;
+ src = info->buffer;
+ for (;;) {
+ if (alpha < gx) {
+ alpha += gy;
+ pos += 2;
+ if (pos == bpos)
+ break;
+ } else {
+ alpha -= gx;
+ distance = gy - alpha;
+ x = (alpha * src[pos - 2]) + (distance * src[pos]);
+ dst[ret++] = x >> roll;
+ x = (alpha * src[pos - 1]) + (distance * src[pos + 1]);
+ dst[ret++] = x >> roll;
+ if (ret == max)
+ break;
}
}
- RATE_ASSERT(sp <= se, ("%s: Source overrun\n", __func__));
-
- info->buffer_pos = sp / info->channels;
- info->alpha = alpha / info->mscale;
-
- return dp / info->channels;
+ info->alpha = alpha / info->scale;
+ info->pos = pos;
+ return ret;
}
-static int
-convert_stereo_down(struct feed_rate_info *info,
- uint32_t src_ticks,
- uint32_t dst_ticks,
- int16_t *dst)
+static uint32_t
+feed_convert_scale32(struct feed_rate_info *info, int16_t *dst, uint32_t max)
{
- int32_t alpha, dalpha, malpha, mroll, sp, dp, se, de, x, o, m,
- mdalpha, mstep;
+ uint32_t ret;
+ int32_t x, pos, bpos, gx, gy, alpha, roll, distance;
int16_t *src;
-
- sp = info->buffer_pos * 2;
- se = sp + src_ticks * 2;
-
+ /*
+ * 32bit scaling.
+ */
+ ret = 0;
+ roll = info->roll;
+ alpha = info->alpha * info->scale;
+ gx = info->gx * info->scale;
+ gy = info->gy * info->scale;
+ pos = info->pos;
+ bpos = info->bpos;
src = info->buffer;
- alpha = info->alpha * info->mscale;
- dalpha = info->dscale * info->mscale; /* Alpha increment */
- malpha = info->sscale * info->mscale; /* Maximum allowed alpha value */
- mroll = info->mroll;
-
- dp = 0;
- de = dst_ticks * 2;
-
- m = dalpha / malpha;
- mstep = m * 2;
- mdalpha = dalpha - m * malpha;
+ for (;;) {
+ if (alpha < gx) {
+ alpha += gy;
+ pos += 2;
+ if (pos == bpos)
+ break;
+ } else {
+ alpha -= gx;
+ distance = gy - alpha;
+ x = (alpha * src[pos - 2]) + (distance * src[pos]);
+ dst[ret++] = x >> roll;
+ x = (alpha * src[pos - 1]) + (distance * src[pos + 1]);
+ dst[ret++] = x >> roll;
+ if (ret == max)
+ break;
+ }
+ }
+ info->alpha = alpha / info->scale;
+ info->pos = pos;
+ return ret;
+}
+static uint32_t
+feed_convert_plain(struct feed_rate_info *info, int16_t *dst, uint32_t max)
+{
+ uint32_t ret;
+ int32_t pos, bpos, gx, gy, alpha;
+ int16_t *src;
/*
- * TODO: eliminate sp or dp from this loop comparison for a few
- * extra % performance.
+ * Plain copy.
*/
- while (sp < se && dp < de) {
- o = malpha - alpha;
- x = alpha * src[sp + 2] + o * src[sp];
- dst[dp++] = x >> mroll;
- x = alpha * src[sp + 3] + o * src[sp + 1];
- dst[dp++] = x >> mroll;
-
- alpha += mdalpha;
- sp += mstep;
- if (alpha >= malpha) {
- alpha -= malpha;
- sp += 2;
+ ret = 0;
+ gx = info->gx;
+ gy = info->gy;
+ alpha = info->alpha;
+ pos = info->pos;
+ bpos = info->bpos;
+ src = info->buffer;
+ for (;;) {
+ if (alpha < gx) {
+ alpha += gy;
+ pos += 2;
+ if (pos == bpos)
+ break;
+ } else {
+ alpha -= gx;
+ dst[ret++] = src[pos];
+ dst[ret++] = src[pos + 1];
+ if (ret == max)
+ break;
}
}
-
- info->buffer_pos = sp / 2;
- info->alpha = alpha / info->mscale;
-
- RATE_ASSERT(info->buffer_pos <= info->buffer_ticks,
- ("%s: Source overrun\n", __func__));
-
- return dp / 2;
+ info->pos = pos;
+ info->alpha = alpha;
+ return ret;
}
-static int
-feed_rate(struct pcm_feeder *f,
- struct pcm_channel *c,
- uint8_t *b,
- uint32_t count,
- void *source)
+static int32_t
+feed_rate(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
+ uint32_t count, void *source)
{
struct feed_rate_info *info = f->data;
-
- uint32_t done, s_ticks, d_ticks;
- done = 0;
-
- RATE_ASSERT(info->channels == 2,
- ("%s: channels (%d) != 2", __func__, info->channels));
-
- while (done < count) {
- /* Slurp in more data if input buffer is not full */
- while (info->buffer_ticks < src_ticks_per_cycle(info)) {
- uint8_t *u8b;
- int fetch;
- fetch = src_bytes_per_cycle(info) -
- info->buffer_ticks * bytes_per_tick(info);
- u8b = (uint8_t*)info->buffer +
- (info->buffer_ticks + 1) *
- bytes_per_tick(info);
- fetch = FEEDER_FEED(f->source, c, u8b, fetch, source);
- RATE_ASSERT(fetch % bytes_per_tick(info) == 0,
- ("%s: fetched unaligned bytes (%d)",
- __func__, fetch));
- info->buffer_ticks += fetch / bytes_per_tick(info);
- RATE_ASSERT(src_ticks_per_cycle(info) >=
- info->buffer_ticks,
- ("%s: buffer overfilled (%d > %d).",
- __func__, info->buffer_ticks,
- src_ticks_per_cycle(info)));
- if (fetch == 0)
+ uint32_t i;
+ int32_t fetch, slot;
+ int16_t *dst = (int16_t *)b;
+ /*
+ * This loop has been optimized to generalize both up / down
+ * sampling without causing missing samples or excessive buffer
+ * feeding.
+ */
+ RATE_ASSERT(count >= 4 && count % 4 == 0,
+ ("%s: Count size not byte integral\n", __func__));
+ count >>= 1;
+ slot = (((info->gx * (count >> 1)) + info->gy - info->alpha - 1) / info->gy) << 1;
+ /*
+ * Optimize buffer feeding aggresively to ensure calculated slot
+ * can be fitted nicely into available buffer free space, hence
+ * avoiding multiple feeding.
+ */
+ if (info->pos != 2 && info->bpos - info->pos == 2 &&
+ info->bpos + slot > info->bufsz) {
+ /*
+ * Copy last unit sample and its previous to
+ * beginning of buffer.
+ */
+ info->buffer[0] = info->buffer[info->pos - 2];
+ info->buffer[1] = info->buffer[info->pos - 1];
+ info->buffer[2] = info->buffer[info->pos];
+ info->buffer[3] = info->buffer[info->pos + 1];
+ info->pos = 2;
+ info->bpos = 4;
+ }
+ RATE_ASSERT(slot >= 0, ("%s: Negative Slot: %d\n",
+ __func__, slot));
+ i = 0;
+ for (;;) {
+ for (;;) {
+ fetch = info->bufsz - info->bpos;
+ RATE_ASSERT(fetch >= 0,
+ ("%s: Buffer overrun: %d > %d\n",
+ __func__, info->bpos, info->bufsz));
+ if (slot < fetch)
+ fetch = slot;
+ if (fetch > 0) {
+ RATE_ASSERT(fetch % 2 == 0,
+ ("%s: Fetch size not sample integral\n",
+ __func__));
+ fetch = FEEDER_FEED(f->source, c,
+ (uint8_t *)(info->buffer + info->bpos),
+ fetch << 1, source);
+ if (fetch == 0)
+ break;
+ RATE_ASSERT(fetch % 4 == 0,
+ ("%s: Fetch size not byte integral\n",
+ __func__));
+ fetch >>= 1;
+ info->bpos += fetch;
+ slot -= fetch;
+ RATE_ASSERT(slot >= 0,
+ ("%s: Negative Slot: %d\n", __func__
+ slot));
+ if (slot == 0)
+ break;
+ if (info->bpos == info->bufsz)
+ break;
+ } else
break;
}
-
- /* Find amount of input buffer data that should be processed */
- d_ticks = (count - done) / bytes_per_tick(info);
- s_ticks = info->buffer_ticks - info->buffer_pos;
- if (info->buffer_ticks != src_ticks_per_cycle(info)) {
- if (s_ticks > 8)
- s_ticks -= 8;
- else
- s_ticks = 0;
- }
-
- d_ticks = info->convert(info, s_ticks, d_ticks,
- (int16_t*)(b + done));
- if (d_ticks == 0)
+ if (info->pos == info->bpos) {
+ RATE_ASSERT(info->pos == 2,
+ ("%s: EOF while in progress\n", __func__));
break;
- done += d_ticks * bytes_per_tick(info);
-
- RATE_ASSERT(info->buffer_pos <= info->buffer_ticks,
- ("%s: buffer_ticks too big\n", __func__));
- RATE_ASSERT(info->buffer_ticks <= src_ticks_per_cycle(info),
- ("too many ticks %d / %d\n",
- info->buffer_ticks, src_ticks_per_cycle(info)));
- RATE_TRACE("%s: ticks %5d / %d pos %d\n", __func__,
- info->buffer_ticks, src_ticks_per_cycle(info),
- info->buffer_pos);
-
- if (src_ticks_per_cycle(info) <= info->buffer_pos) {
- /* End of cycle reached, copy last samples to start */
- uint8_t *u8b;
- u8b = (uint8_t*)info->buffer;
- bcopy(u8b + src_bytes_per_cycle(info), u8b,
- bytes_per_tick(info));
-
- RATE_ASSERT(info->alpha == 0,
- ("%s: completed cycle with "
- "alpha non-zero", __func__, info->alpha));
-
- info->buffer_pos = 0;
- info->buffer_ticks = 0;
}
+ RATE_ASSERT(info->pos <= info->bpos,
+ ("%s: Buffer overrun: %d > %d\n", __func__,
+ info->pos, info->bpos));
+ RATE_ASSERT(info->pos < info->bpos,
+ ("%s: Zero buffer!\n", __func__));
+ RATE_ASSERT((info->bpos - info->pos) % 2 == 0,
+ ("%s: Buffer not sample integral\n", __func__));
+ i += info->convert(info, dst + i, count - i);
+ RATE_ASSERT(info->pos <= info->bpos,
+ ("%s: Buffer overrun: %d > %d\n",
+ __func__, info->pos, info->bpos));
+ if (info->pos == info->bpos) {
+ /*
+ * End of buffer cycle. Copy last unit sample
+ * to beginning of buffer so next cycle can
+ * interpolate using it.
+ */
+ info->buffer[0] = info->buffer[info->pos - 2];
+ info->buffer[1] = info->buffer[info->pos - 1];
+ info->bpos = 2;
+ info->pos = 2;
+ }
+ if (i == count)
+ break;
}
-
- RATE_ASSERT(count >= done,
- ("%s: generated too many bytes of data (%d > %d).",
- __func__, done, count));
-
- if (done != count) {
- RATE_TRACE("Only did %d of %d\n", done, count);
- }
-
- return done;
+ return i << 1;
}
static struct pcm_feederdesc feeder_rate_desc[] = {
@@ -486,4 +804,3 @@
{0, 0}
};
FEEDER_DECLARE(feeder_rate, 2, NULL);
-
--- sys/dev/sound/pcm/feeder_volume.c.orig Thu Jan 1 07:30:00 1970
+++ sys/dev/sound/pcm/feeder_volume.c Fri Sep 16 06:32:00 2005
@@ -0,0 +1,75 @@
+/*-
+ * Copyright (c) 2005 Ariff Abdullah
+ * <skywizard at MyBSD.org.my> 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_volume, a long 'Lost Technology' rather than a new feature.
+ */
+
+#include <dev/sound/pcm/sound.h>
+#include "feeder_if.h"
+
+SND_DECLARE_FILE("$FreeBSD$");
+
+static int
+feed_volume_s16(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
+ uint32_t count, void *source)
+{
+ int i, j, k, vol[2];
+ int16_t *buf;
+
+ k = FEEDER_FEED(f->source, c, b, count & ~1, source);
+ if (k < 2) {
+ device_printf(c->dev, "%s: Not enough data (Got: %d bytes)\n",
+ __func__, k);
+ return 0;
+ }
+ if (k & 1) {
+ device_printf(c->dev, "%s: Bytes not 16bit aligned.\n", __func__);
+ k &= ~1;
+ }
+ i = k >> 1;
+ buf = (int16_t *)b;
+ vol[0] = c->volume & 0x7f;
+ vol[1] = (c->volume >> 8) & 0x7f;
+ while (i > 0) {
+ i--;
+ j = (vol[i & 1] * buf[i]) / 100;
+ if (j > 32767)
+ j = 32767;
+ if (j < -32768)
+ j = -32768;
+ buf[i] = j;
+ }
+ return k;
+}
+
+static struct pcm_feederdesc feeder_volume_s16_desc[] = {
+ {FEEDER_VOLUME, AFMT_S16_LE|AFMT_STEREO, AFMT_S16_LE|AFMT_STEREO, 0},
+ {0, 0, 0, 0},
+};
+static kobj_method_t feeder_volume_s16_methods[] = {
+ KOBJMETHOD(feeder_feed, feed_volume_s16),
+ {0, 0}
+};
+FEEDER_DECLARE(feeder_volume_s16, 2, NULL);
--- sys/dev/sound/pcm/mixer.c.orig Sun Jan 30 09:00:05 2005
+++ sys/dev/sound/pcm/mixer.c Fri Sep 16 06:32:00 2005
@@ -41,6 +41,7 @@
int hwvol_muted;
int hwvol_mixer;
int hwvol_step;
+ device_t dev;
u_int32_t hwvol_mute_level;
u_int32_t devs;
u_int32_t recdevs;
@@ -74,7 +75,7 @@
static struct cdevsw mixer_cdevsw = {
.d_version = D_VERSION,
- .d_flags = D_NEEDGIANT,
+ .d_flags = D_TRACKCLOSE | D_NEEDGIANT,
.d_open = mixer_open,
.d_close = mixer_close,
.d_ioctl = mixer_ioctl,
@@ -113,6 +114,7 @@
static int
mixer_set(struct snd_mixer *mixer, unsigned dev, unsigned lev)
{
+ struct snddev_info *d;
unsigned l, r;
int v;
@@ -122,9 +124,34 @@
l = min((lev & 0x00ff), 100);
r = min(((lev & 0xff00) >> 8), 100);
- v = MIXER_SET(mixer, dev, l, r);
- if (v < 0)
- return -1;
+ d = device_get_softc(mixer->dev);
+ if (dev == SOUND_MIXER_PCM && d &&
+ (d->flags & SD_F_SOFTVOL)) {
+ struct snddev_channel *sce;
+ struct pcm_channel *ch;
+#ifdef USING_MUTEX
+ int locked = (mixer->lock && mtx_owned((struct mtx *)(mixer->lock))) ? 1 : 0;
+
+ if (locked)
+ snd_mtxunlock(mixer->lock);
+#endif
+ SLIST_FOREACH(sce, &d->channels, link) {
+ ch = sce->channel;
+ CHN_LOCK(ch);
+ if (ch->direction == PCMDIR_PLAY &&
+ (ch->feederflags & (1 << FEEDER_VOLUME)))
+ chn_setvolume(ch, l, r);
+ CHN_UNLOCK(ch);
+ }
+#ifdef USING_MUTEX
+ if (locked)
+ snd_mtxlock(mixer->lock);
+#endif
+ } else {
+ v = MIXER_SET(mixer, dev, l, r);
+ if (v < 0)
+ return -1;
+ }
mixer->level[dev] = l | (r << 8);
return 0;
@@ -157,6 +184,9 @@
void
mix_setdevs(struct snd_mixer *m, u_int32_t v)
{
+ struct snddev_info *d = device_get_softc(m->dev);
+ if (d && (d->flags & SD_F_SOFTVOL))
+ v |= SOUND_MASK_PCM;
m->devs = v;
}
@@ -199,6 +229,7 @@
m->type = cls->name;
m->devinfo = devinfo;
m->busy = 0;
+ m->dev = dev;
if (MIXER_INIT(m))
goto bad;
@@ -442,7 +473,7 @@
int v = -1, j = cmd & 0xff;
m = i_dev->si_drv1;
- if (!m->busy)
+ if (mode != -1 && !m->busy)
return EBADF;
s = spltty();
@@ -495,8 +526,10 @@
return;
if (strcmp(name, "mixer") == 0) {
sd = devclass_get_softc(pcm_devclass, snd_unit);
- if (sd != NULL)
+ if (sd != NULL) {
*dev = sd->mixer_dev;
+ dev_ref(*dev);
+ }
}
}
--- sys/dev/sound/pcm/slavechan.c.orig Thu Jan 1 07:30:00 1970
+++ sys/dev/sound/pcm/slavechan.c Fri Sep 16 06:32:00 2005
@@ -0,0 +1,381 @@
+/*
+ * Copyright (c) 2005 Ariff Abdullah
+ * <skywizard at MyBSD.org.my> All rights reserved.
+ *
+ * Derived from vchan.c
+ *
+ * Copyright (c) 2001 Cameron Grant
+ * <cg at 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.
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * *
+ * This new implementation is fully dedicated in memory of Cameron Grant, *
+ * the creator of magnificent, highly addictive feeder infrastructure. *
+ * *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
+ */
+
+#include <dev/sound/pcm/sound.h>
+#include <dev/sound/pcm/vchan.h>
+#include "feeder_if.h"
+
+SND_DECLARE_FILE("$FreeBSD$");
+
+struct slaveinfo {
+ uint32_t spd, fmt, blksz, bps, run;
+ struct pcm_channel *channel, *parent;
+};
+
+static int
+feed_slave(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
+ uint32_t count, void *source)
+{
+ struct pcmchan_children *cce;
+ struct pcm_channel *ch;
+ int cnt = 0;
+
+ cce = SLIST_FIRST(&c->children);
+ if (!cce || !cce->channel)
+ return 0;
+ ch = cce->channel;
+ CHN_LOCK(ch);
+ if (ch->flags & CHN_F_TRIGGERED) {
+ if (ch->flags & CHN_F_MAPPED)
+ sndbuf_acquire(ch->bufsoft, NULL, sndbuf_getfree(ch->bufsoft));
+ cnt = FEEDER_FEED(ch->feeder, ch, b, count, ch->bufsoft);
+ }
+ CHN_UNLOCK(ch);
+ return cnt;
+}
+
+static struct pcm_feederdesc feeder_slave_desc[] = {
+ {FEEDER_SLAVE, AFMT_U8, AFMT_U8, 0},
+ {FEEDER_SLAVE, AFMT_U8|AFMT_STEREO, AFMT_U8|AFMT_STEREO, 0},
+ {FEEDER_SLAVE, AFMT_S8, AFMT_S8, 0},
+ {FEEDER_SLAVE, AFMT_S8|AFMT_STEREO, AFMT_S8|AFMT_STEREO, 0},
+ {FEEDER_SLAVE, AFMT_A_LAW, AFMT_A_LAW, 0},
+ {FEEDER_SLAVE, AFMT_A_LAW|AFMT_STEREO, AFMT_A_LAW|AFMT_STEREO, 0},
+ {FEEDER_SLAVE, AFMT_MU_LAW, AFMT_MU_LAW, 0},
+ {FEEDER_SLAVE, AFMT_MU_LAW|AFMT_STEREO, AFMT_MU_LAW|AFMT_STEREO, 0},
+ {FEEDER_SLAVE, AFMT_U16_LE, AFMT_U16_LE, 0},
+ {FEEDER_SLAVE, AFMT_U16_LE|AFMT_STEREO, AFMT_U16_LE|AFMT_STEREO, 0},
+ {FEEDER_SLAVE, AFMT_S16_LE, AFMT_S16_LE, 0},
+ {FEEDER_SLAVE, AFMT_S16_LE|AFMT_STEREO, AFMT_S16_LE|AFMT_STEREO, 0},
+ {FEEDER_SLAVE, AFMT_U16_BE, AFMT_U16_BE, 0},
+ {FEEDER_SLAVE, AFMT_U16_BE|AFMT_STEREO, AFMT_U16_BE|AFMT_STEREO, 0},
+ {FEEDER_SLAVE, AFMT_S16_BE, AFMT_S16_BE, 0},
+ {FEEDER_SLAVE, AFMT_S16_BE|AFMT_STEREO, AFMT_S16_BE|AFMT_STEREO, 0},
+ {FEEDER_SLAVE, AFMT_U24_LE, AFMT_U24_LE, 0},
+ {FEEDER_SLAVE, AFMT_U24_LE|AFMT_STEREO, AFMT_U24_LE|AFMT_STEREO, 0},
+ {FEEDER_SLAVE, AFMT_S24_LE, AFMT_S24_LE, 0},
+ {FEEDER_SLAVE, AFMT_S24_LE|AFMT_STEREO, AFMT_S24_LE|AFMT_STEREO, 0},
+ {FEEDER_SLAVE, AFMT_U24_BE, AFMT_U24_BE, 0},
+ {FEEDER_SLAVE, AFMT_U24_BE|AFMT_STEREO, AFMT_U24_BE|AFMT_STEREO, 0},
+ {FEEDER_SLAVE, AFMT_S24_BE, AFMT_S24_BE, 0},
+ {FEEDER_SLAVE, AFMT_S24_BE|AFMT_STEREO, AFMT_S24_BE|AFMT_STEREO, 0},
+ {FEEDER_SLAVE, AFMT_U32_LE, AFMT_U32_LE, 0},
+ {FEEDER_SLAVE, AFMT_U32_LE|AFMT_STEREO, AFMT_U32_LE|AFMT_STEREO, 0},
+ {FEEDER_SLAVE, AFMT_S32_LE, AFMT_S32_LE, 0},
+ {FEEDER_SLAVE, AFMT_S32_LE|AFMT_STEREO, AFMT_S32_LE|AFMT_STEREO, 0},
+ {FEEDER_SLAVE, AFMT_U32_BE, AFMT_U32_BE, 0},
+ {FEEDER_SLAVE, AFMT_U32_BE|AFMT_STEREO, AFMT_U32_BE|AFMT_STEREO, 0},
+ {FEEDER_SLAVE, AFMT_S32_BE, AFMT_S32_BE, 0},
+ {FEEDER_SLAVE, AFMT_S32_BE|AFMT_STEREO, AFMT_S32_BE|AFMT_STEREO, 0},
+ {0, 0, 0, 0},
+};
+static kobj_method_t feeder_slave_methods[] = {
+ KOBJMETHOD(feeder_feed, feed_slave),
+ {0, 0}
+};
+FEEDER_DECLARE(feeder_slave, 2, NULL);
+
+static void *
+slave_init(kobj_t obj, void *devinfo, struct snd_dbuf *b,
+ struct pcm_channel *c, int dir)
+{
+ struct slaveinfo *ch;
+ struct pcm_channel *parent = devinfo;
+
+ KASSERT(dir == PCMDIR_PLAY ||
+ dir == PCMDIR_REC, ("slave_init: bad direction"));
+ ch = malloc(sizeof(*ch), M_DEVBUF, M_WAITOK|M_ZERO);
+ if (!ch)
+ return NULL;
+ ch->parent = parent;
+ ch->channel = c;
+ ch->fmt = AFMT_U8;
+ ch->spd = DSP_DEFAULT_SPEED;
+ ch->blksz = 2048;
+ c->flags |= CHN_F_SLAVE;
+ return ch;
+}
+
+static int
+slave_free(kobj_t obj, void *data)
+{
+ return 0;
+}
+
+static int
+slave_setformat(kobj_t obj, void *data, uint32_t format)
+{
+ struct slaveinfo *ch = data;
+ struct pcm_channel *parent = ch->parent;
+ struct pcm_channel *channel = ch->channel;
+ int r;
+
+ CHN_UNLOCK(channel);
+ CHN_LOCK(parent);
+ r = CHANNEL_SETFORMAT(parent->methods, parent->devinfo, format);
+ if (r == 0) {
+ parent->format = format;
+ sndbuf_setfmt(parent->bufhard, format);
+ sndbuf_setfmt(parent->bufsoft, format);
+ }
+ CHN_UNLOCK(parent);
+ CHN_LOCK(channel);
+ if (r == 0) {
+ ch->fmt = format;
+ ch->bps = 1;
+ if (ch->fmt & AFMT_STEREO)
+ ch->bps <<= 1;
+ if (ch->fmt & AFMT_16BIT)
+ ch->bps <<= 1;
+ else if (ch->fmt & AFMT_24BIT)
+ ch->bps *= 3;
+ else if (ch->fmt & AFMT_32BIT)
+ ch->bps <<= 2;
+ sndbuf_setfmt(channel->bufsoft, format);
+ }
+ return r;
+}
+
+static int
+slave_setspeed(kobj_t obj, void *data, uint32_t speed)
+{
+ struct slaveinfo *ch = data;
+ struct pcm_channel *parent = ch->parent;
+ struct pcm_channel *channel = ch->channel;
+ int r, hwspd = speed;
+
+ CHN_UNLOCK(channel);
+ CHN_LOCK(parent);
+ RANGE(hwspd, chn_getcaps(parent)->minspeed, chn_getcaps(parent)->maxspeed);
+ r = CHANNEL_SETSPEED(parent->methods, parent->devinfo, hwspd);
+ parent->speed = r;
+ sndbuf_setspd(parent->bufhard, r);
+ sndbuf_setspd(parent->bufsoft, r);
+ CHN_UNLOCK(parent);
+ CHN_LOCK(channel);
+ ch->spd = r;
+ return ch->spd;
+}
+
+static int
+slave_setblocksize(kobj_t obj, void *data, uint32_t blocksize)
+{
+ struct slaveinfo *ch = data;
+ struct pcm_channel *channel = ch->channel;
+ struct pcm_channel *parent = ch->parent;
+ int prate, crate;
+
+ ch->blksz = blocksize;
+ sndbuf_setblksz(channel->bufhard, blocksize);
+ chn_notify(parent, CHN_N_BLOCKSIZE);
+ CHN_LOCK(parent);
+ crate = ch->spd * ch->bps;
+ prate = sndbuf_getspd(parent->bufhard) * sndbuf_getbps(parent->bufhard);
+ blocksize = sndbuf_getblksz(parent->bufhard);
+ CHN_UNLOCK(parent);
+ blocksize *= prate;
+ blocksize /= crate;
+ return blocksize;
+}
+
+static int
+slave_trigger(kobj_t obj, void *data, int go)
+{
+ struct slaveinfo *ch = data;
+ struct pcm_channel *parent = ch->parent;
+ struct pcm_channel *channel = ch->channel;
+
+ if (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD)
+ return 0;
+ ch->run = (go == PCMTRIG_START) ? 1 : 0;
+ CHN_UNLOCK(channel);
+ chn_notify(parent, CHN_N_TRIGGER);
+ CHN_LOCK(channel);
+ return 0;
+}
+
+static struct pcmchan_caps *
+slave_getcaps(kobj_t obj, void *data)
+{
+ struct slaveinfo *ch = data;
+ struct pcm_channel *channel = ch->channel;
+ struct pcm_channel *parent = ch->parent;
+ struct pcmchan_caps *caps;
+
+ CHN_UNLOCK(channel);
+ CHN_LOCK(parent);
+ caps = chn_getcaps(parent);
+ CHN_UNLOCK(parent);
+ CHN_LOCK(channel);
+ return caps;
+}
+
+static kobj_method_t slave_methods[] = {
+ KOBJMETHOD(channel_init, slave_init),
+ KOBJMETHOD(channel_free, slave_free),
+ KOBJMETHOD(channel_setformat, slave_setformat),
+ KOBJMETHOD(channel_setspeed, slave_setspeed),
+ KOBJMETHOD(channel_setblocksize, slave_setblocksize),
+ KOBJMETHOD(channel_trigger, slave_trigger),
+ KOBJMETHOD(channel_getcaps, slave_getcaps),
+ {0, 0}
+};
+CHANNEL_DECLARE(slave);
+
+int
+slave_create(struct pcm_channel *parent)
+{
+ struct snddev_info *d = parent->parentsnddev;
+ struct pcmchan_children *pce;
+ struct pcm_channel *child;
+ struct pcmchan_caps *parent_caps;
+ int err;
+
+ if (!(parent->flags & CHN_F_BUSY) || !SLIST_EMPTY(&parent->children))
+ return EBUSY;
+
+ parent_caps = chn_getcaps(parent);
+ if (parent_caps == NULL || parent_caps->fmtlist == NULL)
+ return EINVAL;
+
+ CHN_UNLOCK(parent);
+
+ pce = malloc(sizeof(*pce), M_DEVBUF, M_WAITOK | M_ZERO);
+ if (!pce) {
+ CHN_LOCK(parent);
+ return ENOMEM;
+ }
+
+ child = pcm_chn_create(d, parent, &slave_class, PCMDIR_SLAVE, parent);
+ if (!child) {
+ free(pce, M_DEVBUF);
+ CHN_LOCK(parent);
+ return ENODEV;
+ }
+ pce->channel = child;
+
+ err = pcm_chn_add(d, child);
+ if (err) {
+ pcm_chn_destroy(child);
+ free(pce, M_DEVBUF);
+ CHN_LOCK(parent);
+ return err;
+ }
+
+ CHN_LOCK(parent);
+ SLIST_INSERT_HEAD(&parent->children, pce, link);
+ parent->flags |= CHN_F_HAS_SLAVE;
+
+ err = chn_reset(parent, parent_caps->fmtlist[0]);
+ if (!err)
+ err = chn_setspeed(parent, parent_caps->minspeed);
+ if (err) {
+ SLIST_REMOVE(&parent->children, pce, pcmchan_children, link);
+ parent->flags &= ~CHN_F_HAS_SLAVE;
+ CHN_UNLOCK(parent);
+ free(pce, M_DEVBUF);
+ pcm_chn_remove(d, child);
+ pcm_chn_destroy(child);
+ CHN_LOCK(parent);
+ return err;
+ }
+
+ return 0;
+}
+
+int
+slave_destroy(struct pcm_channel *c)
+{
+ struct pcm_channel *parent = c->parentchannel;
+ struct snddev_info *d = parent->parentsnddev;
+ struct pcmchan_children *pce;
+ struct snddev_channel *sce;
+ int err, last;
+
+ CHN_LOCK(parent);
+ if (!(parent->flags & CHN_F_BUSY)) {
+ CHN_UNLOCK(parent);
+ return EBUSY;
+ }
+ if (SLIST_EMPTY(&parent->children)) {
+ CHN_UNLOCK(parent);
+ return EINVAL;
+ }
+ SLIST_FOREACH(pce, &parent->children, link) {
+ if (pce->channel == c)
+ goto gotchslave;
+ }
+ CHN_UNLOCK(parent);
+ return EINVAL;
+gotchslave:
+ SLIST_FOREACH(sce, &d->channels, link) {
+ if (sce->channel == c) {
+ if (sce->dsp_devt)
+ destroy_dev(sce->dsp_devt);
+ if (sce->dspW_devt)
+ destroy_dev(sce->dspW_devt);
+ if (sce->audio_devt)
+ destroy_dev(sce->audio_devt);
+ if (sce->dspr_devt)
+ destroy_dev(sce->dspr_devt);
+ break;
+ }
+ }
+ SLIST_REMOVE(&parent->children, pce, pcmchan_children, link);
+ free(pce, M_DEVBUF);
+
+ last = SLIST_EMPTY(&parent->children);
+ if (last) {
+ parent->flags &= ~CHN_F_BUSY;
+ parent->flags &= ~CHN_F_HAS_SLAVE;
+ }
+ err = pcm_chn_remove(d, c);
+ CHN_UNLOCK(parent);
+ if (!err)
+ err = pcm_chn_destroy(c);
+#if 0
+ if (!err && last) {
+ CHN_LOCK(parent);
+ chn_reset(parent, chn_getcaps(parent)->fmtlist[0]);
+ chn_setspeed(parent, chn_getcaps(parent)->minspeed);
+ CHN_UNLOCK(parent);
+ }
+#endif
+ return err;
+}
--- sys/dev/sound/pcm/sndstat.c.orig Sun Jan 30 09:00:05 2005
+++ sys/dev/sound/pcm/sndstat.c Fri Sep 16 06:32:00 2005
@@ -196,6 +196,42 @@
}
int
+sndstat_acquire(void)
+{
+ intrmask_t s;
+
+ s = spltty();
+ sx_xlock(&sndstat_lock);
+ if (sndstat_isopen) {
+ sx_xunlock(&sndstat_lock);
+ splx(s);
+ return EBUSY;
+ }
+ sndstat_isopen = 1;
+ sx_xunlock(&sndstat_lock);
+ splx(s);
+ return 0;
+}
+
+int
+sndstat_release(void)
+{
+ intrmask_t s;
+
+ s = spltty();
+ sx_xlock(&sndstat_lock);
+ if (!sndstat_isopen) {
+ sx_xunlock(&sndstat_lock);
+ splx(s);
+ return EBADF;
+ }
+ sndstat_isopen = 0;
+ sx_xunlock(&sndstat_lock);
+ splx(s);
+ return 0;
+}
+
+int
sndstat_register(device_t dev, char *str, sndstat_handler handler)
{
intrmask_t s;
@@ -369,16 +405,11 @@
sndstat_dev = 0;
splx(s);
+ sx_xunlock(&sndstat_lock);
sx_destroy(&sndstat_lock);
return 0;
}
-int
-sndstat_busy(void)
-{
- return (sndstat_isopen);
-}
-
static void
sndstat_sysinit(void *p)
{
@@ -388,7 +419,10 @@
static void
sndstat_sysuninit(void *p)
{
- sndstat_uninit();
+ int error;
+
+ error = sndstat_uninit();
+ KASSERT(error == 0, ("%s: error = %d", __func__, error));
}
SYSINIT(sndstat_sysinit, SI_SUB_DRIVERS, SI_ORDER_FIRST, sndstat_sysinit, NULL);
--- sys/dev/sound/pcm/sound.c.orig Sun Jan 30 09:00:05 2005
+++ sys/dev/sound/pcm/sound.c Fri Sep 16 06:32:00 2005
@@ -46,6 +46,9 @@
int snd_maxautovchans = 0;
TUNABLE_INT("hw.snd.maxautovchans", &snd_maxautovchans);
+int snd_slave_enabled = 1;
+TUNABLE_INT("hw.snd.slave_enabled", &snd_slave_enabled);
+
SYSCTL_NODE(_hw, OID_AUTO, snd, CTLFLAG_RD, 0, "Sound driver");
static int sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose);
@@ -166,8 +169,6 @@
struct snddev_channel *sce;
int err;
- snd_mtxassert(d->lock);
-
/* scan for a free channel */
SLIST_FOREACH(sce, &d->channels, link) {
c = sce->channel;
@@ -189,7 +190,8 @@
SLIST_FOREACH(sce, &d->channels, link) {
c = sce->channel;
CHN_LOCK(c);
- if (!SLIST_EMPTY(&c->children)) {
+ if ((c->flags & CHN_F_HAS_VCHAN) &&
+ !SLIST_EMPTY(&c->children)) {
err = vchan_create(c);
CHN_UNLOCK(c);
if (!err)
@@ -246,44 +248,123 @@
static void
pcm_setmaxautovchans(struct snddev_info *d, int num)
{
- struct pcm_channel *c;
+ struct pcm_channel *c, *ch;
struct snddev_channel *sce;
- int err, done;
+ int err, done, foundslave = 0;
+ /*
+ * XXX WOAH... NEED SUPER CLEANUP!!!
+ * Robust, yet confusing. Understanding these will
+ * cause your brain spinning like a Doki Doki Dynamo.
+ */
if (num > 0 && d->vchancount == 0) {
+ ch = NULL;
+ c = NULL;
SLIST_FOREACH(sce, &d->channels, link) {
c = sce->channel;
CHN_LOCK(c);
- if ((c->direction == PCMDIR_PLAY) && !(c->flags & CHN_F_BUSY)) {
- c->flags |= CHN_F_BUSY;
- err = vchan_create(c);
- if (err) {
- c->flags &= ~CHN_F_BUSY;
- CHN_UNLOCK(c);
- device_printf(d->dev, "vchan_create(%s) == %d\n", c->name, err);
- } else
- CHN_UNLOCK(c);
- return;
+ if ((c->direction == PCMDIR_PLAY) &&
+ !(c->flags & CHN_F_BUSY)) {
+ if (c->flags & CHN_F_SLAVE)
+ foundslave = 1;
+ ch = c;
+ c = ch->parentchannel;
+ break;
}
CHN_UNLOCK(c);
}
+ if (ch != NULL && foundslave) {
+ CHN_UNLOCK(ch);
+ snd_mtxlock(d->lock);
+ err = slave_destroy(ch);
+ snd_mtxunlock(d->lock);
+ if (err || c == NULL)
+ ch = NULL;
+ else {
+ ch = c;
+ CHN_LOCK(ch);
+ }
+ }
+ if (ch != NULL) {
+ if (!(ch->flags & CHN_F_BUSY) &&
+ SLIST_EMPTY(&ch->children)) {
+ ch->flags |= CHN_F_BUSY;
+ err = vchan_create(ch);
+ if (err)
+ ch->flags &= ~CHN_F_BUSY;
+ else {
+ CHN_UNLOCK(ch);
+ ch = NULL;
+ }
+ } else {
+ CHN_UNLOCK(ch);
+ ch = NULL;
+ }
+ }
+ if (ch != NULL) {
+ if (snd_slave_enabled && !(ch->flags & CHN_F_BUSY) &&
+ SLIST_EMPTY(&ch->children)) {
+ ch->flags |= CHN_F_BUSY;
+ err = slave_create(ch);
+ if (err)
+ ch->flags &= ~CHN_F_BUSY;
+ }
+ CHN_UNLOCK(ch);
+ }
+ return;
}
if (num == 0 && d->vchancount > 0) {
- done = 0;
- while (!done) {
- done = 1;
+ /*
+ * XXX Keep retrying...
+ */
+ for (done = 0; done < 1024; done++) {
+ ch = NULL;
SLIST_FOREACH(sce, &d->channels, link) {
c = sce->channel;
- if ((c->flags & CHN_F_VIRTUAL) && !(c->flags & CHN_F_BUSY)) {
- done = 0;
- snd_mtxlock(d->lock);
- err = vchan_destroy(c);
- snd_mtxunlock(d->lock);
- if (err)
- device_printf(d->dev, "vchan_destroy(%s) == %d\n", c->name, err);
- break; /* restart */
+ CHN_LOCK(c);
+ if (c->direction == PCMDIR_PLAY &&
+ !(c->flags & CHN_F_BUSY) &&
+ (c->flags & CHN_F_VIRTUAL)) {
+ ch = c;
+ break;
+ }
+ CHN_UNLOCK(c);
+ }
+ if (ch != NULL) {
+ CHN_UNLOCK(ch);
+ snd_mtxlock(d->lock);
+ err = vchan_destroy(ch);
+ if (err)
+ device_printf(d->dev, "vchan_destroy(%s) == %d\n",
+ ch->name, err);
+ snd_mtxunlock(d->lock);
+ } else
+ break;
+ }
+ for (done = 0; done < 1024; done++) {
+ ch = NULL;
+ SLIST_FOREACH(sce, &d->channels, link) {
+ c = sce->channel;
+ CHN_LOCK(c);
+ if (c->direction == PCMDIR_PLAY &&
+ !(c->flags & CHN_F_BUSY) &&
+ c->parentchannel == NULL &&
+ SLIST_EMPTY(&c->children)) {
+ ch = c;
+ break;
}
+ CHN_UNLOCK(c);
}
+ if (ch != NULL) {
+ if (snd_slave_enabled) {
+ ch->flags |= CHN_F_BUSY;
+ err = slave_create(ch);
+ if (err)
+ ch->flags &= ~CHN_F_BUSY;
+ }
+ CHN_UNLOCK(ch);
+ } else
+ break;
}
}
}
@@ -327,7 +408,11 @@
d = devclass_get_softc(pcm_devclass, i);
if (!d)
continue;
- pcm_setmaxautovchans(d, v);
+ if (d->flags & SD_F_AUTOVCHAN) {
+ if (pcm_inprog(d, 1) == 1)
+ pcm_setmaxautovchans(d, v);
+ pcm_inprog(d, -1);
+ }
}
}
snd_maxautovchans = v;
@@ -337,6 +422,122 @@
SYSCTL_PROC(_hw_snd, OID_AUTO, maxautovchans, CTLTYPE_INT | CTLFLAG_RW,
0, sizeof(int), sysctl_hw_snd_maxautovchans, "I", "");
+static void
+pcm_setslave(struct snddev_info *d, int enable)
+{
+ struct pcm_channel *c, *ch;
+ struct snddev_channel *sce;
+ int err, i;
+
+ /*
+ * Be ruthless about consistencies. First, we traverse the list,
+ * scanning for busy channels. If there is a candidate with busy
+ * state, abort operation entirely.
+ */
+ if (enable) {
+ SLIST_FOREACH(sce, &d->channels, link) {
+ c = sce->channel;
+ CHN_LOCK(c);
+ if (c->direction == PCMDIR_PLAY &&
+ c->parentchannel == NULL &&
+ !(c->flags & CHN_F_HAS_VCHAN) &&
+ (c->flags & CHN_F_BUSY)) {
+ CHN_UNLOCK(c);
+ return;
+ }
+ CHN_UNLOCK(c);
+ }
+ for (i = 0; i < 1024; i++) {
+ ch = NULL;
+ SLIST_FOREACH(sce, &d->channels, link) {
+ c = sce->channel;
+ CHN_LOCK(c);
+ if (c->direction == PCMDIR_PLAY &&
+ c->parentchannel == NULL &&
+ !(c->flags & CHN_F_BUSY)) {
+ ch = c;
+ break;
+ }
+ CHN_UNLOCK(c);
+ }
+ if (ch == NULL)
+ break;
+ ch->flags |= CHN_F_BUSY;
+ err = slave_create(ch);
+ if (err) {
+ ch->flags &= ~CHN_F_BUSY;
+ device_printf(d->dev, "slave_create(%s) == %d\n",
+ ch->name, err);
+ }
+ CHN_UNLOCK(ch);
+ }
+ } else {
+ SLIST_FOREACH(sce, &d->channels, link) {
+ c = sce->channel;
+ CHN_LOCK(c);
+ if (c->direction == PCMDIR_PLAY &&
+ (c->flags & CHN_F_SLAVE) &&
+ (c->flags & CHN_F_BUSY)) {
+ CHN_UNLOCK(c);
+ return;
+ }
+ CHN_UNLOCK(c);
+ }
+ for (i = 0; i < 1024; i++) {
+ ch = NULL;
+ SLIST_FOREACH(sce, &d->channels, link) {
+ c = sce->channel;
+ CHN_LOCK(c);
+ if (c->direction == PCMDIR_PLAY &&
+ (c->flags & CHN_F_SLAVE) &&
+ !(c->flags & CHN_F_BUSY)) {
+ ch = c;
+ break;
+ }
+ CHN_UNLOCK(c);
+ }
+ if (ch == NULL)
+ break;
+ CHN_UNLOCK(ch);
+ snd_mtxlock(d->lock);
+ err = slave_destroy(ch);
+ snd_mtxunlock(d->lock);
+ if (err)
+ device_printf(d->dev, "slave_destroy(%s) == %d\n",
+ ch->name, err);
+ }
+ }
+}
+
+static int
+sysctl_hw_snd_slave_enabled(SYSCTL_HANDLER_ARGS)
+{
+ struct snddev_info *d;
+ int i, v, error;
+
+ v = snd_slave_enabled;
+ error = sysctl_handle_int(oidp, &v, sizeof(v), req);
+ if (error == 0 && req->newptr != NULL) {
+ if (v < 0 || v > 1 || pcm_devclass == NULL)
+ return EINVAL;
+ for (i = 0; i < devclass_get_maxunit(pcm_devclass); i++) {
+ d = devclass_get_softc(pcm_devclass, i);
+ if (!d)
+ continue;
+ if ((v == 0 && d->slavecount > 0) ||
+ (v == 1 && d->slavecount == 0)) {
+ if (pcm_inprog(d, 1) == 1)
+ pcm_setslave(d, v);
+ pcm_inprog(d, -1);
+ }
+ }
+ snd_slave_enabled = v;
+ }
+ return (error);
+}
+SYSCTL_PROC(_hw_snd, OID_AUTO, slave_enabled, CTLTYPE_INT | CTLFLAG_RW,
+ 0, sizeof(int), sysctl_hw_snd_slave_enabled, "I", "");
+
struct pcm_channel *
pcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t cls, int dir, void *devinfo)
{
@@ -363,6 +564,12 @@
pnum = &d->vchancount;
break;
+ case PCMDIR_SLAVE:
+ dirs = "slave";
+ direction = PCMDIR_PLAY;
+ pnum = &d->slavecount;
+ break;
+
default:
return NULL;
}
@@ -449,11 +656,45 @@
if (SLIST_EMPTY(&d->channels)) {
SLIST_INSERT_HEAD(&d->channels, sce, link);
} else {
+ /*
+ * Micro optimization, channel ordering:
+ * hw,slave,hw,slave,hw,slave,vch,vch,vch,rec
+ */
after = NULL;
- SLIST_FOREACH(tmp, &d->channels, link) {
- after = tmp;
+ if (ch->flags & CHN_F_VIRTUAL) {
+ /* virtual channel to the end */
+ SLIST_FOREACH(tmp, &d->channels, link) {
+ if (tmp->channel->direction == PCMDIR_REC)
+ break;
+ after = tmp;
+ }
+ } else if (ch->flags & CHN_F_SLAVE) {
+ /* slave channel after parent channel */
+ SLIST_FOREACH(tmp, &d->channels, link) {
+ if (tmp->channel == ch->parentchannel) {
+ after = tmp;
+ break;
+ }
+ }
+ } else {
+ if (ch->direction == PCMDIR_REC) {
+ SLIST_FOREACH(tmp, &d->channels, link) {
+ after = tmp;
+ }
+ } else {
+ SLIST_FOREACH(tmp, &d->channels, link) {
+ if (tmp->channel->direction == PCMDIR_REC)
+ break;
+ if (!(tmp->channel->flags & CHN_F_VIRTUAL))
+ after = tmp;
+ }
+ }
+ }
+ if (after == NULL) {
+ SLIST_INSERT_HEAD(&d->channels, sce, link);
+ } else {
+ SLIST_INSERT_AFTER(after, sce, link);
}
- SLIST_INSERT_AFTER(after, sce, link);
}
snd_mtxunlock(d->lock);
sce->dsp_devt= make_dev(&dsp_cdevsw,
@@ -506,10 +747,15 @@
gotit:
SLIST_REMOVE(&d->channels, sce, snddev_channel, link);
- if (ch->direction == PCMDIR_REC)
- d->reccount--;
- else if (ch->flags & CHN_F_VIRTUAL)
+ /*
+ * Make room for future 'slave' record channel.
+ */
+ if (ch->flags & CHN_F_VIRTUAL)
d->vchancount--;
+ else if (ch->flags & CHN_F_SLAVE)
+ d->slavecount--;
+ else if (ch->direction == PCMDIR_REC)
+ d->reccount--;
else
d->playcount--;
@@ -543,6 +789,7 @@
}
CHN_LOCK(ch);
+
if (snd_maxautovchans > 0 && (d->flags & SD_F_AUTOVCHAN) &&
ch->direction == PCMDIR_PLAY && d->vchancount == 0) {
ch->flags |= CHN_F_BUSY;
@@ -553,6 +800,17 @@
device_printf(d->dev, "vchan_create(%s) == %d\n", ch->name, err);
return err;
}
+ } else if (snd_slave_enabled > 0 && ch->direction == PCMDIR_PLAY &&
+ ch->parentchannel == NULL &&
+ SLIST_EMPTY(&ch->children)) {
+ ch->flags |= CHN_F_BUSY;
+ err = slave_create(ch);
+ if (err) {
+ ch->flags &= ~CHN_F_BUSY;
+ CHN_UNLOCK(ch);
+ device_printf(d->dev, "slave_create(%s) == %d\n", ch->name, err);
+ return err;
+ }
}
CHN_UNLOCK(ch);
@@ -654,17 +912,24 @@
d->lock = snd_mtxcreate(device_get_nameunit(dev), "sound cdev");
+#if 0
+ /*
+ * d->flags should be cleared by the allocator of the softc.
+ * We cannot clear this field here because several devices set
+ * this flag before calling pcm_register().
+ */
d->flags = 0;
+#endif
d->dev = dev;
d->devinfo = devinfo;
d->devcount = 0;
d->reccount = 0;
d->playcount = 0;
+ d->slavecount = 0;
d->vchancount = 0;
d->inprog = 0;
SLIST_INIT(&d->channels);
- SLIST_INIT(&d->channels);
if (((numplay == 0) || (numrec == 0)) && (numplay != numrec))
d->flags |= SD_F_SIMPLEX;
@@ -684,10 +949,10 @@
SYSCTL_ADD_INT(snd_sysctl_tree(dev), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)),
OID_AUTO, "buffersize", CTLFLAG_RD, &d->bufsz, 0, "");
#endif
- if (numplay > 0)
+ if (numplay > 0) {
vchan_initsys(dev);
- if (numplay == 1)
d->flags |= SD_F_AUTOVCHAN;
+ }
sndstat_register(dev, d->status, sndstat_prepare_pcm);
return 0;
@@ -703,42 +968,47 @@
struct snddev_channel *sce;
struct pcm_channel *ch;
+ if (sndstat_acquire() != 0) {
+ device_printf(dev, "unregister: sndstat busy\n");
+ return EBUSY;
+ }
+
snd_mtxlock(d->lock);
if (d->inprog) {
device_printf(dev, "unregister: operation in progress\n");
snd_mtxunlock(d->lock);
+ sndstat_release();
return EBUSY;
}
- if (sndstat_busy() != 0) {
- device_printf(dev, "unregister: sndstat busy\n");
- snd_mtxunlock(d->lock);
- return EBUSY;
- }
-
SLIST_FOREACH(sce, &d->channels, link) {
ch = sce->channel;
if (ch->refcount > 0) {
device_printf(dev, "unregister: channel %s busy (pid %d)\n", ch->name, ch->pid);
snd_mtxunlock(d->lock);
+ sndstat_release();
return EBUSY;
}
}
- SLIST_FOREACH(sce, &d->channels, link) {
- destroy_dev(sce->dsp_devt);
- destroy_dev(sce->dspW_devt);
- destroy_dev(sce->audio_devt);
- if (sce->dspr_devt)
- destroy_dev(sce->dspr_devt);
- }
-
if (mixer_uninit(dev)) {
device_printf(dev, "unregister: mixer busy\n");
snd_mtxunlock(d->lock);
+ sndstat_release();
return EBUSY;
}
+ SLIST_FOREACH(sce, &d->channels, link) {
+ if (sce->dsp_devt)
+ destroy_dev(sce->dsp_devt);
+ if (sce->dspW_devt)
+ destroy_dev(sce->dspW_devt);
+ if (sce->audio_devt)
+ destroy_dev(sce->audio_devt);
+ if (sce->dspr_devt)
+ destroy_dev(sce->dspr_devt);
+ }
+
#ifdef SND_DYNSYSCTL
d->sysctl_tree_top = NULL;
sysctl_ctx_free(&d->sysctl_tree);
@@ -749,9 +1019,10 @@
chn_kill(d->fakechan);
fkchan_kill(d->fakechan);
- sndstat_unregister(dev);
snd_mtxunlock(d->lock);
snd_mtxfree(d->lock);
+ sndstat_unregister(dev);
+ sndstat_release();
return 0;
}
@@ -786,7 +1057,8 @@
} else
rc++;
}
- sbuf_printf(s, " (%dp/%dr/%dv channels%s%s)", d->playcount, d->reccount, d->vchancount,
+ sbuf_printf(s, " (%dp/%ds/%dr/%dv channels%s%s)", d->playcount, d->slavecount,
+ d->reccount, d->vchancount,
(d->flags & SD_F_SIMPLEX)? "" : " duplex",
#ifdef USING_DEVFS
(device_get_unit(dev) == snd_unit)? " default" : ""
@@ -820,11 +1092,19 @@
if (c->bufhard != NULL && c->bufsoft != NULL) {
sbuf_printf(s, "interrupts %d, ", c->interrupts);
if (c->direction == PCMDIR_REC)
- sbuf_printf(s, "overruns %d, hfree %d, sfree %d",
- c->xruns, sndbuf_getfree(c->bufhard), sndbuf_getfree(c->bufsoft));
+ sbuf_printf(s, "overruns %d, hfree %d, sfree %d [b:%d/%d/%d|bs:%d/%d/%d]",
+ c->xruns, 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, ready %d",
- c->xruns, sndbuf_getready(c->bufsoft));
+ sbuf_printf(s, "underruns %d, ready %d [b:%d/%d/%d|bs:%d/%d/%d]",
+ c->xruns, 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");
}
@@ -839,7 +1119,8 @@
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)
+ if (f->desc->type == FEEDER_ROOT || f->desc->type == FEEDER_MIXER ||
+ f->desc->type == FEEDER_SLAVE || f->desc->type == FEEDER_VOLUME)
sbuf_printf(s, "(0x%08x)", f->desc->out);
sbuf_printf(s, " -> ");
f = f->parent;
@@ -862,26 +1143,32 @@
struct snddev_info *d;
struct snddev_channel *sce;
struct pcm_channel *c;
- int err, newcnt, cnt, busy;
- int x;
+ int err, newcnt, cnt;
+ /*
+ * XXX WOAH... NEED SUPER CLEANUP!!!
+ * Robust, yet confusing. Understanding these will
+ * cause your brain spinning like a Doki Doki Dynamo.
+ */
d = oidp->oid_arg1;
- x = pcm_inprog(d, 1);
- if (x != 1) {
+ if (!(d->flags & SD_F_AUTOVCHAN)) {
pcm_inprog(d, -1);
- return EINPROGRESS;
+ return EINVAL;
}
- busy = 0;
cnt = 0;
SLIST_FOREACH(sce, &d->channels, link) {
c = sce->channel;
CHN_LOCK(c);
- if ((c->direction == PCMDIR_PLAY) && (c->flags & CHN_F_VIRTUAL)) {
+ if ((c->direction == PCMDIR_PLAY) &&
+ (c->flags & CHN_F_VIRTUAL)) {
cnt++;
- if (c->flags & CHN_F_BUSY)
- busy++;
+ if (req->newptr != NULL && c->flags & CHN_F_BUSY) {
+ /* Better safe than sorry */
+ CHN_UNLOCK(c);
+ return EBUSY;
+ }
}
CHN_UNLOCK(c);
}
@@ -892,9 +1179,12 @@
if (err == 0 && req->newptr != NULL) {
- if (newcnt < 0 || newcnt > SND_MAXVCHANS) {
- pcm_inprog(d, -1);
+ if (newcnt < 0 || newcnt > SND_MAXVCHANS)
return E2BIG;
+
+ if (pcm_inprog(d, 1) != 1) {
+ pcm_inprog(d, -1);
+ return EINPROGRESS;
}
if (newcnt > cnt) {
@@ -906,7 +1196,7 @@
if (c->direction != PCMDIR_PLAY)
goto next;
/* not a candidate if a virtual channel */
- if (c->flags & CHN_F_VIRTUAL)
+ if (c->flags & (CHN_F_VIRTUAL | CHN_F_SLAVE))
goto next;
/* not a candidate if it's in use */
if (!(c->flags & CHN_F_BUSY) ||
@@ -927,28 +1217,51 @@
pcm_inprog(d, -1);
return EBUSY;
addok:
- c->flags |= CHN_F_BUSY;
+ if (cnt == 0 && !SLIST_EMPTY(&c->children)) {
+ struct pcmchan_children *pce;
+
+ pce = SLIST_FIRST(&c->children);
+ if (pce && pce->channel) {
+ CHN_UNLOCK(c);
+ CHN_LOCK(pce->channel);
+ if (pce->channel->flags & CHN_F_SLAVE) {
+ if (pce->channel->flags & CHN_F_BUSY) {
+ err = EBUSY;
+ CHN_UNLOCK(pce->channel);
+ } else {
+ CHN_UNLOCK(pce->channel);
+ snd_mtxlock(d->lock);
+ err = slave_destroy(pce->channel);
+ snd_mtxunlock(d->lock);
+ }
+ } else
+ CHN_UNLOCK(pce->channel);
+ CHN_LOCK(c);
+ }
+ }
+ if (SLIST_EMPTY(&c->children))
+ c->flags |= CHN_F_BUSY;
while (err == 0 && newcnt > cnt) {
err = vchan_create(c);
if (err == 0)
cnt++;
}
- if (SLIST_EMPTY(&c->children))
- c->flags &= ~CHN_F_BUSY;
+ if (snd_slave_enabled && SLIST_EMPTY(&c->children)) {
+ err = slave_create(c);
+ if (err)
+ c->flags &= ~CHN_F_BUSY;
+ }
CHN_UNLOCK(c);
} else if (newcnt < cnt) {
- if (busy > newcnt) {
- printf("cnt %d, newcnt %d, busy %d\n", cnt, newcnt, busy);
- pcm_inprog(d, -1);
- return EBUSY;
- }
+ struct pcm_channel *ch = NULL;
snd_mtxlock(d->lock);
while (err == 0 && newcnt < cnt) {
SLIST_FOREACH(sce, &d->channels, link) {
c = sce->channel;
CHN_LOCK(c);
- if ((c->flags & (CHN_F_BUSY | CHN_F_VIRTUAL)) == CHN_F_VIRTUAL)
+ if (c->direction == PCMDIR_PLAY &&
+ (c->flags & (CHN_F_BUSY | CHN_F_VIRTUAL)) == CHN_F_VIRTUAL)
goto remok;
CHN_UNLOCK(c);
@@ -957,30 +1270,41 @@
pcm_inprog(d, -1);
return EINVAL;
remok:
+ if (ch == NULL)
+ ch = c->parentchannel;
CHN_UNLOCK(c);
err = vchan_destroy(c);
if (err == 0)
cnt--;
}
snd_mtxunlock(d->lock);
+ if (ch != NULL && snd_slave_enabled &&
+ SLIST_EMPTY(&ch->children)) {
+ CHN_LOCK(ch);
+ ch->flags |= CHN_F_BUSY;
+ err = slave_create(ch);
+ if (err)
+ ch->flags &= ~CHN_F_BUSY;
+ CHN_UNLOCK(ch);
+ }
}
+ pcm_inprog(d, -1);
}
- pcm_inprog(d, -1);
return err;
}
#endif
/************************************************************************/
-#if notyet
static int
sound_modevent(module_t mod, int type, void *data)
{
+#if 0
return (midi_modevent(mod, type, data));
+#else
+ return 0;
+#endif
}
DEV_MODULE(sound, sound_modevent, NULL);
-#else
-DEV_MODULE(sound, NULL, NULL);
-#endif /* notyet */
MODULE_VERSION(sound, SOUND_MODVER);
--- sys/dev/sound/pcm/sound.h.orig Sun Jan 30 09:00:05 2005
+++ sys/dev/sound/pcm/sound.h Fri Sep 16 06:32:00 2005
@@ -130,7 +130,8 @@
#define PCMMKMINOR(u, d, c) ((((c) & 0xff) << 16) | (((u) & 0x0f) << 4) | ((d) & 0x0f))
#define SD_F_SIMPLEX 0x00000001
-#define SD_F_AUTOVCHAN 0x00000002
+#define SD_F_AUTOVCHAN 0x00000002
+#define SD_F_SOFTVOL 0x00000004
#define SD_F_PRIO_RD 0x10000000
#define SD_F_PRIO_WR 0x20000000
#define SD_F_PRIO_SET (SD_F_PRIO_RD | SD_F_PRIO_WR)
@@ -144,16 +145,19 @@
/* 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_U8 | AFMT_S8)
-#define AFMT_SIGNED (AFMT_S16_LE | AFMT_S16_BE | AFMT_S8)
-#define AFMT_BIGENDIAN (AFMT_S16_BE | AFMT_U16_BE)
+#define AFMT_8BIT (AFMT_MU_LAW | AFMT_A_LAW | AFMT_U8 | AFMT_S8)
+#define AFMT_SIGNED (AFMT_S32_LE | AFMT_S32_BE | AFMT_S24_LE | AFMT_S24_BE | \
+ AFMT_S16_LE | AFMT_S16_BE | AFMT_S8)
+#define AFMT_BIGENDIAN (AFMT_S32_BE | AFMT_U32_BE | AFMT_S24_BE | AFMT_U24_BE | \
+ AFMT_S16_BE | AFMT_U16_BE)
struct pcm_channel *fkchan_setup(device_t dev);
int fkchan_kill(struct pcm_channel *c);
/*
- * Major nuber for the sound driver.
+ * Major number for the sound driver.
*/
#define SND_CDEV_MAJOR 30
@@ -240,11 +244,12 @@
int sysctl_hw_snd_vchans(SYSCTL_HANDLER_ARGS);
typedef int (*sndstat_handler)(struct sbuf *s, device_t dev, int verbose);
+int sndstat_acquire(void);
+int sndstat_release(void);
int sndstat_register(device_t dev, char *str, sndstat_handler handler);
int sndstat_registerfile(char *str);
int sndstat_unregister(device_t dev);
int sndstat_unregisterfile(char *str);
-int sndstat_busy(void);
#define SND_DECLARE_FILE(version) \
_SND_DECLARE_FILE(__LINE__, version)
@@ -286,7 +291,7 @@
struct snddev_info {
SLIST_HEAD(, snddev_channel) channels;
struct pcm_channel *fakechan;
- unsigned devcount, playcount, reccount, vchancount;
+ unsigned devcount, playcount, reccount, slavecount, vchancount;
unsigned flags;
int inprog;
unsigned int bufsz;
--- sys/dev/sound/pcm/vchan.c.orig Sun Jan 30 09:00:05 2005
+++ sys/dev/sound/pcm/vchan.c Fri Sep 16 06:32:00 2005
@@ -30,6 +30,14 @@
SND_DECLARE_FILE("$FreeBSD: src/sys/dev/sound/pcm/vchan.c,v 1.16.2.1 2005/01/30 01:00:05 imp Exp $");
+/*
+ * Default speed
+ */
+#define VCHAN_DEFAULT_SPEED 48000
+
+extern int feeder_rate_ratemin;
+extern int feeder_rate_ratemax;
+
struct vchinfo {
u_int32_t spd, fmt, blksz, bps, run;
struct pcm_channel *channel, *parent;
@@ -74,13 +82,21 @@
struct snd_dbuf *src = source;
struct pcmchan_children *cce;
struct pcm_channel *ch;
+ uint32_t sz;
int16_t *tmp, *dst;
- unsigned int cnt;
+ unsigned int cnt, rcnt = 0;
+ #if 0
if (sndbuf_getsize(src) < count)
panic("feed_vchan_s16(%s): tmp buffer size %d < count %d, flags = 0x%x",
c->name, sndbuf_getsize(src), count, c->flags);
+ #endif
+ sz = sndbuf_getsize(src);
+ if (sz < count)
+ count = sz;
count &= ~1;
+ if (count < 2)
+ return 0;
bzero(b, count);
/*
@@ -99,12 +115,14 @@
if (ch->flags & CHN_F_MAPPED)
sndbuf_acquire(ch->bufsoft, NULL, sndbuf_getfree(ch->bufsoft));
cnt = FEEDER_FEED(ch->feeder, ch, (u_int8_t *)tmp, count, ch->bufsoft);
- vchan_mix_s16(dst, tmp, cnt / 2);
+ vchan_mix_s16(dst, tmp, cnt >> 1);
+ if (cnt > rcnt)
+ rcnt = cnt;
}
CHN_UNLOCK(ch);
}
- return count;
+ return rcnt & ~1;
}
static struct pcm_feederdesc feeder_vchan_s16_desc[] = {
@@ -127,6 +145,8 @@
KASSERT(dir == PCMDIR_PLAY, ("vchan_init: bad direction"));
ch = malloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO);
+ if (!ch)
+ return NULL;
ch->parent = parent;
ch->channel = c;
ch->fmt = AFMT_U8;
@@ -154,11 +174,16 @@
ch->fmt = format;
ch->bps = 1;
ch->bps <<= (ch->fmt & AFMT_STEREO)? 1 : 0;
- ch->bps <<= (ch->fmt & AFMT_16BIT)? 1 : 0;
- ch->bps <<= (ch->fmt & AFMT_32BIT)? 2 : 0;
+ if (ch->fmt & AFMT_16BIT)
+ ch->bps <<= 1;
+ else if (ch->fmt & AFMT_24BIT)
+ ch->bps *= 3;
+ else if (ch->fmt & AFMT_32BIT)
+ ch->bps <<= 2;
CHN_UNLOCK(channel);
chn_notify(parent, CHN_N_FORMAT);
CHN_LOCK(channel);
+ sndbuf_setfmt(channel->bufsoft, format);
return 0;
}
@@ -180,12 +205,14 @@
vchan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize)
{
struct vchinfo *ch = data;
+ struct pcm_channel *channel = ch->channel;
struct pcm_channel *parent = ch->parent;
/* struct pcm_channel *channel = ch->channel; */
int prate, crate;
ch->blksz = blocksize;
/* CHN_UNLOCK(channel); */
+ sndbuf_setblksz(channel->bufhard, blocksize);
chn_notify(parent, CHN_N_BLOCKSIZE);
CHN_LOCK(parent);
/* CHN_LOCK(channel); */
@@ -243,6 +270,81 @@
};
CHANNEL_DECLARE(vchan);
+/*
+ * On the fly vchan rate settings
+ */
+#ifdef SND_DYNSYSCTL
+static int
+sysctl_hw_snd_vchanrate(SYSCTL_HANDLER_ARGS)
+{
+ struct snddev_info *d;
+ struct snddev_channel *sce;
+ struct pcm_channel *c, *ch = NULL, *fake;
+ struct pcmchan_caps *caps;
+ int err = 0;
+ int newspd = 0;
+
+ d = oidp->oid_arg1;
+ if (!(d->flags & SD_F_AUTOVCHAN) || d->vchancount < 1)
+ return EINVAL;
+ SLIST_FOREACH(sce, &d->channels, link) {
+ c = sce->channel;
+ CHN_LOCK(c);
+ if (c->direction == PCMDIR_PLAY) {
+ if (c->flags & CHN_F_VIRTUAL) {
+ if (req->newptr != NULL &&
+ (c->flags & CHN_F_BUSY)) {
+ CHN_UNLOCK(c);
+ return EBUSY;
+ }
+ if (ch == NULL)
+ ch = c->parentchannel;
+ }
+ }
+ CHN_UNLOCK(c);
+ }
+ if (ch != NULL) {
+ CHN_LOCK(ch);
+ newspd = ch->speed;
+ CHN_UNLOCK(ch);
+ }
+ err = sysctl_handle_int(oidp, &newspd, sizeof(newspd), req);
+ if (err == 0 && req->newptr != NULL) {
+ if (ch == NULL || newspd < 1 ||
+ newspd < feeder_rate_ratemin ||
+ newspd > feeder_rate_ratemax)
+ return EINVAL;
+ if (pcm_inprog(d, 1) != 1) {
+ pcm_inprog(d, -1);
+ return EINPROGRESS;
+ }
+ CHN_LOCK(ch);
+ caps = chn_getcaps(ch);
+ if (caps == NULL || newspd < caps->minspeed ||
+ newspd > caps->maxspeed) {
+ CHN_UNLOCK(ch);
+ pcm_inprog(d, -1);
+ return EINVAL;
+ }
+ if (newspd != ch->speed) {
+ err = chn_setspeed(ch, newspd);
+ CHN_UNLOCK(ch);
+ if (err == 0) {
+ fake = pcm_getfakechan(d);
+ if (fake != NULL) {
+ CHN_LOCK(fake);
+ fake->speed = newspd;
+ CHN_UNLOCK(fake);
+ }
+ }
+ } else
+ CHN_UNLOCK(ch);
+ pcm_inprog(d, -1);
+ }
+ return err;
+}
+#endif
+
/* virtual channel interface */
int
@@ -250,10 +352,15 @@
{
struct snddev_info *d = parent->parentsnddev;
struct pcmchan_children *pce;
- struct pcm_channel *child;
- int err, first;
+ struct pcm_channel *child, *fake;
+ struct pcmchan_caps *parent_caps;
+ int err, first, speed = 0;
- CHN_UNLOCK(parent);
+ if (!(parent->flags & CHN_F_BUSY))
+ return EBUSY;
+
+
+ CHN_UNLOCK(parent);
pce = malloc(sizeof(*pce), M_DEVBUF, M_WAITOK | M_ZERO);
if (!pce) {
@@ -268,16 +375,7 @@
CHN_LOCK(parent);
return ENODEV;
}
-
- CHN_LOCK(parent);
- if (!(parent->flags & CHN_F_BUSY))
- return EBUSY;
-
- first = SLIST_EMPTY(&parent->children);
- /* add us to our parent channel's children */
pce->channel = child;
- SLIST_INSERT_HEAD(&parent->children, pce, link);
- CHN_UNLOCK(parent);
/* add us to our grandparent's channel list */
/*
@@ -287,20 +385,103 @@
if (err) {
pcm_chn_destroy(child);
free(pce, M_DEVBUF);
+ CHN_LOCK(parent);
+ return err;
}
CHN_LOCK(parent);
- /* XXX gross ugly hack, murder death kill */
- if (first && !err) {
- err = chn_reset(parent, AFMT_STEREO | AFMT_S16_LE);
- if (err)
- printf("chn_reset: %d\n", err);
- err = chn_setspeed(parent, 44100);
- if (err)
- printf("chn_setspeed: %d\n", err);
+ /* add us to our parent channel's children */
+ first = SLIST_EMPTY(&parent->children);
+ SLIST_INSERT_HEAD(&parent->children, pce, link);
+ parent->flags |= CHN_F_HAS_VCHAN;
+
+ if (first) {
+ parent_caps = chn_getcaps(parent);
+ if (parent_caps == NULL)
+ err = EINVAL;
+
+ if (!err)
+ err = chn_reset(parent, AFMT_STEREO | AFMT_S16_LE);
+
+ if (!err) {
+ fake = pcm_getfakechan(d);
+ if (fake != NULL) {
+ /*
+ * Avoid querying kernel hint, use saved value
+ * from fake channel.
+ */
+ CHN_UNLOCK(parent);
+ CHN_LOCK(fake);
+ speed = fake->speed;
+ CHN_UNLOCK(fake);
+ CHN_LOCK(parent);
+ }
+
+ /*
+ * This is very sad. Few soundcards advertised as being
+ * able to do (insanely) higher/lower speed, but in
+ * reality, they simply can't. At least, we give user chance
+ * to set sane value via kernel hints or sysctl.
+ */
+ if (speed < 1) {
+ int r;
+ CHN_UNLOCK(parent);
+ r = resource_int_value(device_get_name(parent->dev),
+ device_get_unit(parent->dev),
+ "vchanrate", &speed);
+ CHN_LOCK(parent);
+ if (r != 0)
+ speed = VCHAN_DEFAULT_SPEED;
+ }
+
+ /*
+ * Limit speed based on driver caps.
+ * This is supposed to help fixed rate, non-VRA
+ * AC97 cards, but.. (see below)
+ */
+ if (speed < parent_caps->minspeed)
+ speed = parent_caps->minspeed;
+ if (speed > parent_caps->maxspeed)
+ speed = parent_caps->maxspeed;
+
+ /*
+ * We still need to limit the speed between
+ * feeder_rate_ratemin <-> feeder_rate_ratemax. This is
+ * just an escape goat if all of the above failed
+ * miserably.
+ */
+ if (speed < feeder_rate_ratemin)
+ speed = feeder_rate_ratemin;
+ if (speed > feeder_rate_ratemax)
+ speed = feeder_rate_ratemax;
+
+ err = chn_setspeed(parent, speed);
+
+ if (!err && fake != NULL) {
+ /*
+ * Save new value to fake channel.
+ */
+ CHN_UNLOCK(parent);
+ CHN_LOCK(fake);
+ fake->speed = speed;
+ CHN_UNLOCK(fake);
+ CHN_LOCK(parent);
+ }
+ }
+
+ if (err) {
+ SLIST_REMOVE(&parent->children, pce, pcmchan_children, link);
+ parent->flags &= ~CHN_F_HAS_VCHAN;
+ CHN_UNLOCK(parent);
+ free(pce, M_DEVBUF);
+ pcm_chn_remove(d, child);
+ pcm_chn_destroy(child);
+ CHN_LOCK(parent);
+ return err;
+ }
}
- return err;
+ return 0;
}
int
@@ -309,6 +490,7 @@
struct pcm_channel *parent = c->parentchannel;
struct snddev_info *d = parent->parentsnddev;
struct pcmchan_children *pce;
+ struct snddev_channel *sce;
int err, last;
CHN_LOCK(parent);
@@ -329,21 +511,44 @@
CHN_UNLOCK(parent);
return EINVAL;
gotch:
+ SLIST_FOREACH(sce, &d->channels, link) {
+ if (sce->channel == c) {
+ if (sce->dsp_devt)
+ destroy_dev(sce->dsp_devt);
+ if (sce->dspW_devt)
+ destroy_dev(sce->dspW_devt);
+ if (sce->audio_devt)
+ destroy_dev(sce->audio_devt);
+ if (sce->dspr_devt)
+ destroy_dev(sce->dspr_devt);
+ break;
+ }
+ }
SLIST_REMOVE(&parent->children, pce, pcmchan_children, link);
free(pce, M_DEVBUF);
last = SLIST_EMPTY(&parent->children);
- if (last)
+ if (last) {
parent->flags &= ~CHN_F_BUSY;
+ parent->flags &= ~CHN_F_HAS_VCHAN;
+ }
/* remove us from our grandparent's channel list */
err = pcm_chn_remove(d, c);
- if (err)
- return err;
CHN_UNLOCK(parent);
/* destroy ourselves */
- err = pcm_chn_destroy(c);
+ if (!err)
+ err = pcm_chn_destroy(c);
+
+#if 0
+ if (!err && last) {
+ CHN_LOCK(parent);
+ chn_reset(parent, chn_getcaps(parent)->fmtlist[0]);
+ chn_setspeed(parent, chn_getcaps(parent)->minspeed);
+ CHN_UNLOCK(parent);
+ }
+#endif
return err;
}
@@ -358,9 +563,10 @@
SYSCTL_ADD_PROC(snd_sysctl_tree(dev), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)),
OID_AUTO, "vchans", CTLTYPE_INT | CTLFLAG_RW, d, sizeof(d),
sysctl_hw_snd_vchans, "I", "");
+ SYSCTL_ADD_PROC(snd_sysctl_tree(dev), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)),
+ OID_AUTO, "vchanrate", CTLTYPE_INT | CTLFLAG_RW, d, sizeof(d),
+ sysctl_hw_snd_vchanrate, "I", "");
#endif
return 0;
}
-
-
--- sys/dev/sound/pcm/vchan.h.orig Sun Jan 30 09:00:05 2005
+++ sys/dev/sound/pcm/vchan.h Fri Sep 16 06:32:00 2005
@@ -29,5 +29,5 @@
int vchan_create(struct pcm_channel *parent);
int vchan_destroy(struct pcm_channel *c);
int vchan_initsys(device_t dev);
-
-
+int slave_create(struct pcm_channel *parent);
+int slave_destroy(struct pcm_channel *c);
--- sys/dev/sound/usb/uaudio.c.orig Fri Apr 15 12:15:24 2005
+++ sys/dev/sound/usb/uaudio.c Fri Sep 16 06:32:00 2005
@@ -4130,10 +4130,10 @@
if (mc->ctl == type) {
if (mc->nchan == 2) {
/* set Right */
- uaudio_ctl_set(sc, SET_CUR, mc, 1, (int)(right*256)/100);
+ uaudio_ctl_set(sc, SET_CUR, mc, 1, (int)(right*255)/100);
}
/* set Left or Mono */
- uaudio_ctl_set(sc, SET_CUR, mc, 0, (int)(left*256)/100);
+ uaudio_ctl_set(sc, SET_CUR, mc, 0, (int)(left*255)/100);
}
}
return;
--- sys/dev/sound/usb/uaudio_pcm.c.orig Wed Apr 20 14:43:41 2005
+++ sys/dev/sound/usb/uaudio_pcm.c Fri Sep 16 06:32:00 2005
@@ -237,11 +237,20 @@
{
u_int32_t mask;
device_t pa_dev;
+ struct snddev_info *d;
struct ua_info *ua = mix_getdevinfo(m);
pa_dev = device_get_parent(ua->sc_dev);
+ d = device_get_softc(ua->sc_dev);
mask = uaudio_query_mix_info(pa_dev);
+ if (d && !(mask & SOUND_MIXER_PCM)) {
+ /*
+ * Emulate missing pcm mixer controller
+ * through FEEDER_VOLUME
+ */
+ d->flags |= SD_F_SOFTVOL;
+ }
mix_setdevs(m, mask);
mask = uaudio_query_recsrc_info(pa_dev);
--- share/man/man4/snd_es137x.4.orig Fri Sep 16 06:25:19 2005
+++ share/man/man4/snd_es137x.4 Fri Sep 16 06:32:00 2005
@@ -39,6 +39,23 @@
bridge driver allows the generic audio driver
.Xr sound 4
to attach to the Ensoniq 137x audio cards.
+.Ss Runtime Configuration
+The following
+.Xr sysctl 8
+variables are available in addition to those available to all
+.Xr sound 4
+devices:
+.Bl -tag -width ".Va hw.snd.pcm%d.latency_timer" -offset indent
+.It Va hw.snd.pcm%d.latency_timer
+Controls the PCI latency timer setting.
+Increasing this value will solve most popping and crackling issues
+(especially on VIA motherboards).
+.It Va hw.snd.pcm%d.spdif_enabled
+Enables S/PDIF output on the primary playback channel.
+This
+.Xr sysctl 8
+variable is available only if the device is known to support S/PDIF output.
+.El
.Sh HARDWARE
The
.Nm
@@ -59,3 +76,4 @@
.An "Russell Cattelan" Aq cattelan at thebarn.com
.An "Cameron Grant" Aq cg at FreeBSD.org
.An "Joachim Kuebart"
+.An "Jonathan Noack" Aq noackjr at alumni.rice.edu
More information about the freebsd-multimedia
mailing list