summaryrefslogtreecommitdiffstats
path: root/sys/dev/sound/pcm
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/sound/pcm')
-rw-r--r--sys/dev/sound/pcm/ac97.c671
-rw-r--r--sys/dev/sound/pcm/ac97.h103
-rw-r--r--sys/dev/sound/pcm/ac97_if.m60
-rw-r--r--sys/dev/sound/pcm/buffer.c665
-rw-r--r--sys/dev/sound/pcm/buffer.h96
-rw-r--r--sys/dev/sound/pcm/channel.c1214
-rw-r--r--sys/dev/sound/pcm/channel.h159
-rw-r--r--sys/dev/sound/pcm/channel_if.m141
-rw-r--r--sys/dev/sound/pcm/dsp.c1147
-rw-r--r--sys/dev/sound/pcm/dsp.h32
-rw-r--r--sys/dev/sound/pcm/fake.c137
-rw-r--r--sys/dev/sound/pcm/feeder.c421
-rw-r--r--sys/dev/sound/pcm/feeder.h83
-rw-r--r--sys/dev/sound/pcm/feeder_fmt.c543
-rw-r--r--sys/dev/sound/pcm/feeder_if.m87
-rw-r--r--sys/dev/sound/pcm/feeder_rate.c192
-rw-r--r--sys/dev/sound/pcm/mixer.c519
-rw-r--r--sys/dev/sound/pcm/mixer.h50
-rw-r--r--sys/dev/sound/pcm/mixer_if.m66
-rw-r--r--sys/dev/sound/pcm/sndstat.c400
-rw-r--r--sys/dev/sound/pcm/sound.c907
-rw-r--r--sys/dev/sound/pcm/sound.h289
-rw-r--r--sys/dev/sound/pcm/vchan.c344
-rw-r--r--sys/dev/sound/pcm/vchan.h33
24 files changed, 8359 insertions, 0 deletions
diff --git a/sys/dev/sound/pcm/ac97.c b/sys/dev/sound/pcm/ac97.c
new file mode 100644
index 0000000..427fb98
--- /dev/null
+++ b/sys/dev/sound/pcm/ac97.c
@@ -0,0 +1,671 @@
+/*
+ * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <dev/sound/pcm/sound.h>
+#include <dev/sound/pcm/ac97.h>
+
+#include "mixer_if.h"
+
+SND_DECLARE_FILE("$FreeBSD$");
+
+MALLOC_DEFINE(M_AC97, "ac97", "ac97 codec");
+
+struct ac97mixtable_entry {
+ int reg:8;
+ unsigned bits:4;
+ unsigned ofs:4;
+ unsigned stereo:1;
+ unsigned mute:1;
+ unsigned recidx:4;
+ unsigned mask:1;
+ unsigned enable:1;
+};
+
+#define AC97_NAMELEN 16
+struct ac97_info {
+ kobj_t methods;
+ device_t dev;
+ void *devinfo;
+ char *id;
+ char rev;
+ unsigned count, caps, se, extcaps, extid, extstat, noext:1;
+ u_int32_t flags;
+ struct ac97mixtable_entry mix[32];
+ char name[AC97_NAMELEN];
+ struct mtx *lock;
+};
+
+struct ac97_codecid {
+ u_int32_t id, noext:1;
+ char *name;
+};
+
+static const struct ac97mixtable_entry ac97mixtable_default[32] = {
+ [SOUND_MIXER_VOLUME] = { AC97_MIX_MASTER, 5, 0, 1, 1, 6, 0, 1 },
+ [SOUND_MIXER_MONITOR] = { AC97_MIX_AUXOUT, 5, 0, 1, 1, 0, 0, 0 },
+ [SOUND_MIXER_PHONEOUT] = { AC97_MIX_MONO, 5, 0, 0, 1, 7, 0, 0 },
+ [SOUND_MIXER_BASS] = { AC97_MIX_TONE, 4, 8, 0, 0, 0, 1, 0 },
+ [SOUND_MIXER_TREBLE] = { AC97_MIX_TONE, 4, 0, 0, 0, 0, 1, 0 },
+ [SOUND_MIXER_PCM] = { AC97_MIX_PCM, 5, 0, 1, 1, 0, 0, 1 },
+ [SOUND_MIXER_SPEAKER] = { AC97_MIX_BEEP, 4, 1, 0, 1, 0, 0, 0 },
+ [SOUND_MIXER_LINE] = { AC97_MIX_LINE, 5, 0, 1, 1, 5, 0, 1 },
+ [SOUND_MIXER_PHONEIN] = { AC97_MIX_PHONE, 5, 0, 0, 1, 8, 0, 0 },
+ [SOUND_MIXER_MIC] = { AC97_MIX_MIC, 5, 0, 0, 1, 1, 0, 1 },
+ [SOUND_MIXER_CD] = { AC97_MIX_CD, 5, 0, 1, 1, 2, 0, 1 },
+ [SOUND_MIXER_LINE1] = { AC97_MIX_AUX, 5, 0, 1, 1, 4, 0, 0 },
+ [SOUND_MIXER_VIDEO] = { AC97_MIX_VIDEO, 5, 0, 1, 1, 3, 0, 0 },
+ [SOUND_MIXER_RECLEV] = { -AC97_MIX_RGAIN, 4, 0, 1, 1, 0, 0, 1 }
+};
+
+static struct ac97_codecid ac97codecid[] = {
+ { 0x41445303, 0, "Analog Devices AD1819" },
+ { 0x41445340, 0, "Analog Devices AD1881" },
+ { 0x41445348, 0, "Analog Devices AD1881A" },
+ { 0x41445360, 0, "Analog Devices AD1885" },
+ { 0x414b4d00, 1, "Asahi Kasei AK4540" },
+ { 0x414b4d01, 1, "Asahi Kasei AK4542" },
+ { 0x414b4d02, 1, "Asahi Kasei AK4543" },
+ { 0x414c4710, 0, "Avance Logic ALC200/200P" },
+ { 0x43525900, 0, "Cirrus Logic CS4297" },
+ { 0x43525903, 0, "Cirrus Logic CS4297" },
+ { 0x43525913, 0, "Cirrus Logic CS4297A" },
+ { 0x43525914, 0, "Cirrus Logic CS4297B" },
+ { 0x43525923, 0, "Cirrus Logic CS4294C" },
+ { 0x4352592b, 0, "Cirrus Logic CS4298C" },
+ { 0x43525931, 0, "Cirrus Logic CS4299A" },
+ { 0x43525933, 0, "Cirrus Logic CS4299C" },
+ { 0x43525934, 0, "Cirrus Logic CS4299D" },
+ { 0x43525941, 0, "Cirrus Logic CS4201A" },
+ { 0x43525951, 0, "Cirrus Logic CS4205A" },
+ { 0x43525961, 0, "Cirrus Logic CS4291A" },
+ { 0x45838308, 0, "ESS Technology ES1921" },
+ { 0x49434511, 0, "ICEnsemble ICE1232" },
+ { 0x4e534331, 0, "National Semiconductor LM4549" },
+ { 0x83847600, 0, "SigmaTel STAC9700/9783/9784" },
+ { 0x83847604, 0, "SigmaTel STAC9701/9703/9704/9705" },
+ { 0x83847605, 0, "SigmaTel STAC9704" },
+ { 0x83847608, 0, "SigmaTel STAC9708/9711" },
+ { 0x83847609, 0, "SigmaTel STAC9721/9723" },
+ { 0x83847644, 0, "SigmaTel STAC9744" },
+ { 0x83847656, 0, "SigmaTel STAC9756/9757" },
+ { 0x53494c22, 0, "Silicon Laboratory Si3036" },
+ { 0x53494c23, 0, "Silicon Laboratory Si3038" },
+ { 0x54524103, 0, "TriTech TR?????" },
+ { 0x54524106, 0, "TriTech TR28026" },
+ { 0x54524108, 0, "TriTech TR28028" },
+ { 0x54524123, 0, "TriTech TR28602" },
+ { 0x574d4c00, 0, "Wolfson WM9701A" },
+ { 0x574d4c03, 0, "Wolfson WM9703/9704" },
+ { 0x574d4c04, 0, "Wolfson WM9704 (quad)" },
+ { 0, 0, NULL }
+};
+
+static char *ac97enhancement[] = {
+ "no 3D Stereo Enhancement",
+ "Analog Devices Phat Stereo",
+ "Creative Stereo Enhancement",
+ "National Semi 3D Stereo Enhancement",
+ "Yamaha Ymersion",
+ "BBE 3D Stereo Enhancement",
+ "Crystal Semi 3D Stereo Enhancement",
+ "Qsound QXpander",
+ "Spatializer 3D Stereo Enhancement",
+ "SRS 3D Stereo Enhancement",
+ "Platform Tech 3D Stereo Enhancement",
+ "AKM 3D Audio",
+ "Aureal Stereo Enhancement",
+ "Aztech 3D Enhancement",
+ "Binaura 3D Audio Enhancement",
+ "ESS Technology Stereo Enhancement",
+ "Harman International VMAx",
+ "Nvidea 3D Stereo Enhancement",
+ "Philips Incredible Sound",
+ "Texas Instruments 3D Stereo Enhancement",
+ "VLSI Technology 3D Stereo Enhancement",
+ "TriTech 3D Stereo Enhancement",
+ "Realtek 3D Stereo Enhancement",
+ "Samsung 3D Stereo Enhancement",
+ "Wolfson Microelectronics 3D Enhancement",
+ "Delta Integration 3D Enhancement",
+ "SigmaTel 3D Enhancement",
+ "Reserved 27",
+ "Rockwell 3D Stereo Enhancement",
+ "Reserved 29",
+ "Reserved 30",
+ "Reserved 31"
+};
+
+static char *ac97feature[] = {
+ "mic channel",
+ "reserved",
+ "tone",
+ "simulated stereo",
+ "headphone",
+ "bass boost",
+ "18 bit DAC",
+ "20 bit DAC",
+ "18 bit ADC",
+ "20 bit ADC"
+};
+
+static char *ac97extfeature[] = {
+ "variable rate PCM",
+ "double rate PCM",
+ "reserved 1",
+ "variable rate mic",
+ "reserved 2",
+ "reserved 3",
+ "center DAC",
+ "surround DAC",
+ "LFE DAC",
+ "AMAP",
+ "reserved 4",
+ "reserved 5",
+ "reserved 6",
+ "reserved 7",
+};
+
+static u_int16_t
+rdcd(struct ac97_info *codec, int reg)
+{
+ return AC97_READ(codec->methods, codec->devinfo, reg);
+}
+
+static void
+wrcd(struct ac97_info *codec, int reg, u_int16_t val)
+{
+ AC97_WRITE(codec->methods, codec->devinfo, reg, val);
+}
+
+static void
+ac97_reset(struct ac97_info *codec)
+{
+ u_int32_t i, ps;
+ wrcd(codec, AC97_REG_RESET, 0);
+ for (i = 0; i < 500; i++) {
+ ps = rdcd(codec, AC97_REG_POWER) & AC97_POWER_STATUS;
+ if (ps == AC97_POWER_STATUS)
+ return;
+ DELAY(1000);
+ }
+ device_printf(codec->dev, "AC97 reset timed out.");
+}
+
+int
+ac97_setrate(struct ac97_info *codec, int which, int rate)
+{
+ u_int16_t v;
+
+ switch(which) {
+ case AC97_REGEXT_FDACRATE:
+ case AC97_REGEXT_SDACRATE:
+ case AC97_REGEXT_LDACRATE:
+ case AC97_REGEXT_LADCRATE:
+ case AC97_REGEXT_MADCRATE:
+ break;
+
+ default:
+ return -1;
+ }
+
+ snd_mtxlock(codec->lock);
+ if (rate != 0) {
+ v = rate;
+ if (codec->extstat & AC97_EXTCAP_DRA)
+ v >>= 1;
+ wrcd(codec, which, v);
+ }
+ v = rdcd(codec, which);
+ if (codec->extstat & AC97_EXTCAP_DRA)
+ v <<= 1;
+ snd_mtxunlock(codec->lock);
+ return v;
+}
+
+int
+ac97_setextmode(struct ac97_info *codec, u_int16_t mode)
+{
+ mode &= AC97_EXTCAPS;
+ if ((mode & ~codec->extcaps) != 0) {
+ device_printf(codec->dev, "ac97 invalid mode set 0x%04x\n",
+ mode);
+ return -1;
+ }
+ snd_mtxlock(codec->lock);
+ wrcd(codec, AC97_REGEXT_STAT, mode);
+ codec->extstat = rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS;
+ snd_mtxunlock(codec->lock);
+ return (mode == codec->extstat)? 0 : -1;
+}
+
+u_int16_t
+ac97_getextmode(struct ac97_info *codec)
+{
+ return codec->extstat;
+}
+
+u_int16_t
+ac97_getextcaps(struct ac97_info *codec)
+{
+ return codec->extcaps;
+}
+
+u_int16_t
+ac97_getcaps(struct ac97_info *codec)
+{
+ return codec->caps;
+}
+
+static int
+ac97_setrecsrc(struct ac97_info *codec, int channel)
+{
+ struct ac97mixtable_entry *e = &codec->mix[channel];
+
+ if (e->recidx > 0) {
+ int val = e->recidx - 1;
+ val |= val << 8;
+ snd_mtxlock(codec->lock);
+ wrcd(codec, AC97_REG_RECSEL, val);
+ snd_mtxunlock(codec->lock);
+ return 0;
+ } else
+ return -1;
+}
+
+static int
+ac97_setmixer(struct ac97_info *codec, unsigned channel, unsigned left, unsigned right)
+{
+ struct ac97mixtable_entry *e = &codec->mix[channel];
+
+ if (e->reg && e->enable && e->bits) {
+ int max, val, reg = (e->reg >= 0)? e->reg : -e->reg;
+
+ if (!e->stereo)
+ right = left;
+ if (e->reg > 0) {
+ left = 100 - left;
+ right = 100 - right;
+ }
+
+ max = (1 << e->bits) - 1;
+ left = (left * max) / 100;
+ right = (right * max) / 100;
+
+ val = (left << 8) | right;
+
+ left = (left * 100) / max;
+ right = (right * 100) / max;
+
+ if (e->reg > 0) {
+ left = 100 - left;
+ right = 100 - right;
+ }
+
+ if (!e->stereo) {
+ val &= max;
+ val <<= e->ofs;
+ if (e->mask) {
+ int cur = rdcd(codec, e->reg);
+ val |= cur & ~(max << e->ofs);
+ }
+ }
+ if (left == 0 && right == 0 && e->mute == 1)
+ val = AC97_MUTE;
+ snd_mtxlock(codec->lock);
+ 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); */
+ return -1;
+ }
+}
+
+#if 0
+static int
+ac97_getmixer(struct ac97_info *codec, int channel)
+{
+ struct ac97mixtable_entry *e = &codec->mix[channel];
+ if (channel < SOUND_MIXER_NRDEVICES && e->reg != 0) {
+ int max, val, volume;
+
+ max = (1 << e->bits) - 1;
+ val = rdcd(code, e->reg);
+ if (val == AC97_MUTE && e->mute == 1)
+ volume = 0;
+ else {
+ if (e->stereo == 0) val >>= e->ofs;
+ val &= max;
+ volume = (val * 100) / max;
+ if (e->reg > 0) volume = 100 - volume;
+ }
+ return volume;
+ } else
+ return -1;
+}
+#endif
+
+static void
+ac97_fix_auxout(struct ac97_info *codec)
+{
+ /* Determine what AUXOUT really means, it can be:
+ *
+ * 1. Headphone out.
+ * 2. 4-Channel Out
+ * 3. True line level out (effectively master volume).
+ *
+ * See Sections 5.2.1 and 5.27 for AUX_OUT Options in AC97r2.{2,3}.
+ */
+ if (codec->caps & AC97_CAP_HEADPHONE) {
+ /* XXX We should probably check the AUX_OUT initial value.
+ * Leave AC97_MIX_AUXOUT - SOUND_MIXER_MONITOR relationship */
+ return;
+ } else if (codec->extcaps & AC97_EXTCAP_SDAC &&
+ rdcd(codec, AC97_MIXEXT_SURROUND) == 0x8080) {
+ /* 4-Channel Out, add an additional gain setting. */
+ codec->mix[SOUND_MIXER_OGAIN] = codec->mix[SOUND_MIXER_MONITOR];
+ } else {
+ /* Master volume is/maybe fixed in h/w, not sufficiently
+ * clear in spec to blat SOUND_MIXER_MASTER. */
+ codec->mix[SOUND_MIXER_OGAIN] = codec->mix[SOUND_MIXER_MONITOR];
+ }
+ /* Blat monitor, inappropriate label if we get here */
+ bzero(&codec->mix[SOUND_MIXER_MONITOR],
+ sizeof(codec->mix[SOUND_MIXER_MONITOR]));
+}
+
+static unsigned
+ac97_initmixer(struct ac97_info *codec)
+{
+ unsigned i, j, k, old;
+ u_int32_t id;
+
+ snd_mtxlock(codec->lock);
+ codec->count = AC97_INIT(codec->methods, codec->devinfo);
+ if (codec->count == 0) {
+ device_printf(codec->dev, "ac97 codec init failed\n");
+ snd_mtxunlock(codec->lock);
+ return ENODEV;
+ }
+
+ wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000);
+ ac97_reset(codec);
+ wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000);
+
+ i = rdcd(codec, AC97_REG_RESET);
+ codec->caps = i & 0x03ff;
+ codec->se = (i & 0x7c00) >> 10;
+
+ id = (rdcd(codec, AC97_REG_ID1) << 16) | rdcd(codec, AC97_REG_ID2);
+ codec->rev = id & 0x000000ff;
+ if (id == 0 || id == 0xffffffff) {
+ device_printf(codec->dev, "ac97 codec invalid or not present (id == %x)\n", id);
+ snd_mtxunlock(codec->lock);
+ return ENODEV;
+ }
+
+ codec->noext = 0;
+ codec->id = NULL;
+ for (i = 0; ac97codecid[i].id; i++) {
+ if (ac97codecid[i].id == id) {
+ codec->id = ac97codecid[i].name;
+ codec->noext = ac97codecid[i].noext;
+ }
+ }
+
+ codec->extcaps = 0;
+ codec->extid = 0;
+ codec->extstat = 0;
+ if (!codec->noext) {
+ i = rdcd(codec, AC97_REGEXT_ID);
+ if (i != 0xffff) {
+ codec->extcaps = i & 0x3fff;
+ codec->extid = (i & 0xc000) >> 14;
+ codec->extstat = rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS;
+ }
+ }
+
+ for (i = 0; i < 32; i++) {
+ codec->mix[i] = ac97mixtable_default[i];
+ }
+ ac97_fix_auxout(codec);
+
+ for (i = 0; i < 32; i++) {
+ k = codec->noext? codec->mix[i].enable : 1;
+ if (k && (codec->mix[i].reg > 0)) {
+ old = rdcd(codec, codec->mix[i].reg);
+ wrcd(codec, codec->mix[i].reg, 0x3f);
+ j = rdcd(codec, codec->mix[i].reg);
+ 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;
+ }
+ /* printf("mixch %d, en=%d, b=%d\n", i, codec->mix[i].enable, codec->mix[i].bits); */
+ }
+
+ if (bootverbose) {
+ device_printf(codec->dev, "ac97 codec id 0x%08x", id);
+ if (codec->id)
+ printf(" (%s)", codec->id);
+ printf("\n");
+ device_printf(codec->dev, "ac97 codec features ");
+ for (i = j = 0; i < 10; i++)
+ if (codec->caps & (1 << i))
+ printf("%s%s", j++? ", " : "", ac97feature[i]);
+ printf("%s%d bit master volume", j++? ", " : "", codec->mix[SOUND_MIXER_VOLUME].bits);
+ printf("%s%s\n", j? ", " : "", ac97enhancement[codec->se]);
+
+ if (codec->extcaps != 0 || codec->extid) {
+ device_printf(codec->dev, "ac97 %s codec",
+ codec->extid? "secondary" : "primary");
+ if (codec->extcaps)
+ printf(" extended features ");
+ for (i = j = 0; i < 14; i++)
+ if (codec->extcaps & (1 << i))
+ printf("%s%s", j++? ", " : "", ac97extfeature[i]);
+ printf("\n");
+ }
+ }
+
+ if ((rdcd(codec, AC97_REG_POWER) & 2) == 0)
+ device_printf(codec->dev, "ac97 codec reports dac not ready\n");
+ snd_mtxunlock(codec->lock);
+ return 0;
+}
+
+static unsigned
+ac97_reinitmixer(struct ac97_info *codec)
+{
+ snd_mtxlock(codec->lock);
+ codec->count = AC97_INIT(codec->methods, codec->devinfo);
+ if (codec->count == 0) {
+ device_printf(codec->dev, "ac97 codec init failed\n");
+ snd_mtxunlock(codec->lock);
+ return ENODEV;
+ }
+
+ wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000);
+ ac97_reset(codec);
+ wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000);
+
+ if (!codec->noext) {
+ wrcd(codec, AC97_REGEXT_STAT, codec->extstat);
+ if ((rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS)
+ != codec->extstat)
+ device_printf(codec->dev, "ac97 codec failed to reset extended mode (%x, got %x)\n",
+ codec->extstat,
+ rdcd(codec, AC97_REGEXT_STAT) &
+ AC97_EXTCAPS);
+ }
+
+ if ((rdcd(codec, AC97_REG_POWER) & 2) == 0)
+ device_printf(codec->dev, "ac97 codec reports dac not ready\n");
+ snd_mtxunlock(codec->lock);
+ return 0;
+}
+
+struct ac97_info *
+ac97_create(device_t dev, void *devinfo, kobj_class_t cls)
+{
+ struct ac97_info *codec;
+
+ codec = (struct ac97_info *)malloc(sizeof *codec, M_AC97, M_NOWAIT);
+ if (codec == NULL)
+ return NULL;
+
+ snprintf(codec->name, AC97_NAMELEN, "%s:ac97", device_get_nameunit(dev));
+ codec->lock = snd_mtxcreate(codec->name, "ac97 codec");
+ codec->methods = kobj_create(cls, M_AC97, M_WAITOK);
+ if (codec->methods == NULL) {
+ snd_mtxlock(codec->lock);
+ snd_mtxfree(codec->lock);
+ free(codec, M_AC97);
+ return NULL;
+ }
+
+ codec->dev = dev;
+ codec->devinfo = devinfo;
+ codec->flags = 0;
+ return codec;
+}
+
+void
+ac97_destroy(struct ac97_info *codec)
+{
+ snd_mtxlock(codec->lock);
+ if (codec->methods != NULL)
+ kobj_delete(codec->methods, M_AC97);
+ snd_mtxfree(codec->lock);
+ free(codec, M_AC97);
+}
+
+void
+ac97_setflags(struct ac97_info *codec, u_int32_t val)
+{
+ codec->flags = val;
+}
+
+u_int32_t
+ac97_getflags(struct ac97_info *codec)
+{
+ return codec->flags;
+}
+
+/* -------------------------------------------------------------------- */
+
+static int
+ac97mix_init(struct snd_mixer *m)
+{
+ struct ac97_info *codec = mix_getdevinfo(m);
+ u_int32_t i, mask;
+
+ if (codec == NULL)
+ return -1;
+
+ if (ac97_initmixer(codec))
+ return -1;
+
+ mask = 0;
+ for (i = 0; i < 32; i++)
+ mask |= codec->mix[i].enable? 1 << i : 0;
+ mix_setdevs(m, mask);
+
+ mask = 0;
+ for (i = 0; i < 32; i++)
+ mask |= codec->mix[i].recidx? 1 << i : 0;
+ mix_setrecdevs(m, mask);
+ return 0;
+}
+
+static int
+ac97mix_uninit(struct snd_mixer *m)
+{
+ struct ac97_info *codec = mix_getdevinfo(m);
+
+ if (codec == NULL)
+ return -1;
+ /*
+ if (ac97_uninitmixer(codec))
+ return -1;
+ */
+ ac97_destroy(codec);
+ return 0;
+}
+
+static int
+ac97mix_reinit(struct snd_mixer *m)
+{
+ struct ac97_info *codec = mix_getdevinfo(m);
+
+ if (codec == NULL)
+ return -1;
+ return ac97_reinitmixer(codec);
+}
+
+static int
+ac97mix_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right)
+{
+ struct ac97_info *codec = mix_getdevinfo(m);
+
+ if (codec == NULL)
+ return -1;
+ return ac97_setmixer(codec, dev, left, right);
+}
+
+static int
+ac97mix_setrecsrc(struct snd_mixer *m, u_int32_t src)
+{
+ int i;
+ struct ac97_info *codec = mix_getdevinfo(m);
+
+ if (codec == NULL)
+ return -1;
+ for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
+ if ((src & (1 << i)) != 0)
+ break;
+ return (ac97_setrecsrc(codec, i) == 0)? 1 << i : -1;
+}
+
+static kobj_method_t ac97mixer_methods[] = {
+ KOBJMETHOD(mixer_init, ac97mix_init),
+ KOBJMETHOD(mixer_uninit, ac97mix_uninit),
+ KOBJMETHOD(mixer_reinit, ac97mix_reinit),
+ KOBJMETHOD(mixer_set, ac97mix_set),
+ KOBJMETHOD(mixer_setrecsrc, ac97mix_setrecsrc),
+ { 0, 0 }
+};
+MIXER_DECLARE(ac97mixer);
+
+/* -------------------------------------------------------------------- */
+
+kobj_class_t
+ac97_getmixerclass(void)
+{
+ return &ac97mixer_class;
+}
+
+
diff --git a/sys/dev/sound/pcm/ac97.h b/sys/dev/sound/pcm/ac97.h
new file mode 100644
index 0000000..9c18121
--- /dev/null
+++ b/sys/dev/sound/pcm/ac97.h
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#define AC97_MUTE 0x8000
+
+#define AC97_REG_RESET 0x00
+#define AC97_CAP_MICCHANNEL (1 << 0)
+#define AC97_CAP_TONE (1 << 2)
+#define AC97_CAP_SIMSTEREO (1 << 3)
+#define AC97_CAP_HEADPHONE (1 << 4)
+#define AC97_CAP_LOUDNESS (1 << 5)
+#define AC97_CAP_DAC_18 (1 << 6)
+#define AC97_CAP_DAC_20 (1 << 7)
+#define AC97_CAP_ADC_18 (1 << 8)
+#define AC97_CAP_ADC_20 (1 << 9)
+#define AC97_MIX_MASTER 0x02
+#define AC97_MIX_AUXOUT 0x04
+#define AC97_MIX_MONO 0x06
+#define AC97_MIX_TONE 0x08
+#define AC97_MIX_BEEP 0x0a
+#define AC97_MIX_PHONE 0x0c
+#define AC97_MIX_MIC 0x0e
+#define AC97_MIX_LINE 0x10
+#define AC97_MIX_CD 0x12
+#define AC97_MIX_VIDEO 0x14
+#define AC97_MIX_AUX 0x16
+#define AC97_MIX_PCM 0x18
+#define AC97_REG_RECSEL 0x1a
+#define AC97_MIX_RGAIN 0x1c
+#define AC97_MIX_MGAIN 0x1e
+#define AC97_REG_GEN 0x20
+#define AC97_REG_3D 0x22
+#define AC97_REG_POWER 0x26
+#define AC97_POWER_ADC (1 << 0)
+#define AC97_POWER_DAC (1 << 1)
+#define AC97_POWER_ANL (1 << 2)
+#define AC97_POWER_REF (1 << 3)
+#define AC97_POWER_STATUS (AC97_POWER_ADC | AC97_POWER_DAC | \
+ AC97_POWER_REF | AC97_POWER_ANL )
+#define AC97_REGEXT_ID 0x28
+#define AC97_EXTCAP_VRA (1 << 0)
+#define AC97_EXTCAP_DRA (1 << 1)
+#define AC97_EXTCAP_VRM (1 << 3)
+#define AC97_EXTCAPS (AC97_EXTCAP_VRA | AC97_EXTCAP_DRA | AC97_EXTCAP_VRM)
+#define AC97_EXTCAP_SDAC (1 << 7)
+
+#define AC97_REGEXT_STAT 0x2a
+#define AC97_REGEXT_FDACRATE 0x2c
+#define AC97_REGEXT_SDACRATE 0x2e
+#define AC97_REGEXT_LDACRATE 0x30
+#define AC97_REGEXT_LADCRATE 0x32
+#define AC97_REGEXT_MADCRATE 0x34
+#define AC97_MIXEXT_CLFE 0x36
+#define AC97_MIXEXT_SURROUND 0x38
+#define AC97_REG_ID1 0x7c
+#define AC97_REG_ID2 0x7e
+
+#define AC97_F_EAPD_INV 0x00000001
+
+#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)
+
+struct ac97_info;
+
+#include "ac97_if.h"
+
+extern kobj_class_t ac97_getmixerclass(void);
+
+struct ac97_info *ac97_create(device_t dev, void *devinfo, kobj_class_t cls);
+void ac97_destroy(struct ac97_info *codec);
+void ac97_setflags(struct ac97_info *codec, u_int32_t val);
+u_int32_t ac97_getflags(struct ac97_info *codec);
+int ac97_setrate(struct ac97_info *codec, int which, int rate);
+int ac97_setextmode(struct ac97_info *codec, u_int16_t mode);
+u_int16_t ac97_getextmode(struct ac97_info *codec);
+u_int16_t ac97_getextcaps(struct ac97_info *codec);
+u_int16_t ac97_getcaps(struct ac97_info *codec);
+
diff --git a/sys/dev/sound/pcm/ac97_if.m b/sys/dev/sound/pcm/ac97_if.m
new file mode 100644
index 0000000..36c5c19
--- /dev/null
+++ b/sys/dev/sound/pcm/ac97_if.m
@@ -0,0 +1,60 @@
+# KOBJ
+#
+# Copyright (c) 2000 Cameron Grant <cg@freebsd.org>
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# $FreeBSD$
+#
+
+#include <dev/sound/pcm/sound.h>
+
+INTERFACE ac97;
+
+CODE {
+
+ static u_int32_t
+ ac97_noinit(kobj_t obj, void *devinfo)
+ {
+ return 1;
+ }
+
+};
+
+METHOD u_int32_t init {
+ kobj_t obj;
+ void *devinfo;
+} DEFAULT ac97_noinit;
+
+METHOD int read {
+ kobj_t obj;
+ void *devinfo;
+ int regno;
+};
+
+METHOD int write {
+ kobj_t obj;
+ void *devinfo;
+ int regno;
+ u_int32_t data;
+};
diff --git a/sys/dev/sound/pcm/buffer.c b/sys/dev/sound/pcm/buffer.c
new file mode 100644
index 0000000..d498591
--- /dev/null
+++ b/sys/dev/sound/pcm/buffer.c
@@ -0,0 +1,665 @@
+/*
+ * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <dev/sound/pcm/sound.h>
+
+#include "feeder_if.h"
+
+SND_DECLARE_FILE("$FreeBSD$");
+
+#define MIN(x, y) (((x) < (y))? (x) : (y))
+
+#define SNDBUF_NAMELEN 48
+struct snd_dbuf {
+ device_t dev;
+ u_int8_t *buf, *tmpbuf;
+ unsigned int bufsize, maxsize;
+ volatile int dl; /* transfer size */
+ volatile int rp; /* pointers to the ready area */
+ volatile int rl; /* length of ready area */
+ volatile int hp;
+ volatile u_int32_t total, prev_total;
+ int isadmachan, dir; /* dma channel */
+ u_int32_t fmt, spd, bps;
+ unsigned int blksz, blkcnt;
+ int xrun;
+ u_int32_t flags;
+ bus_dmamap_t dmamap;
+ bus_dma_tag_t dmatag;
+ struct selinfo sel;
+ char name[SNDBUF_NAMELEN];
+};
+
+struct snd_dbuf *
+sndbuf_create(device_t dev, char *drv, char *desc)
+{
+ struct snd_dbuf *b;
+
+ b = malloc(sizeof(*b), M_DEVBUF, M_WAITOK | M_ZERO);
+ snprintf(b->name, SNDBUF_NAMELEN, "%s:%s", drv, desc);
+ b->dev = dev;
+
+ return b;
+}
+
+void
+sndbuf_destroy(struct snd_dbuf *b)
+{
+ free(b, M_DEVBUF);
+}
+
+static void
+sndbuf_setmap(void *arg, bus_dma_segment_t *segs, int nseg, int error)
+{
+ struct snd_dbuf *b = (struct snd_dbuf *)arg;
+
+ if (bootverbose) {
+ device_printf(b->dev, "sndbuf_setmap %lx, %lx; ", (unsigned long)segs->ds_addr,
+ (unsigned long)segs->ds_len);
+ printf("%p -> %lx\n", b->buf, (unsigned long)vtophys(b->buf));
+ }
+}
+
+/*
+ * Allocate memory for DMA buffer. If the device does not use DMA transfers,
+ * the driver can call malloc(9) and sndbuf_setup() itself.
+ */
+int
+sndbuf_alloc(struct snd_dbuf *b, bus_dma_tag_t dmatag, unsigned int size)
+{
+ b->dmatag = dmatag;
+ b->maxsize = size;
+ b->bufsize = b->maxsize;
+ if (bus_dmamem_alloc(b->dmatag, (void **)&b->buf, BUS_DMA_NOWAIT, &b->dmamap))
+ return ENOSPC;
+ if (bus_dmamap_load(b->dmatag, b->dmamap, b->buf, b->maxsize, sndbuf_setmap, b, 0))
+ return ENOSPC;
+ return sndbuf_resize(b, 2, b->maxsize / 2);
+}
+
+int
+sndbuf_setup(struct snd_dbuf *b, void *buf, unsigned int size)
+{
+ b->buf = buf;
+ b->maxsize = size;
+ b->bufsize = b->maxsize;
+ return sndbuf_resize(b, 2, b->maxsize / 2);
+}
+
+void
+sndbuf_free(struct snd_dbuf *b)
+{
+ if (b->tmpbuf)
+ free(b->tmpbuf, M_DEVBUF);
+ b->tmpbuf = NULL;
+
+ if (b->dmamap)
+ bus_dmamap_unload(b->dmatag, b->dmamap);
+
+ if (b->dmamap && b->buf)
+ bus_dmamem_free(b->dmatag, b->buf, b->dmamap);
+ b->dmamap = NULL;
+ b->buf = NULL;
+}
+
+int
+sndbuf_resize(struct snd_dbuf *b, unsigned int blkcnt, unsigned int blksz)
+{
+ if (b->maxsize == 0)
+ return 0;
+ if (blkcnt == 0)
+ blkcnt = b->blkcnt;
+ if (blksz == 0)
+ blksz = b->blksz;
+ if (blkcnt < 2 || blksz < 16 || (blkcnt * blksz > b->maxsize))
+ return EINVAL;
+ if (blkcnt == b->blkcnt && blksz == b->blksz)
+ return 0;
+ b->blkcnt = blkcnt;
+ b->blksz = blksz;
+ b->bufsize = blkcnt * blksz;
+ if (b->tmpbuf)
+ free(b->tmpbuf, M_DEVBUF);
+ b->tmpbuf = malloc(b->bufsize, M_DEVBUF, M_NOWAIT);
+ sndbuf_reset(b);
+ return 0;
+}
+
+int
+sndbuf_remalloc(struct snd_dbuf *b, unsigned int blkcnt, unsigned int blksz)
+{
+ u_int8_t *buf, *tmpbuf, *f1, *f2;
+ unsigned int bufsize;
+
+ if (blkcnt < 2 || blksz < 16)
+ return EINVAL;
+
+ bufsize = blksz * blkcnt;
+
+
+ buf = malloc(bufsize, M_DEVBUF, M_NOWAIT);
+ if (buf == NULL)
+ return ENOMEM;
+
+ tmpbuf = malloc(bufsize, M_DEVBUF, M_NOWAIT);
+ if (tmpbuf == NULL) {
+ free(buf, M_DEVBUF);
+ return ENOMEM;
+ }
+
+ b->blkcnt = blkcnt;
+ b->blksz = blksz;
+ b->bufsize = bufsize;
+ b->maxsize = bufsize;
+ f1 = b->buf;
+ f2 = b->tmpbuf;
+ b->buf = buf;
+ b->tmpbuf = tmpbuf;
+
+ if (f1)
+ free(f1, M_DEVBUF);
+ if (f2)
+ free(f2, M_DEVBUF);
+
+ sndbuf_reset(b);
+ return 0;
+}
+
+void
+sndbuf_clear(struct snd_dbuf *b, unsigned int length)
+{
+ int i;
+ u_char data, *p;
+
+ if (length == 0)
+ return;
+ if (length > b->bufsize)
+ length = b->bufsize;
+
+ if (b->fmt & AFMT_SIGNED)
+ data = 0x00;
+ else
+ data = 0x80;
+
+ i = sndbuf_getfreeptr(b);
+ p = sndbuf_getbuf(b);
+ while (length > 0) {
+ p[i] = data;
+ length--;
+ i++;
+ if (i >= b->bufsize)
+ i = 0;
+ }
+}
+
+void
+sndbuf_fillsilence(struct snd_dbuf *b)
+{
+ int i;
+ u_char data, *p;
+
+ if (b->fmt & AFMT_SIGNED)
+ data = 0x00;
+ else
+ data = 0x80;
+
+ i = 0;
+ p = sndbuf_getbuf(b);
+ while (i < b->bufsize)
+ p[i++] = data;
+ b->rp = 0;
+ b->rl = b->bufsize;
+}
+
+void
+sndbuf_reset(struct snd_dbuf *b)
+{
+ b->hp = 0;
+ b->rp = 0;
+ b->rl = 0;
+ b->dl = 0;
+ b->prev_total = 0;
+ b->total = 0;
+ b->xrun = 0;
+ if (b->buf && b->bufsize > 0)
+ sndbuf_clear(b, b->bufsize);
+}
+
+u_int32_t
+sndbuf_getfmt(struct snd_dbuf *b)
+{
+ return b->fmt;
+}
+
+int
+sndbuf_setfmt(struct snd_dbuf *b, u_int32_t fmt)
+{
+ b->fmt = fmt;
+ b->bps = 1;
+ b->bps <<= (b->fmt & AFMT_STEREO)? 1 : 0;
+ b->bps <<= (b->fmt & AFMT_16BIT)? 1 : 0;
+ b->bps <<= (b->fmt & AFMT_32BIT)? 2 : 0;
+ return 0;
+}
+
+unsigned int
+sndbuf_getspd(struct snd_dbuf *b)
+{
+ return b->spd;
+}
+
+void
+sndbuf_setspd(struct snd_dbuf *b, unsigned int spd)
+{
+ b->spd = spd;
+}
+
+unsigned int
+sndbuf_getalign(struct snd_dbuf *b)
+{
+ static int align[] = {0, 1, 1, 2, 2, 2, 2, 3};
+
+ return align[b->bps - 1];
+}
+
+unsigned int
+sndbuf_getblkcnt(struct snd_dbuf *b)
+{
+ return b->blkcnt;
+}
+
+void
+sndbuf_setblkcnt(struct snd_dbuf *b, unsigned int blkcnt)
+{
+ b->blkcnt = blkcnt;
+}
+
+unsigned int
+sndbuf_getblksz(struct snd_dbuf *b)
+{
+ return b->blksz;
+}
+
+void
+sndbuf_setblksz(struct snd_dbuf *b, unsigned int blksz)
+{
+ b->blksz = blksz;
+}
+
+unsigned int
+sndbuf_getbps(struct snd_dbuf *b)
+{
+ return b->bps;
+}
+
+void *
+sndbuf_getbuf(struct snd_dbuf *b)
+{
+ return b->buf;
+}
+
+void *
+sndbuf_getbufofs(struct snd_dbuf *b, unsigned int ofs)
+{
+ KASSERT(ofs < b->bufsize, ("%s: ofs invalid %d", __func__, ofs));
+
+ return b->buf + ofs;
+}
+
+unsigned int
+sndbuf_getsize(struct snd_dbuf *b)
+{
+ return b->bufsize;
+}
+
+unsigned int
+sndbuf_getmaxsize(struct snd_dbuf *b)
+{
+ return b->maxsize;
+}
+
+unsigned int
+sndbuf_runsz(struct snd_dbuf *b)
+{
+ return b->dl;
+}
+
+void
+sndbuf_setrun(struct snd_dbuf *b, int go)
+{
+ b->dl = go? b->blksz : 0;
+}
+
+struct selinfo *
+sndbuf_getsel(struct snd_dbuf *b)
+{
+ return &b->sel;
+}
+
+/************************************************************/
+unsigned int
+sndbuf_getxrun(struct snd_dbuf *b)
+{
+ SNDBUF_LOCKASSERT(b);
+
+ return b->xrun;
+}
+
+void
+sndbuf_setxrun(struct snd_dbuf *b, unsigned int cnt)
+{
+ SNDBUF_LOCKASSERT(b);
+
+ b->xrun = cnt;
+}
+
+unsigned int
+sndbuf_gethwptr(struct snd_dbuf *b)
+{
+ SNDBUF_LOCKASSERT(b);
+
+ return b->hp;
+}
+
+void
+sndbuf_sethwptr(struct snd_dbuf *b, unsigned int ptr)
+{
+ SNDBUF_LOCKASSERT(b);
+
+ b->hp = ptr;
+}
+
+unsigned int
+sndbuf_getready(struct snd_dbuf *b)
+{
+ SNDBUF_LOCKASSERT(b);
+ KASSERT((b->rl >= 0) && (b->rl <= b->bufsize), ("%s: b->rl invalid %d", __func__, b->rl));
+
+ return b->rl;
+}
+
+unsigned int
+sndbuf_getreadyptr(struct snd_dbuf *b)
+{
+ SNDBUF_LOCKASSERT(b);
+ KASSERT((b->rp >= 0) && (b->rp <= b->bufsize), ("%s: b->rp invalid %d", __func__, b->rp));
+
+ return b->rp;
+}
+
+unsigned int
+sndbuf_getfree(struct snd_dbuf *b)
+{
+ SNDBUF_LOCKASSERT(b);
+ KASSERT((b->rl >= 0) && (b->rl <= b->bufsize), ("%s: b->rl invalid %d", __func__, b->rl));
+
+ return b->bufsize - b->rl;
+}
+
+unsigned int
+sndbuf_getfreeptr(struct snd_dbuf *b)
+{
+ SNDBUF_LOCKASSERT(b);
+ KASSERT((b->rp >= 0) && (b->rp <= b->bufsize), ("%s: b->rp invalid %d", __func__, b->rp));
+ KASSERT((b->rl >= 0) && (b->rl <= b->bufsize), ("%s: b->rl invalid %d", __func__, b->rl));
+
+ return (b->rp + b->rl) % b->bufsize;
+}
+
+unsigned int
+sndbuf_getblocks(struct snd_dbuf *b)
+{
+ SNDBUF_LOCKASSERT(b);
+
+ return b->total / b->blksz;
+}
+
+unsigned int
+sndbuf_getprevblocks(struct snd_dbuf *b)
+{
+ SNDBUF_LOCKASSERT(b);
+
+ return b->prev_total / b->blksz;
+}
+
+unsigned int
+sndbuf_gettotal(struct snd_dbuf *b)
+{
+ SNDBUF_LOCKASSERT(b);
+
+ return b->total;
+}
+
+void
+sndbuf_updateprevtotal(struct snd_dbuf *b)
+{
+ SNDBUF_LOCKASSERT(b);
+
+ b->prev_total = b->total;
+}
+
+/************************************************************/
+
+int
+sndbuf_acquire(struct snd_dbuf *b, u_int8_t *from, unsigned int count)
+{
+ int l;
+
+ KASSERT(count <= sndbuf_getfree(b), ("%s: count %d > free %d", __func__, count, sndbuf_getfree(b)));
+ KASSERT((b->rl >= 0) && (b->rl <= b->bufsize), ("%s: b->rl invalid %d", __func__, b->rl));
+ b->total += count;
+ if (from != NULL) {
+ while (count > 0) {
+ l = MIN(count, sndbuf_getsize(b) - sndbuf_getfreeptr(b));
+ bcopy(from, sndbuf_getbufofs(b, sndbuf_getfreeptr(b)), l);
+ from += l;
+ b->rl += l;
+ count -= l;
+ }
+ } else
+ b->rl += count;
+ KASSERT((b->rl >= 0) && (b->rl <= b->bufsize), ("%s: b->rl invalid %d, count %d", __func__, b->rl, count));
+
+ return 0;
+}
+
+int
+sndbuf_dispose(struct snd_dbuf *b, u_int8_t *to, unsigned int count)
+{
+ int l;
+
+ KASSERT(count <= sndbuf_getready(b), ("%s: count %d > ready %d", __func__, count, sndbuf_getready(b)));
+ KASSERT((b->rl >= 0) && (b->rl <= b->bufsize), ("%s: b->rl invalid %d", __func__, b->rl));
+ if (to != NULL) {
+ while (count > 0) {
+ l = MIN(count, sndbuf_getsize(b) - sndbuf_getreadyptr(b));
+ bcopy(sndbuf_getbufofs(b, sndbuf_getreadyptr(b)), to, l);
+ to += l;
+ b->rl -= l;
+ b->rp = (b->rp + l) % b->bufsize;
+ count -= l;
+ }
+ } else {
+ b->rl -= count;
+ b->rp = (b->rp + count) % b->bufsize;
+ }
+ KASSERT((b->rl >= 0) && (b->rl <= b->bufsize), ("%s: b->rl invalid %d, count %d", __func__, b->rl, count));
+
+ return 0;
+}
+
+int
+sndbuf_uiomove(struct snd_dbuf *b, struct uio *uio, unsigned int count)
+{
+ int x, c, p, rd, err;
+
+ err = 0;
+ rd = (uio->uio_rw == UIO_READ)? 1 : 0;
+ if (count > uio->uio_resid)
+ return EINVAL;
+
+ if (count > (rd? sndbuf_getready(b) : sndbuf_getfree(b))) {
+ return EINVAL;
+ }
+
+ while (err == 0 && count > 0) {
+ p = rd? sndbuf_getreadyptr(b) : sndbuf_getfreeptr(b);
+ c = MIN(count, sndbuf_getsize(b) - p);
+ x = uio->uio_resid;
+ err = uiomove(sndbuf_getbufofs(b, p), c, uio);
+ x -= uio->uio_resid;
+ count -= x;
+ x = rd? sndbuf_dispose(b, NULL, x) : sndbuf_acquire(b, NULL, x);
+ }
+
+ return 0;
+}
+
+/* count is number of bytes we want added to destination buffer */
+int
+sndbuf_feed(struct snd_dbuf *from, struct snd_dbuf *to, struct pcm_channel *channel, struct pcm_feeder *feeder, unsigned int count)
+{
+ KASSERT(count > 0, ("can't feed 0 bytes"));
+
+ if (sndbuf_getfree(to) < count)
+ return EINVAL;
+
+ count = FEEDER_FEED(feeder, channel, to->tmpbuf, count, from);
+ if (count)
+ sndbuf_acquire(to, to->tmpbuf, count);
+ /* the root feeder has called sndbuf_dispose(from, , bytes fetched) */
+
+ return 0;
+}
+
+/************************************************************/
+
+void
+sndbuf_dump(struct snd_dbuf *b, char *s, u_int32_t what)
+{
+ printf("%s: [", s);
+ if (what & 0x01)
+ printf(" bufsize: %d, maxsize: %d", b->bufsize, b->maxsize);
+ if (what & 0x02)
+ printf(" dl: %d, rp: %d, rl: %d, hp: %d", b->dl, b->rp, b->rl, b->hp);
+ if (what & 0x04)
+ printf(" total: %d, prev_total: %d, xrun: %d", b->total, b->prev_total, b->xrun);
+ if (what & 0x08)
+ printf(" fmt: 0x%x, spd: %d", b->fmt, b->spd);
+ if (what & 0x10)
+ printf(" blksz: %d, blkcnt: %d, flags: 0x%x", b->blksz, b->blkcnt, b->flags);
+ printf(" ]\n");
+}
+
+/************************************************************/
+u_int32_t
+sndbuf_getflags(struct snd_dbuf *b)
+{
+ return b->flags;
+}
+
+void
+sndbuf_setflags(struct snd_dbuf *b, u_int32_t flags, int on)
+{
+ b->flags &= ~flags;
+ if (on)
+ b->flags |= flags;
+}
+
+/************************************************************/
+
+int
+sndbuf_isadmasetup(struct snd_dbuf *b, struct resource *drq)
+{
+ /* should do isa_dma_acquire/isa_dma_release here */
+ if (drq == NULL) {
+ b->isadmachan = -1;
+ } else {
+ sndbuf_setflags(b, SNDBUF_F_ISADMA, 1);
+ b->isadmachan = rman_get_start(drq);
+ }
+ return 0;
+}
+
+int
+sndbuf_isadmasetdir(struct snd_dbuf *b, int dir)
+{
+ KASSERT(b, ("sndbuf_isadmasetdir called with b == NULL"));
+ KASSERT(sndbuf_getflags(b) & SNDBUF_F_ISADMA, ("sndbuf_isadmasetdir called on non-ISA buffer"));
+
+ b->dir = (dir == PCMDIR_PLAY)? ISADMA_WRITE : ISADMA_READ;
+ return 0;
+}
+
+void
+sndbuf_isadma(struct snd_dbuf *b, int go)
+{
+ KASSERT(b, ("sndbuf_isadma called with b == NULL"));
+ KASSERT(sndbuf_getflags(b) & SNDBUF_F_ISADMA, ("sndbuf_isadma called on non-ISA buffer"));
+
+ switch (go) {
+ case PCMTRIG_START:
+ /* isa_dmainit(b->chan, size); */
+ isa_dmastart(b->dir | ISADMA_RAW, b->buf, b->bufsize, b->isadmachan);
+ break;
+
+ case PCMTRIG_STOP:
+ case PCMTRIG_ABORT:
+ isa_dmastop(b->isadmachan);
+ isa_dmadone(b->dir | ISADMA_RAW, b->buf, b->bufsize, b->isadmachan);
+ break;
+ }
+
+ DEB(printf("buf 0x%p ISA DMA %s, channel %d\n",
+ b,
+ (go == PCMTRIG_START)? "started" : "stopped",
+ b->isadmachan));
+}
+
+int
+sndbuf_isadmaptr(struct snd_dbuf *b)
+{
+ int i;
+
+ KASSERT(b, ("sndbuf_isadmaptr called with b == NULL"));
+ KASSERT(sndbuf_getflags(b) & SNDBUF_F_ISADMA, ("sndbuf_isadmaptr called on non-ISA buffer"));
+
+ if (!sndbuf_runsz(b))
+ return 0;
+ i = isa_dmastatus(b->isadmachan);
+ KASSERT(i >= 0, ("isa_dmastatus returned %d", i));
+ return b->bufsize - i;
+}
+
+void
+sndbuf_isadmabounce(struct snd_dbuf *b)
+{
+ KASSERT(b, ("sndbuf_isadmabounce called with b == NULL"));
+ KASSERT(sndbuf_getflags(b) & SNDBUF_F_ISADMA, ("sndbuf_isadmabounce called on non-ISA buffer"));
+
+ /* tell isa_dma to bounce data in/out */
+}
+
diff --git a/sys/dev/sound/pcm/buffer.h b/sys/dev/sound/pcm/buffer.h
new file mode 100644
index 0000000..930d2e5
--- /dev/null
+++ b/sys/dev/sound/pcm/buffer.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#define ISA_DMA(b) (sndbuf_getflags((b)) & SNDBUF_F_ISADMA)
+#define SNDBUF_LOCKASSERT(b)
+
+#define SNDBUF_F_ISADMA 0x00000001
+#define SNDBUF_F_XRUN 0x00000002
+#define SNDBUF_F_RUNNING 0x00000004
+
+struct snd_dbuf *sndbuf_create(device_t dev, char *drv, char *desc);
+void sndbuf_destroy(struct snd_dbuf *b);
+
+void sndbuf_dump(struct snd_dbuf *b, char *s, u_int32_t what);
+
+int sndbuf_alloc(struct snd_dbuf *b, bus_dma_tag_t dmatag, unsigned int size);
+int sndbuf_setup(struct snd_dbuf *b, void *buf, unsigned int size);
+void sndbuf_free(struct snd_dbuf *b);
+int sndbuf_resize(struct snd_dbuf *b, unsigned int blkcnt, unsigned int blksz);
+int sndbuf_remalloc(struct snd_dbuf *b, unsigned int blkcnt, unsigned int blksz);
+void sndbuf_reset(struct snd_dbuf *b);
+void sndbuf_clear(struct snd_dbuf *b, unsigned int length);
+void sndbuf_fillsilence(struct snd_dbuf *b);
+
+u_int32_t sndbuf_getfmt(struct snd_dbuf *b);
+int sndbuf_setfmt(struct snd_dbuf *b, u_int32_t fmt);
+unsigned int sndbuf_getspd(struct snd_dbuf *b);
+void sndbuf_setspd(struct snd_dbuf *b, unsigned int spd);
+unsigned int sndbuf_getbps(struct snd_dbuf *b);
+
+void *sndbuf_getbuf(struct snd_dbuf *b);
+void *sndbuf_getbufofs(struct snd_dbuf *b, unsigned int ofs);
+unsigned int sndbuf_getsize(struct snd_dbuf *b);
+unsigned int sndbuf_getmaxsize(struct snd_dbuf *b);
+unsigned int sndbuf_getalign(struct snd_dbuf *b);
+unsigned int sndbuf_getblkcnt(struct snd_dbuf *b);
+void sndbuf_setblkcnt(struct snd_dbuf *b, unsigned int blkcnt);
+unsigned int sndbuf_getblksz(struct snd_dbuf *b);
+void sndbuf_setblksz(struct snd_dbuf *b, unsigned int blksz);
+unsigned int sndbuf_runsz(struct snd_dbuf *b);
+void sndbuf_setrun(struct snd_dbuf *b, int go);
+struct selinfo *sndbuf_getsel(struct snd_dbuf *b);
+
+unsigned int sndbuf_getxrun(struct snd_dbuf *b);
+void sndbuf_setxrun(struct snd_dbuf *b, unsigned int cnt);
+unsigned int sndbuf_gethwptr(struct snd_dbuf *b);
+void sndbuf_sethwptr(struct snd_dbuf *b, unsigned int ptr);
+unsigned int sndbuf_getfree(struct snd_dbuf *b);
+unsigned int sndbuf_getfreeptr(struct snd_dbuf *b);
+unsigned int sndbuf_getready(struct snd_dbuf *b);
+unsigned int sndbuf_getreadyptr(struct snd_dbuf *b);
+unsigned int sndbuf_getblocks(struct snd_dbuf *b);
+unsigned int sndbuf_getprevblocks(struct snd_dbuf *b);
+unsigned int sndbuf_gettotal(struct snd_dbuf *b);
+void sndbuf_updateprevtotal(struct snd_dbuf *b);
+
+int sndbuf_acquire(struct snd_dbuf *b, u_int8_t *from, unsigned int count);
+int sndbuf_dispose(struct snd_dbuf *b, u_int8_t *to, unsigned int count);
+int sndbuf_uiomove(struct snd_dbuf *b, struct uio *uio, unsigned int count);
+int sndbuf_feed(struct snd_dbuf *from, struct snd_dbuf *to, struct pcm_channel *channel, struct pcm_feeder *feeder, unsigned int count);
+
+u_int32_t sndbuf_getflags(struct snd_dbuf *b);
+void sndbuf_setflags(struct snd_dbuf *b, u_int32_t flags, int on);
+
+int sndbuf_isadmasetup(struct snd_dbuf *b, struct resource *drq);
+int sndbuf_isadmasetdir(struct snd_dbuf *b, int dir);
+void sndbuf_isadma(struct snd_dbuf *b, int go);
+int sndbuf_isadmaptr(struct snd_dbuf *b);
+void sndbuf_isadmabounce(struct snd_dbuf *b);
+
+
diff --git a/sys/dev/sound/pcm/channel.c b/sys/dev/sound/pcm/channel.c
new file mode 100644
index 0000000..9bf9158
--- /dev/null
+++ b/sys/dev/sound/pcm/channel.c
@@ -0,0 +1,1214 @@
+/*
+ * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
+ * Portions Copyright by Luigi Rizzo - 1997-99
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <dev/sound/pcm/sound.h>
+
+#include "feeder_if.h"
+
+SND_DECLARE_FILE("$FreeBSD$");
+
+#define MIN_CHUNK_SIZE 256 /* for uiomove etc. */
+#define DMA_ALIGN_THRESHOLD 4
+#define DMA_ALIGN_MASK (~(DMA_ALIGN_THRESHOLD - 1))
+
+#define MIN(x, y) (((x) < (y))? (x) : (y))
+#define CANCHANGE(c) (!(c->flags & CHN_F_TRIGGERED))
+
+/*
+#define DEB(x) x
+*/
+
+static int chn_targetirqrate = 32;
+TUNABLE_INT("hw.snd.targetirqrate", &chn_targetirqrate);
+
+static int
+sysctl_hw_snd_targetirqrate(SYSCTL_HANDLER_ARGS)
+{
+ int err, val;
+
+ val = chn_targetirqrate;
+ err = sysctl_handle_int(oidp, &val, sizeof(val), req);
+ if (val < 16 || val > 512)
+ err = EINVAL;
+ else
+ chn_targetirqrate = val;
+
+ return err;
+}
+SYSCTL_PROC(_hw_snd, OID_AUTO, targetirqrate, CTLTYPE_INT | CTLFLAG_RW,
+ 0, sizeof(int), sysctl_hw_snd_targetirqrate, "I", "");
+static int report_soft_formats = 1;
+SYSCTL_INT(_hw_snd, OID_AUTO, report_soft_formats, CTLFLAG_RW,
+ &report_soft_formats, 1, "report software-emulated formats");
+
+static int chn_buildfeeder(struct pcm_channel *c);
+
+static void
+chn_lockinit(struct pcm_channel *c)
+{
+ c->lock = snd_mtxcreate(c->name, "pcm channel");
+}
+
+static void
+chn_lockdestroy(struct pcm_channel *c)
+{
+ snd_mtxfree(c->lock);
+}
+
+static int
+chn_polltrigger(struct pcm_channel *c)
+{
+ struct snd_dbuf *bs = c->bufsoft;
+ unsigned amt, lim;
+
+ CHN_LOCKASSERT(c);
+ if (c->flags & CHN_F_MAPPED) {
+ if (sndbuf_getprevblocks(bs) == 0)
+ return 1;
+ else
+ return (sndbuf_getblocks(bs) > sndbuf_getprevblocks(bs))? 1 : 0;
+ } else {
+ amt = (c->direction == PCMDIR_PLAY)? sndbuf_getfree(bs) : sndbuf_getready(bs);
+ lim = (c->flags & CHN_F_HAS_SIZE)? sndbuf_getblksz(bs) : 1;
+ lim = 1;
+ return (amt >= lim)? 1 : 0;
+ }
+ return 0;
+}
+
+static int
+chn_pollreset(struct pcm_channel *c)
+{
+ struct snd_dbuf *bs = c->bufsoft;
+
+ CHN_LOCKASSERT(c);
+ sndbuf_updateprevtotal(bs);
+ return 1;
+}
+
+static void
+chn_wakeup(struct pcm_channel *c)
+{
+ struct snd_dbuf *bs = c->bufsoft;
+
+ CHN_LOCKASSERT(c);
+ if (SEL_WAITING(sndbuf_getsel(bs)) && chn_polltrigger(c))
+ selwakeup(sndbuf_getsel(bs));
+ wakeup(bs);
+}
+
+static int
+chn_sleep(struct pcm_channel *c, char *str, int timeout)
+{
+ struct snd_dbuf *bs = c->bufsoft;
+ int ret;
+
+ CHN_LOCKASSERT(c);
+#ifdef USING_MUTEX
+ ret = msleep(bs, c->lock, PRIBIO | PCATCH, str, timeout);
+#else
+ ret = tsleep(bs, PRIBIO | PCATCH, str, timeout);
+#endif
+
+ return ret;
+}
+
+/*
+ * chn_dmaupdate() tracks the status of a dma transfer,
+ * updating pointers. It must be called at spltty().
+ */
+
+static unsigned int
+chn_dmaupdate(struct pcm_channel *c)
+{
+ struct snd_dbuf *b = c->bufhard;
+ unsigned int delta, old, hwptr, amt;
+
+ KASSERT(sndbuf_getsize(b) > 0, ("bufsize == 0"));
+ CHN_LOCKASSERT(c);
+
+ old = sndbuf_gethwptr(b);
+ hwptr = chn_getptr(c);
+ delta = (sndbuf_getsize(b) + hwptr - old) % sndbuf_getsize(b);
+ sndbuf_sethwptr(b, hwptr);
+
+ DEB(
+ if (delta >= ((sndbuf_getsize(b) * 15) / 16)) {
+ if (!(c->flags & (CHN_F_CLOSING | CHN_F_ABORTING)))
+ device_printf(c->dev, "hwptr went backwards %d -> %d\n", old, hwptr);
+ }
+ );
+
+ if (c->direction == PCMDIR_PLAY) {
+ amt = MIN(delta, sndbuf_getready(b));
+ if (amt > 0)
+ sndbuf_dispose(b, NULL, amt);
+ } else {
+ amt = MIN(delta, sndbuf_getfree(b));
+ if (amt > 0)
+ sndbuf_acquire(b, NULL, amt);
+ }
+
+ return delta;
+}
+
+void
+chn_wrupdate(struct pcm_channel *c)
+{
+ int ret;
+
+ CHN_LOCKASSERT(c);
+ KASSERT(c->direction == PCMDIR_PLAY, ("chn_wrupdate on bad channel"));
+
+ if ((c->flags & (CHN_F_MAPPED | CHN_F_VIRTUAL)) || !(c->flags & CHN_F_TRIGGERED))
+ return;
+ chn_dmaupdate(c);
+ ret = chn_wrfeed(c);
+ /* tell the driver we've updated the primary buffer */
+ chn_trigger(c, PCMTRIG_EMLDMAWR);
+ DEB(if (ret)
+ printf("chn_wrupdate: chn_wrfeed returned %d\n", ret);)
+
+}
+
+int
+chn_wrfeed(struct pcm_channel *c)
+{
+ struct snd_dbuf *b = c->bufhard;
+ struct snd_dbuf *bs = c->bufsoft;
+ unsigned int ret, amt;
+
+ CHN_LOCKASSERT(c);
+ DEB(
+ if (c->flags & CHN_F_CLOSING) {
+ sndbuf_dump(b, "b", 0x02);
+ sndbuf_dump(bs, "bs", 0x02);
+ })
+
+ if (c->flags & CHN_F_MAPPED)
+ sndbuf_acquire(bs, NULL, sndbuf_getfree(bs));
+
+ amt = sndbuf_getfree(b);
+ if (sndbuf_getready(bs) < amt)
+ c->xruns++;
+
+ ret = (amt > 0)? sndbuf_feed(bs, b, c, c->feeder, amt) : ENOSPC;
+ if (ret == 0 && sndbuf_getfree(b) < amt)
+ chn_wakeup(c);
+
+ return ret;
+}
+
+static void
+chn_wrintr(struct pcm_channel *c)
+{
+ int ret;
+
+ CHN_LOCKASSERT(c);
+ /* update pointers in primary buffer */
+ chn_dmaupdate(c);
+ /* ...and feed from secondary to primary */
+ ret = chn_wrfeed(c);
+ /* tell the driver we've updated the primary buffer */
+ chn_trigger(c, PCMTRIG_EMLDMAWR);
+ DEB(if (ret)
+ printf("chn_wrintr: chn_wrfeed returned %d\n", ret);)
+}
+
+/*
+ * user write routine - uiomove data into secondary buffer, trigger if necessary
+ * if blocking, sleep, rinse and repeat.
+ *
+ * called externally, so must handle locking
+ */
+
+int
+chn_write(struct pcm_channel *c, struct uio *buf)
+{
+ int ret, timeout, newsize, count, sz;
+ struct snd_dbuf *bs = c->bufsoft;
+
+ CHN_LOCKASSERT(c);
+ /*
+ * XXX Certain applications attempt to write larger size
+ * of pcm data than c->blocksize2nd without blocking,
+ * resulting partial write. Expand the block size so that
+ * the write operation avoids blocking.
+ */
+ if ((c->flags & CHN_F_NBIO) && buf->uio_resid > sndbuf_getblksz(bs)) {
+ DEB(device_printf(c->dev, "broken app, nbio and tried to write %d bytes with fragsz %d\n",
+ buf->uio_resid, sndbuf_getblksz(bs)));
+ newsize = 16;
+ while (newsize < min(buf->uio_resid, CHN_2NDBUFMAXSIZE / 2))
+ newsize <<= 1;
+ chn_setblocksize(c, sndbuf_getblkcnt(bs), newsize);
+ DEB(device_printf(c->dev, "frags reset to %d x %d\n", sndbuf_getblkcnt(bs), sndbuf_getblksz(bs)));
+ }
+
+ ret = 0;
+ count = hz;
+ while (!ret && (buf->uio_resid > 0) && (count > 0)) {
+ sz = sndbuf_getfree(bs);
+ if (sz == 0) {
+ if (c->flags & CHN_F_NBIO)
+ ret = EWOULDBLOCK;
+ else {
+ timeout = (hz * sndbuf_getblksz(bs)) / (sndbuf_getspd(bs) * sndbuf_getbps(bs));
+ if (timeout < 1)
+ timeout = 1;
+ timeout = 1;
+ ret = chn_sleep(c, "pcmwr", timeout);
+ if (ret == EWOULDBLOCK) {
+ count -= timeout;
+ ret = 0;
+ } else if (ret == 0)
+ count = hz;
+ }
+ } else {
+ sz = MIN(sz, buf->uio_resid);
+ KASSERT(sz > 0, ("confusion in chn_write"));
+ /* printf("sz: %d\n", sz); */
+ ret = sndbuf_uiomove(bs, buf, sz);
+ if (ret == 0 && !(c->flags & CHN_F_TRIGGERED))
+ chn_start(c, 0);
+ }
+ }
+ /* printf("ret: %d left: %d\n", ret, buf->uio_resid); */
+
+ if (count <= 0) {
+ c->flags |= CHN_F_DEAD;
+ printf("%s: play interrupt timeout, channel dead\n", c->name);
+ }
+
+ return ret;
+}
+
+static int
+chn_rddump(struct pcm_channel *c, unsigned int cnt)
+{
+ struct snd_dbuf *b = c->bufhard;
+
+ CHN_LOCKASSERT(c);
+ sndbuf_setxrun(b, sndbuf_getxrun(b) + cnt);
+ return sndbuf_dispose(b, NULL, cnt);
+}
+
+/*
+ * Feed new data from the read buffer. Can be called in the bottom half.
+ * Hence must be called at spltty.
+ */
+int
+chn_rdfeed(struct pcm_channel *c)
+{
+ struct snd_dbuf *b = c->bufhard;
+ struct snd_dbuf *bs = c->bufsoft;
+ unsigned int ret, amt;
+
+ CHN_LOCKASSERT(c);
+ DEB(
+ if (c->flags & CHN_F_CLOSING) {
+ sndbuf_dump(b, "b", 0x02);
+ sndbuf_dump(bs, "bs", 0x02);
+ })
+
+ amt = sndbuf_getready(b);
+ if (sndbuf_getfree(bs) < amt) {
+ c->xruns++;
+ amt = sndbuf_getfree(bs);
+ }
+ ret = (amt > 0)? sndbuf_feed(b, bs, c, c->feeder, amt) : 0;
+
+ amt = sndbuf_getready(b);
+ if (amt > 0)
+ chn_rddump(c, amt);
+
+ chn_wakeup(c);
+
+ return ret;
+}
+
+void
+chn_rdupdate(struct pcm_channel *c)
+{
+ int ret;
+
+ CHN_LOCKASSERT(c);
+ KASSERT(c->direction == PCMDIR_REC, ("chn_rdupdate on bad channel"));
+
+ if ((c->flags & CHN_F_MAPPED) || !(c->flags & CHN_F_TRIGGERED))
+ return;
+ chn_trigger(c, PCMTRIG_EMLDMARD);
+ chn_dmaupdate(c);
+ ret = chn_rdfeed(c);
+ if (ret)
+ printf("chn_rdfeed: %d\n", ret);
+
+}
+
+/* read interrupt routine. Must be called with interrupts blocked. */
+static void
+chn_rdintr(struct pcm_channel *c)
+{
+ int ret;
+
+ CHN_LOCKASSERT(c);
+ /* tell the driver to update the primary buffer if non-dma */
+ chn_trigger(c, PCMTRIG_EMLDMARD);
+ /* update pointers in primary buffer */
+ chn_dmaupdate(c);
+ /* ...and feed from primary to secondary */
+ ret = chn_rdfeed(c);
+}
+
+/*
+ * user read routine - trigger if necessary, uiomove data from secondary buffer
+ * if blocking, sleep, rinse and repeat.
+ *
+ * called externally, so must handle locking
+ */
+
+int
+chn_read(struct pcm_channel *c, struct uio *buf)
+{
+ int ret, timeout, sz, count;
+ struct snd_dbuf *bs = c->bufsoft;
+
+ CHN_LOCKASSERT(c);
+ if (!(c->flags & CHN_F_TRIGGERED))
+ chn_start(c, 0);
+
+ ret = 0;
+ count = hz;
+ while (!ret && (buf->uio_resid > 0) && (count > 0)) {
+ sz = MIN(buf->uio_resid, sndbuf_getready(bs));
+
+ if (sz > 0) {
+ ret = sndbuf_uiomove(bs, buf, sz);
+ } else {
+ if (c->flags & CHN_F_NBIO) {
+ ret = EWOULDBLOCK;
+ } else {
+ timeout = (hz * sndbuf_getblksz(bs)) / (sndbuf_getspd(bs) * sndbuf_getbps(bs));
+ if (timeout < 1)
+ timeout = 1;
+ ret = chn_sleep(c, "pcmrd", timeout);
+ if (ret == EWOULDBLOCK) {
+ count -= timeout;
+ ret = 0;
+ } else {
+ count = hz;
+ }
+
+ }
+ }
+ }
+
+ if (count <= 0) {
+ c->flags |= CHN_F_DEAD;
+ printf("%s: record interrupt timeout, channel dead\n", c->name);
+ }
+
+ return ret;
+}
+
+void
+chn_intr(struct pcm_channel *c)
+{
+ CHN_LOCK(c);
+ c->interrupts++;
+ if (c->direction == PCMDIR_PLAY)
+ chn_wrintr(c);
+ else
+ chn_rdintr(c);
+ CHN_UNLOCK(c);
+}
+
+u_int32_t
+chn_start(struct pcm_channel *c, int force)
+{
+ u_int32_t i, j;
+ struct snd_dbuf *b = c->bufhard;
+ struct snd_dbuf *bs = c->bufsoft;
+
+ CHN_LOCKASSERT(c);
+ /* if we're running, or if we're prevented from triggering, bail */
+ if ((c->flags & CHN_F_TRIGGERED) || ((c->flags & CHN_F_NOTRIGGER) && !force))
+ return EINVAL;
+
+ i = (c->direction == PCMDIR_PLAY)? sndbuf_getready(bs) : sndbuf_getfree(bs);
+ j = (c->direction == PCMDIR_PLAY)? sndbuf_getfree(b) : sndbuf_getready(b);
+ if (force || (i >= j)) {
+ c->flags |= CHN_F_TRIGGERED;
+ /*
+ * if we're starting because a vchan started, don't feed any data
+ * or it becomes impossible to start vchans synchronised with the
+ * first one. the hardbuf should be empty so we top it up with
+ * silence to give it something to chew. the real data will be
+ * fed at the first irq.
+ */
+ if (c->direction == PCMDIR_PLAY) {
+ if (SLIST_EMPTY(&c->children))
+ chn_wrfeed(c);
+ else
+ sndbuf_fillsilence(b);
+ }
+ sndbuf_setrun(b, 1);
+ c->xruns = 0;
+ chn_trigger(c, PCMTRIG_START);
+ return 0;
+ }
+
+ return 0;
+}
+
+void
+chn_resetbuf(struct pcm_channel *c)
+{
+ struct snd_dbuf *b = c->bufhard;
+ struct snd_dbuf *bs = c->bufsoft;
+
+ c->blocks = 0;
+ sndbuf_reset(b);
+ sndbuf_reset(bs);
+}
+
+/*
+ * chn_sync waits until the space in the given channel goes above
+ * a threshold. The threshold is checked against fl or rl respectively.
+ * Assume that the condition can become true, do not check here...
+ */
+int
+chn_sync(struct pcm_channel *c, int threshold)
+{
+ u_long rdy;
+ int ret;
+ struct snd_dbuf *bs = c->bufsoft;
+
+ CHN_LOCKASSERT(c);
+ for (;;) {
+ rdy = (c->direction == PCMDIR_PLAY)? sndbuf_getfree(bs) : sndbuf_getready(bs);
+ if (rdy <= threshold) {
+ ret = chn_sleep(c, "pcmsyn", 1);
+ if (ret == ERESTART || ret == EINTR) {
+ DEB(printf("chn_sync: tsleep returns %d\n", ret));
+ return -1;
+ }
+ } else
+ break;
+ }
+ return 0;
+}
+
+/* called externally, handle locking */
+int
+chn_poll(struct pcm_channel *c, int ev, struct thread *td)
+{
+ struct snd_dbuf *bs = c->bufsoft;
+ int ret;
+
+ CHN_LOCKASSERT(c);
+ if (!(c->flags & CHN_F_MAPPED) && !(c->flags & CHN_F_TRIGGERED))
+ chn_start(c, 1);
+ ret = 0;
+ if (chn_polltrigger(c) && chn_pollreset(c))
+ ret = ev;
+ else
+ selrecord(td, sndbuf_getsel(bs));
+ return ret;
+}
+
+/*
+ * chn_abort terminates a running dma transfer. it may sleep up to 200ms.
+ * it returns the number of bytes that have not been transferred.
+ *
+ * called from: dsp_close, dsp_ioctl, with channel locked
+ */
+int
+chn_abort(struct pcm_channel *c)
+{
+ int missing = 0;
+ struct snd_dbuf *b = c->bufhard;
+ struct snd_dbuf *bs = c->bufsoft;
+
+ CHN_LOCKASSERT(c);
+ if (!(c->flags & CHN_F_TRIGGERED))
+ return 0;
+ c->flags |= CHN_F_ABORTING;
+
+ c->flags &= ~CHN_F_TRIGGERED;
+ /* kill the channel */
+ chn_trigger(c, PCMTRIG_ABORT);
+ sndbuf_setrun(b, 0);
+ if (!(c->flags & CHN_F_VIRTUAL))
+ chn_dmaupdate(c);
+ missing = sndbuf_getready(bs) + sndbuf_getready(b);
+
+ c->flags &= ~CHN_F_ABORTING;
+ return missing;
+}
+
+/*
+ * this routine tries to flush the dma transfer. It is called
+ * on a close. We immediately abort any read DMA
+ * operation, and then wait for the play buffer to drain.
+ *
+ * called from: dsp_close
+ */
+
+int
+chn_flush(struct pcm_channel *c)
+{
+ int ret, count, resid, resid_p;
+ struct snd_dbuf *b = c->bufhard;
+ struct snd_dbuf *bs = c->bufsoft;
+
+ CHN_LOCKASSERT(c);
+ KASSERT(c->direction == PCMDIR_PLAY, ("chn_wrupdate on bad channel"));
+ DEB(printf("chn_flush c->flags 0x%08x\n", c->flags));
+ if (!(c->flags & CHN_F_TRIGGERED))
+ return 0;
+
+ c->flags |= CHN_F_CLOSING;
+ resid = sndbuf_getready(bs) + sndbuf_getready(b);
+ resid_p = resid;
+ count = 10;
+ ret = 0;
+ while ((count > 0) && (resid > sndbuf_getsize(b)) && (ret == 0)) {
+ /* still pending output data. */
+ ret = chn_sleep(c, "pcmflu", hz / 10);
+ if (ret == EWOULDBLOCK)
+ ret = 0;
+ if (ret == 0) {
+ resid = sndbuf_getready(bs) + sndbuf_getready(b);
+ if (resid >= resid_p)
+ count--;
+ resid_p = resid;
+ }
+ }
+ if (count == 0)
+ DEB(printf("chn_flush: timeout\n"));
+
+ c->flags &= ~CHN_F_TRIGGERED;
+ /* kill the channel */
+ chn_trigger(c, PCMTRIG_ABORT);
+ sndbuf_setrun(b, 0);
+
+ c->flags &= ~CHN_F_CLOSING;
+ return 0;
+}
+
+int
+fmtvalid(u_int32_t fmt, u_int32_t *fmtlist)
+{
+ int i;
+
+ for (i = 0; fmtlist[i]; i++)
+ if (fmt == fmtlist[i])
+ return 1;
+ return 0;
+}
+
+int
+chn_reset(struct pcm_channel *c, u_int32_t fmt)
+{
+ int hwspd, r;
+
+ CHN_LOCKASSERT(c);
+ c->flags &= CHN_F_RESET;
+ c->interrupts = 0;
+ c->xruns = 0;
+
+ r = CHANNEL_RESET(c->methods, c->devinfo);
+ if (fmt != 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;
+
+ if (r == 0)
+ r = chn_setformat(c, fmt);
+ if (r == 0)
+ r = chn_setspeed(c, hwspd);
+ if (r == 0)
+ r = chn_setvolume(c, 100, 100);
+ }
+ if (r == 0)
+ r = chn_setblocksize(c, 0, 0);
+ if (r == 0) {
+ chn_resetbuf(c);
+ r = CHANNEL_RESETDONE(c->methods, c->devinfo);
+ }
+ return r;
+}
+
+int
+chn_init(struct pcm_channel *c, void *devinfo, int dir)
+{
+ struct feeder_class *fc;
+ struct snd_dbuf *b, *bs;
+ int ret;
+
+ chn_lockinit(c);
+
+ b = NULL;
+ bs = NULL;
+ c->devinfo = NULL;
+ c->feeder = NULL;
+
+ ret = EINVAL;
+ fc = feeder_getclass(NULL);
+ if (fc == NULL)
+ goto out;
+ if (chn_addfeeder(c, fc, NULL))
+ goto out;
+
+ ret = ENOMEM;
+ b = sndbuf_create(c->dev, c->name, "primary");
+ if (b == NULL)
+ goto out;
+ bs = sndbuf_create(c->dev, c->name, "secondary");
+ if (bs == NULL)
+ goto out;
+ sndbuf_setup(bs, NULL, 0);
+ c->bufhard = b;
+ c->bufsoft = bs;
+ c->flags = 0;
+ c->feederflags = 0;
+
+ ret = ENODEV;
+ c->devinfo = CHANNEL_INIT(c->methods, devinfo, b, c, dir);
+ if (c->devinfo == NULL)
+ goto out;
+
+ ret = ENOMEM;
+ if ((sndbuf_getsize(b) == 0) && ((c->flags & CHN_F_VIRTUAL) == 0))
+ goto out;
+
+ ret = chn_setdir(c, dir);
+ if (ret)
+ goto out;
+
+ ret = sndbuf_setfmt(b, AFMT_U8);
+ if (ret)
+ goto out;
+
+ ret = sndbuf_setfmt(bs, AFMT_U8);
+ if (ret)
+ goto out;
+
+
+out:
+ if (ret) {
+ if (c->devinfo) {
+ if (CHANNEL_FREE(c->methods, c->devinfo))
+ sndbuf_free(b);
+ }
+ if (bs)
+ sndbuf_destroy(bs);
+ if (b)
+ sndbuf_destroy(b);
+ c->flags |= CHN_F_DEAD;
+ chn_lockdestroy(c);
+
+ return ret;
+ }
+
+ return 0;
+}
+
+int
+chn_kill(struct pcm_channel *c)
+{
+ struct snd_dbuf *b = c->bufhard;
+ struct snd_dbuf *bs = c->bufsoft;
+
+ if (c->flags & CHN_F_TRIGGERED)
+ chn_trigger(c, PCMTRIG_ABORT);
+ while (chn_removefeeder(c) == 0);
+ if (CHANNEL_FREE(c->methods, c->devinfo))
+ sndbuf_free(b);
+ c->flags |= CHN_F_DEAD;
+ sndbuf_destroy(bs);
+ sndbuf_destroy(b);
+ chn_lockdestroy(c);
+ return 0;
+}
+
+int
+chn_setdir(struct pcm_channel *c, int dir)
+{
+ struct snd_dbuf *b = c->bufhard;
+ int r;
+
+ CHN_LOCKASSERT(c);
+ c->direction = dir;
+ r = CHANNEL_SETDIR(c->methods, c->devinfo, c->direction);
+ if (!r && ISA_DMA(b))
+ sndbuf_isadmasetdir(b, c->direction);
+ return r;
+}
+
+int
+chn_setvolume(struct pcm_channel *c, int left, int right)
+{
+ CHN_LOCKASSERT(c);
+ /* could add a feeder for volume changing if channel returns -1 */
+ c->volume = (left << 8) | right;
+ return 0;
+}
+
+static int
+chn_tryspeed(struct pcm_channel *c, int speed)
+{
+ struct pcm_feeder *f;
+ struct snd_dbuf *b = c->bufhard;
+ struct snd_dbuf *bs = c->bufsoft;
+ struct snd_dbuf *x;
+ int r, delta;
+
+ CHN_LOCKASSERT(c);
+ DEB(printf("setspeed, channel %s\n", c->name));
+ DEB(printf("want speed %d, ", speed));
+ if (speed <= 0)
+ return EINVAL;
+ if (CANCHANGE(c)) {
+ r = 0;
+ c->speed = speed;
+ sndbuf_setspd(bs, speed);
+ RANGE(speed, chn_getcaps(c)->minspeed, chn_getcaps(c)->maxspeed);
+ DEB(printf("try speed %d, ", speed));
+ sndbuf_setspd(b, CHANNEL_SETSPEED(c->methods, c->devinfo, speed));
+ DEB(printf("got speed %d\n", sndbuf_getspd(b)));
+
+ delta = sndbuf_getspd(b) - sndbuf_getspd(bs);
+ if (delta < 0)
+ delta = -delta;
+
+ c->feederflags &= ~(1 << FEEDER_RATE);
+ if (delta > 500)
+ c->feederflags |= 1 << FEEDER_RATE;
+ else
+ sndbuf_setspd(bs, sndbuf_getspd(b));
+
+ r = chn_buildfeeder(c);
+ DEB(printf("r = %d\n", r));
+ if (r)
+ goto out;
+
+ r = chn_setblocksize(c, 0, 0);
+ if (r)
+ goto out;
+
+ if (!(c->feederflags & (1 << FEEDER_RATE)))
+ goto out;
+
+ r = EINVAL;
+ f = chn_findfeeder(c, FEEDER_RATE);
+ DEB(printf("feedrate = %p\n", f));
+ if (f == NULL)
+ goto out;
+
+ x = (c->direction == PCMDIR_REC)? b : bs;
+ r = FEEDER_SET(f, FEEDRATE_SRC, sndbuf_getspd(x));
+ DEB(printf("feeder_set(FEEDRATE_SRC, %d) = %d\n", sndbuf_getspd(x), r));
+ if (r)
+ goto out;
+
+ x = (c->direction == PCMDIR_REC)? bs : b;
+ r = FEEDER_SET(f, FEEDRATE_DST, sndbuf_getspd(x));
+ DEB(printf("feeder_set(FEEDRATE_DST, %d) = %d\n", sndbuf_getspd(x), r));
+out:
+ DEB(printf("setspeed done, r = %d\n", r));
+ return r;
+ } else
+ return EINVAL;
+}
+
+int
+chn_setspeed(struct pcm_channel *c, int speed)
+{
+ int r, oldspeed = c->speed;
+
+ r = chn_tryspeed(c, speed);
+ if (r) {
+ DEB(printf("Failed to set speed %d falling back to %d\n", speed, oldspeed));
+ chn_tryspeed(c, oldspeed);
+ }
+ return r;
+}
+
+static int
+chn_tryformat(struct pcm_channel *c, u_int32_t fmt)
+{
+ struct snd_dbuf *b = c->bufhard;
+ struct snd_dbuf *bs = c->bufsoft;
+ int r;
+
+ CHN_LOCKASSERT(c);
+ if (CANCHANGE(c)) {
+ DEB(printf("want format %d\n", fmt));
+ c->format = fmt;
+ r = chn_buildfeeder(c);
+ if (r == 0) {
+ sndbuf_setfmt(bs, c->format);
+ chn_resetbuf(c);
+ r = CHANNEL_SETFORMAT(c->methods, c->devinfo, sndbuf_getfmt(b));
+ if (r == 0)
+ r = chn_tryspeed(c, c->speed);
+ }
+ return r;
+ } else
+ return EINVAL;
+}
+
+int
+chn_setformat(struct pcm_channel *c, u_int32_t fmt)
+{
+ u_int32_t oldfmt = c->format;
+ int r;
+
+ r = chn_tryformat(c, fmt);
+ if (r) {
+ DEB(printf("Format change %d failed, reverting to %d\n", fmt, oldfmt));
+ chn_tryformat(c, oldfmt);
+ }
+ return r;
+}
+
+int
+chn_setblocksize(struct pcm_channel *c, int blkcnt, int blksz)
+{
+ struct snd_dbuf *b = c->bufhard;
+ struct snd_dbuf *bs = c->bufsoft;
+ int bufsz, irqhz, tmp, ret;
+
+ CHN_LOCKASSERT(c);
+ if (!CANCHANGE(c) || (c->flags & CHN_F_MAPPED))
+ return EINVAL;
+
+ ret = 0;
+ DEB(printf("%s(%d, %d)\n", __func__, blkcnt, blksz));
+ if (blksz == 0 || blksz == -1) {
+ if (blksz == -1)
+ c->flags &= ~CHN_F_HAS_SIZE;
+ if (!(c->flags & CHN_F_HAS_SIZE)) {
+ blksz = (sndbuf_getbps(bs) * sndbuf_getspd(bs)) / chn_targetirqrate;
+ tmp = 32;
+ while (tmp <= blksz)
+ tmp <<= 1;
+ tmp >>= 1;
+ blksz = tmp;
+ blkcnt = CHN_2NDBUFMAXSIZE / blksz;
+
+ RANGE(blksz, 16, CHN_2NDBUFMAXSIZE / 2);
+ RANGE(blkcnt, 2, CHN_2NDBUFMAXSIZE / blksz);
+ DEB(printf("%s: defaulting to (%d, %d)\n", __func__, blkcnt, blksz));
+ } else {
+ blkcnt = sndbuf_getblkcnt(bs);
+ blksz = sndbuf_getblksz(bs);
+ DEB(printf("%s: updating (%d, %d)\n", __func__, blkcnt, blksz));
+ }
+ } else {
+ ret = EINVAL;
+ if ((blksz < 16) || (blkcnt < 2) || (blkcnt * blksz > CHN_2NDBUFMAXSIZE))
+ goto out;
+ ret = 0;
+ c->flags |= CHN_F_HAS_SIZE;
+ }
+
+ bufsz = blkcnt * blksz;
+
+ ret = sndbuf_remalloc(bs, blkcnt, blksz);
+ if (ret)
+ goto out;
+
+ /* adjust for different hw format/speed */
+ irqhz = (sndbuf_getbps(bs) * sndbuf_getspd(bs)) / sndbuf_getblksz(bs);
+ DEB(printf("%s: soft bps %d, spd %d, irqhz == %d\n", __func__, sndbuf_getbps(bs), sndbuf_getspd(bs), irqhz));
+ RANGE(irqhz, 16, 512);
+
+ sndbuf_setblksz(b, (sndbuf_getbps(b) * sndbuf_getspd(b)) / irqhz);
+
+ /* round down to 2^x */
+ blksz = 32;
+ while (blksz <= sndbuf_getblksz(b))
+ blksz <<= 1;
+ blksz >>= 1;
+
+ /* round down to fit hw buffer size */
+ RANGE(blksz, 16, sndbuf_getmaxsize(b) / 2);
+ DEB(printf("%s: hard blksz requested %d (maxsize %d), ", __func__, blksz, sndbuf_getmaxsize(b)));
+
+ sndbuf_setblksz(b, CHANNEL_SETBLOCKSIZE(c->methods, c->devinfo, blksz));
+
+ irqhz = (sndbuf_getbps(b) * sndbuf_getspd(b)) / sndbuf_getblksz(b);
+ DEB(printf("got %d, irqhz == %d\n", sndbuf_getblksz(b), irqhz));
+
+ chn_resetbuf(c);
+out:
+ return ret;
+}
+
+int
+chn_trigger(struct pcm_channel *c, int go)
+{
+ struct snd_dbuf *b = c->bufhard;
+ int ret;
+
+ CHN_LOCKASSERT(c);
+ if (ISA_DMA(b) && (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD))
+ sndbuf_isadmabounce(b);
+ ret = CHANNEL_TRIGGER(c->methods, c->devinfo, go);
+
+ return ret;
+}
+
+int
+chn_getptr(struct pcm_channel *c)
+{
+ int hwptr;
+ int a = (1 << c->align) - 1;
+
+ CHN_LOCKASSERT(c);
+ hwptr = (c->flags & CHN_F_TRIGGERED)? CHANNEL_GETPTR(c->methods, c->devinfo) : 0;
+ /* don't allow unaligned values in the hwa ptr */
+#if 1
+ hwptr &= ~a ; /* Apply channel align mask */
+#endif
+ hwptr &= DMA_ALIGN_MASK; /* Apply DMA align mask */
+ return hwptr;
+}
+
+struct pcmchan_caps *
+chn_getcaps(struct pcm_channel *c)
+{
+ CHN_LOCKASSERT(c);
+ return CHANNEL_GETCAPS(c->methods, c->devinfo);
+}
+
+u_int32_t
+chn_getformats(struct pcm_channel *c)
+{
+ u_int32_t *fmtlist, fmts;
+ int i;
+
+ fmtlist = chn_getcaps(c)->fmtlist;
+ fmts = 0;
+ for (i = 0; fmtlist[i]; i++)
+ fmts |= fmtlist[i];
+
+ /* report software-supported formats */
+ if (report_soft_formats)
+ fmts |= AFMT_MU_LAW|AFMT_A_LAW|AFMT_U16_LE|AFMT_U16_BE|
+ AFMT_S16_LE|AFMT_S16_BE|AFMT_U8|AFMT_S8;
+
+ return fmts;
+}
+
+static int
+chn_buildfeeder(struct pcm_channel *c)
+{
+ struct feeder_class *fc;
+ struct pcm_feederdesc desc;
+ u_int32_t tmp[2], type, flags, hwfmt;
+ int err;
+
+ CHN_LOCKASSERT(c);
+ while (chn_removefeeder(c) == 0);
+ KASSERT((c->feeder == NULL), ("feeder chain not empty"));
+
+ c->align = sndbuf_getalign(c->bufsoft);
+
+ if (SLIST_EMPTY(&c->children)) {
+ fc = feeder_getclass(NULL);
+ KASSERT(fc != NULL, ("can't find root feeder"));
+
+ err = chn_addfeeder(c, fc, NULL);
+ if (err) {
+ DEB(printf("can't add root feeder, err %d\n", err));
+
+ return err;
+ }
+ c->feeder->desc->out = c->format;
+ } else {
+ desc.type = FEEDER_MIXER;
+ desc.in = 0;
+ desc.out = c->format;
+ desc.flags = 0;
+ fc = feeder_getclass(&desc);
+ if (fc == NULL) {
+ DEB(printf("can't find vchan feeder\n"));
+
+ return EOPNOTSUPP;
+ }
+
+ err = chn_addfeeder(c, fc, &desc);
+ if (err) {
+ DEB(printf("can't add vchan feeder, err %d\n", err));
+
+ return err;
+ }
+ }
+ flags = c->feederflags;
+
+ DEB(printf("not mapped, feederflags %x\n", flags));
+
+ for (type = FEEDER_RATE; type <= FEEDER_LAST; type++) {
+ if (flags & (1 << type)) {
+ desc.type = type;
+ desc.in = 0;
+ desc.out = 0;
+ desc.flags = 0;
+ DEB(printf("find feeder type %d, ", type));
+ fc = feeder_getclass(&desc);
+ DEB(printf("got %p\n", fc));
+ if (fc == NULL) {
+ DEB(printf("can't find required feeder type %d\n", type));
+
+ return EOPNOTSUPP;
+ }
+
+ if (c->feeder->desc->out != fc->desc->in) {
+ DEB(printf("build fmtchain from %x to %x: ", c->feeder->desc->out, fc->desc->in));
+ tmp[0] = fc->desc->in;
+ tmp[1] = 0;
+ if (chn_fmtchain(c, tmp) == 0) {
+ DEB(printf("failed\n"));
+
+ return ENODEV;
+ }
+ DEB(printf("ok\n"));
+ }
+
+ err = chn_addfeeder(c, fc, fc->desc);
+ if (err) {
+ DEB(printf("can't add feeder %p, output %x, err %d\n", fc, fc->desc->out, err));
+
+ return err;
+ }
+ DEB(printf("added feeder %p, output %x\n", fc, c->feeder->desc->out));
+ }
+ }
+
+ if (fmtvalid(c->feeder->desc->out, chn_getcaps(c)->fmtlist)) {
+ hwfmt = c->feeder->desc->out;
+ } 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("[%8x] ", *x);
+ x++;
+ }
+#endif
+ hwfmt = chn_fmtchain(c, chn_getcaps(c)->fmtlist);
+ }
+ }
+
+ if (hwfmt == 0)
+ return ENODEV;
+
+ sndbuf_setfmt(c->bufhard, hwfmt);
+
+ return 0;
+}
+
+int
+chn_notify(struct pcm_channel *c, u_int32_t flags)
+{
+ struct pcmchan_children *pce;
+ struct pcm_channel *child;
+ int run;
+
+ if (SLIST_EMPTY(&c->children))
+ return ENODEV;
+
+ run = (c->flags & CHN_F_TRIGGERED)? 1 : 0;
+ /*
+ * if the hwchan is running, we can't change its rate, format or
+ * blocksize
+ */
+ if (run)
+ flags &= CHN_N_VOLUME | CHN_N_TRIGGER;
+
+ if (flags & CHN_N_RATE) {
+ /*
+ * we could do something here, like scan children and decide on
+ * the most appropriate rate to mix at, but we don't for now
+ */
+ }
+ if (flags & CHN_N_FORMAT) {
+ /*
+ * we could do something here, like scan children and decide on
+ * the most appropriate mixer feeder to use, but we don't for now
+ */
+ }
+ if (flags & CHN_N_VOLUME) {
+ /*
+ * we could do something here but we don't for now
+ */
+ }
+ if (flags & CHN_N_BLOCKSIZE) {
+ int blksz;
+ /*
+ * scan the children, find the lowest blocksize and use that
+ * for the hard blocksize
+ */
+ blksz = sndbuf_getmaxsize(c->bufhard) / 2;
+ SLIST_FOREACH(pce, &c->children, link) {
+ child = pce->channel;
+ if (sndbuf_getblksz(child->bufhard) < blksz)
+ blksz = sndbuf_getblksz(child->bufhard);
+ }
+ chn_setblocksize(c, 2, blksz);
+ }
+ if (flags & CHN_N_TRIGGER) {
+ int nrun;
+ /*
+ * scan the children, and figure out if any are running
+ * if so, we need to be running, otherwise we need to be stopped
+ * if we aren't in our target sstate, move to it
+ */
+ nrun = 0;
+ SLIST_FOREACH(pce, &c->children, link) {
+ child = pce->channel;
+ if (child->flags & CHN_F_TRIGGERED)
+ nrun = 1;
+ }
+ if (nrun && !run)
+ chn_start(c, 1);
+ if (!nrun && run)
+ chn_abort(c);
+ }
+ return 0;
+}
diff --git a/sys/dev/sound/pcm/channel.h b/sys/dev/sound/pcm/channel.h
new file mode 100644
index 0000000..4db60cb
--- /dev/null
+++ b/sys/dev/sound/pcm/channel.h
@@ -0,0 +1,159 @@
+/*
+ * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+struct pcmchan_children {
+ SLIST_ENTRY(pcmchan_children) link;
+ struct pcm_channel *channel;
+};
+
+struct pcmchan_caps {
+ u_int32_t minspeed, maxspeed;
+ u_int32_t *fmtlist;
+ u_int32_t caps;
+};
+
+#define CHN_NAMELEN 32
+struct pcm_channel {
+ kobj_t methods;
+
+ int num;
+ pid_t pid;
+ int refcount;
+ struct pcm_feeder *feeder;
+ u_int32_t align;
+
+ int volume;
+ u_int32_t speed;
+ u_int32_t format;
+ u_int32_t flags;
+ u_int32_t feederflags;
+ u_int32_t blocks;
+
+ int direction;
+ unsigned int interrupts, xruns;
+ struct snd_dbuf *bufhard, *bufsoft;
+ struct snddev_info *parentsnddev;
+ struct pcm_channel *parentchannel;
+ void *devinfo;
+ device_t dev;
+ char name[CHN_NAMELEN];
+ struct mtx *lock;
+ SLIST_HEAD(, pcmchan_children) children;
+};
+
+#include "channel_if.h"
+
+int chn_reinit(struct pcm_channel *c);
+int chn_write(struct pcm_channel *c, struct uio *buf);
+int chn_read(struct pcm_channel *c, struct uio *buf);
+u_int32_t chn_start(struct pcm_channel *c, int force);
+int chn_sync(struct pcm_channel *c, int threshold);
+int chn_flush(struct pcm_channel *c);
+int chn_poll(struct pcm_channel *c, int ev, struct thread *td);
+
+int chn_init(struct pcm_channel *c, void *devinfo, int dir);
+int chn_kill(struct pcm_channel *c);
+int chn_setdir(struct pcm_channel *c, int dir);
+int chn_reset(struct pcm_channel *c, u_int32_t fmt);
+int chn_setvolume(struct pcm_channel *c, int left, int right);
+int chn_setspeed(struct pcm_channel *c, int speed);
+int chn_setformat(struct pcm_channel *c, u_int32_t fmt);
+int chn_setblocksize(struct pcm_channel *c, int blkcnt, int blksz);
+int chn_trigger(struct pcm_channel *c, int go);
+int chn_getptr(struct pcm_channel *c);
+struct pcmchan_caps *chn_getcaps(struct pcm_channel *c);
+u_int32_t chn_getformats(struct pcm_channel *c);
+
+void chn_resetbuf(struct pcm_channel *c);
+void chn_intr(struct pcm_channel *c);
+int chn_wrfeed(struct pcm_channel *c);
+int chn_rdfeed(struct pcm_channel *c);
+int chn_abort(struct pcm_channel *c);
+
+void chn_wrupdate(struct pcm_channel *c);
+void chn_rdupdate(struct pcm_channel *c);
+
+int chn_notify(struct pcm_channel *c, u_int32_t flags);
+
+#ifdef USING_MUTEX
+#define CHN_LOCK(c) mtx_lock((struct mtx *)((c)->lock))
+#define CHN_UNLOCK(c) mtx_unlock((struct mtx *)((c)->lock))
+#define CHN_LOCKASSERT(c)
+#else
+#define CHN_LOCK(c)
+#define CHN_UNLOCK(c)
+#define CHN_LOCKASSERT(c)
+#endif
+
+int fmtvalid(u_int32_t fmt, u_int32_t *fmtlist);
+
+#define PCMDIR_VIRTUAL 2
+#define PCMDIR_PLAY 1
+#define PCMDIR_REC -1
+
+#define PCMTRIG_START 1
+#define PCMTRIG_EMLDMAWR 2
+#define PCMTRIG_EMLDMARD 3
+#define PCMTRIG_STOP 0
+#define PCMTRIG_ABORT -1
+
+#define CHN_F_CLOSING 0x00000004 /* a pending close */
+#define CHN_F_ABORTING 0x00000008 /* a pending abort */
+#define CHN_F_RUNNING 0x00000010 /* dma is running */
+#define CHN_F_TRIGGERED 0x00000020
+#define CHN_F_NOTRIGGER 0x00000040
+
+#define CHN_F_BUSY 0x00001000 /* has been opened */
+#define CHN_F_HAS_SIZE 0x00002000 /* user set block size */
+#define CHN_F_NBIO 0x00004000 /* do non-blocking i/o */
+#define CHN_F_MAPPED 0x00010000 /* has been mmap()ed */
+#define CHN_F_DEAD 0x00020000
+#define CHN_F_BADSETTING 0x00040000
+
+#define CHN_F_VIRTUAL 0x10000000 /* not backed by hardware */
+
+#define CHN_F_RESET (CHN_F_BUSY | CHN_F_DEAD | CHN_F_VIRTUAL)
+
+#define CHN_N_RATE 0x00000001
+#define CHN_N_FORMAT 0x00000002
+#define CHN_N_VOLUME 0x00000004
+#define CHN_N_BLOCKSIZE 0x00000008
+#define CHN_N_TRIGGER 0x00000010
+
+/*
+ * This should be large enough to hold all pcm data between
+ * tsleeps in chn_{read,write} at the highest sample rate.
+ * (which is usually 48kHz * 16bit * stereo = 192000 bytes/sec)
+ */
+#define CHN_2NDBUFBLKSIZE (2 * 1024)
+/* The total number of blocks per secondary bufhard. */
+#define CHN_2NDBUFBLKNUM (32)
+/* The size of a whole secondary bufhard. */
+#define CHN_2NDBUFMAXSIZE (131072)
+
+#define CHANNEL_DECLARE(name) static DEFINE_CLASS(name, name ## _methods, sizeof(struct kobj))
diff --git a/sys/dev/sound/pcm/channel_if.m b/sys/dev/sound/pcm/channel_if.m
new file mode 100644
index 0000000..9d8e289
--- /dev/null
+++ b/sys/dev/sound/pcm/channel_if.m
@@ -0,0 +1,141 @@
+# KOBJ
+#
+# Copyright (c) 2000 Cameron Grant <cg@freebsd.org>
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# $FreeBSD$
+#
+
+#include <dev/sound/pcm/sound.h>
+
+INTERFACE channel;
+
+CODE {
+
+ static int
+ channel_nosetdir(kobj_t obj, void *data, int dir)
+ {
+ return 0;
+ }
+
+ static int
+ channel_noreset(kobj_t obj, void *data)
+ {
+ return 0;
+ }
+
+ static int
+ channel_noresetdone(kobj_t obj, void *data)
+ {
+ return 0;
+ }
+
+ static int
+ channel_nofree(kobj_t obj, void *data)
+ {
+ return 1;
+ }
+
+ static u_int32_t
+ channel_nogetptr(kobj_t obj, void *data)
+ {
+ return 0;
+ }
+
+ static int
+ channel_nonotify(kobj_t obj, void *data, u_int32_t changed)
+ {
+ return 0;
+ }
+
+};
+
+METHOD void* init {
+ kobj_t obj;
+ void *devinfo;
+ struct snd_dbuf *b;
+ struct pcm_channel *c;
+ int dir;
+};
+
+METHOD int free {
+ kobj_t obj;
+ void *data;
+} DEFAULT channel_nofree;
+
+METHOD int reset {
+ kobj_t obj;
+ void *data;
+} DEFAULT channel_noreset;
+
+METHOD int resetdone {
+ kobj_t obj;
+ void *data;
+} DEFAULT channel_noresetdone;
+
+METHOD int setdir {
+ kobj_t obj;
+ void *data;
+ int dir;
+} DEFAULT channel_nosetdir;
+
+METHOD u_int32_t setformat {
+ kobj_t obj;
+ void *data;
+ u_int32_t format;
+};
+
+METHOD u_int32_t setspeed {
+ kobj_t obj;
+ void *data;
+ u_int32_t speed;
+};
+
+METHOD u_int32_t setblocksize {
+ kobj_t obj;
+ void *data;
+ u_int32_t blocksize;
+};
+
+METHOD int trigger {
+ kobj_t obj;
+ void *data;
+ int go;
+};
+
+METHOD u_int32_t getptr {
+ kobj_t obj;
+ void *data;
+} DEFAULT channel_nogetptr;
+
+METHOD struct pcmchan_caps* getcaps {
+ kobj_t obj;
+ void *data;
+};
+
+METHOD int notify {
+ kobj_t obj;
+ void *data;
+ u_int32_t changed;
+} DEFAULT channel_nonotify;
diff --git a/sys/dev/sound/pcm/dsp.c b/sys/dev/sound/pcm/dsp.c
new file mode 100644
index 0000000..7baa442
--- /dev/null
+++ b/sys/dev/sound/pcm/dsp.c
@@ -0,0 +1,1147 @@
+/*
+ * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/queue.h>
+
+#include <dev/sound/pcm/sound.h>
+
+SND_DECLARE_FILE("$FreeBSD$");
+
+#define OLDPCM_IOCTL
+
+static d_open_t dsp_open;
+static d_close_t dsp_close;
+static d_read_t dsp_read;
+static d_write_t dsp_write;
+static d_ioctl_t dsp_ioctl;
+static d_poll_t dsp_poll;
+static d_mmap_t dsp_mmap;
+
+static struct cdevsw dsp_cdevsw = {
+ /* open */ dsp_open,
+ /* close */ dsp_close,
+ /* read */ dsp_read,
+ /* write */ dsp_write,
+ /* ioctl */ dsp_ioctl,
+ /* poll */ dsp_poll,
+ /* mmap */ dsp_mmap,
+ /* strategy */ nostrategy,
+ /* name */ "dsp",
+ /* maj */ SND_CDEV_MAJOR,
+ /* dump */ nodump,
+ /* psize */ nopsize,
+ /* flags */ 0,
+};
+
+#ifdef USING_DEVFS
+static eventhandler_tag dsp_ehtag;
+#endif
+
+static struct snddev_info *
+dsp_get_info(dev_t dev)
+{
+ struct snddev_info *d;
+ int unit;
+
+ unit = PCMUNIT(dev);
+ if (unit >= devclass_get_maxunit(pcm_devclass))
+ return NULL;
+ d = devclass_get_softc(pcm_devclass, unit);
+
+ return d;
+}
+
+static u_int32_t
+dsp_get_flags(dev_t dev)
+{
+ device_t bdev;
+ int unit;
+
+ unit = PCMUNIT(dev);
+ if (unit >= devclass_get_maxunit(pcm_devclass))
+ return 0xffffffff;
+ bdev = devclass_get_device(pcm_devclass, unit);
+
+ return pcm_getflags(bdev);
+}
+
+static void
+dsp_set_flags(dev_t dev, u_int32_t flags)
+{
+ device_t bdev;
+ int unit;
+
+ unit = PCMUNIT(dev);
+ if (unit >= devclass_get_maxunit(pcm_devclass))
+ return;
+ bdev = devclass_get_device(pcm_devclass, unit);
+
+ pcm_setflags(bdev, flags);
+}
+
+/*
+ * return the channels channels associated with an open device instance.
+ * set the priority if the device is simplex and one direction (only) is
+ * specified.
+ * lock channels specified.
+ */
+static int
+getchns(dev_t dev, struct pcm_channel **rdch, struct pcm_channel **wrch, u_int32_t prio)
+{
+ struct snddev_info *d;
+ u_int32_t flags;
+
+ flags = dsp_get_flags(dev);
+ d = dsp_get_info(dev);
+ pcm_lock(d);
+ pcm_inprog(d, 1);
+ KASSERT((flags & SD_F_PRIO_SET) != SD_F_PRIO_SET, \
+ ("getchns: read and write both prioritised"));
+
+ if ((flags & SD_F_PRIO_SET) == 0 && (prio != (SD_F_PRIO_RD | SD_F_PRIO_WR))) {
+ flags |= prio & (SD_F_PRIO_RD | SD_F_PRIO_WR);
+ dsp_set_flags(dev, flags);
+ }
+
+ *rdch = dev->si_drv1;
+ *wrch = dev->si_drv2;
+ if ((flags & SD_F_SIMPLEX) && (flags & SD_F_PRIO_SET)) {
+ if (prio) {
+ if (*rdch && flags & SD_F_PRIO_WR) {
+ dev->si_drv1 = NULL;
+ *rdch = pcm_getfakechan(d);
+ } else if (*wrch && flags & SD_F_PRIO_RD) {
+ dev->si_drv2 = NULL;
+ *wrch = pcm_getfakechan(d);
+ }
+ }
+
+ pcm_getfakechan(d)->flags |= CHN_F_BUSY;
+ }
+ pcm_unlock(d);
+
+ if (*rdch && *rdch != pcm_getfakechan(d) && (prio & SD_F_PRIO_RD))
+ CHN_LOCK(*rdch);
+ if (*wrch && *wrch != pcm_getfakechan(d) && (prio & SD_F_PRIO_WR))
+ CHN_LOCK(*wrch);
+
+ return 0;
+}
+
+/* unlock specified channels */
+static void
+relchns(dev_t dev, struct pcm_channel *rdch, struct pcm_channel *wrch, u_int32_t prio)
+{
+ struct snddev_info *d;
+
+ d = dsp_get_info(dev);
+ if (wrch && wrch != pcm_getfakechan(d) && (prio & SD_F_PRIO_WR))
+ CHN_UNLOCK(wrch);
+ if (rdch && rdch != pcm_getfakechan(d) && (prio & SD_F_PRIO_RD))
+ CHN_UNLOCK(rdch);
+ pcm_lock(d);
+ pcm_inprog(d, -1);
+ pcm_unlock(d);
+}
+
+static int
+dsp_open(dev_t i_dev, int flags, int mode, struct thread *td)
+{
+ struct pcm_channel *rdch, *wrch;
+ struct snddev_info *d;
+ intrmask_t s;
+ u_int32_t fmt;
+ int devtype;
+
+ s = spltty();
+ d = dsp_get_info(i_dev);
+ devtype = PCMDEV(i_dev);
+
+ /* decide default format */
+ switch (devtype) {
+ case SND_DEV_DSP16:
+ fmt = AFMT_S16_LE;
+ break;
+
+ case SND_DEV_DSP:
+ fmt = AFMT_U8;
+ break;
+
+ case SND_DEV_AUDIO:
+ fmt = AFMT_MU_LAW;
+ break;
+
+ case SND_DEV_NORESET:
+ fmt = 0;
+ break;
+
+ case SND_DEV_DSPREC:
+ fmt = AFMT_U8;
+ if (mode & FWRITE) {
+ splx(s);
+ return EINVAL;
+ }
+ break;
+
+ default:
+ panic("impossible devtype %d", devtype);
+ }
+
+ /* lock snddev so nobody else can monkey with it */
+ pcm_lock(d);
+
+ rdch = i_dev->si_drv1;
+ wrch = i_dev->si_drv2;
+
+ if ((dsp_get_flags(i_dev) & SD_F_SIMPLEX) && (rdch || wrch)) {
+ /* simplex device, already open, exit */
+ pcm_unlock(d);
+ splx(s);
+ return EBUSY;
+ }
+
+ if (((flags & FREAD) && rdch) || ((flags & FWRITE) && wrch)) {
+ /* device already open in one or both directions */
+ pcm_unlock(d);
+ splx(s);
+ return EBUSY;
+ }
+
+ /* if we get here, the open request is valid */
+ if (flags & FREAD) {
+ /* open for read */
+ 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;
+ }
+ /* got a channel, already locked for us */
+ }
+
+ if (flags & FWRITE) {
+ /* open for write */
+ wrch = pcm_chnalloc(d, PCMDIR_PLAY, td->td_proc->p_pid, -1);
+ if (!wrch) {
+ /* no channel available */
+ if (flags & FREAD) {
+ /* just opened a read channel, release it */
+ pcm_chnrelease(rdch);
+ }
+ /* exit */
+ pcm_unlock(d);
+ splx(s);
+ return EBUSY;
+ }
+ /* got a channel, already locked for us */
+ }
+
+ i_dev->si_drv1 = rdch;
+ i_dev->si_drv2 = wrch;
+ pcm_unlock(d);
+ /* finished with snddev, new channels still locked */
+
+ /* bump refcounts, reset and unlock any channels that we just opened */
+ if (flags & FREAD) {
+ if (chn_reset(rdch, fmt)) {
+ pcm_lock(d);
+ pcm_chnrelease(rdch);
+ if (wrch && (flags & FWRITE))
+ pcm_chnrelease(wrch);
+ pcm_unlock(d);
+ splx(s);
+ return ENODEV;
+ }
+ if (flags & O_NONBLOCK)
+ rdch->flags |= CHN_F_NBIO;
+ pcm_chnref(rdch, 1);
+ CHN_UNLOCK(rdch);
+ }
+ if (flags & FWRITE) {
+ if (chn_reset(wrch, fmt)) {
+ pcm_lock(d);
+ pcm_chnrelease(wrch);
+ if (flags & FREAD) {
+ CHN_LOCK(rdch);
+ pcm_chnref(rdch, -1);
+ pcm_chnrelease(rdch);
+ CHN_UNLOCK(rdch);
+ }
+ pcm_unlock(d);
+ splx(s);
+ return ENODEV;
+ }
+ if (flags & O_NONBLOCK)
+ wrch->flags |= CHN_F_NBIO;
+ pcm_chnref(wrch, 1);
+ CHN_UNLOCK(wrch);
+ }
+ splx(s);
+ return 0;
+}
+
+static int
+dsp_close(dev_t i_dev, int flags, int mode, struct thread *td)
+{
+ struct pcm_channel *rdch, *wrch;
+ struct snddev_info *d;
+ intrmask_t s;
+ int exit;
+
+ s = spltty();
+ d = dsp_get_info(i_dev);
+ pcm_lock(d);
+ rdch = i_dev->si_drv1;
+ wrch = i_dev->si_drv2;
+
+ exit = 0;
+
+ /* decrement refcount for each channel, exit if nonzero */
+ if (rdch) {
+ CHN_LOCK(rdch);
+ if (pcm_chnref(rdch, -1) > 0) {
+ CHN_UNLOCK(rdch);
+ exit = 1;
+ }
+ }
+ if (wrch) {
+ CHN_LOCK(wrch);
+ if (pcm_chnref(wrch, -1) > 0) {
+ CHN_UNLOCK(wrch);
+ exit = 1;
+ }
+ }
+ if (exit) {
+ pcm_unlock(d);
+ splx(s);
+ return 0;
+ }
+
+ /* both refcounts are zero, abort and release */
+
+ if (pcm_getfakechan(d))
+ pcm_getfakechan(d)->flags = 0;
+
+ i_dev->si_drv1 = NULL;
+ i_dev->si_drv2 = NULL;
+
+ dsp_set_flags(i_dev, dsp_get_flags(i_dev) & ~SD_F_TRANSIENT);
+ pcm_unlock(d);
+
+ if (rdch) {
+ chn_abort(rdch); /* won't sleep */
+ rdch->flags &= ~(CHN_F_RUNNING | CHN_F_MAPPED | CHN_F_DEAD);
+ chn_reset(rdch, 0);
+ pcm_chnrelease(rdch);
+ }
+ if (wrch) {
+ chn_flush(wrch); /* may sleep */
+ wrch->flags &= ~(CHN_F_RUNNING | CHN_F_MAPPED | CHN_F_DEAD);
+ chn_reset(wrch, 0);
+ pcm_chnrelease(wrch);
+ }
+
+ splx(s);
+ return 0;
+}
+
+static int
+dsp_read(dev_t i_dev, struct uio *buf, int flag)
+{
+ struct pcm_channel *rdch, *wrch;
+ intrmask_t s;
+ int ret;
+
+ s = spltty();
+ getchns(i_dev, &rdch, &wrch, SD_F_PRIO_RD);
+
+ KASSERT(rdch, ("dsp_read: nonexistant channel"));
+ KASSERT(rdch->flags & CHN_F_BUSY, ("dsp_read: nonbusy channel"));
+
+ if (rdch->flags & (CHN_F_MAPPED | CHN_F_DEAD)) {
+ relchns(i_dev, rdch, wrch, SD_F_PRIO_RD);
+ splx(s);
+ return EINVAL;
+ }
+ if (!(rdch->flags & CHN_F_RUNNING))
+ rdch->flags |= CHN_F_RUNNING;
+ ret = chn_read(rdch, buf);
+ relchns(i_dev, rdch, wrch, SD_F_PRIO_RD);
+
+ splx(s);
+ return ret;
+}
+
+static int
+dsp_write(dev_t i_dev, struct uio *buf, int flag)
+{
+ struct pcm_channel *rdch, *wrch;
+ intrmask_t s;
+ int ret;
+
+ s = spltty();
+ getchns(i_dev, &rdch, &wrch, SD_F_PRIO_WR);
+
+ KASSERT(wrch, ("dsp_write: nonexistant channel"));
+ KASSERT(wrch->flags & CHN_F_BUSY, ("dsp_write: nonbusy channel"));
+
+ if (wrch->flags & (CHN_F_MAPPED | CHN_F_DEAD)) {
+ relchns(i_dev, rdch, wrch, SD_F_PRIO_WR);
+ splx(s);
+ return EINVAL;
+ }
+ if (!(wrch->flags & CHN_F_RUNNING))
+ wrch->flags |= CHN_F_RUNNING;
+ ret = chn_write(wrch, buf);
+ relchns(i_dev, rdch, wrch, SD_F_PRIO_WR);
+
+ splx(s);
+ return ret;
+}
+
+static int
+dsp_ioctl(dev_t i_dev, u_long cmd, caddr_t arg, int mode, struct thread *td)
+{
+ struct pcm_channel *wrch, *rdch;
+ struct snddev_info *d;
+ intrmask_t s;
+ int kill;
+ int ret = 0, *arg_i = (int *)arg, tmp;
+
+ /*
+ * this is an evil hack to allow broken apps to perform mixer ioctls
+ * on dsp devices.
+ */
+
+ if (IOCGROUP(cmd) == 'M') {
+ dev_t pdev;
+
+ pdev = makedev(SND_CDEV_MAJOR, PCMMKMINOR(PCMUNIT(i_dev), SND_DEV_CTL, 0));
+ return mixer_ioctl(pdev, cmd, arg, mode, td);
+ }
+
+ s = spltty();
+ d = dsp_get_info(i_dev);
+ getchns(i_dev, &rdch, &wrch, 0);
+
+ kill = 0;
+ if (wrch && (wrch->flags & CHN_F_DEAD))
+ kill |= 1;
+ if (rdch && (rdch->flags & CHN_F_DEAD))
+ kill |= 2;
+ if (kill == 3) {
+ relchns(i_dev, rdch, wrch, 0);
+ splx(s);
+ return EINVAL;
+ }
+ if (kill & 1)
+ wrch = NULL;
+ if (kill & 2)
+ rdch = NULL;
+
+ switch(cmd) {
+#ifdef OLDPCM_IOCTL
+ /*
+ * we start with the new ioctl interface.
+ */
+ case AIONWRITE: /* how many bytes can write ? */
+/*
+ if (wrch && wrch->bufhard.dl)
+ while (chn_wrfeed(wrch) == 0);
+*/
+ *arg_i = wrch? sndbuf_getfree(wrch->bufsoft) : 0;
+ break;
+
+ case AIOSSIZE: /* set the current blocksize */
+ {
+ struct snd_size *p = (struct snd_size *)arg;
+
+ p->play_size = 0;
+ p->rec_size = 0;
+ if (wrch) {
+ CHN_LOCK(wrch);
+ chn_setblocksize(wrch, 2, p->play_size);
+ p->play_size = sndbuf_getblksz(wrch->bufsoft);
+ CHN_UNLOCK(wrch);
+ }
+ if (rdch) {
+ CHN_LOCK(rdch);
+ chn_setblocksize(rdch, 2, p->rec_size);
+ p->rec_size = sndbuf_getblksz(rdch->bufsoft);
+ CHN_UNLOCK(rdch);
+ }
+ }
+ break;
+ case AIOGSIZE: /* get the current blocksize */
+ {
+ struct snd_size *p = (struct snd_size *)arg;
+
+ if (wrch)
+ p->play_size = sndbuf_getblksz(wrch->bufsoft);
+ if (rdch)
+ p->rec_size = sndbuf_getblksz(rdch->bufsoft);
+ }
+ break;
+
+ case AIOSFMT:
+ {
+ snd_chan_param *p = (snd_chan_param *)arg;
+
+ if (wrch) {
+ CHN_LOCK(wrch);
+ chn_setformat(wrch, p->play_format);
+ chn_setspeed(wrch, p->play_rate);
+ CHN_UNLOCK(wrch);
+ }
+ if (rdch) {
+ CHN_LOCK(rdch);
+ chn_setformat(rdch, p->rec_format);
+ chn_setspeed(rdch, p->rec_rate);
+ CHN_UNLOCK(rdch);
+ }
+ }
+ /* FALLTHROUGH */
+
+ case AIOGFMT:
+ {
+ snd_chan_param *p = (snd_chan_param *)arg;
+
+ p->play_rate = wrch? wrch->speed : 0;
+ p->rec_rate = rdch? rdch->speed : 0;
+ p->play_format = wrch? wrch->format : 0;
+ p->rec_format = rdch? rdch->format : 0;
+ }
+ break;
+
+ case AIOGCAP: /* get capabilities */
+ {
+ snd_capabilities *p = (snd_capabilities *)arg;
+ struct pcmchan_caps *pcaps = NULL, *rcaps = NULL;
+ dev_t pdev;
+
+ if (rdch) {
+ CHN_LOCK(rdch);
+ rcaps = chn_getcaps(rdch);
+ }
+ if (wrch) {
+ CHN_LOCK(wrch);
+ pcaps = chn_getcaps(wrch);
+ }
+ p->rate_min = max(rcaps? rcaps->minspeed : 0,
+ pcaps? pcaps->minspeed : 0);
+ p->rate_max = min(rcaps? rcaps->maxspeed : 1000000,
+ pcaps? pcaps->maxspeed : 1000000);
+ p->bufsize = min(rdch? sndbuf_getsize(rdch->bufsoft) : 1000000,
+ wrch? sndbuf_getsize(wrch->bufsoft) : 1000000);
+ /* XXX bad on sb16 */
+ p->formats = (rdch? chn_getformats(rdch) : 0xffffffff) &
+ (wrch? chn_getformats(wrch) : 0xffffffff);
+ if (rdch && wrch)
+ p->formats |= (dsp_get_flags(i_dev) & SD_F_SIMPLEX)? 0 : AFMT_FULLDUPLEX;
+ pdev = makedev(SND_CDEV_MAJOR, PCMMKMINOR(PCMUNIT(i_dev), SND_DEV_CTL, 0));
+ p->mixers = 1; /* default: one mixer */
+ p->inputs = pdev->si_drv1? mix_getdevs(pdev->si_drv1) : 0;
+ p->left = p->right = 100;
+ if (wrch)
+ CHN_UNLOCK(wrch);
+ if (rdch)
+ CHN_UNLOCK(rdch);
+ }
+ break;
+
+ case AIOSTOP:
+ if (*arg_i == AIOSYNC_PLAY && wrch)
+ *arg_i = chn_abort(wrch);
+ else if (*arg_i == AIOSYNC_CAPTURE && rdch)
+ *arg_i = chn_abort(rdch);
+ else {
+ printf("AIOSTOP: bad channel 0x%x\n", *arg_i);
+ *arg_i = 0;
+ }
+ break;
+
+ case AIOSYNC:
+ printf("AIOSYNC chan 0x%03lx pos %lu unimplemented\n",
+ ((snd_sync_parm *)arg)->chan, ((snd_sync_parm *)arg)->pos);
+ break;
+#endif
+ /*
+ * here follow the standard ioctls (filio.h etc.)
+ */
+ case FIONREAD: /* get # bytes to read */
+/* if (rdch && rdch->bufhard.dl)
+ while (chn_rdfeed(rdch) == 0);
+*/ *arg_i = rdch? sndbuf_getready(rdch->bufsoft) : 0;
+ break;
+
+ case FIOASYNC: /*set/clear async i/o */
+ DEB( printf("FIOASYNC\n") ; )
+ break;
+
+ case SNDCTL_DSP_NONBLOCK:
+ case FIONBIO: /* set/clear non-blocking i/o */
+ if (rdch)
+ rdch->flags &= ~CHN_F_NBIO;
+ if (wrch)
+ wrch->flags &= ~CHN_F_NBIO;
+ if (*arg_i) {
+ if (rdch)
+ rdch->flags |= CHN_F_NBIO;
+ if (wrch)
+ wrch->flags |= CHN_F_NBIO;
+ }
+ break;
+
+ /*
+ * Finally, here is the linux-compatible ioctl interface
+ */
+#define THE_REAL_SNDCTL_DSP_GETBLKSIZE _IOWR('P', 4, int)
+ case THE_REAL_SNDCTL_DSP_GETBLKSIZE:
+ case SNDCTL_DSP_GETBLKSIZE:
+ if (wrch)
+ *arg_i = sndbuf_getblksz(wrch->bufsoft);
+ else if (rdch)
+ *arg_i = sndbuf_getblksz(rdch->bufsoft);
+ else
+ *arg_i = 0;
+ break ;
+
+ case SNDCTL_DSP_SETBLKSIZE:
+ RANGE(*arg_i, 16, 65536);
+ if (wrch) {
+ CHN_LOCK(wrch);
+ chn_setblocksize(wrch, 2, *arg_i);
+ CHN_UNLOCK(wrch);
+ }
+ if (rdch) {
+ CHN_LOCK(rdch);
+ chn_setblocksize(rdch, 2, *arg_i);
+ CHN_UNLOCK(rdch);
+ }
+ break;
+
+ case SNDCTL_DSP_RESET:
+ DEB(printf("dsp reset\n"));
+ if (wrch)
+ chn_abort(wrch);
+ if (rdch)
+ chn_abort(rdch);
+ break;
+
+ case SNDCTL_DSP_SYNC:
+ DEB(printf("dsp sync\n"));
+ /* chn_sync may sleep */
+ if (wrch) {
+ CHN_LOCK(wrch);
+ chn_sync(wrch, sndbuf_getsize(wrch->bufsoft) - 4);
+ CHN_UNLOCK(wrch);
+ }
+ break;
+
+ case SNDCTL_DSP_SPEED:
+ /* chn_setspeed may sleep */
+ tmp = 0;
+ if (wrch) {
+ CHN_LOCK(wrch);
+ ret = chn_setspeed(wrch, *arg_i);
+ tmp = wrch->speed;
+ CHN_UNLOCK(wrch);
+ }
+ if (rdch && ret == 0) {
+ CHN_LOCK(rdch);
+ ret = chn_setspeed(rdch, *arg_i);
+ if (tmp == 0)
+ tmp = rdch->speed;
+ CHN_UNLOCK(rdch);
+ }
+ *arg_i = tmp;
+ break;
+
+ case SOUND_PCM_READ_RATE:
+ *arg_i = wrch? wrch->speed : rdch->speed;
+ break;
+
+ case SNDCTL_DSP_STEREO:
+ tmp = -1;
+ *arg_i = (*arg_i)? AFMT_STEREO : 0;
+ if (wrch) {
+ CHN_LOCK(wrch);
+ ret = chn_setformat(wrch, (wrch->format & ~AFMT_STEREO) | *arg_i);
+ tmp = (wrch->format & AFMT_STEREO)? 1 : 0;
+ CHN_UNLOCK(wrch);
+ }
+ if (rdch && ret == 0) {
+ CHN_LOCK(rdch);
+ ret = chn_setformat(rdch, (rdch->format & ~AFMT_STEREO) | *arg_i);
+ if (tmp == -1)
+ tmp = (rdch->format & AFMT_STEREO)? 1 : 0;
+ CHN_UNLOCK(rdch);
+ }
+ *arg_i = tmp;
+ break;
+
+ case SOUND_PCM_WRITE_CHANNELS:
+/* case SNDCTL_DSP_CHANNELS: ( == SOUND_PCM_WRITE_CHANNELS) */
+ if (*arg_i != 0) {
+ tmp = 0;
+ *arg_i = (*arg_i != 1)? AFMT_STEREO : 0;
+ if (wrch) {
+ CHN_LOCK(wrch);
+ ret = chn_setformat(wrch, (wrch->format & ~AFMT_STEREO) | *arg_i);
+ tmp = (wrch->format & AFMT_STEREO)? 2 : 1;
+ CHN_UNLOCK(wrch);
+ }
+ if (rdch && ret == 0) {
+ CHN_LOCK(rdch);
+ ret = chn_setformat(rdch, (rdch->format & ~AFMT_STEREO) | *arg_i);
+ if (tmp == 0)
+ tmp = (rdch->format & AFMT_STEREO)? 2 : 1;
+ CHN_UNLOCK(rdch);
+ }
+ *arg_i = tmp;
+ } else {
+ *arg_i = ((wrch? wrch->format : rdch->format) & AFMT_STEREO)? 2 : 1;
+ }
+ break;
+
+ case SOUND_PCM_READ_CHANNELS:
+ *arg_i = ((wrch? wrch->format : rdch->format) & AFMT_STEREO)? 2 : 1;
+ break;
+
+ case SNDCTL_DSP_GETFMTS: /* returns a mask of supported fmts */
+ *arg_i = wrch? chn_getformats(wrch) : chn_getformats(rdch);
+ break ;
+
+ case SNDCTL_DSP_SETFMT: /* sets _one_ format */
+ /* XXX locking */
+ if ((*arg_i != AFMT_QUERY)) {
+ tmp = 0;
+ if (wrch) {
+ CHN_LOCK(wrch);
+ ret = chn_setformat(wrch, (*arg_i) | (wrch->format & AFMT_STEREO));
+ tmp = wrch->format & ~AFMT_STEREO;
+ CHN_UNLOCK(wrch);
+ }
+ if (rdch && ret == 0) {
+ CHN_LOCK(rdch);
+ ret = chn_setformat(rdch, (*arg_i) | (rdch->format & AFMT_STEREO));
+ if (tmp == 0)
+ tmp = rdch->format & ~AFMT_STEREO;
+ CHN_UNLOCK(rdch);
+ }
+ *arg_i = tmp;
+ } else
+ *arg_i = (wrch? wrch->format : rdch->format) & ~AFMT_STEREO;
+ break;
+
+ case SNDCTL_DSP_SETFRAGMENT:
+ /* XXX locking */
+ DEB(printf("SNDCTL_DSP_SETFRAGMENT 0x%08x\n", *(int *)arg));
+ {
+ u_int32_t fragln = (*arg_i) & 0x0000ffff;
+ u_int32_t maxfrags = ((*arg_i) & 0xffff0000) >> 16;
+ u_int32_t fragsz;
+
+ RANGE(fragln, 4, 16);
+ fragsz = 1 << fragln;
+
+ if (maxfrags == 0)
+ maxfrags = CHN_2NDBUFMAXSIZE / fragsz;
+ if (maxfrags < 2) {
+ ret = EINVAL;
+ break;
+ }
+ if (maxfrags * fragsz > CHN_2NDBUFMAXSIZE)
+ maxfrags = CHN_2NDBUFMAXSIZE / fragsz;
+
+ DEB(printf("SNDCTL_DSP_SETFRAGMENT %d frags, %d sz\n", maxfrags, fragsz));
+ if (rdch) {
+ CHN_LOCK(rdch);
+ ret = chn_setblocksize(rdch, maxfrags, fragsz);
+ maxfrags = sndbuf_getblkcnt(rdch->bufsoft);
+ fragsz = sndbuf_getblksz(rdch->bufsoft);
+ CHN_UNLOCK(rdch);
+ }
+ if (wrch && ret == 0) {
+ CHN_LOCK(wrch);
+ ret = chn_setblocksize(wrch, maxfrags, fragsz);
+ maxfrags = sndbuf_getblkcnt(wrch->bufsoft);
+ fragsz = sndbuf_getblksz(wrch->bufsoft);
+ CHN_UNLOCK(wrch);
+ }
+
+ fragln = 0;
+ while (fragsz > 1) {
+ fragln++;
+ fragsz >>= 1;
+ }
+ *arg_i = (maxfrags << 16) | fragln;
+ }
+ break;
+
+ case SNDCTL_DSP_GETISPACE:
+ /* return the size of data available in the input queue */
+ {
+ audio_buf_info *a = (audio_buf_info *)arg;
+ if (rdch) {
+ struct snd_dbuf *bs = rdch->bufsoft;
+
+ CHN_LOCK(rdch);
+ a->bytes = sndbuf_getready(bs);
+ a->fragments = a->bytes / sndbuf_getblksz(bs);
+ a->fragstotal = sndbuf_getblkcnt(bs);
+ a->fragsize = sndbuf_getblksz(bs);
+ CHN_UNLOCK(rdch);
+ }
+ }
+ break;
+
+ case SNDCTL_DSP_GETOSPACE:
+ /* return space available in the output queue */
+ {
+ audio_buf_info *a = (audio_buf_info *)arg;
+ if (wrch) {
+ struct snd_dbuf *bs = wrch->bufsoft;
+
+ CHN_LOCK(wrch);
+ chn_wrupdate(wrch);
+ a->bytes = sndbuf_getfree(bs);
+ a->fragments = a->bytes / sndbuf_getblksz(bs);
+ a->fragstotal = sndbuf_getblkcnt(bs);
+ a->fragsize = sndbuf_getblksz(bs);
+ CHN_UNLOCK(wrch);
+ }
+ }
+ break;
+
+ case SNDCTL_DSP_GETIPTR:
+ {
+ count_info *a = (count_info *)arg;
+ if (rdch) {
+ struct snd_dbuf *bs = rdch->bufsoft;
+
+ CHN_LOCK(rdch);
+ chn_rdupdate(rdch);
+ a->bytes = sndbuf_gettotal(bs);
+ a->blocks = sndbuf_getblocks(bs) - rdch->blocks;
+ a->ptr = sndbuf_getreadyptr(bs);
+ rdch->blocks = sndbuf_getblocks(bs);
+ CHN_UNLOCK(rdch);
+ } else
+ ret = EINVAL;
+ }
+ break;
+
+ case SNDCTL_DSP_GETOPTR:
+ {
+ count_info *a = (count_info *)arg;
+ if (wrch) {
+ struct snd_dbuf *bs = wrch->bufsoft;
+
+ CHN_LOCK(wrch);
+ chn_wrupdate(wrch);
+ a->bytes = sndbuf_gettotal(bs);
+ a->blocks = sndbuf_getblocks(bs) - wrch->blocks;
+ a->ptr = sndbuf_getreadyptr(bs);
+ wrch->blocks = sndbuf_getblocks(bs);
+ CHN_UNLOCK(wrch);
+ } else
+ ret = EINVAL;
+ }
+ break;
+
+ case SNDCTL_DSP_GETCAPS:
+ *arg_i = DSP_CAP_REALTIME | DSP_CAP_MMAP | DSP_CAP_TRIGGER;
+ if (rdch && wrch && !(dsp_get_flags(i_dev) & SD_F_SIMPLEX))
+ *arg_i |= DSP_CAP_DUPLEX;
+ break;
+
+ case SOUND_PCM_READ_BITS:
+ *arg_i = ((wrch? wrch->format : rdch->format) & AFMT_16BIT)? 16 : 8;
+ break;
+
+ case SNDCTL_DSP_SETTRIGGER:
+ if (rdch) {
+ CHN_LOCK(rdch);
+ rdch->flags &= ~(CHN_F_TRIGGERED | CHN_F_NOTRIGGER);
+ if (*arg_i & PCM_ENABLE_INPUT)
+ chn_start(rdch, 1);
+ else
+ rdch->flags |= CHN_F_NOTRIGGER;
+ CHN_UNLOCK(rdch);
+ }
+ if (wrch) {
+ CHN_LOCK(wrch);
+ wrch->flags &= ~(CHN_F_TRIGGERED | CHN_F_NOTRIGGER);
+ if (*arg_i & PCM_ENABLE_OUTPUT)
+ chn_start(wrch, 1);
+ else
+ wrch->flags |= CHN_F_NOTRIGGER;
+ CHN_UNLOCK(wrch);
+ }
+ break;
+
+ case SNDCTL_DSP_GETTRIGGER:
+ *arg_i = 0;
+ if (wrch && wrch->flags & CHN_F_TRIGGERED)
+ *arg_i |= PCM_ENABLE_OUTPUT;
+ if (rdch && rdch->flags & CHN_F_TRIGGERED)
+ *arg_i |= PCM_ENABLE_INPUT;
+ break;
+
+ case SNDCTL_DSP_GETODELAY:
+ if (wrch) {
+ struct snd_dbuf *b = wrch->bufhard;
+ struct snd_dbuf *bs = wrch->bufsoft;
+
+ CHN_LOCK(wrch);
+ chn_wrupdate(wrch);
+ *arg_i = sndbuf_getready(b) + sndbuf_getready(bs);
+ CHN_UNLOCK(wrch);
+ } else
+ ret = EINVAL;
+ break;
+
+ case SNDCTL_DSP_POST:
+ if (wrch) {
+ CHN_LOCK(wrch);
+ wrch->flags &= ~CHN_F_NOTRIGGER;
+ chn_start(wrch, 1);
+ CHN_UNLOCK(wrch);
+ }
+ break;
+
+ case SNDCTL_DSP_MAPINBUF:
+ case SNDCTL_DSP_MAPOUTBUF:
+ case SNDCTL_DSP_SETSYNCRO:
+ /* undocumented */
+
+ case SNDCTL_DSP_SUBDIVIDE:
+ case SOUND_PCM_WRITE_FILTER:
+ case SOUND_PCM_READ_FILTER:
+ /* dunno what these do, don't sound important */
+ default:
+ DEB(printf("default ioctl fn 0x%08lx fail\n", cmd));
+ ret = EINVAL;
+ break;
+ }
+ relchns(i_dev, rdch, wrch, 0);
+ splx(s);
+ return ret;
+}
+
+static int
+dsp_poll(dev_t i_dev, int events, struct thread *td)
+{
+ struct pcm_channel *wrch = NULL, *rdch = NULL;
+ intrmask_t s;
+ int ret, e;
+
+ s = spltty();
+ ret = 0;
+ getchns(i_dev, &rdch, &wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
+
+ if (wrch) {
+ e = (events & (POLLOUT | POLLWRNORM));
+ if (e)
+ ret |= chn_poll(wrch, e, td);
+ }
+ if (rdch) {
+ e = (events & (POLLIN | POLLRDNORM));
+ if (e)
+ ret |= chn_poll(rdch, e, td);
+ }
+ relchns(i_dev, rdch, wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
+
+ splx(s);
+ return ret;
+}
+
+static int
+dsp_mmap(dev_t i_dev, vm_offset_t offset, int nprot)
+{
+ struct pcm_channel *wrch = NULL, *rdch = NULL, *c;
+ intrmask_t s;
+ int ret;
+
+ if (nprot & PROT_EXEC)
+ return -1;
+
+ s = spltty();
+ getchns(i_dev, &rdch, &wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
+#if 0
+ /*
+ * XXX the linux api uses the nprot to select read/write buffer
+ * our vm system doesn't allow this, so force write buffer
+ */
+
+ if (wrch && (nprot & PROT_WRITE)) {
+ c = wrch;
+ } else if (rdch && (nprot & PROT_READ)) {
+ c = rdch;
+ } else {
+ splx(s);
+ return -1;
+ }
+#else
+ c = wrch;
+#endif
+
+ if (c == NULL) {
+ relchns(i_dev, rdch, wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
+ splx(s);
+ return -1;
+ }
+
+ if (offset >= sndbuf_getsize(c->bufsoft)) {
+ relchns(i_dev, rdch, wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
+ splx(s);
+ return -1;
+ }
+
+ if (!(c->flags & CHN_F_MAPPED))
+ c->flags |= CHN_F_MAPPED;
+
+ ret = atop(vtophys(sndbuf_getbufofs(c->bufsoft, offset)));
+ relchns(i_dev, rdch, wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
+
+ splx(s);
+ return ret;
+}
+
+int
+dsp_register(int unit, int channel)
+{
+ make_dev(&dsp_cdevsw, PCMMKMINOR(unit, SND_DEV_DSP, channel),
+ UID_ROOT, GID_WHEEL, 0666, "dsp%d.%d", unit, channel);
+ make_dev(&dsp_cdevsw, PCMMKMINOR(unit, SND_DEV_DSP16, channel),
+ UID_ROOT, GID_WHEEL, 0666, "dspW%d.%d", unit, channel);
+ make_dev(&dsp_cdevsw, PCMMKMINOR(unit, SND_DEV_AUDIO, channel),
+ UID_ROOT, GID_WHEEL, 0666, "audio%d.%d", unit, channel);
+
+ return 0;
+}
+
+int
+dsp_registerrec(int unit, int channel)
+{
+ make_dev(&dsp_cdevsw, PCMMKMINOR(unit, SND_DEV_DSPREC, channel),
+ UID_ROOT, GID_WHEEL, 0666, "dspr%d.%d", unit, channel);
+
+ return 0;
+}
+
+int
+dsp_unregister(int unit, int channel)
+{
+ dev_t pdev;
+
+ pdev = makedev(SND_CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_DSP, channel));
+ destroy_dev(pdev);
+ pdev = makedev(SND_CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_DSP16, channel));
+ destroy_dev(pdev);
+ pdev = makedev(SND_CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_AUDIO, channel));
+ destroy_dev(pdev);
+
+ return 0;
+}
+
+int
+dsp_unregisterrec(int unit, int channel)
+{
+ dev_t pdev;
+
+ pdev = makedev(SND_CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_DSPREC, channel));
+ destroy_dev(pdev);
+
+ return 0;
+}
+
+#ifdef USING_DEVFS
+static void
+dsp_clone(void *arg, char *name, int namelen, dev_t *dev)
+{
+ dev_t pdev;
+ int i, cont, unit, devtype;
+ int devtypes[3] = {SND_DEV_DSP, SND_DEV_DSP16, SND_DEV_AUDIO};
+ char *devnames[3] = {"dsp", "dspW", "audio"};
+
+ if (*dev != NODEV)
+ return;
+ if (pcm_devclass == NULL)
+ return;
+
+ devtype = 0;
+ unit = -1;
+ for (i = 0; (i < 3) && (unit == -1); i++) {
+ devtype = devtypes[i];
+ if (strcmp(name, devnames[i]) == 0) {
+ unit = snd_unit;
+ } else {
+ if (dev_stdclone(name, NULL, devnames[i], &unit) != 1)
+ unit = -1;
+ }
+ }
+ if (unit == -1 || unit >= devclass_get_maxunit(pcm_devclass))
+ return;
+
+ cont = 1;
+ for (i = 0; cont; i++) {
+ pdev = makedev(SND_CDEV_MAJOR, PCMMKMINOR(unit, devtype, i));
+ if (pdev->si_flags & SI_NAMED) {
+ if ((pdev->si_drv1 == NULL) && (pdev->si_drv2 == NULL)) {
+ *dev = pdev;
+ return;
+ }
+ } else {
+ cont = 0;
+ }
+ }
+}
+
+static void
+dsp_sysinit(void *p)
+{
+ dsp_ehtag = EVENTHANDLER_REGISTER(dev_clone, dsp_clone, 0, 1000);
+}
+
+static void
+dsp_sysuninit(void *p)
+{
+ if (dsp_ehtag != NULL)
+ EVENTHANDLER_DEREGISTER(dev_clone, dsp_ehtag);
+}
+
+SYSINIT(dsp_sysinit, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, dsp_sysinit, NULL);
+SYSUNINIT(dsp_sysuninit, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, dsp_sysuninit, NULL);
+#endif
+
+
diff --git a/sys/dev/sound/pcm/dsp.h b/sys/dev/sound/pcm/dsp.h
new file mode 100644
index 0000000..7943bec
--- /dev/null
+++ b/sys/dev/sound/pcm/dsp.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+int dsp_register(int unit, int channel);
+int dsp_registerrec(int unit, int channel);
+int dsp_unregister(int unit, int channel);
+int dsp_unregisterrec(int unit, int channel);
diff --git a/sys/dev/sound/pcm/fake.c b/sys/dev/sound/pcm/fake.c
new file mode 100644
index 0000000..8c4ba77
--- /dev/null
+++ b/sys/dev/sound/pcm/fake.c
@@ -0,0 +1,137 @@
+/*
+ * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <dev/sound/pcm/sound.h>
+
+SND_DECLARE_FILE("$FreeBSD$");
+
+static u_int32_t fk_fmt[] = {
+ AFMT_U8,
+ AFMT_STEREO | AFMT_U8,
+ AFMT_S8,
+ AFMT_STEREO | AFMT_S8,
+ AFMT_S16_LE,
+ AFMT_STEREO | AFMT_S16_LE,
+ AFMT_U16_LE,
+ AFMT_STEREO | AFMT_U16_LE,
+ AFMT_S16_BE,
+ AFMT_STEREO | AFMT_S16_BE,
+ AFMT_U16_BE,
+ AFMT_STEREO | AFMT_U16_BE,
+ 0
+};
+static struct pcmchan_caps fk_caps = {0, 1000000, fk_fmt, 0};
+
+#define FKBUFSZ 4096
+static char fakebuf[FKBUFSZ];
+
+/* channel interface */
+static void *
+fkchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir)
+{
+ sndbuf_setup(b, fakebuf, FKBUFSZ);
+ return (void *)0xbabef00d;
+}
+
+static int
+fkchan_free(kobj_t obj, void *data)
+{
+ return 0;
+}
+
+static int
+fkchan_setformat(kobj_t obj, void *data, u_int32_t format)
+{
+ return 0;
+}
+
+static int
+fkchan_setspeed(kobj_t obj, void *data, u_int32_t speed)
+{
+ return speed;
+}
+
+static int
+fkchan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize)
+{
+ return blocksize;
+}
+
+static int
+fkchan_trigger(kobj_t obj, void *data, int go)
+{
+ return 0;
+}
+
+static int
+fkchan_getptr(kobj_t obj, void *data)
+{
+ return 0;
+}
+
+static struct pcmchan_caps *
+fkchan_getcaps(kobj_t obj, void *data)
+{
+ return &fk_caps;
+}
+
+static kobj_method_t fkchan_methods[] = {
+ KOBJMETHOD(channel_init, fkchan_init),
+ KOBJMETHOD(channel_free, fkchan_free),
+ KOBJMETHOD(channel_setformat, fkchan_setformat),
+ KOBJMETHOD(channel_setspeed, fkchan_setspeed),
+ KOBJMETHOD(channel_setblocksize, fkchan_setblocksize),
+ KOBJMETHOD(channel_trigger, fkchan_trigger),
+ KOBJMETHOD(channel_getptr, fkchan_getptr),
+ KOBJMETHOD(channel_getcaps, fkchan_getcaps),
+ { 0, 0 }
+};
+CHANNEL_DECLARE(fkchan);
+
+struct pcm_channel *
+fkchan_setup(device_t dev)
+{
+ struct snddev_info *d = device_get_softc(dev);
+ struct pcm_channel *c;
+
+ c = malloc(sizeof(*c), M_DEVBUF, M_WAITOK);
+ c->methods = kobj_create(&fkchan_class, M_DEVBUF, M_WAITOK);
+ c->parentsnddev = d;
+ snprintf(c->name, CHN_NAMELEN, "%s:fake", device_get_nameunit(dev));
+
+ return c;
+}
+
+int
+fkchan_kill(struct pcm_channel *c)
+{
+ kobj_delete(c->methods, M_DEVBUF);
+ c->methods = NULL;
+ free(c, M_DEVBUF);
+ return 0;
+}
+
+
diff --git a/sys/dev/sound/pcm/feeder.c b/sys/dev/sound/pcm/feeder.c
new file mode 100644
index 0000000..424da4d
--- /dev/null
+++ b/sys/dev/sound/pcm/feeder.c
@@ -0,0 +1,421 @@
+/*
+ * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <dev/sound/pcm/sound.h>
+
+#include "feeder_if.h"
+
+SND_DECLARE_FILE("$FreeBSD$");
+
+MALLOC_DEFINE(M_FEEDER, "feeder", "pcm feeder");
+
+#define MAXFEEDERS 256
+#undef FEEDER_DEBUG
+
+struct feedertab_entry {
+ SLIST_ENTRY(feedertab_entry) link;
+ struct feeder_class *feederclass;
+ struct pcm_feederdesc *desc;
+
+ int idx;
+};
+static SLIST_HEAD(, feedertab_entry) feedertab;
+
+/*****************************************************************************/
+
+void
+feeder_register(void *p)
+{
+ static int feedercnt = 0;
+
+ struct feeder_class *fc = p;
+ struct feedertab_entry *fte;
+ int i;
+
+ if (feedercnt == 0) {
+ KASSERT(fc->desc == NULL, ("first feeder not root: %s", fc->name));
+
+ SLIST_INIT(&feedertab);
+ fte = malloc(sizeof(*fte), M_FEEDER, M_WAITOK | M_ZERO);
+ if (fte == NULL) {
+ printf("can't allocate memory for root feeder: %s\n",
+ fc->name);
+
+ return;
+ }
+ fte->feederclass = fc;
+ fte->desc = NULL;
+ fte->idx = feedercnt;
+ SLIST_INSERT_HEAD(&feedertab, fte, link);
+ feedercnt++;
+
+ /* we've got our root feeder so don't veto pcm loading anymore */
+ pcm_veto_load = 0;
+
+ return;
+ }
+
+ KASSERT(fc->desc != NULL, ("feeder '%s' has no descriptor", fc->name));
+
+ /* beyond this point failure is non-fatal but may result in some translations being unavailable */
+ i = 0;
+ while ((feedercnt < MAXFEEDERS) && (fc->desc[i].type > 0)) {
+ /* printf("adding feeder %s, %x -> %x\n", fc->name, fc->desc[i].in, fc->desc[i].out); */
+ fte = malloc(sizeof(*fte), M_FEEDER, M_WAITOK | M_ZERO);
+ if (fte == NULL) {
+ printf("can't allocate memory for feeder '%s', %x -> %x\n", fc->name, fc->desc[i].in, fc->desc[i].out);
+
+ return;
+ }
+ fte->feederclass = fc;
+ fte->desc = &fc->desc[i];
+ fte->idx = feedercnt;
+ fte->desc->idx = feedercnt;
+ SLIST_INSERT_HEAD(&feedertab, fte, link);
+ i++;
+ }
+ feedercnt++;
+ if (feedercnt >= MAXFEEDERS)
+ printf("MAXFEEDERS (%d >= %d) exceeded\n", feedercnt, MAXFEEDERS);
+}
+
+static void
+feeder_unregisterall(void *p)
+{
+ struct feedertab_entry *fte, *next;
+
+ next = SLIST_FIRST(&feedertab);
+ while (next != NULL) {
+ fte = next;
+ next = SLIST_NEXT(fte, link);
+ free(fte, M_FEEDER);
+ }
+}
+
+static int
+cmpdesc(struct pcm_feederdesc *n, struct pcm_feederdesc *m)
+{
+ return ((n->type == m->type) &&
+ ((n->in == 0) || (n->in == m->in)) &&
+ ((n->out == 0) || (n->out == m->out)) &&
+ (n->flags == m->flags));
+}
+
+static void
+feeder_destroy(struct pcm_feeder *f)
+{
+ FEEDER_FREE(f);
+ kobj_delete((kobj_t)f, M_FEEDER);
+}
+
+static struct pcm_feeder *
+feeder_create(struct feeder_class *fc, struct pcm_feederdesc *desc)
+{
+ struct pcm_feeder *f;
+ int err;
+
+ f = (struct pcm_feeder *)kobj_create((kobj_class_t)fc, M_FEEDER, M_NOWAIT | M_ZERO);
+ if (f == NULL)
+ return NULL;
+
+ f->align = fc->align;
+ f->data = fc->data;
+ f->source = NULL;
+ f->parent = NULL;
+ f->class = fc;
+ f->desc = &(f->desc_static);
+
+ if (desc) {
+ *(f->desc) = *desc;
+ } else {
+ f->desc->type = FEEDER_ROOT;
+ f->desc->in = 0;
+ f->desc->out = 0;
+ f->desc->flags = 0;
+ f->desc->idx = 0;
+ }
+
+ err = FEEDER_INIT(f);
+ if (err) {
+ printf("feeder_init(%p) on %s returned %d\n", f, fc->name, err);
+ feeder_destroy(f);
+
+ return NULL;
+ }
+
+ return f;
+}
+
+struct feeder_class *
+feeder_getclass(struct pcm_feederdesc *desc)
+{
+ struct feedertab_entry *fte;
+
+ SLIST_FOREACH(fte, &feedertab, link) {
+ if ((desc == NULL) && (fte->desc == NULL))
+ return fte->feederclass;
+ if ((fte->desc != NULL) && (desc != NULL) && cmpdesc(desc, fte->desc))
+ return fte->feederclass;
+ }
+ return NULL;
+}
+
+int
+chn_addfeeder(struct pcm_channel *c, struct feeder_class *fc, struct pcm_feederdesc *desc)
+{
+ struct pcm_feeder *nf;
+
+ nf = feeder_create(fc, desc);
+ if (nf == NULL)
+ return ENOSPC;
+
+ nf->source = c->feeder;
+
+ if (nf->align > 0)
+ c->align += nf->align;
+ else if (nf->align < 0 && c->align < -nf->align)
+ c->align = -nf->align;
+
+ c->feeder = nf;
+
+ return 0;
+}
+
+int
+chn_removefeeder(struct pcm_channel *c)
+{
+ struct pcm_feeder *f;
+
+ if (c->feeder == NULL)
+ return -1;
+ f = c->feeder;
+ c->feeder = c->feeder->source;
+ feeder_destroy(f);
+
+ return 0;
+}
+
+struct pcm_feeder *
+chn_findfeeder(struct pcm_channel *c, u_int32_t type)
+{
+ struct pcm_feeder *f;
+
+ f = c->feeder;
+ while (f != NULL) {
+ if (f->desc->type == type)
+ return f;
+ f = f->source;
+ }
+
+ return NULL;
+}
+
+static int
+chainok(struct pcm_feeder *test, struct pcm_feeder *stop)
+{
+ u_int32_t visited[MAXFEEDERS / 32];
+ u_int32_t idx, mask;
+
+ bzero(visited, sizeof(visited));
+ while (test && (test != stop)) {
+ idx = test->desc->idx;
+ if (idx < 0)
+ panic("bad idx %d", idx);
+ if (idx >= MAXFEEDERS)
+ panic("bad idx %d", idx);
+ mask = 1 << (idx & 31);
+ idx >>= 5;
+ if (visited[idx] & mask)
+ return 0;
+ visited[idx] |= mask;
+ test = test->source;
+ }
+
+ return 1;
+}
+
+static struct pcm_feeder *
+feeder_fmtchain(u_int32_t *to, struct pcm_feeder *source, struct pcm_feeder *stop, int maxdepth)
+{
+ struct feedertab_entry *fte;
+ struct pcm_feeder *try, *ret;
+
+ /* printf("trying %s (%x -> %x)...\n", source->class->name, source->desc->in, source->desc->out); */
+ if (fmtvalid(source->desc->out, to)) {
+ /* printf("got it\n"); */
+ return source;
+ }
+
+ if (maxdepth < 0)
+ return NULL;
+
+ SLIST_FOREACH(fte, &feedertab, link) {
+ if (fte->desc == NULL)
+ continue;
+ if (fte->desc->type != FEEDER_FMT)
+ continue;
+ if (fte->desc->in == source->desc->out) {
+ try = feeder_create(fte->feederclass, fte->desc);
+ if (try) {
+ try->source = source;
+ ret = chainok(try, stop)? feeder_fmtchain(to, try, stop, maxdepth - 1) : NULL;
+ if (ret != NULL)
+ return ret;
+ feeder_destroy(try);
+ }
+ }
+ }
+ /* printf("giving up %s...\n", source->class->name); */
+
+ return NULL;
+}
+
+u_int32_t
+chn_fmtchain(struct pcm_channel *c, u_int32_t *to)
+{
+ struct pcm_feeder *try, *del, *stop;
+ u_int32_t tmpfrom[2], best, *from;
+ int i, max, bestmax;
+
+ KASSERT(c != NULL, ("c == NULL"));
+ KASSERT(c->feeder != NULL, ("c->feeder == NULL"));
+ KASSERT(to != NULL, ("to == NULL"));
+ KASSERT(to[0] != 0, ("to[0] == 0"));
+
+ stop = c->feeder;
+
+ if (c->direction == PCMDIR_REC && c->feeder->desc->type == FEEDER_ROOT) {
+ from = chn_getcaps(c)->fmtlist;
+ } else {
+ tmpfrom[0] = c->feeder->desc->out;
+ tmpfrom[1] = 0;
+ from = tmpfrom;
+ }
+
+ i = 0;
+ best = 0;
+ bestmax = 100;
+ while (from[i] != 0) {
+ c->feeder->desc->out = from[i];
+ try = NULL;
+ max = 0;
+ while (try == NULL && max < 8) {
+ try = feeder_fmtchain(to, c->feeder, stop, max);
+ if (try == NULL)
+ max++;
+ }
+ if (try != NULL && max < bestmax) {
+ bestmax = max;
+ best = from[i];
+ }
+ while (try != NULL && try != stop) {
+ del = try;
+ try = try->source;
+ feeder_destroy(del);
+ }
+ i++;
+ }
+ if (best == 0)
+ return 0;
+
+ c->feeder->desc->out = best;
+ try = feeder_fmtchain(to, c->feeder, stop, bestmax);
+ if (try == NULL)
+ return 0;
+
+ c->feeder = try;
+ c->align = 0;
+#ifdef FEEDER_DEBUG
+ printf("\n\nchain: ");
+#endif
+ while (try && (try != stop)) {
+#ifdef FEEDER_DEBUG
+ printf("%s [%d]", try->class->name, try->desc->idx);
+ if (try->source)
+ printf(" -> ");
+#endif
+ if (try->source)
+ try->source->parent = try;
+ if (try->align > 0)
+ c->align += try->align;
+ else if (try->align < 0 && c->align < -try->align)
+ c->align = -try->align;
+ try = try->source;
+ }
+#ifdef FEEDER_DEBUG
+ printf("%s [%d]\n", try->class->name, try->desc->idx);
+#endif
+
+ return (c->direction == PCMDIR_REC)? best : c->feeder->desc->out;
+}
+
+/*****************************************************************************/
+
+static int
+feed_root(struct pcm_feeder *feeder, struct pcm_channel *ch, u_int8_t *buffer, u_int32_t count, void *source)
+{
+ struct snd_dbuf *src = source;
+ int l;
+ u_int8_t x;
+
+ KASSERT(count > 0, ("feed_root: count == 0"));
+ /* count &= ~((1 << ch->align) - 1); */
+ KASSERT(count > 0, ("feed_root: aligned count == 0 (align = %d)", ch->align));
+
+ l = min(count, sndbuf_getready(src));
+ sndbuf_dispose(src, buffer, l);
+
+/*
+ if (l < count)
+ printf("appending %d bytes\n", count - l);
+*/
+
+ x = (sndbuf_getfmt(src) & AFMT_SIGNED)? 0 : 0x80;
+ while (l < count)
+ buffer[l++] = x;
+
+ return count;
+}
+
+static kobj_method_t feeder_root_methods[] = {
+ KOBJMETHOD(feeder_feed, feed_root),
+ { 0, 0 }
+};
+static struct feeder_class feeder_root_class = {
+ name: "feeder_root",
+ methods: feeder_root_methods,
+ size: sizeof(struct pcm_feeder),
+ align: 0,
+ desc: NULL,
+ data: NULL,
+};
+SYSINIT(feeder_root, SI_SUB_DRIVERS, SI_ORDER_FIRST, feeder_register, &feeder_root_class);
+SYSUNINIT(feeder_root, SI_SUB_DRIVERS, SI_ORDER_FIRST, feeder_unregisterall, NULL);
+
+
+
+
+
diff --git a/sys/dev/sound/pcm/feeder.h b/sys/dev/sound/pcm/feeder.h
new file mode 100644
index 0000000..9fb8ff7
--- /dev/null
+++ b/sys/dev/sound/pcm/feeder.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+struct pcm_feederdesc {
+ u_int32_t type;
+ u_int32_t in, out;
+ u_int32_t flags;
+ int idx;
+};
+
+struct feeder_class {
+ KOBJ_CLASS_FIELDS;
+ int align;
+ struct pcm_feederdesc *desc;
+ void *data;
+};
+
+struct pcm_feeder {
+ KOBJ_FIELDS;
+ int align;
+ struct pcm_feederdesc *desc, desc_static;
+ void *data;
+ struct feeder_class *class;
+ struct pcm_feeder *source, *parent;
+
+};
+
+void feeder_register(void *p);
+struct feeder_class *feeder_getclass(struct pcm_feederdesc *desc);
+
+u_int32_t chn_fmtchain(struct pcm_channel *c, u_int32_t *to);
+int chn_addfeeder(struct pcm_channel *c, struct feeder_class *fc, struct pcm_feederdesc *desc);
+int chn_removefeeder(struct pcm_channel *c);
+struct pcm_feeder *chn_findfeeder(struct pcm_channel *c, u_int32_t type);
+
+#define FEEDER_DECLARE(feeder, palign, pdata) \
+static struct feeder_class feeder ## _class = { \
+ name: #feeder, \
+ methods: feeder ## _methods, \
+ size: sizeof(struct pcm_feeder), \
+ align: palign, \
+ desc: feeder ## _desc, \
+ data: pdata, \
+}; \
+SYSINIT(feeder, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, feeder_register, &feeder ## _class);
+
+#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_LAST FEEDER_VOLUME
+
+#define FEEDRATE_SRC 1
+#define FEEDRATE_DST 2
+
+
diff --git a/sys/dev/sound/pcm/feeder_fmt.c b/sys/dev/sound/pcm/feeder_fmt.c
new file mode 100644
index 0000000..b4fa4f3
--- /dev/null
+++ b/sys/dev/sound/pcm/feeder_fmt.c
@@ -0,0 +1,543 @@
+/*
+ * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <dev/sound/pcm/sound.h>
+
+#include "feeder_if.h"
+
+SND_DECLARE_FILE("$FreeBSD$");
+
+MALLOC_DEFINE(M_FMTFEEDER, "fmtfeed", "pcm format feeder");
+
+#define FEEDBUFSZ 8192
+
+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,
+};
+
+static unsigned char u8_to_ulaw[] = {
+ 0, 0, 0, 0, 0, 1, 1, 1,
+ 1, 2, 2, 2, 2, 3, 3, 3,
+ 3, 4, 4, 4, 4, 5, 5, 5,
+ 5, 6, 6, 6, 6, 7, 7, 7,
+ 7, 8, 8, 8, 8, 9, 9, 9,
+ 9, 10, 10, 10, 10, 11, 11, 11,
+ 11, 12, 12, 12, 12, 13, 13, 13,
+ 13, 14, 14, 14, 14, 15, 15, 15,
+ 15, 16, 16, 17, 17, 18, 18, 19,
+ 19, 20, 20, 21, 21, 22, 22, 23,
+ 23, 24, 24, 25, 25, 26, 26, 27,
+ 27, 28, 28, 29, 29, 30, 30, 31,
+ 31, 32, 33, 34, 35, 36, 37, 38,
+ 39, 40, 41, 42, 43, 44, 45, 46,
+ 47, 49, 51, 53, 55, 57, 59, 61,
+ 63, 66, 70, 74, 78, 84, 92, 104,
+ 254, 231, 219, 211, 205, 201, 197, 193,
+ 190, 188, 186, 184, 182, 180, 178, 176,
+ 175, 174, 173, 172, 171, 170, 169, 168,
+ 167, 166, 165, 164, 163, 162, 161, 160,
+ 159, 159, 158, 158, 157, 157, 156, 156,
+ 155, 155, 154, 154, 153, 153, 152, 152,
+ 151, 151, 150, 150, 149, 149, 148, 148,
+ 147, 147, 146, 146, 145, 145, 144, 144,
+ 143, 143, 143, 143, 142, 142, 142, 142,
+ 141, 141, 141, 141, 140, 140, 140, 140,
+ 139, 139, 139, 139, 138, 138, 138, 138,
+ 137, 137, 137, 137, 136, 136, 136, 136,
+ 135, 135, 135, 135, 134, 134, 134, 134,
+ 133, 133, 133, 133, 132, 132, 132, 132,
+ 131, 131, 131, 131, 130, 130, 130, 130,
+ 129, 129, 129, 129, 128, 128, 128, 128,
+};
+
+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 int
+feed_8to16le(struct pcm_feeder *f, struct pcm_channel *c, u_int8_t *b, u_int32_t count, void *source)
+{
+ int i, j, k;
+
+ 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;
+ }
+ return k * 2;
+}
+
+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 kobj_method_t feeder_8to16le_methods[] = {
+ KOBJMETHOD(feeder_feed, feed_8to16le),
+ { 0, 0 }
+};
+FEEDER_DECLARE(feeder_8to16le, 0, NULL);
+
+/*****************************************************************************/
+
+static int
+feed_16to8_init(struct pcm_feeder *f)
+{
+ f->data = malloc(FEEDBUFSZ, M_FMTFEEDER, M_NOWAIT | M_ZERO);
+ return (f->data)? 0 : ENOMEM;
+}
+
+static int
+feed_16to8_free(struct pcm_feeder *f)
+{
+ if (f->data)
+ free(f->data, M_FMTFEEDER);
+ f->data = NULL;
+ return 0;
+}
+
+static int
+feed_16leto8(struct pcm_feeder *f, struct pcm_channel *c, u_int8_t *b, u_int32_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;
+ }
+ return i;
+}
+
+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_S16_LE, AFMT_S8, 0},
+ {FEEDER_FMT, AFMT_S16_LE | AFMT_STEREO, AFMT_S8 | AFMT_STEREO, 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 }
+};
+FEEDER_DECLARE(feeder_16leto8, 1, NULL);
+
+/*****************************************************************************/
+
+static int
+feed_monotostereo8(struct pcm_feeder *f, struct pcm_channel *c, u_int8_t *b, u_int32_t count, void *source)
+{
+ int i, j, 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--] = b[j];
+ j--;
+ }
+ return k * 2;
+}
+
+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 kobj_method_t feeder_monotostereo8_methods[] = {
+ KOBJMETHOD(feeder_feed, feed_monotostereo8),
+ { 0, 0 }
+};
+FEEDER_DECLARE(feeder_monotostereo8, 0, NULL);
+
+/*****************************************************************************/
+
+static int
+feed_monotostereo16(struct pcm_feeder *f, struct pcm_channel *c, u_int8_t *b, u_int32_t count, void *source)
+{
+ int i, j, k = FEEDER_FEED(f->source, c, b, count / 2, source);
+ u_int8_t x, y;
+
+ 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;
+ }
+ return k * 2;
+}
+
+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 kobj_method_t feeder_monotostereo16_methods[] = {
+ KOBJMETHOD(feeder_feed, feed_monotostereo16),
+ { 0, 0 }
+};
+FEEDER_DECLARE(feeder_monotostereo16, 0, NULL);
+
+/*****************************************************************************/
+
+static int
+feed_stereotomono8_init(struct pcm_feeder *f)
+{
+ f->data = malloc(FEEDBUFSZ, M_FMTFEEDER, M_NOWAIT | M_ZERO);
+ return (f->data)? 0 : ENOMEM;
+}
+
+static int
+feed_stereotomono8_free(struct pcm_feeder *f)
+{
+ if (f->data)
+ free(f->data, M_FMTFEEDER);
+ f->data = NULL;
+ return 0;
+}
+
+static int
+feed_stereotomono8(struct pcm_feeder *f, struct pcm_channel *c, u_int8_t *b, u_int32_t count, void *source)
+{
+ u_int32_t i = 0, toget = count * 2;
+ int j = 0, 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;
+ }
+ return i;
+}
+
+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 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 }
+};
+FEEDER_DECLARE(feeder_stereotomono8, 1, NULL);
+
+/*****************************************************************************/
+
+static int
+feed_stereotomono16_init(struct pcm_feeder *f)
+{
+ f->data = malloc(FEEDBUFSZ, M_FMTFEEDER, M_NOWAIT | M_ZERO);
+ return (f->data)? 0 : ENOMEM;
+}
+
+static int
+feed_stereotomono16_free(struct pcm_feeder *f)
+{
+ if (f->data)
+ free(f->data, M_FMTFEEDER);
+ f->data = NULL;
+ return 0;
+}
+
+static int
+feed_stereotomono16(struct pcm_feeder *f, struct pcm_channel *c, u_int8_t *b, u_int32_t count, void *source)
+{
+ u_int32_t i = 0, toget = count * 2;
+ int j = 0, k;
+
+ 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;
+ }
+ return i;
+}
+
+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 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 }
+};
+FEEDER_DECLARE(feeder_stereotomono16, 1, NULL);
+
+/*****************************************************************************/
+
+static int
+feed_endian(struct pcm_feeder *f, struct pcm_channel *c, u_int8_t *b, u_int32_t count, void *source)
+{
+ u_int8_t t;
+ int i = 0, j = FEEDER_FEED(f->source, c, b, count, source);
+
+ while (i < j) {
+ t = b[i];
+ b[i] = b[i + 1];
+ b[i + 1] = t;
+ i += 2;
+ }
+ return i;
+}
+
+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 }
+};
+FEEDER_DECLARE(feeder_endian, 0, NULL);
+
+/*****************************************************************************/
+
+static int
+feed_sign(struct pcm_feeder *f, struct pcm_channel *c, u_int8_t *b, u_int32_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;
+
+ while (i < j) {
+ b[i + ofs] ^= 0x80;
+ i += ssz;
+ }
+ return i;
+}
+
+static struct pcm_feederdesc feeder_sign8_desc[] = {
+ {FEEDER_FMT, AFMT_U8, AFMT_S8, 0},
+ {FEEDER_FMT, AFMT_U8 | AFMT_STEREO, AFMT_S8 | AFMT_STEREO, 0},
+ {FEEDER_FMT, AFMT_S8, AFMT_U8, 0},
+ {FEEDER_FMT, AFMT_S8 | AFMT_STEREO, AFMT_U8 | AFMT_STEREO, 0},
+ {0},
+};
+static kobj_method_t feeder_sign8_methods[] = {
+ KOBJMETHOD(feeder_feed, feed_sign),
+ { 0, 0 }
+};
+FEEDER_DECLARE(feeder_sign8, 0, (void *)1);
+
+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_S16_LE, AFMT_U16_LE, 0},
+ {FEEDER_FMT, AFMT_S16_LE | AFMT_STEREO, AFMT_U16_LE | AFMT_STEREO, 0},
+ {0},
+};
+static kobj_method_t feeder_sign16le_methods[] = {
+ KOBJMETHOD(feeder_feed, feed_sign),
+ { 0, 0 }
+};
+FEEDER_DECLARE(feeder_sign16le, -1, (void *)2);
+
+/*****************************************************************************/
+
+static int
+feed_table(struct pcm_feeder *f, struct pcm_channel *c, u_int8_t *b, u_int32_t count, void *source)
+{
+ int i = 0, j = FEEDER_FEED(f->source, c, b, count, source);
+
+ while (i < j) {
+ b[i] = ((u_int8_t *)f->data)[b[i]];
+ i++;
+ }
+ return i;
+}
+
+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 kobj_method_t feeder_ulawtoalaw_methods[] = {
+ KOBJMETHOD(feeder_feed, feed_table),
+ { 0, 0 }
+};
+FEEDER_DECLARE(feeder_ulawtoalaw, 0, ulaw_to_alaw);
+
+
+
diff --git a/sys/dev/sound/pcm/feeder_if.m b/sys/dev/sound/pcm/feeder_if.m
new file mode 100644
index 0000000..a100a6d
--- /dev/null
+++ b/sys/dev/sound/pcm/feeder_if.m
@@ -0,0 +1,87 @@
+# KOBJ
+#
+# Copyright (c) 2000 Cameron Grant <cg@freebsd.org>
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# $FreeBSD$
+#
+
+#include <dev/sound/pcm/sound.h>
+
+INTERFACE feeder;
+
+CODE {
+
+ static int
+ feeder_noinit(struct pcm_feeder* feeder)
+ {
+ return 0;
+ }
+
+ static int
+ feeder_nofree(struct pcm_feeder* feeder)
+ {
+ return 0;
+ }
+
+ static int
+ feeder_noset(struct pcm_feeder* feeder, int what, int value)
+ {
+ return -1;
+ }
+
+ static int
+ feeder_noget(struct pcm_feeder* feeder, int what)
+ {
+ return -1;
+ }
+
+};
+
+METHOD int init {
+ struct pcm_feeder* feeder;
+} DEFAULT feeder_noinit;
+
+METHOD int free {
+ struct pcm_feeder* feeder;
+} DEFAULT feeder_nofree;
+
+METHOD int set {
+ struct pcm_feeder* feeder;
+ int what;
+ int value;
+} DEFAULT feeder_noset;
+
+METHOD int get {
+ struct pcm_feeder* feeder;
+ int what;
+} DEFAULT feeder_noget;
+
+METHOD int feed {
+ struct pcm_feeder* feeder;
+ struct pcm_channel* c;
+ u_int8_t* buffer;
+ u_int32_t count;
+ void* source;
+};
diff --git a/sys/dev/sound/pcm/feeder_rate.c b/sys/dev/sound/pcm/feeder_rate.c
new file mode 100644
index 0000000..fca795c
--- /dev/null
+++ b/sys/dev/sound/pcm/feeder_rate.c
@@ -0,0 +1,192 @@
+/*
+ * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <dev/sound/pcm/sound.h>
+
+#include "feeder_if.h"
+
+SND_DECLARE_FILE("$FreeBSD$");
+
+MALLOC_DEFINE(M_RATEFEEDER, "ratefeed", "pcm rate feeder");
+
+#define FEEDBUFSZ 8192
+#undef FEEDER_DEBUG
+
+struct feed_rate_info {
+ u_int32_t src, dst;
+ int srcpos, srcinc;
+ int16_t *buffer;
+ u_int16_t alpha;
+};
+
+static int
+feed_rate_setup(struct pcm_feeder *f)
+{
+ struct feed_rate_info *info = f->data;
+
+ info->srcinc = (info->src << 16) / info->dst;
+ /* srcinc is 16.16 fixed point increment for srcpos for each dstpos */
+ info->srcpos = 0;
+ return 0;
+}
+
+static int
+feed_rate_set(struct pcm_feeder *f, int what, int value)
+{
+ struct feed_rate_info *info = f->data;
+
+ switch(what) {
+ case FEEDRATE_SRC:
+ info->src = value;
+ break;
+ case FEEDRATE_DST:
+ info->dst = value;
+ break;
+ default:
+ return -1;
+ }
+ return feed_rate_setup(f);
+}
+
+static int
+feed_rate_get(struct pcm_feeder *f, int what)
+{
+ 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 -1;
+}
+
+static int
+feed_rate_init(struct pcm_feeder *f)
+{
+ struct feed_rate_info *info;
+
+ info = malloc(sizeof(*info), M_RATEFEEDER, M_NOWAIT | M_ZERO);
+ if (info == NULL)
+ return ENOMEM;
+ info->buffer = malloc(FEEDBUFSZ, M_RATEFEEDER, M_NOWAIT | M_ZERO);
+ if (info->buffer == NULL) {
+ free(info, M_RATEFEEDER);
+ return ENOMEM;
+ }
+ info->src = DSP_DEFAULT_SPEED;
+ info->dst = DSP_DEFAULT_SPEED;
+ info->alpha = 0;
+ f->data = info;
+ return feed_rate_setup(f);
+}
+
+static int
+feed_rate_free(struct pcm_feeder *f)
+{
+ 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
+feed_rate(struct pcm_feeder *f, struct pcm_channel *c, u_int8_t *b, u_int32_t count, void *source)
+{
+ struct feed_rate_info *info = f->data;
+ int16_t *destbuf = (int16_t *)b;
+ int fetch, v, alpha, hidelta, spos, dpos;
+
+ /*
+ * at this point:
+ * info->srcpos is 24.8 fixed offset into the fetchbuffer. 0 <= srcpos <= 0xff
+ *
+ * our input and output are always AFMT_S16LE stereo. this simplifies things.
+ */
+
+ /*
+ * we start by fetching enough source data into our buffer to generate
+ * about as much as was requested. we put it at offset 2 in the
+ * buffer so that we can interpolate from the last samples in the
+ * previous iteration- when we finish we will move our last samples
+ * to the start of the buffer.
+ */
+ spos = 0;
+ dpos = 0;
+
+ /* fetch is in bytes */
+ fetch = (count * info->srcinc) >> 16;
+ fetch = min(fetch, FEEDBUFSZ - 4) & ~3;
+ if (fetch == 0)
+ return 0;
+ fetch = FEEDER_FEED(f->source, c, ((u_int8_t *)info->buffer) + 4, fetch, source);
+ fetch /= 2;
+
+ alpha = info->alpha;
+ hidelta = min(info->srcinc >> 16, 1) * 2;
+ while ((spos + hidelta + 1) < fetch) {
+ v = (info->buffer[spos] * (0xffff - alpha)) + (info->buffer[spos + hidelta] * alpha);
+ destbuf[dpos++] = v >> 16;
+
+ v = (info->buffer[spos + 1] * (0xffff - alpha)) + (info->buffer[spos + hidelta + 1] * alpha);
+ destbuf[dpos++] = v >> 16;
+
+ alpha += info->srcinc;
+ spos += (alpha >> 16) * 2;
+ alpha &= 0xffff;
+
+ }
+ info->alpha = alpha & 0xffff;
+ info->buffer[0] = info->buffer[spos - hidelta];
+ info->buffer[1] = info->buffer[spos - hidelta + 1];
+
+ count = dpos * 2;
+ return count;
+}
+
+static struct pcm_feederdesc feeder_rate_desc[] = {
+ {FEEDER_RATE, AFMT_S16_LE | AFMT_STEREO, AFMT_S16_LE | AFMT_STEREO, 0},
+ {0},
+};
+static kobj_method_t feeder_rate_methods[] = {
+ KOBJMETHOD(feeder_init, feed_rate_init),
+ KOBJMETHOD(feeder_free, feed_rate_free),
+ KOBJMETHOD(feeder_set, feed_rate_set),
+ KOBJMETHOD(feeder_get, feed_rate_get),
+ KOBJMETHOD(feeder_feed, feed_rate),
+ { 0, 0 }
+};
+FEEDER_DECLARE(feeder_rate, 2, NULL);
+
+
diff --git a/sys/dev/sound/pcm/mixer.c b/sys/dev/sound/pcm/mixer.c
new file mode 100644
index 0000000..182d290
--- /dev/null
+++ b/sys/dev/sound/pcm/mixer.c
@@ -0,0 +1,519 @@
+/*
+ * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <dev/sound/pcm/sound.h>
+
+#include "mixer_if.h"
+
+SND_DECLARE_FILE("$FreeBSD$");
+
+MALLOC_DEFINE(M_MIXER, "mixer", "mixer");
+
+#define MIXER_NAMELEN 16
+struct snd_mixer {
+ KOBJ_FIELDS;
+ const char *type;
+ void *devinfo;
+ int busy;
+ int hwvol_muted;
+ int hwvol_mixer;
+ int hwvol_step;
+ u_int32_t hwvol_mute_level;
+ u_int32_t devs;
+ u_int32_t recdevs;
+ u_int32_t recsrc;
+ u_int16_t level[32];
+ char name[MIXER_NAMELEN];
+ struct mtx *lock;
+};
+
+static u_int16_t snd_mixerdefaults[SOUND_MIXER_NRDEVICES] = {
+ [SOUND_MIXER_VOLUME] = 75,
+ [SOUND_MIXER_BASS] = 50,
+ [SOUND_MIXER_TREBLE] = 50,
+ [SOUND_MIXER_SYNTH] = 75,
+ [SOUND_MIXER_PCM] = 75,
+ [SOUND_MIXER_SPEAKER] = 75,
+ [SOUND_MIXER_LINE] = 75,
+ [SOUND_MIXER_MIC] = 0,
+ [SOUND_MIXER_CD] = 75,
+ [SOUND_MIXER_LINE1] = 75,
+ [SOUND_MIXER_VIDEO] = 75,
+ [SOUND_MIXER_RECLEV] = 0,
+ [SOUND_MIXER_OGAIN] = 50,
+ [SOUND_MIXER_MONITOR] = 75,
+};
+
+static char* snd_mixernames[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_NAMES;
+
+static d_open_t mixer_open;
+static d_close_t mixer_close;
+
+static struct cdevsw mixer_cdevsw = {
+ /* open */ mixer_open,
+ /* close */ mixer_close,
+ /* read */ noread,
+ /* write */ nowrite,
+ /* ioctl */ mixer_ioctl,
+ /* poll */ nopoll,
+ /* mmap */ nommap,
+ /* strategy */ nostrategy,
+ /* name */ "mixer",
+ /* maj */ SND_CDEV_MAJOR,
+ /* dump */ nodump,
+ /* psize */ nopsize,
+ /* flags */ 0,
+};
+
+#ifdef USING_DEVFS
+static eventhandler_tag mixer_ehtag;
+#endif
+
+static dev_t
+mixer_get_devt(device_t dev)
+{
+ dev_t pdev;
+ int unit;
+
+ unit = device_get_unit(dev);
+ pdev = makedev(SND_CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_CTL, 0));
+
+ return pdev;
+}
+
+#ifdef SND_DYNSYSCTL
+static int
+mixer_lookup(char *devname)
+{
+ int i;
+
+ for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
+ if (strncmp(devname, snd_mixernames[i],
+ strlen(snd_mixernames[i])) == 0)
+ return i;
+ return -1;
+}
+#endif
+
+static int
+mixer_set(struct snd_mixer *mixer, unsigned dev, unsigned lev)
+{
+ unsigned l, r;
+ int v;
+
+ if ((dev >= SOUND_MIXER_NRDEVICES) || (0 == (mixer->devs & (1 << dev))))
+ return -1;
+
+ l = min((lev & 0x00ff), 100);
+ r = min(((lev & 0xff00) >> 8), 100);
+
+ v = MIXER_SET(mixer, dev, l, r);
+ if (v < 0)
+ return -1;
+
+ mixer->level[dev] = l | (r << 8);
+ return 0;
+}
+
+static int
+mixer_get(struct snd_mixer *mixer, int dev)
+{
+ if ((dev < SOUND_MIXER_NRDEVICES) && (mixer->devs & (1 << dev)))
+ return mixer->level[dev];
+ else return -1;
+}
+
+static int
+mixer_setrecsrc(struct snd_mixer *mixer, u_int32_t src)
+{
+ src &= mixer->recdevs;
+ if (src == 0)
+ src = SOUND_MASK_MIC;
+ mixer->recsrc = MIXER_SETRECSRC(mixer, src);
+ return 0;
+}
+
+static int
+mixer_getrecsrc(struct snd_mixer *mixer)
+{
+ return mixer->recsrc;
+}
+
+void
+mix_setdevs(struct snd_mixer *m, u_int32_t v)
+{
+ m->devs = v;
+}
+
+void
+mix_setrecdevs(struct snd_mixer *m, u_int32_t v)
+{
+ m->recdevs = v;
+}
+
+u_int32_t
+mix_getdevs(struct snd_mixer *m)
+{
+ return m->devs;
+}
+
+u_int32_t
+mix_getrecdevs(struct snd_mixer *m)
+{
+ return m->recdevs;
+}
+
+void *
+mix_getdevinfo(struct snd_mixer *m)
+{
+ return m->devinfo;
+}
+
+int
+mixer_init(device_t dev, kobj_class_t cls, void *devinfo)
+{
+ struct snd_mixer *m;
+ u_int16_t v;
+ dev_t pdev;
+ int i, unit;
+
+ m = (struct snd_mixer *)kobj_create(cls, M_MIXER, M_WAITOK | M_ZERO);
+ snprintf(m->name, MIXER_NAMELEN, "%s:mixer", device_get_nameunit(dev));
+ m->lock = snd_mtxcreate(m->name, "pcm mixer");
+ m->type = cls->name;
+ m->devinfo = devinfo;
+ m->busy = 0;
+
+ if (MIXER_INIT(m))
+ goto bad;
+
+ for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
+ v = snd_mixerdefaults[i];
+ mixer_set(m, i, v | (v << 8));
+ }
+
+ mixer_setrecsrc(m, SOUND_MASK_MIC);
+
+ unit = device_get_unit(dev);
+ pdev = make_dev(&mixer_cdevsw, PCMMKMINOR(unit, SND_DEV_CTL, 0),
+ UID_ROOT, GID_WHEEL, 0666, "mixer%d", unit);
+ pdev->si_drv1 = m;
+
+ return 0;
+
+bad:
+ snd_mtxlock(m->lock);
+ snd_mtxfree(m->lock);
+ kobj_delete((kobj_t)m, M_MIXER);
+ return -1;
+}
+
+int
+mixer_uninit(device_t dev)
+{
+ int i;
+ struct snd_mixer *m;
+ dev_t pdev;
+
+ pdev = mixer_get_devt(dev);
+ m = pdev->si_drv1;
+ snd_mtxlock(m->lock);
+
+ if (m->busy) {
+ snd_mtxunlock(m->lock);
+ return EBUSY;
+ }
+
+ pdev->si_drv1 = NULL;
+ destroy_dev(pdev);
+
+ for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
+ mixer_set(m, i, 0);
+
+ mixer_setrecsrc(m, SOUND_MASK_MIC);
+
+ MIXER_UNINIT(m);
+
+ snd_mtxfree(m->lock);
+ kobj_delete((kobj_t)m, M_MIXER);
+
+ return 0;
+}
+
+int
+mixer_reinit(device_t dev)
+{
+ struct snd_mixer *m;
+ dev_t pdev;
+ int i;
+
+ pdev = mixer_get_devt(dev);
+ m = pdev->si_drv1;
+ snd_mtxlock(m->lock);
+
+ i = MIXER_REINIT(m);
+ if (i) {
+ snd_mtxunlock(m->lock);
+ return i;
+ }
+
+ for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
+ mixer_set(m, i, m->level[i]);
+
+ mixer_setrecsrc(m, m->recsrc);
+ snd_mtxunlock(m->lock);
+
+ return 0;
+}
+
+#ifdef SND_DYNSYSCTL
+static int
+sysctl_hw_snd_hwvol_mixer(SYSCTL_HANDLER_ARGS)
+{
+ char devname[32];
+ int error, dev;
+ struct snd_mixer *m;
+
+ m = oidp->oid_arg1;
+ snd_mtxlock(m->lock);
+ strncpy(devname, snd_mixernames[m->hwvol_mixer], sizeof(devname));
+ snd_mtxunlock(m->lock);
+ error = sysctl_handle_string(oidp, &devname[0], sizeof(devname), req);
+ snd_mtxlock(m->lock);
+ if (error == 0 && req->newptr != NULL) {
+ dev = mixer_lookup(devname);
+ if (dev == -1) {
+ snd_mtxunlock(m->lock);
+ return EINVAL;
+ }
+ else if (dev != m->hwvol_mixer) {
+ m->hwvol_mixer = dev;
+ m->hwvol_muted = 0;
+ }
+ }
+ snd_mtxunlock(m->lock);
+ return error;
+}
+#endif
+
+int
+mixer_hwvol_init(device_t dev)
+{
+ struct snd_mixer *m;
+ dev_t pdev;
+
+ pdev = mixer_get_devt(dev);
+ m = pdev->si_drv1;
+ snd_mtxlock(m->lock);
+
+ m->hwvol_mixer = SOUND_MIXER_VOLUME;
+ m->hwvol_step = 5;
+#ifdef SND_DYNSYSCTL
+ SYSCTL_ADD_INT(snd_sysctl_tree(dev), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)),
+ OID_AUTO, "hwvol_step", CTLFLAG_RW, &m->hwvol_step, 0, "");
+ SYSCTL_ADD_PROC(snd_sysctl_tree(dev), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)),
+ OID_AUTO, "hwvol_mixer", CTLTYPE_STRING | CTLFLAG_RW, m, 0,
+ sysctl_hw_snd_hwvol_mixer, "A", "");
+#endif
+ snd_mtxunlock(m->lock);
+ return 0;
+}
+
+void
+mixer_hwvol_mute(device_t dev)
+{
+ struct snd_mixer *m;
+ dev_t pdev;
+
+ pdev = mixer_get_devt(dev);
+ m = pdev->si_drv1;
+ snd_mtxlock(m->lock);
+ if (m->hwvol_muted) {
+ m->hwvol_muted = 0;
+ mixer_set(m, m->hwvol_mixer, m->hwvol_mute_level);
+ } else {
+ m->hwvol_muted++;
+ m->hwvol_mute_level = mixer_get(m, m->hwvol_mixer);
+ mixer_set(m, m->hwvol_mixer, 0);
+ }
+ snd_mtxunlock(m->lock);
+}
+
+void
+mixer_hwvol_step(device_t dev, int left_step, int right_step)
+{
+ struct snd_mixer *m;
+ int level, left, right;
+ dev_t pdev;
+
+ pdev = mixer_get_devt(dev);
+ m = pdev->si_drv1;
+ snd_mtxlock(m->lock);
+ if (m->hwvol_muted) {
+ m->hwvol_muted = 0;
+ level = m->hwvol_mute_level;
+ } else
+ level = mixer_get(m, m->hwvol_mixer);
+ if (level != -1) {
+ left = level & 0xff;
+ right = level >> 8;
+ left += left_step * m->hwvol_step;
+ if (left < 0)
+ left = 0;
+ right += right_step * m->hwvol_step;
+ if (right < 0)
+ right = 0;
+ mixer_set(m, m->hwvol_mixer, left | right << 8);
+ }
+ snd_mtxunlock(m->lock);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static int
+mixer_open(dev_t i_dev, int flags, int mode, struct thread *td)
+{
+ struct snd_mixer *m;
+ intrmask_t s;
+
+ m = i_dev->si_drv1;
+ s = spltty();
+ snd_mtxlock(m->lock);
+
+ m->busy++;
+
+ snd_mtxunlock(m->lock);
+ splx(s);
+ return 0;
+}
+
+static int
+mixer_close(dev_t i_dev, int flags, int mode, struct thread *td)
+{
+ struct snd_mixer *m;
+ intrmask_t s;
+
+ m = i_dev->si_drv1;
+ s = spltty();
+ snd_mtxlock(m->lock);
+
+ if (!m->busy) {
+ snd_mtxunlock(m->lock);
+ splx(s);
+ return EBADF;
+ }
+ m->busy--;
+
+ snd_mtxunlock(m->lock);
+ splx(s);
+ return 0;
+}
+
+int
+mixer_ioctl(dev_t i_dev, u_long cmd, caddr_t arg, int mode, struct thread *td)
+{
+ struct snd_mixer *m;
+ intrmask_t s;
+ int ret, *arg_i = (int *)arg;
+ int v = -1, j = cmd & 0xff;
+
+ m = i_dev->si_drv1;
+ if (!m->busy)
+ return EBADF;
+
+ s = spltty();
+ snd_mtxlock(m->lock);
+ if ((cmd & MIXER_WRITE(0)) == MIXER_WRITE(0)) {
+ if (j == SOUND_MIXER_RECSRC)
+ ret = mixer_setrecsrc(m, *arg_i);
+ else
+ ret = mixer_set(m, j, *arg_i);
+ snd_mtxunlock(m->lock);
+ splx(s);
+ return (ret == 0)? 0 : ENXIO;
+ }
+
+ if ((cmd & MIXER_READ(0)) == MIXER_READ(0)) {
+ switch (j) {
+ case SOUND_MIXER_DEVMASK:
+ case SOUND_MIXER_CAPS:
+ case SOUND_MIXER_STEREODEVS:
+ v = mix_getdevs(m);
+ break;
+
+ case SOUND_MIXER_RECMASK:
+ v = mix_getrecdevs(m);
+ break;
+
+ case SOUND_MIXER_RECSRC:
+ v = mixer_getrecsrc(m);
+ break;
+
+ default:
+ v = mixer_get(m, j);
+ }
+ *arg_i = v;
+ snd_mtxunlock(m->lock);
+ return (v != -1)? 0 : ENXIO;
+ }
+ snd_mtxunlock(m->lock);
+ splx(s);
+ return ENXIO;
+}
+
+#ifdef USING_DEVFS
+static void
+mixer_clone(void *arg, char *name, int namelen, dev_t *dev)
+{
+ dev_t pdev;
+
+ if (*dev != NODEV)
+ return;
+ if (strcmp(name, "mixer") == 0) {
+ pdev = makedev(SND_CDEV_MAJOR, PCMMKMINOR(snd_unit, SND_DEV_CTL, 0));
+ if (pdev->si_flags & SI_NAMED)
+ *dev = pdev;
+ }
+}
+
+static void
+mixer_sysinit(void *p)
+{
+ mixer_ehtag = EVENTHANDLER_REGISTER(dev_clone, mixer_clone, 0, 1000);
+}
+
+static void
+mixer_sysuninit(void *p)
+{
+ if (mixer_ehtag != NULL)
+ EVENTHANDLER_DEREGISTER(dev_clone, mixer_ehtag);
+}
+
+SYSINIT(mixer_sysinit, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, mixer_sysinit, NULL);
+SYSUNINIT(mixer_sysuninit, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, mixer_sysuninit, NULL);
+#endif
+
+
diff --git a/sys/dev/sound/pcm/mixer.h b/sys/dev/sound/pcm/mixer.h
new file mode 100644
index 0000000..4f51bf2
--- /dev/null
+++ b/sys/dev/sound/pcm/mixer.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+int mixer_init(device_t dev, kobj_class_t cls, void *devinfo);
+int mixer_uninit(device_t dev);
+int mixer_reinit(device_t dev);
+int mixer_ioctl(dev_t i_dev, u_long cmd, caddr_t arg, int mode, struct thread *td);
+
+int mixer_hwvol_init(device_t dev);
+void mixer_hwvol_mute(device_t dev);
+void mixer_hwvol_step(device_t dev, int left_step, int right_step);
+
+void mix_setdevs(struct snd_mixer *m, u_int32_t v);
+void mix_setrecdevs(struct snd_mixer *m, u_int32_t v);
+u_int32_t mix_getdevs(struct snd_mixer *m);
+u_int32_t mix_getrecdevs(struct snd_mixer *m);
+void *mix_getdevinfo(struct snd_mixer *m);
+
+/*
+ * this is a kludge to allow hiding of the struct snd_mixer definition
+ * 512 should be enough for all architectures
+ */
+#define MIXER_SIZE (512 + sizeof(struct kobj))
+
+#define MIXER_DECLARE(name) DEFINE_CLASS(name, name ## _methods, MIXER_SIZE)
diff --git a/sys/dev/sound/pcm/mixer_if.m b/sys/dev/sound/pcm/mixer_if.m
new file mode 100644
index 0000000..38d99fb
--- /dev/null
+++ b/sys/dev/sound/pcm/mixer_if.m
@@ -0,0 +1,66 @@
+# KOBJ
+#
+# Copyright (c) 2000 Cameron Grant <cg@freebsd.org>
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# $FreeBSD$
+#
+
+#include <dev/sound/pcm/sound.h>
+
+INTERFACE mixer;
+
+CODE {
+
+ static int
+ mixer_noreinit(struct snd_mixer *m)
+ {
+ return 0;
+ }
+
+};
+
+METHOD int init {
+ struct snd_mixer *m;
+};
+
+METHOD int reinit {
+ struct snd_mixer *m;
+} DEFAULT mixer_noreinit;
+
+METHOD int uninit {
+ struct snd_mixer *m;
+};
+
+METHOD int set {
+ struct snd_mixer *m;
+ unsigned dev;
+ unsigned left;
+ unsigned right;
+};
+
+METHOD u_int32_t setrecsrc {
+ struct snd_mixer *m;
+ u_int32_t src;
+};
diff --git a/sys/dev/sound/pcm/sndstat.c b/sys/dev/sound/pcm/sndstat.c
new file mode 100644
index 0000000..bdaff2b
--- /dev/null
+++ b/sys/dev/sound/pcm/sndstat.c
@@ -0,0 +1,400 @@
+/*
+ * Copyright (c) 2001 Cameron Grant <gandalf@vilnya.demon.co.uk>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <dev/sound/pcm/sound.h>
+#include <dev/sound/pcm/vchan.h>
+
+SND_DECLARE_FILE("$FreeBSD$");
+
+#define SS_TYPE_MODULE 0
+#define SS_TYPE_FIRST 1
+#define SS_TYPE_PCM 1
+#define SS_TYPE_MIDI 2
+#define SS_TYPE_SEQUENCER 3
+#define SS_TYPE_LAST 3
+
+static d_open_t sndstat_open;
+static d_close_t sndstat_close;
+static d_read_t sndstat_read;
+
+static struct cdevsw sndstat_cdevsw = {
+ /* open */ sndstat_open,
+ /* close */ sndstat_close,
+ /* read */ sndstat_read,
+ /* write */ nowrite,
+ /* ioctl */ noioctl,
+ /* poll */ nopoll,
+ /* mmap */ nommap,
+ /* strategy */ nostrategy,
+ /* name */ "sndstat",
+ /* maj */ SND_CDEV_MAJOR,
+ /* dump */ nodump,
+ /* psize */ nopsize,
+ /* flags */ 0,
+};
+
+struct sndstat_entry {
+ SLIST_ENTRY(sndstat_entry) link;
+ device_t dev;
+ char *str;
+ sndstat_handler handler;
+ int type, unit;
+};
+
+#ifdef USING_MUTEX
+static struct mtx sndstat_lock;
+#endif
+static struct sbuf sndstat_sbuf;
+static dev_t sndstat_dev = 0;
+static int sndstat_isopen = 0;
+static int sndstat_bufptr;
+static int sndstat_maxunit = -1;
+static int sndstat_files = 0;
+
+static SLIST_HEAD(, sndstat_entry) sndstat_devlist = SLIST_HEAD_INITIALIZER(none);
+
+static int sndstat_verbose = 1;
+#ifdef USING_MUTEX
+TUNABLE_INT("hw.snd.verbose", &sndstat_verbose);
+#else
+TUNABLE_INT_DECL("hw.snd.verbose", 1, sndstat_verbose);
+#endif
+
+static int sndstat_prepare(struct sbuf *s);
+
+static int
+sysctl_hw_sndverbose(SYSCTL_HANDLER_ARGS)
+{
+ intrmask_t s;
+ int error, verbose;
+
+ verbose = sndstat_verbose;
+ error = sysctl_handle_int(oidp, &verbose, sizeof(verbose), req);
+ if (error == 0 && req->newptr != NULL) {
+ s = spltty();
+ mtx_lock(&sndstat_lock);
+ if (verbose < 0 || verbose > 3)
+ error = EINVAL;
+ else
+ sndstat_verbose = verbose;
+ mtx_unlock(&sndstat_lock);
+ splx(s);
+ }
+ return error;
+}
+SYSCTL_PROC(_hw_snd, OID_AUTO, verbose, CTLTYPE_INT | CTLFLAG_RW,
+ 0, sizeof(int), sysctl_hw_sndverbose, "I", "");
+
+static int
+sndstat_open(dev_t i_dev, int flags, int mode, struct thread *td)
+{
+ intrmask_t s;
+ int error;
+
+ s = spltty();
+ mtx_lock(&sndstat_lock);
+ if (sndstat_isopen) {
+ mtx_unlock(&sndstat_lock);
+ splx(s);
+ return EBUSY;
+ }
+ sndstat_isopen = 1;
+ mtx_unlock(&sndstat_lock);
+ splx(s);
+ if (sbuf_new(&sndstat_sbuf, NULL, 4096, 0) == NULL) {
+ error = ENXIO;
+ goto out;
+ }
+ sndstat_bufptr = 0;
+ error = (sndstat_prepare(&sndstat_sbuf) > 0) ? 0 : ENOMEM;
+out:
+ if (error) {
+ s = spltty();
+ mtx_lock(&sndstat_lock);
+ sndstat_isopen = 0;
+ mtx_unlock(&sndstat_lock);
+ splx(s);
+ }
+ return (error);
+}
+
+static int
+sndstat_close(dev_t i_dev, int flags, int mode, struct thread *td)
+{
+ intrmask_t s;
+
+ s = spltty();
+ mtx_lock(&sndstat_lock);
+ if (!sndstat_isopen) {
+ mtx_unlock(&sndstat_lock);
+ splx(s);
+ return EBADF;
+ }
+ sbuf_delete(&sndstat_sbuf);
+ sndstat_isopen = 0;
+
+ mtx_unlock(&sndstat_lock);
+ splx(s);
+ return 0;
+}
+
+static int
+sndstat_read(dev_t i_dev, struct uio *buf, int flag)
+{
+ intrmask_t s;
+ int l, err;
+
+ s = spltty();
+ mtx_lock(&sndstat_lock);
+ if (!sndstat_isopen) {
+ mtx_unlock(&sndstat_lock);
+ splx(s);
+ return EBADF;
+ }
+ l = min(buf->uio_resid, sbuf_len(&sndstat_sbuf) - sndstat_bufptr);
+ err = (l > 0)? uiomove(sbuf_data(&sndstat_sbuf) + sndstat_bufptr, l, buf) : 0;
+ sndstat_bufptr += l;
+
+ mtx_unlock(&sndstat_lock);
+ splx(s);
+ return err;
+}
+
+/************************************************************************/
+
+static struct sndstat_entry *
+sndstat_find(int type, int unit)
+{
+ struct sndstat_entry *ent;
+
+ SLIST_FOREACH(ent, &sndstat_devlist, link) {
+ if (ent->type == type && ent->unit == unit)
+ return ent;
+ }
+
+ return NULL;
+}
+
+int
+sndstat_register(device_t dev, char *str, sndstat_handler handler)
+{
+ intrmask_t s;
+ struct sndstat_entry *ent;
+ const char *devtype;
+ int type, unit;
+
+ if (dev) {
+ unit = device_get_unit(dev);
+ devtype = device_get_name(dev);
+ if (!strcmp(devtype, "pcm"))
+ type = SS_TYPE_PCM;
+ else if (!strcmp(devtype, "midi"))
+ type = SS_TYPE_MIDI;
+ else if (!strcmp(devtype, "sequencer"))
+ type = SS_TYPE_SEQUENCER;
+ else
+ return EINVAL;
+ } else {
+ type = SS_TYPE_MODULE;
+ unit = -1;
+ }
+
+ ent = malloc(sizeof *ent, M_DEVBUF, M_ZERO | M_WAITOK);
+ if (!ent)
+ return ENOSPC;
+
+ ent->dev = dev;
+ ent->str = str;
+ ent->type = type;
+ ent->unit = unit;
+ ent->handler = handler;
+
+ s = spltty();
+ mtx_lock(&sndstat_lock);
+ SLIST_INSERT_HEAD(&sndstat_devlist, ent, link);
+ if (type == SS_TYPE_MODULE)
+ sndstat_files++;
+ sndstat_maxunit = (unit > sndstat_maxunit)? unit : sndstat_maxunit;
+ mtx_unlock(&sndstat_lock);
+ splx(s);
+
+ return 0;
+}
+
+int
+sndstat_registerfile(char *str)
+{
+ return sndstat_register(NULL, str, NULL);
+}
+
+int
+sndstat_unregister(device_t dev)
+{
+ intrmask_t s;
+ struct sndstat_entry *ent;
+
+ s = spltty();
+ mtx_lock(&sndstat_lock);
+ SLIST_FOREACH(ent, &sndstat_devlist, link) {
+ if (ent->dev == dev) {
+ SLIST_REMOVE(&sndstat_devlist, ent, sndstat_entry, link);
+ mtx_unlock(&sndstat_lock);
+ splx(s);
+ free(ent, M_DEVBUF);
+
+ return 0;
+ }
+ }
+ mtx_unlock(&sndstat_lock);
+ splx(s);
+
+ return ENXIO;
+}
+
+int
+sndstat_unregisterfile(char *str)
+{
+ intrmask_t s;
+ struct sndstat_entry *ent;
+
+ s = spltty();
+ mtx_lock(&sndstat_lock);
+ SLIST_FOREACH(ent, &sndstat_devlist, link) {
+ if (ent->dev == NULL && ent->str == str) {
+ SLIST_REMOVE(&sndstat_devlist, ent, sndstat_entry, link);
+ sndstat_files--;
+ mtx_unlock(&sndstat_lock);
+ splx(s);
+ free(ent, M_DEVBUF);
+
+ return 0;
+ }
+ }
+ mtx_unlock(&sndstat_lock);
+ splx(s);
+
+ return ENXIO;
+}
+
+/************************************************************************/
+
+static int
+sndstat_prepare(struct sbuf *s)
+{
+ struct sndstat_entry *ent;
+ int i, j;
+
+ sbuf_printf(s, "FreeBSD Audio Driver (newpcm)\n");
+ if (SLIST_EMPTY(&sndstat_devlist)) {
+ sbuf_printf(s, "No devices installed.\n");
+ sbuf_finish(s);
+ return sbuf_len(s);
+ }
+
+ sbuf_printf(s, "Installed devices:\n");
+
+ for (i = 0; i <= sndstat_maxunit; i++) {
+ for (j = SS_TYPE_FIRST; j <= SS_TYPE_LAST; j++) {
+ ent = sndstat_find(j, i);
+ if (!ent)
+ continue;
+ sbuf_printf(s, "%s:", device_get_nameunit(ent->dev));
+ sbuf_printf(s, " <%s>", device_get_desc(ent->dev));
+ sbuf_printf(s, " %s", ent->str);
+ if (ent->handler)
+ ent->handler(s, ent->dev, sndstat_verbose);
+ else
+ sbuf_printf(s, " [no handler]");
+ sbuf_printf(s, "\n");
+ }
+ }
+
+ if (sndstat_verbose >= 3 && sndstat_files > 0) {
+ sbuf_printf(s, "\nFile Versions:\n");
+
+ SLIST_FOREACH(ent, &sndstat_devlist, link) {
+ if (ent->dev == NULL && ent->str != NULL)
+ sbuf_printf(s, "%s\n", ent->str);
+ }
+ }
+
+ sbuf_finish(s);
+ return sbuf_len(s);
+}
+
+static int
+sndstat_init(void)
+{
+ mtx_init(&sndstat_lock, "sndstat", NULL, 0);
+ sndstat_dev = make_dev(&sndstat_cdevsw, SND_DEV_STATUS, UID_ROOT, GID_WHEEL, 0444, "sndstat");
+
+ return (sndstat_dev != 0)? 0 : ENXIO;
+}
+
+static int
+sndstat_uninit(void)
+{
+ intrmask_t s;
+
+ s = spltty();
+ mtx_lock(&sndstat_lock);
+ if (sndstat_isopen) {
+ mtx_unlock(&sndstat_lock);
+ splx(s);
+ return EBUSY;
+ }
+
+ if (sndstat_dev)
+ destroy_dev(sndstat_dev);
+ sndstat_dev = 0;
+
+ splx(s);
+ mtx_destroy(&sndstat_lock);
+ return 0;
+}
+
+int
+sndstat_busy(void)
+{
+ return (sndstat_isopen);
+}
+
+static void
+sndstat_sysinit(void *p)
+{
+ sndstat_init();
+}
+
+static void
+sndstat_sysuninit(void *p)
+{
+ sndstat_uninit();
+}
+
+SYSINIT(sndstat_sysinit, SI_SUB_DRIVERS, SI_ORDER_FIRST, sndstat_sysinit, NULL);
+SYSUNINIT(sndstat_sysuninit, SI_SUB_DRIVERS, SI_ORDER_FIRST, sndstat_sysuninit, NULL);
+
+
diff --git a/sys/dev/sound/pcm/sound.c b/sys/dev/sound/pcm/sound.c
new file mode 100644
index 0000000..2b77684
--- /dev/null
+++ b/sys/dev/sound/pcm/sound.c
@@ -0,0 +1,907 @@
+/*
+ * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
+ * (C) 1997 Luigi Rizzo (luigi@iet.unipi.it)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <dev/sound/pcm/sound.h>
+#include <dev/sound/pcm/vchan.h>
+#include <sys/sysctl.h>
+
+#include "feeder_if.h"
+
+SND_DECLARE_FILE("$FreeBSD$");
+
+struct snddev_channel {
+ SLIST_ENTRY(snddev_channel) link;
+ struct pcm_channel *channel;
+};
+
+struct snddev_info {
+ SLIST_HEAD(, snddev_channel) channels;
+ struct pcm_channel *fakechan;
+ unsigned devcount, playcount, reccount, vchancount;
+ unsigned flags;
+ int inprog;
+ unsigned int bufsz;
+ void *devinfo;
+ device_t dev;
+ char status[SND_STATUSLEN];
+ struct sysctl_ctx_list sysctl_tree;
+ struct sysctl_oid *sysctl_tree_top;
+ struct mtx *lock;
+};
+
+devclass_t pcm_devclass;
+
+int pcm_veto_load = 1;
+
+#ifdef USING_DEVFS
+int snd_unit = 0;
+TUNABLE_INT("hw.snd.unit", &snd_unit);
+#endif
+
+int snd_maxautovchans = 0;
+TUNABLE_INT("hw.snd.maxautovchans", &snd_maxautovchans);
+
+SYSCTL_NODE(_hw, OID_AUTO, snd, CTLFLAG_RD, 0, "Sound driver");
+
+static int sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose);
+
+struct sysctl_ctx_list *
+snd_sysctl_tree(device_t dev)
+{
+ struct snddev_info *d = device_get_softc(dev);
+
+ return &d->sysctl_tree;
+}
+
+struct sysctl_oid *
+snd_sysctl_tree_top(device_t dev)
+{
+ struct snddev_info *d = device_get_softc(dev);
+
+ return d->sysctl_tree_top;
+}
+
+void *
+snd_mtxcreate(const char *desc, const char *type)
+{
+#ifdef USING_MUTEX
+ struct mtx *m;
+
+ m = malloc(sizeof(*m), M_DEVBUF, M_WAITOK | M_ZERO);
+ if (m == NULL)
+ return NULL;
+ mtx_init(m, desc, type, MTX_RECURSE);
+ return m;
+#else
+ return (void *)0xcafebabe;
+#endif
+}
+
+void
+snd_mtxfree(void *m)
+{
+#ifdef USING_MUTEX
+ struct mtx *mtx = m;
+
+ /* mtx_assert(mtx, MA_OWNED); */
+ mtx_destroy(mtx);
+ free(mtx, M_DEVBUF);
+#endif
+}
+
+void
+snd_mtxassert(void *m)
+{
+#ifdef USING_MUTEX
+#ifdef INVARIANTS
+ struct mtx *mtx = m;
+
+ mtx_assert(mtx, MA_OWNED);
+#endif
+#endif
+}
+/*
+void
+snd_mtxlock(void *m)
+{
+#ifdef USING_MUTEX
+ struct mtx *mtx = m;
+
+ mtx_lock(mtx);
+#endif
+}
+
+void
+snd_mtxunlock(void *m)
+{
+#ifdef USING_MUTEX
+ struct mtx *mtx = m;
+
+ mtx_unlock(mtx);
+#endif
+}
+*/
+int
+snd_setup_intr(device_t dev, struct resource *res, int flags, driver_intr_t hand, void *param, void **cookiep)
+{
+#ifdef USING_MUTEX
+ flags &= INTR_MPSAFE;
+ flags |= INTR_TYPE_AV;
+#else
+ flags = INTR_TYPE_AV;
+#endif
+ return bus_setup_intr(dev, res, flags, hand, param, cookiep);
+}
+
+void
+pcm_lock(struct snddev_info *d)
+{
+ snd_mtxlock(d->lock);
+}
+
+void
+pcm_unlock(struct snddev_info *d)
+{
+ snd_mtxunlock(d->lock);
+}
+
+struct pcm_channel *
+pcm_getfakechan(struct snddev_info *d)
+{
+ return d->fakechan;
+}
+
+/* return a locked channel */
+struct pcm_channel *
+pcm_chnalloc(struct snddev_info *d, int direction, pid_t pid, int chnum)
+{
+ struct pcm_channel *c;
+ struct snddev_channel *sce;
+ int err;
+
+ snd_mtxassert(d->lock);
+
+ /* scan for a free channel */
+ SLIST_FOREACH(sce, &d->channels, link) {
+ c = sce->channel;
+ CHN_LOCK(c);
+ if ((c->direction == direction) && !(c->flags & CHN_F_BUSY)) {
+ if (chnum == -1 || c->num == chnum) {
+ c->flags |= CHN_F_BUSY;
+ c->pid = pid;
+ return c;
+ }
+ }
+ CHN_UNLOCK(c);
+ }
+
+ /* no channel available */
+ if (direction == PCMDIR_PLAY) {
+ if ((d->vchancount > 0) && (d->vchancount < snd_maxautovchans)) {
+ /* try to create a vchan */
+ SLIST_FOREACH(sce, &d->channels, link) {
+ c = sce->channel;
+ if (!SLIST_EMPTY(&c->children)) {
+ err = vchan_create(c);
+ if (!err)
+ return pcm_chnalloc(d, direction, pid, -1);
+ else
+ device_printf(d->dev, "vchan_create(%s) == %d\n", c->name, err);
+ }
+ }
+ }
+ }
+
+ return NULL;
+}
+
+/* release a locked channel and unlock it */
+int
+pcm_chnrelease(struct pcm_channel *c)
+{
+ CHN_LOCKASSERT(c);
+ c->flags &= ~CHN_F_BUSY;
+ c->pid = -1;
+ CHN_UNLOCK(c);
+ return 0;
+}
+
+int
+pcm_chnref(struct pcm_channel *c, int ref)
+{
+ int r;
+
+ CHN_LOCKASSERT(c);
+ c->refcount += ref;
+ r = c->refcount;
+ return r;
+}
+
+int
+pcm_inprog(struct snddev_info *d, int delta)
+{
+ d->inprog += delta;
+ return d->inprog;
+}
+
+static void
+pcm_setmaxautovchans(struct snddev_info *d, int num)
+{
+ struct pcm_channel *c;
+ struct snddev_channel *sce;
+ int err, done;
+
+ if (num > 0 && d->vchancount == 0) {
+ SLIST_FOREACH(sce, &d->channels, link) {
+ c = sce->channel;
+ if ((c->direction == PCMDIR_PLAY) && !(c->flags & CHN_F_BUSY)) {
+ c->flags |= CHN_F_BUSY;
+ err = vchan_create(c);
+ if (err) {
+ device_printf(d->dev, "vchan_create(%s) == %d\n", c->name, err);
+ c->flags &= ~CHN_F_BUSY;
+ }
+ return;
+ }
+ }
+ }
+ if (num == 0 && d->vchancount > 0) {
+ done = 0;
+ while (!done) {
+ done = 1;
+ SLIST_FOREACH(sce, &d->channels, link) {
+ c = sce->channel;
+ if ((c->flags & CHN_F_VIRTUAL) && !(c->flags & CHN_F_BUSY)) {
+ done = 0;
+ err = vchan_destroy(c);
+ if (err)
+ device_printf(d->dev, "vchan_destroy(%s) == %d\n", c->name, err);
+ break; /* restart */
+ }
+ }
+ }
+ }
+}
+
+#ifdef USING_DEVFS
+static int
+sysctl_hw_snd_unit(SYSCTL_HANDLER_ARGS)
+{
+ struct snddev_info *d;
+ int error, unit;
+
+ unit = snd_unit;
+ error = sysctl_handle_int(oidp, &unit, sizeof(unit), req);
+ if (error == 0 && req->newptr != NULL) {
+ if (unit < 0 || unit >= devclass_get_maxunit(pcm_devclass))
+ return EINVAL;
+ d = devclass_get_softc(pcm_devclass, unit);
+ if (d == NULL || SLIST_EMPTY(&d->channels))
+ return EINVAL;
+ snd_unit = unit;
+ }
+ return (error);
+}
+SYSCTL_PROC(_hw_snd, OID_AUTO, unit, CTLTYPE_INT | CTLFLAG_RW,
+ 0, sizeof(int), sysctl_hw_snd_unit, "I", "");
+#endif
+
+static int
+sysctl_hw_snd_maxautovchans(SYSCTL_HANDLER_ARGS)
+{
+ struct snddev_info *d;
+ int i, v, error;
+
+ v = snd_maxautovchans;
+ error = sysctl_handle_int(oidp, &v, sizeof(v), req);
+ if (error == 0 && req->newptr != NULL) {
+ if (v < 0 || v >= SND_MAXVCHANS)
+ return EINVAL;
+ if (v != snd_maxautovchans) {
+ for (i = 0; i < devclass_get_maxunit(pcm_devclass); i++) {
+ d = devclass_get_softc(pcm_devclass, i);
+ if (!d)
+ continue;
+ pcm_setmaxautovchans(d, v);
+ }
+ }
+ snd_maxautovchans = v;
+ }
+ return (error);
+}
+SYSCTL_PROC(_hw_snd, OID_AUTO, maxautovchans, CTLTYPE_INT | CTLFLAG_RW,
+ 0, sizeof(int), sysctl_hw_snd_maxautovchans, "I", "");
+
+struct pcm_channel *
+pcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t cls, int dir, void *devinfo)
+{
+ struct pcm_channel *ch;
+ char *dirs;
+ int err, *pnum;
+
+ switch(dir) {
+ case PCMDIR_PLAY:
+ dirs = "play";
+ pnum = &d->playcount;
+ break;
+
+ case PCMDIR_REC:
+ dirs = "record";
+ pnum = &d->reccount;
+ break;
+
+ case PCMDIR_VIRTUAL:
+ dirs = "virtual";
+ dir = PCMDIR_PLAY;
+ pnum = &d->vchancount;
+ break;
+
+ default:
+ return NULL;
+ }
+
+ ch = malloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO);
+ if (!ch)
+ return NULL;
+
+ ch->methods = kobj_create(cls, M_DEVBUF, M_WAITOK);
+ if (!ch->methods) {
+ free(ch, M_DEVBUF);
+
+ return NULL;
+ }
+
+ snd_mtxlock(d->lock);
+ ch->num = (*pnum)++;
+ snd_mtxunlock(d->lock);
+
+ ch->pid = -1;
+ ch->parentsnddev = d;
+ ch->parentchannel = parent;
+ ch->dev = d->dev;
+ snprintf(ch->name, 32, "%s:%s:%d", device_get_nameunit(ch->dev), dirs, ch->num);
+
+ err = chn_init(ch, devinfo, dir);
+ if (err) {
+ device_printf(d->dev, "chn_init(%s) failed: err = %d\n", ch->name, err);
+ kobj_delete(ch->methods, M_DEVBUF);
+ free(ch, M_DEVBUF);
+ snd_mtxlock(d->lock);
+ (*pnum)--;
+ snd_mtxunlock(d->lock);
+
+ return NULL;
+ }
+
+ return ch;
+}
+
+int
+pcm_chn_destroy(struct pcm_channel *ch)
+{
+ struct snddev_info *d;
+ int err;
+
+ d = ch->parentsnddev;
+ err = chn_kill(ch);
+ if (err) {
+ device_printf(d->dev, "chn_kill(%s) failed, err = %d\n", ch->name, err);
+ return err;
+ }
+
+ kobj_delete(ch->methods, M_DEVBUF);
+ free(ch, M_DEVBUF);
+
+ return 0;
+}
+
+int
+pcm_chn_add(struct snddev_info *d, struct pcm_channel *ch, int mkdev)
+{
+ struct snddev_channel *sce, *tmp, *after;
+ int unit = device_get_unit(d->dev);
+ int x = -1;
+
+ sce = malloc(sizeof(*sce), M_DEVBUF, M_WAITOK | M_ZERO);
+ if (!sce) {
+ return ENOMEM;
+ }
+
+ snd_mtxlock(d->lock);
+ sce->channel = ch;
+ if (SLIST_EMPTY(&d->channels)) {
+ SLIST_INSERT_HEAD(&d->channels, sce, link);
+ } else {
+ after = NULL;
+ SLIST_FOREACH(tmp, &d->channels, link) {
+ after = tmp;
+ }
+ SLIST_INSERT_AFTER(after, sce, link);
+ }
+ if (mkdev)
+ x = d->devcount++;
+ snd_mtxunlock(d->lock);
+
+ if (mkdev) {
+ dsp_register(unit, x);
+ if (ch->direction == PCMDIR_REC)
+ dsp_registerrec(unit, ch->num);
+ }
+
+ return 0;
+}
+
+int
+pcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch, int rmdev)
+{
+ struct snddev_channel *sce;
+ int unit = device_get_unit(d->dev);
+
+ snd_mtxlock(d->lock);
+ SLIST_FOREACH(sce, &d->channels, link) {
+ if (sce->channel == ch)
+ goto gotit;
+ }
+ snd_mtxunlock(d->lock);
+ return EINVAL;
+gotit:
+ SLIST_REMOVE(&d->channels, sce, snddev_channel, link);
+ if (rmdev) {
+ dsp_unregister(unit, --d->devcount);
+ if (ch->direction == PCMDIR_REC)
+ dsp_unregisterrec(unit, ch->num);
+ }
+
+ if (ch->direction == PCMDIR_REC)
+ d->reccount--;
+ else if (ch->flags & CHN_F_VIRTUAL)
+ d->vchancount--;
+ else
+ d->playcount--;
+
+ snd_mtxunlock(d->lock);
+ free(sce, M_DEVBUF);
+
+ return 0;
+}
+
+int
+pcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo)
+{
+ struct snddev_info *d = device_get_softc(dev);
+ struct pcm_channel *ch;
+ int err;
+
+ ch = pcm_chn_create(d, NULL, cls, dir, devinfo);
+ if (!ch) {
+ device_printf(d->dev, "pcm_chn_create(%s, %d, %p) failed\n", cls->name, dir, devinfo);
+ return ENODEV;
+ }
+
+ err = pcm_chn_add(d, ch, 1);
+ if (err) {
+ device_printf(d->dev, "pcm_chn_add(%s) failed, err=%d\n", ch->name, err);
+ snd_mtxunlock(d->lock);
+ pcm_chn_destroy(ch);
+ return err;
+ }
+
+ if (snd_maxautovchans > 0 && (d->flags & SD_F_AUTOVCHAN) &&
+ ch->direction == PCMDIR_PLAY && d->vchancount == 0) {
+ ch->flags |= CHN_F_BUSY;
+ err = vchan_create(ch);
+ if (err) {
+ device_printf(d->dev, "vchan_create(%s) == %d\n", ch->name, err);
+ ch->flags &= ~CHN_F_BUSY;
+ }
+ }
+
+ return err;
+}
+
+static int
+pcm_killchan(device_t dev)
+{
+ struct snddev_info *d = device_get_softc(dev);
+ struct snddev_channel *sce;
+ struct pcm_channel *ch;
+ int error = 0;
+
+ snd_mtxlock(d->lock);
+ sce = SLIST_FIRST(&d->channels);
+ snd_mtxunlock(d->lock);
+ ch = sce->channel;
+
+ error = pcm_chn_remove(d, sce->channel, 1);
+ if (error)
+ return (error);
+ return (pcm_chn_destroy(ch));
+}
+
+int
+pcm_setstatus(device_t dev, char *str)
+{
+ struct snddev_info *d = device_get_softc(dev);
+
+ snd_mtxlock(d->lock);
+ strncpy(d->status, str, SND_STATUSLEN);
+ snd_mtxunlock(d->lock);
+ return 0;
+}
+
+u_int32_t
+pcm_getflags(device_t dev)
+{
+ struct snddev_info *d = device_get_softc(dev);
+
+ return d->flags;
+}
+
+void
+pcm_setflags(device_t dev, u_int32_t val)
+{
+ struct snddev_info *d = device_get_softc(dev);
+
+ d->flags = val;
+}
+
+void *
+pcm_getdevinfo(device_t dev)
+{
+ struct snddev_info *d = device_get_softc(dev);
+
+ return d->devinfo;
+}
+
+unsigned int
+pcm_getbuffersize(device_t dev, unsigned int min, unsigned int deflt, unsigned int max)
+{
+ struct snddev_info *d = device_get_softc(dev);
+ int sz, x;
+
+ sz = 0;
+ if (resource_int_value(device_get_name(dev), device_get_unit(dev), "buffersize", &sz) == 0) {
+ x = sz;
+ RANGE(sz, min, max);
+ if (x != sz)
+ device_printf(dev, "'buffersize=%d' hint is out of range (%d-%d), using %d\n", x, min, max, sz);
+ x = min;
+ while (x < sz)
+ x <<= 1;
+ if (x > sz)
+ x >>= 1;
+ if (x != sz) {
+ device_printf(dev, "'buffersize=%d' hint is not a power of 2, using %d\n", sz, x);
+ sz = x;
+ }
+ } else {
+ sz = deflt;
+ }
+
+ d->bufsz = sz;
+
+ return sz;
+}
+
+int
+pcm_register(device_t dev, void *devinfo, int numplay, int numrec)
+{
+ struct snddev_info *d = device_get_softc(dev);
+
+ if (pcm_veto_load) {
+ device_printf(dev, "disabled due to an error while initialising: %d\n", pcm_veto_load);
+
+ return EINVAL;
+ }
+
+ d->lock = snd_mtxcreate(device_get_nameunit(dev), "sound cdev");
+
+ d->flags = 0;
+ d->dev = dev;
+ d->devinfo = devinfo;
+ d->devcount = 0;
+ d->reccount = 0;
+ d->playcount = 0;
+ d->vchancount = 0;
+ d->inprog = 0;
+
+ if (((numplay == 0) || (numrec == 0)) && (numplay != numrec))
+ d->flags |= SD_F_SIMPLEX;
+
+ d->fakechan = fkchan_setup(dev);
+ chn_init(d->fakechan, NULL, 0);
+
+#ifdef SND_DYNSYSCTL
+ sysctl_ctx_init(&d->sysctl_tree);
+ d->sysctl_tree_top = SYSCTL_ADD_NODE(&d->sysctl_tree,
+ SYSCTL_STATIC_CHILDREN(_hw_snd), OID_AUTO,
+ device_get_nameunit(dev), CTLFLAG_RD, 0, "");
+ if (d->sysctl_tree_top == NULL) {
+ sysctl_ctx_free(&d->sysctl_tree);
+ goto no;
+ }
+ 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)
+ vchan_initsys(dev);
+ if (numplay == 1)
+ d->flags |= SD_F_AUTOVCHAN;
+
+ sndstat_register(dev, d->status, sndstat_prepare_pcm);
+ return 0;
+no:
+ snd_mtxfree(d->lock);
+ return ENXIO;
+}
+
+int
+pcm_unregister(device_t dev)
+{
+ struct snddev_info *d = device_get_softc(dev);
+ struct snddev_channel *sce;
+ struct pcm_channel *ch;
+
+ snd_mtxlock(d->lock);
+ if (d->inprog) {
+ device_printf(dev, "unregister: operation in progress\n");
+ snd_mtxunlock(d->lock);
+ 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);
+ return EBUSY;
+ }
+ }
+ if (mixer_uninit(dev)) {
+ device_printf(dev, "unregister: mixer busy\n");
+ snd_mtxunlock(d->lock);
+ return EBUSY;
+ }
+
+#ifdef SND_DYNSYSCTL
+ d->sysctl_tree_top = NULL;
+ sysctl_ctx_free(&d->sysctl_tree);
+#endif
+ while (!SLIST_EMPTY(&d->channels))
+ pcm_killchan(dev);
+
+ chn_kill(d->fakechan);
+ fkchan_kill(d->fakechan);
+
+ sndstat_unregister(dev);
+ snd_mtxfree(d->lock);
+ return 0;
+}
+
+/************************************************************************/
+
+static int
+sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose)
+{
+ struct snddev_info *d;
+ struct snddev_channel *sce;
+ struct pcm_channel *c;
+ struct pcm_feeder *f;
+ int pc, rc, vc;
+
+ if (verbose < 1)
+ return 0;
+
+ d = device_get_softc(dev);
+ if (!d)
+ return ENXIO;
+
+ snd_mtxlock(d->lock);
+ if (!SLIST_EMPTY(&d->channels)) {
+ pc = rc = vc = 0;
+ SLIST_FOREACH(sce, &d->channels, link) {
+ c = sce->channel;
+ if (c->direction == PCMDIR_PLAY) {
+ if (c->flags & CHN_F_VIRTUAL)
+ vc++;
+ else
+ pc++;
+ } else
+ rc++;
+ }
+ sbuf_printf(s, " (%dp/%dr/%dv channels%s%s)", d->playcount, d->reccount, d->vchancount,
+ (d->flags & SD_F_SIMPLEX)? "" : " duplex",
+#ifdef USING_DEVFS
+ (device_get_unit(dev) == snd_unit)? " default" : ""
+#else
+ ""
+#endif
+ );
+ if (verbose <= 1)
+ goto skipverbose;
+ SLIST_FOREACH(sce, &d->channels, link) {
+ c = sce->channel;
+ sbuf_printf(s, "\n\t");
+
+ sbuf_printf(s, "%s[%s]: ", c->parentchannel? c->parentchannel->name : "", c->name);
+ sbuf_printf(s, "spd %d", c->speed);
+ if (c->speed != sndbuf_getspd(c->bufhard))
+ sbuf_printf(s, "/%d", sndbuf_getspd(c->bufhard));
+ sbuf_printf(s, ", fmt 0x%08x", c->format);
+ if (c->format != sndbuf_getfmt(c->bufhard))
+ sbuf_printf(s, "/0x%08x", sndbuf_getfmt(c->bufhard));
+ sbuf_printf(s, ", flags %08x", c->flags);
+ if (c->pid != -1)
+ sbuf_printf(s, ", pid %d", c->pid);
+ sbuf_printf(s, "\n\t");
+
+ 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));
+ else
+ sbuf_printf(s, "underruns %d, ready %d",
+ c->xruns, sndbuf_getready(c->bufsoft));
+ sbuf_printf(s, "\n\t");
+ }
+
+ sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "hardware" : "userland");
+ sbuf_printf(s, " -> ");
+ f = c->feeder;
+ while (f->source != NULL)
+ f = f->source;
+ while (f != NULL) {
+ sbuf_printf(s, "%s", f->class->name);
+ if (f->desc->type == FEEDER_FMT)
+ sbuf_printf(s, "(0x%08x -> 0x%08x)", f->desc->in, f->desc->out);
+ if (f->desc->type == FEEDER_RATE)
+ sbuf_printf(s, "(%d -> %d)", FEEDER_GET(f, FEEDRATE_SRC), FEEDER_GET(f, FEEDRATE_DST));
+ if (f->desc->type == FEEDER_ROOT || f->desc->type == FEEDER_MIXER)
+ sbuf_printf(s, "(0x%08x)", f->desc->out);
+ sbuf_printf(s, " -> ");
+ f = f->parent;
+ }
+ sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "userland" : "hardware");
+ }
+ } else
+ sbuf_printf(s, " (mixer only)");
+skipverbose:
+ snd_mtxunlock(d->lock);
+
+ return 0;
+}
+
+/************************************************************************/
+
+#ifdef SND_DYNSYSCTL
+int
+sysctl_hw_snd_vchans(SYSCTL_HANDLER_ARGS)
+{
+ struct snddev_info *d;
+ struct snddev_channel *sce;
+ struct pcm_channel *c;
+ int err, newcnt, cnt;
+
+ d = oidp->oid_arg1;
+
+ pcm_lock(d);
+ cnt = 0;
+ SLIST_FOREACH(sce, &d->channels, link) {
+ c = sce->channel;
+ if ((c->direction == PCMDIR_PLAY) && (c->flags & CHN_F_VIRTUAL))
+ cnt++;
+ }
+ newcnt = cnt;
+
+ pcm_unlock(d);
+ err = sysctl_handle_int(oidp, &newcnt, sizeof(newcnt), req);
+ pcm_lock(d);
+ /*
+ * Since we dropped the pcm_lock, reload cnt now as it may
+ * have changed.
+ */
+ cnt = 0;
+ SLIST_FOREACH(sce, &d->channels, link) {
+ c = sce->channel;
+ if ((c->direction == PCMDIR_PLAY) && (c->flags & CHN_F_VIRTUAL))
+ cnt++;
+ }
+ if (err == 0 && req->newptr != NULL) {
+ if (newcnt < 0 || newcnt > SND_MAXVCHANS) {
+ pcm_unlock(d);
+ return EINVAL;
+ }
+
+ if (newcnt > cnt) {
+ /* add new vchans - find a parent channel first */
+ SLIST_FOREACH(sce, &d->channels, link) {
+ c = sce->channel;
+ /* not a candidate if not a play channel */
+ if (c->direction != PCMDIR_PLAY)
+ continue;
+ /* not a candidate if a virtual channel */
+ if (c->flags & CHN_F_VIRTUAL)
+ continue;
+ /* not a candidate if it's in use */
+ if ((c->flags & CHN_F_BUSY) && (SLIST_EMPTY(&c->children)))
+ continue;
+ /*
+ * if we get here we're a nonvirtual play channel, and either
+ * 1) not busy
+ * 2) busy with children, not directly open
+ *
+ * thus we can add children
+ */
+ goto addok;
+ }
+ pcm_unlock(d);
+ return EBUSY;
+addok:
+ 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;
+ } else if (newcnt < cnt) {
+ while (err == 0 && newcnt < cnt) {
+ SLIST_FOREACH(sce, &d->channels, link) {
+ c = sce->channel;
+ if ((c->flags & (CHN_F_BUSY | CHN_F_VIRTUAL)) == CHN_F_VIRTUAL)
+ goto remok;
+ }
+ pcm_unlock(d);
+ return EINVAL;
+remok:
+ err = vchan_destroy(c);
+ if (err == 0)
+ cnt--;
+ }
+ }
+ }
+
+ pcm_unlock(d);
+ return err;
+}
+#endif
+
+/************************************************************************/
+
+static moduledata_t sndpcm_mod = {
+ "snd_pcm",
+ NULL,
+ NULL
+};
+DECLARE_MODULE(snd_pcm, sndpcm_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
+MODULE_VERSION(snd_pcm, PCM_MODVER);
diff --git a/sys/dev/sound/pcm/sound.h b/sys/dev/sound/pcm/sound.h
new file mode 100644
index 0000000..5038a28
--- /dev/null
+++ b/sys/dev/sound/pcm/sound.h
@@ -0,0 +1,289 @@
+/*
+ * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
+ * Copyright by Hannu Savolainen 1995
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * first, include kernel header files.
+ */
+
+#ifndef _OS_H_
+#define _OS_H_
+
+#ifdef _KERNEL
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/ioccom.h>
+#include <sys/filio.h>
+#include <sys/sockio.h>
+#include <sys/fcntl.h>
+#include <sys/tty.h>
+#include <sys/proc.h>
+#include <sys/kernel.h> /* for DATA_SET */
+#include <sys/module.h>
+#include <sys/conf.h>
+#include <sys/file.h>
+#include <sys/uio.h>
+#include <sys/syslog.h>
+#include <sys/errno.h>
+#include <sys/malloc.h>
+#include <sys/bus.h>
+#if __FreeBSD_version < 500000
+#include <sys/buf.h>
+#endif
+#include <machine/clock.h> /* for DELAY */
+#include <machine/resource.h>
+#include <machine/bus.h>
+#include <sys/rman.h>
+#include <sys/mman.h>
+#include <sys/poll.h>
+#include <sys/sbuf.h>
+#include <sys/soundcard.h>
+#include <sys/sysctl.h>
+#include <isa/isavar.h>
+#include <sys/kobj.h>
+#include <vm/vm.h>
+#include <vm/pmap.h>
+
+#undef USING_MUTEX
+#undef USING_DEVFS
+
+#if __FreeBSD_version > 500000
+#include <sys/lock.h>
+#include <sys/mutex.h>
+
+#define USING_MUTEX
+#define USING_DEVFS
+#endif
+
+#define SND_DYNSYSCTL
+
+#ifndef INTR_MPSAFE
+#define INTR_TYPE_AV INTR_TYPE_TTY
+#endif
+
+#ifndef INTR_MPSAFE
+#define INTR_MPSAFE 0
+#endif
+
+struct pcm_channel;
+struct pcm_feeder;
+struct snd_dbuf;
+struct snd_mixer;
+
+#include <dev/sound/pcm/buffer.h>
+#include <dev/sound/pcm/channel.h>
+#include <dev/sound/pcm/feeder.h>
+#include <dev/sound/pcm/mixer.h>
+#include <dev/sound/pcm/dsp.h>
+
+#define PCM_SOFTC_SIZE 512
+
+#define SND_STATUSLEN 64
+/* descriptor of audio device */
+#ifndef ISADMA_WRITE
+#define ISADMA_WRITE B_WRITE
+#define ISADMA_READ B_READ
+#define ISADMA_RAW B_RAW
+#endif
+
+#define PCM_MODVER 1
+
+#define PCM_MINVER 1
+#define PCM_PREFVER PCM_MODVER
+#define PCM_MAXVER 1
+
+/*
+PROPOSAL:
+each unit needs:
+status, mixer, dsp, dspW, audio, sequencer, midi-in, seq2, sndproc = 9 devices
+dspW and audio are deprecated.
+dsp needs min 64 channels, will give it 256
+
+minor = (unit << 20) + (dev << 16) + channel
+currently minor = (channel << 16) + (unit << 4) + dev
+
+nomenclature:
+ /dev/pcmX/dsp.(0..255)
+ /dev/pcmX/dspW
+ /dev/pcmX/audio
+ /dev/pcmX/status
+ /dev/pcmX/mixer
+ [etc.]
+*/
+
+#define PCMMINOR(x) (minor(x))
+#define PCMCHAN(x) ((PCMMINOR(x) & 0x00ff0000) >> 16)
+#define PCMUNIT(x) ((PCMMINOR(x) & 0x000000f0) >> 4)
+#define PCMDEV(x) (PCMMINOR(x) & 0x0000000f)
+#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_PRIO_RD 0x10000000
+#define SD_F_PRIO_WR 0x20000000
+#define SD_F_PRIO_SET (SD_F_PRIO_RD | SD_F_PRIO_WR)
+#define SD_F_DIR_SET 0x40000000
+#define SD_F_TRANSIENT 0xf0000000
+
+/* many variables should be reduced to a range. Here define a macro */
+#define RANGE(var, low, high) (var) = \
+ (((var)<(low))? (low) : ((var)>(high))? (high) : (var))
+#define DSP_BUFFSIZE (8192)
+
+/* 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_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)
+
+struct pcm_channel *fkchan_setup(device_t dev);
+int fkchan_kill(struct pcm_channel *c);
+
+/*
+ * Major nuber for the sound driver.
+ */
+#define SND_CDEV_MAJOR 30
+
+#define SND_MAXVCHANS 255
+
+/*
+ * Minor numbers for the sound driver.
+ *
+ * Unfortunately Creative called the codec chip of SB as a DSP. For this
+ * reason the /dev/dsp is reserved for digitized audio use. There is a
+ * device for true DSP processors but it will be called something else.
+ * In v3.0 it's /dev/sndproc but this could be a temporary solution.
+ */
+
+#define SND_DEV_CTL 0 /* Control port /dev/mixer */
+#define SND_DEV_SEQ 1 /* Sequencer /dev/sequencer */
+#define SND_DEV_MIDIN 2 /* Raw midi access */
+#define SND_DEV_DSP 3 /* Digitized voice /dev/dsp */
+#define SND_DEV_AUDIO 4 /* Sparc compatible /dev/audio */
+#define SND_DEV_DSP16 5 /* Like /dev/dsp but 16 bits/sample */
+#define SND_DEV_STATUS 6 /* /dev/sndstat */
+ /* #7 not in use now. */
+#define SND_DEV_SEQ2 8 /* /dev/sequencer, level 2 interface */
+#define SND_DEV_SNDPROC 9 /* /dev/sndproc for programmable devices */
+#define SND_DEV_PSS SND_DEV_SNDPROC /* ? */
+#define SND_DEV_NORESET 10
+#define SND_DEV_DSPREC 11 /* recording channels */
+
+#define DSP_DEFAULT_SPEED 8000
+
+#define ON 1
+#define OFF 0
+
+extern int pcm_veto_load;
+extern int snd_unit;
+extern devclass_t pcm_devclass;
+
+/*
+ * some macros for debugging purposes
+ * DDB/DEB to enable/disable debugging stuff
+ * BVDDB to enable debugging when bootverbose
+ */
+#define DDB(x) x /* XXX */
+#define BVDDB(x) if (bootverbose) x
+
+#ifndef DEB
+#define DEB(x)
+#endif
+
+SYSCTL_DECL(_hw_snd);
+
+struct sysctl_ctx_list *snd_sysctl_tree(device_t dev);
+struct sysctl_oid *snd_sysctl_tree_top(device_t dev);
+
+void pcm_lock(struct snddev_info *d);
+void pcm_unlock(struct snddev_info *d);
+struct pcm_channel *pcm_getfakechan(struct snddev_info *d);
+struct pcm_channel *pcm_chnalloc(struct snddev_info *d, int direction, pid_t pid, int chnum);
+int pcm_chnrelease(struct pcm_channel *c);
+int pcm_chnref(struct pcm_channel *c, int ref);
+int pcm_inprog(struct snddev_info *d, int delta);
+
+struct pcm_channel *pcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t cls, int dir, void *devinfo);
+int pcm_chn_destroy(struct pcm_channel *ch);
+int pcm_chn_add(struct snddev_info *d, struct pcm_channel *ch, int mkdev);
+int pcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch, int rmdev);
+
+int pcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo);
+unsigned int pcm_getbuffersize(device_t dev, unsigned int min, unsigned int deflt, unsigned int max);
+int pcm_register(device_t dev, void *devinfo, int numplay, int numrec);
+int pcm_unregister(device_t dev);
+int pcm_setstatus(device_t dev, char *str);
+u_int32_t pcm_getflags(device_t dev);
+void pcm_setflags(device_t dev, u_int32_t val);
+void *pcm_getdevinfo(device_t dev);
+
+int snd_setup_intr(device_t dev, struct resource *res, int flags,
+ driver_intr_t hand, void *param, void **cookiep);
+
+void *snd_mtxcreate(const char *desc, const char *type);
+void snd_mtxfree(void *m);
+void snd_mtxassert(void *m);
+/*
+void snd_mtxlock(void *m);
+void snd_mtxunlock(void *m);
+*/
+int sysctl_hw_snd_vchans(SYSCTL_HANDLER_ARGS);
+
+typedef int (*sndstat_handler)(struct sbuf *s, device_t dev, int verbose);
+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)
+
+#define _SND_DECLARE_FILE(uniq, version) \
+ __SND_DECLARE_FILE(uniq, version)
+
+#define __SND_DECLARE_FILE(uniq, version) \
+ static char sndstat_vinfo[] = version; \
+ SYSINIT(sdf_ ## uniq, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, sndstat_registerfile, sndstat_vinfo); \
+ SYSUNINIT(sdf_ ## uniq, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, sndstat_unregisterfile, sndstat_vinfo);
+
+/* usage of flags in device config entry (config file) */
+#define DV_F_DRQ_MASK 0x00000007 /* mask for secondary drq */
+#define DV_F_DUAL_DMA 0x00000010 /* set to use secondary dma channel */
+
+/* ought to be made obsolete */
+#define DV_F_DEV_MASK 0x0000ff00 /* force device type/class */
+#define DV_F_DEV_SHIFT 8 /* force device type/class */
+
+#define snd_mtxlock(m) mtx_lock(m)
+#define snd_mtxunlock(m) mtx_unlock(m)
+
+#endif /* _KERNEL */
+
+#endif /* _OS_H_ */
diff --git a/sys/dev/sound/pcm/vchan.c b/sys/dev/sound/pcm/vchan.c
new file mode 100644
index 0000000..8da3c3e
--- /dev/null
+++ b/sys/dev/sound/pcm/vchan.c
@@ -0,0 +1,344 @@
+/*
+ * Copyright (c) 2001 Cameron Grant <gandalf@vilnya.demon.co.uk>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <dev/sound/pcm/sound.h>
+#include <dev/sound/pcm/vchan.h>
+#include "feeder_if.h"
+
+SND_DECLARE_FILE("$FreeBSD$");
+
+struct vchinfo {
+ u_int32_t spd, fmt, blksz, bps, run;
+ struct pcm_channel *channel, *parent;
+ struct pcmchan_caps caps;
+};
+
+static u_int32_t vchan_fmt[] = {
+ AFMT_S16_LE,
+ AFMT_STEREO | AFMT_S16_LE,
+ 0
+};
+
+static int
+vchan_mix_s16(int16_t *to, int16_t *tmp, unsigned int count)
+{
+ /*
+ * to is the output buffer, tmp is the input buffer
+ * count is the number of 16bit samples to mix
+ */
+ int i;
+ int x;
+
+ for(i = 0; i < count; i++) {
+ x = to[i];
+ x += tmp[i];
+ if (x < -32768) {
+ /* printf("%d + %d = %d (u)\n", to[i], tmp[i], x); */
+ x = -32768;
+ }
+ if (x > 32767) {
+ /* printf("%d + %d = %d (o)\n", to[i], tmp[i], x); */
+ x = 32767;
+ }
+ to[i] = x & 0x0000ffff;
+ }
+ return 0;
+}
+
+static int
+feed_vchan_s16(struct pcm_feeder *f, struct pcm_channel *c, u_int8_t *b, u_int32_t count, void *source)
+{
+ /* we're going to abuse things a bit */
+ struct snd_dbuf *src = source;
+ struct pcmchan_children *cce;
+ struct pcm_channel *ch;
+ int16_t *tmp, *dst;
+ unsigned int cnt;
+
+ KASSERT(sndbuf_getsize(src) >= count, ("bad bufsize"));
+ count &= ~1;
+ bzero(b, count);
+
+ /*
+ * we are going to use our source as a temporary buffer since it's
+ * got no other purpose. we obtain our data by traversing the channel
+ * list of children and calling vchan_mix_* to mix count bytes from each
+ * into our destination buffer, b
+ */
+ dst = (int16_t *)b;
+ tmp = (int16_t *)sndbuf_getbuf(src);
+ bzero(tmp, count);
+ SLIST_FOREACH(cce, &c->children, link) {
+ ch = cce->channel;
+ 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, (u_int8_t *)tmp, count, ch->bufsoft);
+ vchan_mix_s16(dst, tmp, cnt / 2);
+ }
+ }
+
+ return count;
+}
+
+static struct pcm_feederdesc feeder_vchan_s16_desc[] = {
+ {FEEDER_MIXER, AFMT_S16_LE, AFMT_S16_LE, 0},
+ {FEEDER_MIXER, AFMT_S16_LE | AFMT_STEREO, AFMT_S16_LE | AFMT_STEREO, 0},
+ {0},
+};
+static kobj_method_t feeder_vchan_s16_methods[] = {
+ KOBJMETHOD(feeder_feed, feed_vchan_s16),
+ { 0, 0 }
+};
+FEEDER_DECLARE(feeder_vchan_s16, 2, NULL);
+
+/************************************************************/
+
+static void *
+vchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir)
+{
+ struct vchinfo *ch;
+ struct pcm_channel *parent = devinfo;
+
+ KASSERT(dir == PCMDIR_PLAY, ("vchan_init: bad direction"));
+ ch = malloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO);
+ ch->parent = parent;
+ ch->channel = c;
+ ch->fmt = AFMT_U8;
+ ch->spd = DSP_DEFAULT_SPEED;
+ ch->blksz = 2048;
+
+ c->flags |= CHN_F_VIRTUAL;
+
+ return ch;
+}
+
+static int
+vchan_free(kobj_t obj, void *data)
+{
+ return 0;
+}
+
+static int
+vchan_setformat(kobj_t obj, void *data, u_int32_t format)
+{
+ struct vchinfo *ch = data;
+ struct pcm_channel *parent = ch->parent;
+
+ 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;
+ chn_notify(parent, CHN_N_FORMAT);
+ return 0;
+}
+
+static int
+vchan_setspeed(kobj_t obj, void *data, u_int32_t speed)
+{
+ struct vchinfo *ch = data;
+ struct pcm_channel *parent = ch->parent;
+
+ ch->spd = speed;
+ chn_notify(parent, CHN_N_RATE);
+ return speed;
+}
+
+static int
+vchan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize)
+{
+ struct vchinfo *ch = data;
+ struct pcm_channel *parent = ch->parent;
+ int prate, crate;
+
+ ch->blksz = blocksize;
+ chn_notify(parent, CHN_N_BLOCKSIZE);
+
+ crate = ch->spd * ch->bps;
+ prate = sndbuf_getspd(parent->bufhard) * sndbuf_getbps(parent->bufhard);
+ blocksize = sndbuf_getblksz(parent->bufhard);
+ blocksize *= prate;
+ blocksize /= crate;
+
+ return blocksize;
+}
+
+static int
+vchan_trigger(kobj_t obj, void *data, int go)
+{
+ struct vchinfo *ch = data;
+ struct pcm_channel *parent = ch->parent;
+
+ if (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD)
+ return 0;
+
+ ch->run = (go == PCMTRIG_START)? 1 : 0;
+ chn_notify(parent, CHN_N_TRIGGER);
+
+ return 0;
+}
+
+static struct pcmchan_caps *
+vchan_getcaps(kobj_t obj, void *data)
+{
+ struct vchinfo *ch = data;
+
+ ch->caps.minspeed = sndbuf_getspd(ch->parent->bufhard);
+ ch->caps.maxspeed = ch->caps.minspeed;
+ ch->caps.fmtlist = vchan_fmt;
+ ch->caps.caps = 0;
+
+ return &ch->caps;
+}
+
+static kobj_method_t vchan_methods[] = {
+ KOBJMETHOD(channel_init, vchan_init),
+ KOBJMETHOD(channel_free, vchan_free),
+ KOBJMETHOD(channel_setformat, vchan_setformat),
+ KOBJMETHOD(channel_setspeed, vchan_setspeed),
+ KOBJMETHOD(channel_setblocksize, vchan_setblocksize),
+ KOBJMETHOD(channel_trigger, vchan_trigger),
+ KOBJMETHOD(channel_getcaps, vchan_getcaps),
+ { 0, 0 }
+};
+CHANNEL_DECLARE(vchan);
+
+/* virtual channel interface */
+
+int
+vchan_create(struct pcm_channel *parent)
+{
+ struct snddev_info *d = parent->parentsnddev;
+ struct pcmchan_children *pce;
+ struct pcm_channel *child;
+ int err, first;
+
+ pce = malloc(sizeof(*pce), M_DEVBUF, M_WAITOK | M_ZERO);
+ if (!pce) {
+ return ENOMEM;
+ }
+
+ /* create a new playback channel */
+ child = pcm_chn_create(d, parent, &vchan_class, PCMDIR_VIRTUAL, parent);
+ if (!child) {
+ free(pce, M_DEVBUF);
+ return ENODEV;
+ }
+
+ CHN_LOCK(parent);
+ if (!(parent->flags & CHN_F_BUSY)) {
+ CHN_UNLOCK(parent);
+ 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 */
+ err = pcm_chn_add(d, child, !first);
+ if (err) {
+ pcm_chn_destroy(child);
+ free(pce, M_DEVBUF);
+ }
+
+ /* 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);
+ }
+
+ return err;
+}
+
+int
+vchan_destroy(struct pcm_channel *c)
+{
+ struct pcm_channel *parent = c->parentchannel;
+ struct snddev_info *d = parent->parentsnddev;
+ struct pcmchan_children *pce;
+ 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;
+ }
+
+ /* remove us from our parent's children list */
+ SLIST_FOREACH(pce, &parent->children, link) {
+ if (pce->channel == c)
+ goto gotch;
+ }
+ CHN_UNLOCK(parent);
+ return EINVAL;
+gotch:
+ SLIST_REMOVE(&parent->children, pce, pcmchan_children, link);
+ free(pce, M_DEVBUF);
+
+ last = SLIST_EMPTY(&parent->children);
+ if (last)
+ parent->flags &= ~CHN_F_BUSY;
+
+ /* remove us from our grantparent's channel list */
+ err = pcm_chn_remove(d, c, !last);
+ if (err)
+ return err;
+
+ CHN_UNLOCK(parent);
+ /* destroy ourselves */
+ err = pcm_chn_destroy(c);
+
+ return err;
+}
+
+int
+vchan_initsys(device_t dev)
+{
+#ifdef SND_DYNSYSCTL
+ struct snddev_info *d;
+
+ d = device_get_softc(dev);
+ 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", "");
+#endif
+
+ return 0;
+}
+
+
diff --git a/sys/dev/sound/pcm/vchan.h b/sys/dev/sound/pcm/vchan.h
new file mode 100644
index 0000000..d3121e9
--- /dev/null
+++ b/sys/dev/sound/pcm/vchan.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2001 Cameron Grant <gandalf@vilnya.demon.co.uk>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+int vchan_create(struct pcm_channel *parent);
+int vchan_destroy(struct pcm_channel *c);
+int vchan_initsys(device_t dev);
+
+
OpenPOWER on IntegriCloud