Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 16 Sep 2005 06:36:31 +0800
From:      Ariff Abdullah <skywizard@MyBSD.org.my>
To:        lofi@freebsd.org, perlfu@gmail.com
Cc:        freebsd-multimedia@freebsd.org
Subject:   Re: Vchan/Sound/patch hard locks...
Message-ID:  <20050916063631.34e65dc3.skywizard@MyBSD.org.my>
In-Reply-To: <200509152331.24060.lofi@freebsd.org>
References:  <20050914170228.328c571e.perlfu@gmail.com> <20050915065846.0732a58b.skywizard@MyBSD.org.my> <20050915150051.7d31bd99.perlfu@gmail.com> <200509152331.24060.lofi@freebsd.org>

next in thread | previous in thread | raw e-mail | index | archive | help
This is a multi-part message in MIME format.

--Multipart=_Fri__16_Sep_2005_06_36_31_+0800_FNRSpxZB0VwFh.HR
Content-Type: text/plain; charset=US-ASCII
Content-Transfer-Encoding: 7bit

On Thu, 15 Sep 2005 23:31:17 +0200
Michael Nottebrock <lofi@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)

--Multipart=_Fri__16_Sep_2005_06_36_31_+0800_FNRSpxZB0VwFh.HR
Content-Type: text/plain;
 name="new.diff"
Content-Disposition: attachment;
 filename="new.diff"
Content-Transfer-Encoding: 7bit

--- 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@freebsd.org>
+ * Copyright (c) 2005 Ariff Abdullah <skywizard@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@vilnya.demon.co.uk>
  * Copyright (c) 2003 Orion Hodson <orion@freebsd.org>
+ * Copyright (c) 2005 Ariff Abdullah <skywizard@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@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@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@MyBSD.org.my> All rights reserved.
+ *
+ * Derived from vchan.c
+ *
+ * Copyright (c) 2001 Cameron Grant
+ *        <cg@freebsd.org> All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *                                                                         *
+ * 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@thebarn.com
 .An "Cameron Grant" Aq cg@FreeBSD.org
 .An "Joachim Kuebart"
+.An "Jonathan Noack" Aq noackjr@alumni.rice.edu

--Multipart=_Fri__16_Sep_2005_06_36_31_+0800_FNRSpxZB0VwFh.HR--



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20050916063631.34e65dc3.skywizard>