From owner-freebsd-multimedia@FreeBSD.ORG Thu Sep 15 22:36:46 2005 Return-Path: X-Original-To: freebsd-multimedia@freebsd.org Delivered-To: freebsd-multimedia@freebsd.org Received: from mx1.FreeBSD.org (mx1.freebsd.org [216.136.204.125]) by hub.freebsd.org (Postfix) with ESMTP id DBFA116A41F; Thu, 15 Sep 2005 22:36:46 +0000 (GMT) (envelope-from skywizard@MyBSD.org.my) Received: from tomoyo.MyBSD.org.my (tomoyo.mybsd.org.my [202.157.186.227]) by mx1.FreeBSD.org (Postfix) with ESMTP id D6E6743D48; Thu, 15 Sep 2005 22:36:38 +0000 (GMT) (envelope-from skywizard@MyBSD.org.my) Received: from localhost (localhost [127.0.0.1]) by tomoyo.MyBSD.org.my (Postfix) with ESMTP id 6A6446CC36; Fri, 16 Sep 2005 06:44:45 +0800 (MYT) Received: from tomoyo.MyBSD.org.my ([127.0.0.1]) by localhost (tomoyo.MyBSD.org.my [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id 62050-01; Fri, 16 Sep 2005 06:44:44 +0800 (MYT) Received: from kasumi.MyBSD.org.my (unknown [218.111.181.140]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (No client certificate requested) by tomoyo.MyBSD.org.my (Postfix) with ESMTP id EF9866CC32; Fri, 16 Sep 2005 06:44:35 +0800 (MYT) Date: Fri, 16 Sep 2005 06:36:31 +0800 From: Ariff Abdullah To: lofi@freebsd.org, perlfu@gmail.com 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> Organization: MyBSD X-Mailer: /usr/local/lib/ruby/1.8/net/smtp.rb Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="Multipart=_Fri__16_Sep_2005_06_36_31_+0800_FNRSpxZB0VwFh.HR" X-Virus-Scanned: by amavisd-new-antivirus-mail-gateway at TOMOYO.MYBSD.ORG.MY Cc: freebsd-multimedia@freebsd.org Subject: Re: Vchan/Sound/patch hard locks... X-BeenThere: freebsd-multimedia@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: Multimedia discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 15 Sep 2005 22:36:47 -0000 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 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, "\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 + * Copyright (c) 2005 Ariff Abdullah * 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 - #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 * Copyright (c) 2003 Orion Hodson + * Copyright (c) 2005 Ariff Abdullah * 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 + * 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 #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 + * 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 +#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 + * All rights reserved. + * + * Derived from vchan.c + * + * Copyright (c) 2001 Cameron Grant + * 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 +#include +#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--