aboutsummaryrefslogtreecommitdiff
path: root/sound/core/oss
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
commit1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch)
tree0bba044c4ce775e45a88a51686b5d9f90697ea9d /sound/core/oss
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history, even though we have it. We can create a separate "historical" git archive of that later if we want to, and in the meantime it's about 3.2GB when imported into git - space that would just make the early git days unnecessarily complicated, when we don't have a lot of good infrastructure for it. Let it rip!
Diffstat (limited to 'sound/core/oss')
-rw-r--r--sound/core/oss/Makefile12
-rw-r--r--sound/core/oss/copy.c87
-rw-r--r--sound/core/oss/io.c134
-rw-r--r--sound/core/oss/linear.c158
-rw-r--r--sound/core/oss/mixer_oss.c1340
-rw-r--r--sound/core/oss/mulaw.c308
-rw-r--r--sound/core/oss/pcm_oss.c2530
-rw-r--r--sound/core/oss/pcm_plugin.c921
-rw-r--r--sound/core/oss/pcm_plugin.h250
-rw-r--r--sound/core/oss/plugin_ops.h536
-rw-r--r--sound/core/oss/rate.c378
-rw-r--r--sound/core/oss/route.c519
12 files changed, 7173 insertions, 0 deletions
diff --git a/sound/core/oss/Makefile b/sound/core/oss/Makefile
new file mode 100644
index 00000000000..e6d5a045ba2
--- /dev/null
+++ b/sound/core/oss/Makefile
@@ -0,0 +1,12 @@
+#
+# Makefile for ALSA
+# Copyright (c) 1999 by Jaroslav Kysela <perex@suse.cz>
+#
+
+snd-mixer-oss-objs := mixer_oss.o
+
+snd-pcm-oss-objs := pcm_oss.o pcm_plugin.o \
+ io.o copy.o linear.o mulaw.o route.o rate.o
+
+obj-$(CONFIG_SND_MIXER_OSS) += snd-mixer-oss.o
+obj-$(CONFIG_SND_PCM_OSS) += snd-pcm-oss.o
diff --git a/sound/core/oss/copy.c b/sound/core/oss/copy.c
new file mode 100644
index 00000000000..edecbe7417b
--- /dev/null
+++ b/sound/core/oss/copy.c
@@ -0,0 +1,87 @@
+/*
+ * Linear conversion Plug-In
+ * Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org>
+ *
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/time.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include "pcm_plugin.h"
+
+static snd_pcm_sframes_t copy_transfer(snd_pcm_plugin_t *plugin,
+ const snd_pcm_plugin_channel_t *src_channels,
+ snd_pcm_plugin_channel_t *dst_channels,
+ snd_pcm_uframes_t frames)
+{
+ unsigned int channel;
+ unsigned int nchannels;
+
+ snd_assert(plugin != NULL && src_channels != NULL && dst_channels != NULL, return -ENXIO);
+ if (frames == 0)
+ return 0;
+ nchannels = plugin->src_format.channels;
+ for (channel = 0; channel < nchannels; channel++) {
+ snd_assert(src_channels->area.first % 8 == 0 &&
+ src_channels->area.step % 8 == 0,
+ return -ENXIO);
+ snd_assert(dst_channels->area.first % 8 == 0 &&
+ dst_channels->area.step % 8 == 0,
+ return -ENXIO);
+ if (!src_channels->enabled) {
+ if (dst_channels->wanted)
+ snd_pcm_area_silence(&dst_channels->area, 0, frames, plugin->dst_format.format);
+ dst_channels->enabled = 0;
+ continue;
+ }
+ dst_channels->enabled = 1;
+ snd_pcm_area_copy(&src_channels->area, 0, &dst_channels->area, 0, frames, plugin->src_format.format);
+ src_channels++;
+ dst_channels++;
+ }
+ return frames;
+}
+
+int snd_pcm_plugin_build_copy(snd_pcm_plug_t *plug,
+ snd_pcm_plugin_format_t *src_format,
+ snd_pcm_plugin_format_t *dst_format,
+ snd_pcm_plugin_t **r_plugin)
+{
+ int err;
+ snd_pcm_plugin_t *plugin;
+ int width;
+
+ snd_assert(r_plugin != NULL, return -ENXIO);
+ *r_plugin = NULL;
+
+ snd_assert(src_format->format == dst_format->format, return -ENXIO);
+ snd_assert(src_format->rate == dst_format->rate, return -ENXIO);
+ snd_assert(src_format->channels == dst_format->channels, return -ENXIO);
+
+ width = snd_pcm_format_physical_width(src_format->format);
+ snd_assert(width > 0, return -ENXIO);
+
+ err = snd_pcm_plugin_build(plug, "copy", src_format, dst_format,
+ 0, &plugin);
+ if (err < 0)
+ return err;
+ plugin->transfer = copy_transfer;
+ *r_plugin = plugin;
+ return 0;
+}
diff --git a/sound/core/oss/io.c b/sound/core/oss/io.c
new file mode 100644
index 00000000000..bb1c99a5b73
--- /dev/null
+++ b/sound/core/oss/io.c
@@ -0,0 +1,134 @@
+/*
+ * PCM I/O Plug-In Interface
+ * Copyright (c) 1999 by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/time.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include "pcm_plugin.h"
+
+#define pcm_write(plug,buf,count) snd_pcm_oss_write3(plug,buf,count,1)
+#define pcm_writev(plug,vec,count) snd_pcm_oss_writev3(plug,vec,count,1)
+#define pcm_read(plug,buf,count) snd_pcm_oss_read3(plug,buf,count,1)
+#define pcm_readv(plug,vec,count) snd_pcm_oss_readv3(plug,vec,count,1)
+
+/*
+ * Basic io plugin
+ */
+
+static snd_pcm_sframes_t io_playback_transfer(snd_pcm_plugin_t *plugin,
+ const snd_pcm_plugin_channel_t *src_channels,
+ snd_pcm_plugin_channel_t *dst_channels ATTRIBUTE_UNUSED,
+ snd_pcm_uframes_t frames)
+{
+ snd_assert(plugin != NULL, return -ENXIO);
+ snd_assert(src_channels != NULL, return -ENXIO);
+ if (plugin->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED) {
+ return pcm_write(plugin->plug, src_channels->area.addr, frames);
+ } else {
+ int channel, channels = plugin->dst_format.channels;
+ void **bufs = (void**)plugin->extra_data;
+ snd_assert(bufs != NULL, return -ENXIO);
+ for (channel = 0; channel < channels; channel++) {
+ if (src_channels[channel].enabled)
+ bufs[channel] = src_channels[channel].area.addr;
+ else
+ bufs[channel] = NULL;
+ }
+ return pcm_writev(plugin->plug, bufs, frames);
+ }
+}
+
+static snd_pcm_sframes_t io_capture_transfer(snd_pcm_plugin_t *plugin,
+ const snd_pcm_plugin_channel_t *src_channels ATTRIBUTE_UNUSED,
+ snd_pcm_plugin_channel_t *dst_channels,
+ snd_pcm_uframes_t frames)
+{
+ snd_assert(plugin != NULL, return -ENXIO);
+ snd_assert(dst_channels != NULL, return -ENXIO);
+ if (plugin->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED) {
+ return pcm_read(plugin->plug, dst_channels->area.addr, frames);
+ } else {
+ int channel, channels = plugin->dst_format.channels;
+ void **bufs = (void**)plugin->extra_data;
+ snd_assert(bufs != NULL, return -ENXIO);
+ for (channel = 0; channel < channels; channel++) {
+ if (dst_channels[channel].enabled)
+ bufs[channel] = dst_channels[channel].area.addr;
+ else
+ bufs[channel] = NULL;
+ }
+ return pcm_readv(plugin->plug, bufs, frames);
+ }
+ return 0;
+}
+
+static snd_pcm_sframes_t io_src_channels(snd_pcm_plugin_t *plugin,
+ snd_pcm_uframes_t frames,
+ snd_pcm_plugin_channel_t **channels)
+{
+ int err;
+ unsigned int channel;
+ snd_pcm_plugin_channel_t *v;
+ err = snd_pcm_plugin_client_channels(plugin, frames, &v);
+ if (err < 0)
+ return err;
+ *channels = v;
+ if (plugin->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED) {
+ for (channel = 0; channel < plugin->src_format.channels; ++channel, ++v)
+ v->wanted = 1;
+ }
+ return frames;
+}
+
+int snd_pcm_plugin_build_io(snd_pcm_plug_t *plug,
+ snd_pcm_hw_params_t *params,
+ snd_pcm_plugin_t **r_plugin)
+{
+ int err;
+ snd_pcm_plugin_format_t format;
+ snd_pcm_plugin_t *plugin;
+
+ snd_assert(r_plugin != NULL, return -ENXIO);
+ *r_plugin = NULL;
+ snd_assert(plug != NULL && params != NULL, return -ENXIO);
+ format.format = params_format(params);
+ format.rate = params_rate(params);
+ format.channels = params_channels(params);
+ err = snd_pcm_plugin_build(plug, "I/O io",
+ &format, &format,
+ sizeof(void *) * format.channels,
+ &plugin);
+ if (err < 0)
+ return err;
+ plugin->access = params_access(params);
+ if (snd_pcm_plug_stream(plug) == SNDRV_PCM_STREAM_PLAYBACK) {
+ plugin->transfer = io_playback_transfer;
+ if (plugin->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED)
+ plugin->client_channels = io_src_channels;
+ } else {
+ plugin->transfer = io_capture_transfer;
+ }
+
+ *r_plugin = plugin;
+ return 0;
+}
diff --git a/sound/core/oss/linear.c b/sound/core/oss/linear.c
new file mode 100644
index 00000000000..12ed27a57b2
--- /dev/null
+++ b/sound/core/oss/linear.c
@@ -0,0 +1,158 @@
+/*
+ * Linear conversion Plug-In
+ * Copyright (c) 1999 by Jaroslav Kysela <perex@suse.cz>,
+ * Abramo Bagnara <abramo@alsa-project.org>
+ *
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/time.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include "pcm_plugin.h"
+
+/*
+ * Basic linear conversion plugin
+ */
+
+typedef struct linear_private_data {
+ int conv;
+} linear_t;
+
+static void convert(snd_pcm_plugin_t *plugin,
+ const snd_pcm_plugin_channel_t *src_channels,
+ snd_pcm_plugin_channel_t *dst_channels,
+ snd_pcm_uframes_t frames)
+{
+#define CONV_LABELS
+#include "plugin_ops.h"
+#undef CONV_LABELS
+ linear_t *data = (linear_t *)plugin->extra_data;
+ void *conv = conv_labels[data->conv];
+ int channel;
+ int nchannels = plugin->src_format.channels;
+ for (channel = 0; channel < nchannels; ++channel) {
+ char *src;
+ char *dst;
+ int src_step, dst_step;
+ snd_pcm_uframes_t frames1;
+ if (!src_channels[channel].enabled) {
+ if (dst_channels[channel].wanted)
+ snd_pcm_area_silence(&dst_channels[channel].area, 0, frames, plugin->dst_format.format);
+ dst_channels[channel].enabled = 0;
+ continue;
+ }
+ dst_channels[channel].enabled = 1;
+ src = src_channels[channel].area.addr + src_channels[channel].area.first / 8;
+ dst = dst_channels[channel].area.addr + dst_channels[channel].area.first / 8;
+ src_step = src_channels[channel].area.step / 8;
+ dst_step = dst_channels[channel].area.step / 8;
+ frames1 = frames;
+ while (frames1-- > 0) {
+ goto *conv;
+#define CONV_END after
+#include "plugin_ops.h"
+#undef CONV_END
+ after:
+ src += src_step;
+ dst += dst_step;
+ }
+ }
+}
+
+static snd_pcm_sframes_t linear_transfer(snd_pcm_plugin_t *plugin,
+ const snd_pcm_plugin_channel_t *src_channels,
+ snd_pcm_plugin_channel_t *dst_channels,
+ snd_pcm_uframes_t frames)
+{
+ linear_t *data;
+
+ snd_assert(plugin != NULL && src_channels != NULL && dst_channels != NULL, return -ENXIO);
+ data = (linear_t *)plugin->extra_data;
+ if (frames == 0)
+ return 0;
+#ifdef CONFIG_SND_DEBUG
+ {
+ unsigned int channel;
+ for (channel = 0; channel < plugin->src_format.channels; channel++) {
+ snd_assert(src_channels[channel].area.first % 8 == 0 &&
+ src_channels[channel].area.step % 8 == 0,
+ return -ENXIO);
+ snd_assert(dst_channels[channel].area.first % 8 == 0 &&
+ dst_channels[channel].area.step % 8 == 0,
+ return -ENXIO);
+ }
+ }
+#endif
+ convert(plugin, src_channels, dst_channels, frames);
+ return frames;
+}
+
+int conv_index(int src_format, int dst_format)
+{
+ int src_endian, dst_endian, sign, src_width, dst_width;
+
+ sign = (snd_pcm_format_signed(src_format) !=
+ snd_pcm_format_signed(dst_format));
+#ifdef SNDRV_LITTLE_ENDIAN
+ src_endian = snd_pcm_format_big_endian(src_format);
+ dst_endian = snd_pcm_format_big_endian(dst_format);
+#else
+ src_endian = snd_pcm_format_little_endian(src_format);
+ dst_endian = snd_pcm_format_little_endian(dst_format);
+#endif
+
+ if (src_endian < 0)
+ src_endian = 0;
+ if (dst_endian < 0)
+ dst_endian = 0;
+
+ src_width = snd_pcm_format_width(src_format) / 8 - 1;
+ dst_width = snd_pcm_format_width(dst_format) / 8 - 1;
+
+ return src_width * 32 + src_endian * 16 + sign * 8 + dst_width * 2 + dst_endian;
+}
+
+int snd_pcm_plugin_build_linear(snd_pcm_plug_t *plug,
+ snd_pcm_plugin_format_t *src_format,
+ snd_pcm_plugin_format_t *dst_format,
+ snd_pcm_plugin_t **r_plugin)
+{
+ int err;
+ struct linear_private_data *data;
+ snd_pcm_plugin_t *plugin;
+
+ snd_assert(r_plugin != NULL, return -ENXIO);
+ *r_plugin = NULL;
+
+ snd_assert(src_format->rate == dst_format->rate, return -ENXIO);
+ snd_assert(src_format->channels == dst_format->channels, return -ENXIO);
+ snd_assert(snd_pcm_format_linear(src_format->format) &&
+ snd_pcm_format_linear(dst_format->format), return -ENXIO);
+
+ err = snd_pcm_plugin_build(plug, "linear format conversion",
+ src_format, dst_format,
+ sizeof(linear_t), &plugin);
+ if (err < 0)
+ return err;
+ data = (linear_t *)plugin->extra_data;
+ data->conv = conv_index(src_format->format, dst_format->format);
+ plugin->transfer = linear_transfer;
+ *r_plugin = plugin;
+ return 0;
+}
diff --git a/sound/core/oss/mixer_oss.c b/sound/core/oss/mixer_oss.c
new file mode 100644
index 00000000000..98ed9a9f0da
--- /dev/null
+++ b/sound/core/oss/mixer_oss.c
@@ -0,0 +1,1340 @@
+/*
+ * OSS emulation layer for the mixer interface
+ * Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/smp_lock.h>
+#include <linux/slab.h>
+#include <linux/time.h>
+#include <sound/core.h>
+#include <sound/minors.h>
+#include <sound/control.h>
+#include <sound/info.h>
+#include <sound/mixer_oss.h>
+#include <linux/soundcard.h>
+
+#define OSS_ALSAEMULVER _SIOR ('M', 249, int)
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
+MODULE_DESCRIPTION("Mixer OSS emulation for ALSA.");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_SNDRV_MINOR(SNDRV_MINOR_OSS_MIXER);
+
+static int snd_mixer_oss_open(struct inode *inode, struct file *file)
+{
+ int cardnum = SNDRV_MINOR_OSS_CARD(iminor(inode));
+ snd_card_t *card;
+ snd_mixer_oss_file_t *fmixer;
+ int err;
+
+ if ((card = snd_cards[cardnum]) == NULL)
+ return -ENODEV;
+ if (card->mixer_oss == NULL)
+ return -ENODEV;
+ err = snd_card_file_add(card, file);
+ if (err < 0)
+ return err;
+ fmixer = kcalloc(1, sizeof(*fmixer), GFP_KERNEL);
+ if (fmixer == NULL) {
+ snd_card_file_remove(card, file);
+ return -ENOMEM;
+ }
+ fmixer->card = card;
+ fmixer->mixer = card->mixer_oss;
+ file->private_data = fmixer;
+ if (!try_module_get(card->module)) {
+ kfree(fmixer);
+ snd_card_file_remove(card, file);
+ return -EFAULT;
+ }
+ return 0;
+}
+
+static int snd_mixer_oss_release(struct inode *inode, struct file *file)
+{
+ snd_mixer_oss_file_t *fmixer;
+
+ if (file->private_data) {
+ fmixer = (snd_mixer_oss_file_t *) file->private_data;
+ module_put(fmixer->card->module);
+ snd_card_file_remove(fmixer->card, file);
+ kfree(fmixer);
+ }
+ return 0;
+}
+
+static int snd_mixer_oss_info(snd_mixer_oss_file_t *fmixer,
+ mixer_info __user *_info)
+{
+ snd_card_t *card = fmixer->card;
+ snd_mixer_oss_t *mixer = fmixer->mixer;
+ struct mixer_info info;
+
+ memset(&info, 0, sizeof(info));
+ strlcpy(info.id, mixer && mixer->id[0] ? mixer->id : card->driver, sizeof(info.id));
+ strlcpy(info.name, mixer && mixer->name[0] ? mixer->name : card->mixername, sizeof(info.name));
+ info.modify_counter = card->mixer_oss_change_count;
+ if (copy_to_user(_info, &info, sizeof(info)))
+ return -EFAULT;
+ return 0;
+}
+
+static int snd_mixer_oss_info_obsolete(snd_mixer_oss_file_t *fmixer,
+ _old_mixer_info __user *_info)
+{
+ snd_card_t *card = fmixer->card;
+ snd_mixer_oss_t *mixer = fmixer->mixer;
+ _old_mixer_info info;
+
+ memset(&info, 0, sizeof(info));
+ strlcpy(info.id, mixer && mixer->id[0] ? mixer->id : card->driver, sizeof(info.id));
+ strlcpy(info.name, mixer && mixer->name[0] ? mixer->name : card->mixername, sizeof(info.name));
+ if (copy_to_user(_info, &info, sizeof(info)))
+ return -EFAULT;
+ return 0;
+}
+
+static int snd_mixer_oss_caps(snd_mixer_oss_file_t *fmixer)
+{
+ snd_mixer_oss_t *mixer = fmixer->mixer;
+ int result = 0;
+
+ if (mixer == NULL)
+ return -EIO;
+ if (mixer->get_recsrc && mixer->put_recsrc)
+ result |= SOUND_CAP_EXCL_INPUT;
+ return result;
+}
+
+static int snd_mixer_oss_devmask(snd_mixer_oss_file_t *fmixer)
+{
+ snd_mixer_oss_t *mixer = fmixer->mixer;
+ snd_mixer_oss_slot_t *pslot;
+ int result = 0, chn;
+
+ if (mixer == NULL)
+ return -EIO;
+ for (chn = 0; chn < 31; chn++) {
+ pslot = &mixer->slots[chn];
+ if (pslot->put_volume || pslot->put_recsrc)
+ result |= 1 << chn;
+ }
+ return result;
+}
+
+static int snd_mixer_oss_stereodevs(snd_mixer_oss_file_t *fmixer)
+{
+ snd_mixer_oss_t *mixer = fmixer->mixer;
+ snd_mixer_oss_slot_t *pslot;
+ int result = 0, chn;
+
+ if (mixer == NULL)
+ return -EIO;
+ for (chn = 0; chn < 31; chn++) {
+ pslot = &mixer->slots[chn];
+ if (pslot->put_volume && pslot->stereo)
+ result |= 1 << chn;
+ }
+ return result;
+}
+
+static int snd_mixer_oss_recmask(snd_mixer_oss_file_t *fmixer)
+{
+ snd_mixer_oss_t *mixer = fmixer->mixer;
+ int result = 0;
+
+ if (mixer == NULL)
+ return -EIO;
+ if (mixer->put_recsrc && mixer->get_recsrc) { /* exclusive */
+ result = mixer->mask_recsrc;
+ } else {
+ snd_mixer_oss_slot_t *pslot;
+ int chn;
+ for (chn = 0; chn < 31; chn++) {
+ pslot = &mixer->slots[chn];
+ if (pslot->put_recsrc)
+ result |= 1 << chn;
+ }
+ }
+ return result;
+}
+
+static int snd_mixer_oss_get_recsrc(snd_mixer_oss_file_t *fmixer)
+{
+ snd_mixer_oss_t *mixer = fmixer->mixer;
+ int result = 0;
+
+ if (mixer == NULL)
+ return -EIO;
+ if (mixer->put_recsrc && mixer->get_recsrc) { /* exclusive */
+ int err;
+ if ((err = mixer->get_recsrc(fmixer, &result)) < 0)
+ return err;
+ result = 1 << result;
+ } else {
+ snd_mixer_oss_slot_t *pslot;
+ int chn;
+ for (chn = 0; chn < 31; chn++) {
+ pslot = &mixer->slots[chn];
+ if (pslot->get_recsrc) {
+ int active = 0;
+ pslot->get_recsrc(fmixer, pslot, &active);
+ if (active)
+ result |= 1 << chn;
+ }
+ }
+ }
+ return mixer->oss_recsrc = result;
+}
+
+static int snd_mixer_oss_set_recsrc(snd_mixer_oss_file_t *fmixer, int recsrc)
+{
+ snd_mixer_oss_t *mixer = fmixer->mixer;
+ snd_mixer_oss_slot_t *pslot;
+ int chn, active;
+ int result = 0;
+
+ if (mixer == NULL)
+ return -EIO;
+ if (mixer->get_recsrc && mixer->put_recsrc) { /* exclusive input */
+ if (recsrc & ~mixer->oss_recsrc)
+ recsrc &= ~mixer->oss_recsrc;
+ mixer->put_recsrc(fmixer, ffz(~recsrc));
+ mixer->get_recsrc(fmixer, &result);
+ result = 1 << result;
+ }
+ for (chn = 0; chn < 31; chn++) {
+ pslot = &mixer->slots[chn];
+ if (pslot->put_recsrc) {
+ active = (recsrc & (1 << chn)) ? 1 : 0;
+ pslot->put_recsrc(fmixer, pslot, active);
+ }
+ }
+ if (! result) {
+ for (chn = 0; chn < 31; chn++) {
+ pslot = &mixer->slots[chn];
+ if (pslot->get_recsrc) {
+ active = 0;
+ pslot->get_recsrc(fmixer, pslot, &active);
+ if (active)
+ result |= 1 << chn;
+ }
+ }
+ }
+ return result;
+}
+
+static int snd_mixer_oss_get_volume(snd_mixer_oss_file_t *fmixer, int slot)
+{
+ snd_mixer_oss_t *mixer = fmixer->mixer;
+ snd_mixer_oss_slot_t *pslot;
+ int result = 0, left, right;
+
+ if (mixer == NULL || slot > 30)
+ return -EIO;
+ pslot = &mixer->slots[slot];
+ left = pslot->volume[0];
+ right = pslot->volume[1];
+ if (pslot->get_volume)
+ result = pslot->get_volume(fmixer, pslot, &left, &right);
+ if (!pslot->stereo)
+ right = left;
+ snd_assert(left >= 0 && left <= 100, return -EIO);
+ snd_assert(right >= 0 && right <= 100, return -EIO);
+ if (result >= 0) {
+ pslot->volume[0] = left;
+ pslot->volume[1] = right;
+ result = (left & 0xff) | ((right & 0xff) << 8);
+ }
+ return result;
+}
+
+static int snd_mixer_oss_set_volume(snd_mixer_oss_file_t *fmixer,
+ int slot, int volume)
+{
+ snd_mixer_oss_t *mixer = fmixer->mixer;
+ snd_mixer_oss_slot_t *pslot;
+ int result = 0, left = volume & 0xff, right = (volume >> 8) & 0xff;
+
+ if (mixer == NULL || slot > 30)
+ return -EIO;
+ pslot = &mixer->slots[slot];
+ if (left > 100)
+ left = 100;
+ if (right > 100)
+ right = 100;
+ if (!pslot->stereo)
+ right = left;
+ if (pslot->put_volume)
+ result = pslot->put_volume(fmixer, pslot, left, right);
+ if (result < 0)
+ return result;
+ pslot->volume[0] = left;
+ pslot->volume[1] = right;
+ return (left & 0xff) | ((right & 0xff) << 8);
+}
+
+static int snd_mixer_oss_ioctl1(snd_mixer_oss_file_t *fmixer, unsigned int cmd, unsigned long arg)
+{
+ void __user *argp = (void __user *)arg;
+ int __user *p = argp;
+ int tmp;
+
+ snd_assert(fmixer != NULL, return -ENXIO);
+ if (((cmd >> 8) & 0xff) == 'M') {
+ switch (cmd) {
+ case SOUND_MIXER_INFO:
+ return snd_mixer_oss_info(fmixer, argp);
+ case SOUND_OLD_MIXER_INFO:
+ return snd_mixer_oss_info_obsolete(fmixer, argp);
+ case SOUND_MIXER_WRITE_RECSRC:
+ if (get_user(tmp, p))
+ return -EFAULT;
+ tmp = snd_mixer_oss_set_recsrc(fmixer, tmp);
+ if (tmp < 0)
+ return tmp;
+ return put_user(tmp, p);
+ case OSS_GETVERSION:
+ return put_user(SNDRV_OSS_VERSION, p);
+ case OSS_ALSAEMULVER:
+ return put_user(1, p);
+ case SOUND_MIXER_READ_DEVMASK:
+ tmp = snd_mixer_oss_devmask(fmixer);
+ if (tmp < 0)
+ return tmp;
+ return put_user(tmp, p);
+ case SOUND_MIXER_READ_STEREODEVS:
+ tmp = snd_mixer_oss_stereodevs(fmixer);
+ if (tmp < 0)
+ return tmp;
+ return put_user(tmp, p);
+ case SOUND_MIXER_READ_RECMASK:
+ tmp = snd_mixer_oss_recmask(fmixer);
+ if (tmp < 0)
+ return tmp;
+ return put_user(tmp, p);
+ case SOUND_MIXER_READ_CAPS:
+ tmp = snd_mixer_oss_caps(fmixer);
+ if (tmp < 0)
+ return tmp;
+ return put_user(tmp, p);
+ case SOUND_MIXER_READ_RECSRC:
+ tmp = snd_mixer_oss_get_recsrc(fmixer);
+ if (tmp < 0)
+ return tmp;
+ return put_user(tmp, p);
+ }
+ }
+ if (cmd & SIOC_IN) {
+ if (get_user(tmp, p))
+ return -EFAULT;
+ tmp = snd_mixer_oss_set_volume(fmixer, cmd & 0xff, tmp);
+ if (tmp < 0)
+ return tmp;
+ return put_user(tmp, p);
+ } else if (cmd & SIOC_OUT) {
+ tmp = snd_mixer_oss_get_volume(fmixer, cmd & 0xff);
+ if (tmp < 0)
+ return tmp;
+ return put_user(tmp, p);
+ }
+ return -ENXIO;
+}
+
+static long snd_mixer_oss_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ return snd_mixer_oss_ioctl1((snd_mixer_oss_file_t *) file->private_data, cmd, arg);
+}
+
+int snd_mixer_oss_ioctl_card(snd_card_t *card, unsigned int cmd, unsigned long arg)
+{
+ snd_mixer_oss_file_t fmixer;
+
+ snd_assert(card != NULL, return -ENXIO);
+ if (card->mixer_oss == NULL)
+ return -ENXIO;
+ memset(&fmixer, 0, sizeof(fmixer));
+ fmixer.card = card;
+ fmixer.mixer = card->mixer_oss;
+ return snd_mixer_oss_ioctl1(&fmixer, cmd, arg);
+}
+
+#ifdef CONFIG_COMPAT
+/* all compatible */
+#define snd_mixer_oss_ioctl_compat snd_mixer_oss_ioctl
+#else
+#define snd_mixer_oss_ioctl_compat NULL
+#endif
+
+/*
+ * REGISTRATION PART
+ */
+
+static struct file_operations snd_mixer_oss_f_ops =
+{
+ .owner = THIS_MODULE,
+ .open = snd_mixer_oss_open,
+ .release = snd_mixer_oss_release,
+ .unlocked_ioctl = snd_mixer_oss_ioctl,
+ .compat_ioctl = snd_mixer_oss_ioctl_compat,
+};
+
+static snd_minor_t snd_mixer_oss_reg =
+{
+ .comment = "mixer",
+ .f_ops = &snd_mixer_oss_f_ops,
+};
+
+/*
+ * utilities
+ */
+
+static long snd_mixer_oss_conv(long val, long omin, long omax, long nmin, long nmax)
+{
+ long orange = omax - omin, nrange = nmax - nmin;
+
+ if (orange == 0)
+ return 0;
+ return ((nrange * (val - omin)) + (orange / 2)) / orange + nmin;
+}
+
+/* convert from alsa native to oss values (0-100) */
+static long snd_mixer_oss_conv1(long val, long min, long max, int *old)
+{
+ if (val == snd_mixer_oss_conv(*old, 0, 100, min, max))
+ return *old;
+ return snd_mixer_oss_conv(val, min, max, 0, 100);
+}
+
+/* convert from oss to alsa native values */
+static long snd_mixer_oss_conv2(long val, long min, long max)
+{
+ return snd_mixer_oss_conv(val, 0, 100, min, max);
+}
+
+#if 0
+static void snd_mixer_oss_recsrce_set(snd_card_t *card, int slot)
+{
+ snd_mixer_oss_t *mixer = card->mixer_oss;
+ if (mixer)
+ mixer->mask_recsrc |= 1 << slot;
+}
+
+static int snd_mixer_oss_recsrce_get(snd_card_t *card, int slot)
+{
+ snd_mixer_oss_t *mixer = card->mixer_oss;
+ if (mixer && (mixer->mask_recsrc & (1 << slot)))
+ return 1;
+ return 0;
+}
+#endif
+
+#define SNDRV_MIXER_OSS_SIGNATURE 0x65999250
+
+#define SNDRV_MIXER_OSS_ITEM_GLOBAL 0
+#define SNDRV_MIXER_OSS_ITEM_GSWITCH 1
+#define SNDRV_MIXER_OSS_ITEM_GROUTE 2
+#define SNDRV_MIXER_OSS_ITEM_GVOLUME 3
+#define SNDRV_MIXER_OSS_ITEM_PSWITCH 4
+#define SNDRV_MIXER_OSS_ITEM_PROUTE 5
+#define SNDRV_MIXER_OSS_ITEM_PVOLUME 6
+#define SNDRV_MIXER_OSS_ITEM_CSWITCH 7
+#define SNDRV_MIXER_OSS_ITEM_CROUTE 8
+#define SNDRV_MIXER_OSS_ITEM_CVOLUME 9
+#define SNDRV_MIXER_OSS_ITEM_CAPTURE 10
+
+#define SNDRV_MIXER_OSS_ITEM_COUNT 11
+
+#define SNDRV_MIXER_OSS_PRESENT_GLOBAL (1<<0)
+#define SNDRV_MIXER_OSS_PRESENT_GSWITCH (1<<1)
+#define SNDRV_MIXER_OSS_PRESENT_GROUTE (1<<2)
+#define SNDRV_MIXER_OSS_PRESENT_GVOLUME (1<<3)
+#define SNDRV_MIXER_OSS_PRESENT_PSWITCH (1<<4)
+#define SNDRV_MIXER_OSS_PRESENT_PROUTE (1<<5)
+#define SNDRV_MIXER_OSS_PRESENT_PVOLUME (1<<6)
+#define SNDRV_MIXER_OSS_PRESENT_CSWITCH (1<<7)
+#define SNDRV_MIXER_OSS_PRESENT_CROUTE (1<<8)
+#define SNDRV_MIXER_OSS_PRESENT_CVOLUME (1<<9)
+#define SNDRV_MIXER_OSS_PRESENT_CAPTURE (1<<10)
+
+struct slot {
+ unsigned int signature;
+ unsigned int present;
+ unsigned int channels;
+ unsigned int numid[SNDRV_MIXER_OSS_ITEM_COUNT];
+ unsigned int capture_item;
+ struct snd_mixer_oss_assign_table *assigned;
+ unsigned int allocated: 1;
+};
+
+#define ID_UNKNOWN ((unsigned int)-1)
+
+static snd_kcontrol_t *snd_mixer_oss_test_id(snd_mixer_oss_t *mixer, const char *name, int index)
+{
+ snd_card_t * card = mixer->card;
+ snd_ctl_elem_id_t id;
+
+ memset(&id, 0, sizeof(id));
+ id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ strcpy(id.name, name);
+ id.index = index;
+ return snd_ctl_find_id(card, &id);
+}
+
+static void snd_mixer_oss_get_volume1_vol(snd_mixer_oss_file_t *fmixer,
+ snd_mixer_oss_slot_t *pslot,
+ unsigned int numid,
+ int *left, int *right)
+{
+ snd_ctl_elem_info_t *uinfo;
+ snd_ctl_elem_value_t *uctl;
+ snd_kcontrol_t *kctl;
+ snd_card_t *card = fmixer->card;
+
+ if (numid == ID_UNKNOWN)
+ return;
+ down_read(&card->controls_rwsem);
+ if ((kctl = snd_ctl_find_numid(card, numid)) == NULL) {
+ up_read(&card->controls_rwsem);
+ return;
+ }
+ uinfo = kcalloc(1, sizeof(*uinfo), GFP_KERNEL);
+ uctl = kcalloc(1, sizeof(*uctl), GFP_KERNEL);
+ if (uinfo == NULL || uctl == NULL)
+ goto __unalloc;
+ snd_runtime_check(!kctl->info(kctl, uinfo), goto __unalloc);
+ snd_runtime_check(!kctl->get(kctl, uctl), goto __unalloc);
+ snd_runtime_check(uinfo->type != SNDRV_CTL_ELEM_TYPE_BOOLEAN || uinfo->value.integer.min != 0 || uinfo->value.integer.max != 1, goto __unalloc);
+ *left = snd_mixer_oss_conv1(uctl->value.integer.value[0], uinfo->value.integer.min, uinfo->value.integer.max, &pslot->volume[0]);
+ if (uinfo->count > 1)
+ *right = snd_mixer_oss_conv1(uctl->value.integer.value[1], uinfo->value.integer.min, uinfo->value.integer.max, &pslot->volume[1]);
+ __unalloc:
+ up_read(&card->controls_rwsem);
+ kfree(uctl);
+ kfree(uinfo);
+}
+
+static void snd_mixer_oss_get_volume1_sw(snd_mixer_oss_file_t *fmixer,
+ snd_mixer_oss_slot_t *pslot,
+ unsigned int numid,
+ int *left, int *right,
+ int route)
+{
+ snd_ctl_elem_info_t *uinfo;
+ snd_ctl_elem_value_t *uctl;
+ snd_kcontrol_t *kctl;
+ snd_card_t *card = fmixer->card;
+
+ if (numid == ID_UNKNOWN)
+ return;
+ down_read(&card->controls_rwsem);
+ if ((kctl = snd_ctl_find_numid(card, numid)) == NULL) {
+ up_read(&card->controls_rwsem);
+ return;
+ }
+ uinfo = kcalloc(1, sizeof(*uinfo), GFP_KERNEL);
+ uctl = kcalloc(1, sizeof(*uctl), GFP_KERNEL);
+ if (uinfo == NULL || uctl == NULL)
+ goto __unalloc;
+ snd_runtime_check(!kctl->info(kctl, uinfo), goto __unalloc);
+ snd_runtime_check(!kctl->get(kctl, uctl), goto __unalloc);
+ if (!uctl->value.integer.value[0]) {
+ *left = 0;
+ if (uinfo->count == 1)
+ *right = 0;
+ }
+ if (uinfo->count > 1 && !uctl->value.integer.value[route ? 3 : 1])
+ *right = 0;
+ __unalloc:
+ up_read(&card->controls_rwsem);
+ kfree(uctl);
+ kfree(uinfo);
+}
+
+static int snd_mixer_oss_get_volume1(snd_mixer_oss_file_t *fmixer,
+ snd_mixer_oss_slot_t *pslot,
+ int *left, int *right)
+{
+ struct slot *slot = (struct slot *)pslot->private_data;
+
+ *left = *right = 100;
+ if (slot->present & SNDRV_MIXER_OSS_PRESENT_PVOLUME) {
+ snd_mixer_oss_get_volume1_vol(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_PVOLUME], left, right);
+ } else if (slot->present & SNDRV_MIXER_OSS_PRESENT_GVOLUME) {
+ snd_mixer_oss_get_volume1_vol(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_GVOLUME], left, right);
+ } else if (slot->present & SNDRV_MIXER_OSS_PRESENT_GLOBAL) {
+ snd_mixer_oss_get_volume1_vol(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_GLOBAL], left, right);
+ }
+ if (slot->present & SNDRV_MIXER_OSS_PRESENT_PSWITCH) {
+ snd_mixer_oss_get_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_PSWITCH], left, right, 0);
+ } else if (slot->present & SNDRV_MIXER_OSS_PRESENT_GSWITCH) {
+ snd_mixer_oss_get_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_GSWITCH], left, right, 0);
+ } else if (slot->present & SNDRV_MIXER_OSS_PRESENT_PROUTE) {
+ snd_mixer_oss_get_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_PROUTE], left, right, 1);
+ } else if (slot->present & SNDRV_MIXER_OSS_PRESENT_GROUTE) {
+ snd_mixer_oss_get_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_GROUTE], left, right, 1);
+ }
+ return 0;
+}
+
+static void snd_mixer_oss_put_volume1_vol(snd_mixer_oss_file_t *fmixer,
+ snd_mixer_oss_slot_t *pslot,
+ unsigned int numid,
+ int left, int right)
+{
+ snd_ctl_elem_info_t *uinfo;
+ snd_ctl_elem_value_t *uctl;
+ snd_kcontrol_t *kctl;
+ snd_card_t *card = fmixer->card;
+ int res;
+
+ if (numid == ID_UNKNOWN)
+ return;
+ down_read(&card->controls_rwsem);
+ if ((kctl = snd_ctl_find_numid(card, numid)) == NULL)
+ return;
+ uinfo = kcalloc(1, sizeof(*uinfo), GFP_KERNEL);
+ uctl = kcalloc(1, sizeof(*uctl), GFP_KERNEL);
+ if (uinfo == NULL || uctl == NULL)
+ goto __unalloc;
+ snd_runtime_check(!kctl->info(kctl, uinfo), goto __unalloc);
+ snd_runtime_check(uinfo->type != SNDRV_CTL_ELEM_TYPE_BOOLEAN || uinfo->value.integer.min != 0 || uinfo->value.integer.max != 1, goto __unalloc);
+ uctl->value.integer.value[0] = snd_mixer_oss_conv2(left, uinfo->value.integer.min, uinfo->value.integer.max);
+ if (uinfo->count > 1)
+ uctl->value.integer.value[1] = snd_mixer_oss_conv2(right, uinfo->value.integer.min, uinfo->value.integer.max);
+ snd_runtime_check((res = kctl->put(kctl, uctl)) >= 0, goto __unalloc);
+ if (res > 0)
+ snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id);
+ __unalloc:
+ up_read(&card->controls_rwsem);
+ kfree(uctl);
+ kfree(uinfo);
+}
+
+static void snd_mixer_oss_put_volume1_sw(snd_mixer_oss_file_t *fmixer,
+ snd_mixer_oss_slot_t *pslot,
+ unsigned int numid,
+ int left, int right,
+ int route)
+{
+ snd_ctl_elem_info_t *uinfo;
+ snd_ctl_elem_value_t *uctl;
+ snd_kcontrol_t *kctl;
+ snd_card_t *card = fmixer->card;
+ int res;
+
+ if (numid == ID_UNKNOWN)
+ return;
+ down_read(&card->controls_rwsem);
+ if ((kctl = snd_ctl_find_numid(card, numid)) == NULL) {
+ up_read(&fmixer->card->controls_rwsem);
+ return;
+ }
+ uinfo = kcalloc(1, sizeof(*uinfo), GFP_KERNEL);
+ uctl = kcalloc(1, sizeof(*uctl), GFP_KERNEL);
+ if (uinfo == NULL || uctl == NULL)
+ goto __unalloc;
+ snd_runtime_check(!kctl->info(kctl, uinfo), goto __unalloc);
+ if (uinfo->count > 1) {
+ uctl->value.integer.value[0] = left > 0 ? 1 : 0;
+ uctl->value.integer.value[route ? 3 : 1] = right > 0 ? 1 : 0;
+ if (route) {
+ uctl->value.integer.value[1] =
+ uctl->value.integer.value[2] = 0;
+ }
+ } else {
+ uctl->value.integer.value[0] = (left > 0 || right > 0) ? 1 : 0;
+ }
+ snd_runtime_check((res = kctl->put(kctl, uctl)) >= 0, goto __unalloc);
+ if (res > 0)
+ snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id);
+ __unalloc:
+ up_read(&card->controls_rwsem);
+ kfree(uctl);
+ kfree(uinfo);
+}
+
+static int snd_mixer_oss_put_volume1(snd_mixer_oss_file_t *fmixer,
+ snd_mixer_oss_slot_t *pslot,
+ int left, int right)
+{
+ struct slot *slot = (struct slot *)pslot->private_data;
+
+ if (slot->present & SNDRV_MIXER_OSS_PRESENT_PVOLUME) {
+ snd_mixer_oss_put_volume1_vol(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_PVOLUME], left, right);
+ if (slot->present & SNDRV_MIXER_OSS_PRESENT_CVOLUME)
+ snd_mixer_oss_put_volume1_vol(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_CVOLUME], left, right);
+ } else if (slot->present & SNDRV_MIXER_OSS_PRESENT_GVOLUME) {
+ snd_mixer_oss_put_volume1_vol(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_GVOLUME], left, right);
+ } else if (slot->present & SNDRV_MIXER_OSS_PRESENT_GLOBAL) {
+ snd_mixer_oss_put_volume1_vol(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_GLOBAL], left, right);
+ }
+ if (left || right) {
+ if (slot->present & SNDRV_MIXER_OSS_PRESENT_PSWITCH)
+ snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_PSWITCH], left, right, 0);
+ if (slot->present & SNDRV_MIXER_OSS_PRESENT_GSWITCH)
+ snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_GSWITCH], left, right, 0);
+ if (slot->present & SNDRV_MIXER_OSS_PRESENT_PROUTE)
+ snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_PROUTE], left, right, 1);
+ if (slot->present & SNDRV_MIXER_OSS_PRESENT_GROUTE)
+ snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_GROUTE], left, right, 1);
+ } else {
+ if (slot->present & SNDRV_MIXER_OSS_PRESENT_PSWITCH) {
+ snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_PSWITCH], left, right, 0);
+ } else if (slot->present & SNDRV_MIXER_OSS_PRESENT_GSWITCH) {
+ snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_GSWITCH], left, right, 0);
+ } else if (slot->present & SNDRV_MIXER_OSS_PRESENT_PROUTE) {
+ snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_PROUTE], left, right, 1);
+ } else if (slot->present & SNDRV_MIXER_OSS_PRESENT_GROUTE) {
+ snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_GROUTE], left, right, 1);
+ }
+ }
+ return 0;
+}
+
+static int snd_mixer_oss_get_recsrc1_sw(snd_mixer_oss_file_t *fmixer,
+ snd_mixer_oss_slot_t *pslot,
+ int *active)
+{
+ struct slot *slot = (struct slot *)pslot->private_data;
+ int left, right;
+
+ left = right = 1;
+ snd_mixer_oss_get_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_CSWITCH], &left, &right, 0);
+ *active = (left || right) ? 1 : 0;
+ return 0;
+}
+
+static int snd_mixer_oss_get_recsrc1_route(snd_mixer_oss_file_t *fmixer,
+ snd_mixer_oss_slot_t *pslot,
+ int *active)
+{
+ struct slot *slot = (struct slot *)pslot->private_data;
+ int left, right;
+
+ left = right = 1;
+ snd_mixer_oss_get_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_CROUTE], &left, &right, 1);
+ *active = (left || right) ? 1 : 0;
+ return 0;
+}
+
+static int snd_mixer_oss_put_recsrc1_sw(snd_mixer_oss_file_t *fmixer,
+ snd_mixer_oss_slot_t *pslot,
+ int active)
+{
+ struct slot *slot = (struct slot *)pslot->private_data;
+
+ snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_CSWITCH], active, active, 0);
+ return 0;
+}
+
+static int snd_mixer_oss_put_recsrc1_route(snd_mixer_oss_file_t *fmixer,
+ snd_mixer_oss_slot_t *pslot,
+ int active)
+{
+ struct slot *slot = (struct slot *)pslot->private_data;
+
+ snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_CROUTE], active, active, 1);
+ return 0;
+}
+
+static int snd_mixer_oss_get_recsrc2(snd_mixer_oss_file_t *fmixer, unsigned int *active_index)
+{
+ snd_card_t *card = fmixer->card;
+ snd_mixer_oss_t *mixer = fmixer->mixer;
+ snd_kcontrol_t *kctl;
+ snd_mixer_oss_slot_t *pslot;
+ struct slot *slot;
+ snd_ctl_elem_info_t *uinfo;
+ snd_ctl_elem_value_t *uctl;
+ int err, idx;
+
+ uinfo = kcalloc(1, sizeof(*uinfo), GFP_KERNEL);
+ uctl = kcalloc(1, sizeof(*uctl), GFP_KERNEL);
+ if (uinfo == NULL || uctl == NULL) {
+ err = -ENOMEM;
+ goto __unlock;
+ }
+ down_read(&card->controls_rwsem);
+ kctl = snd_mixer_oss_test_id(mixer, "Capture Source", 0);
+ snd_runtime_check(kctl != NULL, err = -ENOENT; goto __unlock);
+ snd_runtime_check(!(err = kctl->info(kctl, uinfo)), goto __unlock);
+ snd_runtime_check(!(err = kctl->get(kctl, uctl)), goto __unlock);
+ for (idx = 0; idx < 32; idx++) {
+ if (!(mixer->mask_recsrc & (1 << idx)))
+ continue;
+ pslot = &mixer->slots[idx];
+ slot = (struct slot *)pslot->private_data;
+ if (slot->signature != SNDRV_MIXER_OSS_SIGNATURE)
+ continue;
+ if (!(slot->present & SNDRV_MIXER_OSS_PRESENT_CAPTURE))
+ continue;
+ if (slot->capture_item == uctl->value.enumerated.item[0]) {
+ *active_index = idx;
+ break;
+ }
+ }
+ err = 0;
+ __unlock:
+ up_read(&card->controls_rwsem);
+ kfree(uctl);
+ kfree(uinfo);
+ return err;
+}
+
+static int snd_mixer_oss_put_recsrc2(snd_mixer_oss_file_t *fmixer, unsigned int active_index)
+{
+ snd_card_t *card = fmixer->card;
+ snd_mixer_oss_t *mixer = fmixer->mixer;
+ snd_kcontrol_t *kctl;
+ snd_mixer_oss_slot_t *pslot;
+ struct slot *slot = NULL;
+ snd_ctl_elem_info_t *uinfo;
+ snd_ctl_elem_value_t *uctl;
+ int err;
+ unsigned int idx;
+
+ uinfo = kcalloc(1, sizeof(*uinfo), GFP_KERNEL);
+ uctl = kcalloc(1, sizeof(*uctl), GFP_KERNEL);
+ if (uinfo == NULL || uctl == NULL) {
+ err = -ENOMEM;
+ goto __unlock;
+ }
+ down_read(&card->controls_rwsem);
+ kctl = snd_mixer_oss_test_id(mixer, "Capture Source", 0);
+ snd_runtime_check(kctl != NULL, err = -ENOENT; goto __unlock);
+ snd_runtime_check(!(err = kctl->info(kctl, uinfo)), goto __unlock);
+ for (idx = 0; idx < 32; idx++) {
+ if (!(mixer->mask_recsrc & (1 << idx)))
+ continue;
+ pslot = &mixer->slots[idx];
+ slot = (struct slot *)pslot->private_data;
+ if (slot->signature != SNDRV_MIXER_OSS_SIGNATURE)
+ continue;
+ if (!(slot->present & SNDRV_MIXER_OSS_PRESENT_CAPTURE))
+ continue;
+ if (idx == active_index)
+ break;
+ slot = NULL;
+ }
+ snd_runtime_check(slot != NULL, goto __unlock);
+ for (idx = 0; idx < uinfo->count; idx++)
+ uctl->value.enumerated.item[idx] = slot->capture_item;
+ snd_runtime_check((err = kctl->put(kctl, uctl)) >= 0, );
+ if (err > 0)
+ snd_ctl_notify(fmixer->card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id);
+ err = 0;
+ __unlock:
+ up_read(&card->controls_rwsem);
+ kfree(uctl);
+ kfree(uinfo);
+ return err;
+}
+
+struct snd_mixer_oss_assign_table {
+ int oss_id;
+ const char *name;
+ int index;
+};
+
+static int snd_mixer_oss_build_test(snd_mixer_oss_t *mixer, struct slot *slot, const char *name, int index, int item)
+{
+ snd_ctl_elem_info_t *info;
+ snd_kcontrol_t *kcontrol;
+ snd_card_t *card = mixer->card;
+ int err;
+
+ down_read(&card->controls_rwsem);
+ kcontrol = snd_mixer_oss_test_id(mixer, name, index);
+ if (kcontrol == NULL) {
+ up_read(&card->controls_rwsem);
+ return 0;
+ }
+ info = kmalloc(sizeof(*info), GFP_KERNEL);
+ if (! info) {
+ up_read(&card->controls_rwsem);
+ return -ENOMEM;
+ }
+ if ((err = kcontrol->info(kcontrol, info)) < 0) {
+ up_read(&card->controls_rwsem);
+ kfree(info);
+ return err;
+ }
+ slot->numid[item] = kcontrol->id.numid;
+ up_read(&card->controls_rwsem);
+ if (info->count > slot->channels)
+ slot->channels = info->count;
+ slot->present |= 1 << item;
+ kfree(info);
+ return 0;
+}
+
+static void snd_mixer_oss_slot_free(snd_mixer_oss_slot_t *chn)
+{
+ struct slot *p = (struct slot *)chn->private_data;
+ if (p) {
+ if (p->allocated && p->assigned) {
+ kfree(p->assigned->name);
+ kfree(p->assigned);
+ }
+ kfree(p);
+ }
+}
+
+static void mixer_slot_clear(snd_mixer_oss_slot_t *rslot)
+{
+ int idx = rslot->number; /* remember this */
+ if (rslot->private_free)
+ rslot->private_free(rslot);
+ memset(rslot, 0, sizeof(*rslot));
+ rslot->number = idx;
+}
+
+/*
+ * build an OSS mixer element.
+ * ptr_allocated means the entry is dynamically allocated (change via proc file).
+ * when replace_old = 1, the old entry is replaced with the new one.
+ */
+static int snd_mixer_oss_build_input(snd_mixer_oss_t *mixer, struct snd_mixer_oss_assign_table *ptr, int ptr_allocated, int replace_old)
+{
+ struct slot slot;
+ struct slot *pslot;
+ snd_kcontrol_t *kctl;
+ snd_mixer_oss_slot_t *rslot;
+ char str[64];
+
+ /* check if already assigned */
+ if (mixer->slots[ptr->oss_id].get_volume && ! replace_old)
+ return 0;
+
+ memset(&slot, 0, sizeof(slot));
+ memset(slot.numid, 0xff, sizeof(slot.numid)); /* ID_UNKNOWN */
+ if (snd_mixer_oss_build_test(mixer, &slot, ptr->name, ptr->index,
+ SNDRV_MIXER_OSS_ITEM_GLOBAL))
+ return 0;
+ sprintf(str, "%s Switch", ptr->name);
+ if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index,
+ SNDRV_MIXER_OSS_ITEM_GSWITCH))
+ return 0;
+ sprintf(str, "%s Route", ptr->name);
+ if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index,
+ SNDRV_MIXER_OSS_ITEM_GROUTE))
+ return 0;
+ sprintf(str, "%s Volume", ptr->name);
+ if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index,
+ SNDRV_MIXER_OSS_ITEM_GVOLUME))
+ return 0;
+ sprintf(str, "%s Playback Switch", ptr->name);
+ if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index,
+ SNDRV_MIXER_OSS_ITEM_PSWITCH))
+ return 0;
+ sprintf(str, "%s Playback Route", ptr->name);
+ if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index,
+ SNDRV_MIXER_OSS_ITEM_PROUTE))
+ return 0;
+ sprintf(str, "%s Playback Volume", ptr->name);
+ if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index,
+ SNDRV_MIXER_OSS_ITEM_PVOLUME))
+ return 0;
+ sprintf(str, "%s Capture Switch", ptr->name);
+ if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index,
+ SNDRV_MIXER_OSS_ITEM_CSWITCH))
+ return 0;
+ sprintf(str, "%s Capture Route", ptr->name);
+ if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index,
+ SNDRV_MIXER_OSS_ITEM_CROUTE))
+ return 0;
+ sprintf(str, "%s Capture Volume", ptr->name);
+ if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index,
+ SNDRV_MIXER_OSS_ITEM_CVOLUME))
+ return 0;
+ down_read(&mixer->card->controls_rwsem);
+ if (ptr->index == 0 && (kctl = snd_mixer_oss_test_id(mixer, "Capture Source", 0)) != NULL) {
+ snd_ctl_elem_info_t *uinfo;
+
+ uinfo = kmalloc(sizeof(*uinfo), GFP_KERNEL);
+ if (! uinfo) {
+ up_read(&mixer->card->controls_rwsem);
+ return -ENOMEM;
+ }
+
+ memset(uinfo, 0, sizeof(*uinfo));
+ if (kctl->info(kctl, uinfo)) {
+ up_read(&mixer->card->controls_rwsem);
+ return 0;
+ }
+ strcpy(str, ptr->name);
+ if (!strcmp(str, "Master"))
+ strcpy(str, "Mix");
+ if (!strcmp(str, "Master Mono"))
+ strcpy(str, "Mix Mono");
+ slot.capture_item = 0;
+ if (!strcmp(uinfo->value.enumerated.name, str)) {
+ slot.present |= SNDRV_MIXER_OSS_PRESENT_CAPTURE;
+ } else {
+ for (slot.capture_item = 1; slot.capture_item < uinfo->value.enumerated.items; slot.capture_item++) {
+ uinfo->value.enumerated.item = slot.capture_item;
+ if (kctl->info(kctl, uinfo)) {
+ up_read(&mixer->card->controls_rwsem);
+ return 0;
+ }
+ if (!strcmp(uinfo->value.enumerated.name, str)) {
+ slot.present |= SNDRV_MIXER_OSS_PRESENT_CAPTURE;
+ break;
+ }
+ }
+ }
+ kfree(uinfo);
+ }
+ up_read(&mixer->card->controls_rwsem);
+ if (slot.present != 0) {
+ pslot = (struct slot *)kmalloc(sizeof(slot), GFP_KERNEL);
+ snd_runtime_check(pslot != NULL, return -ENOMEM);
+ *pslot = slot;
+ pslot->signature = SNDRV_MIXER_OSS_SIGNATURE;
+ pslot->assigned = ptr;
+ pslot->allocated = ptr_allocated;
+ rslot = &mixer->slots[ptr->oss_id];
+ mixer_slot_clear(rslot);
+ rslot->stereo = slot.channels > 1 ? 1 : 0;
+ rslot->get_volume = snd_mixer_oss_get_volume1;
+ rslot->put_volume = snd_mixer_oss_put_volume1;
+ /* note: ES18xx have both Capture Source and XX Capture Volume !!! */
+ if (slot.present & SNDRV_MIXER_OSS_PRESENT_CSWITCH) {
+ rslot->get_recsrc = snd_mixer_oss_get_recsrc1_sw;
+ rslot->put_recsrc = snd_mixer_oss_put_recsrc1_sw;
+ } else if (slot.present & SNDRV_MIXER_OSS_PRESENT_CROUTE) {
+ rslot->get_recsrc = snd_mixer_oss_get_recsrc1_route;
+ rslot->put_recsrc = snd_mixer_oss_put_recsrc1_route;
+ } else if (slot.present & SNDRV_MIXER_OSS_PRESENT_CAPTURE) {
+ mixer->mask_recsrc |= 1 << ptr->oss_id;
+ }
+ rslot->private_data = pslot;
+ rslot->private_free = snd_mixer_oss_slot_free;
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ */
+#define MIXER_VOL(name) [SOUND_MIXER_##name] = #name
+static char *oss_mixer_names[SNDRV_OSS_MAX_MIXERS] = {
+ MIXER_VOL(VOLUME),
+ MIXER_VOL(BASS),
+ MIXER_VOL(TREBLE),
+ MIXER_VOL(SYNTH),
+ MIXER_VOL(PCM),
+ MIXER_VOL(SPEAKER),
+ MIXER_VOL(LINE),
+ MIXER_VOL(MIC),
+ MIXER_VOL(CD),
+ MIXER_VOL(IMIX),
+ MIXER_VOL(ALTPCM),
+ MIXER_VOL(RECLEV),
+ MIXER_VOL(IGAIN),
+ MIXER_VOL(OGAIN),
+ MIXER_VOL(LINE1),
+ MIXER_VOL(LINE2),
+ MIXER_VOL(LINE3),
+ MIXER_VOL(DIGITAL1),
+ MIXER_VOL(DIGITAL2),
+ MIXER_VOL(DIGITAL3),
+ MIXER_VOL(PHONEIN),
+ MIXER_VOL(PHONEOUT),
+ MIXER_VOL(VIDEO),
+ MIXER_VOL(RADIO),
+ MIXER_VOL(MONITOR),
+};
+
+/*
+ * /proc interface
+ */
+
+static void snd_mixer_oss_proc_read(snd_info_entry_t *entry,
+ snd_info_buffer_t * buffer)
+{
+ snd_mixer_oss_t *mixer = entry->private_data;
+ int i;
+
+ down(&mixer->reg_mutex);
+ for (i = 0; i < SNDRV_OSS_MAX_MIXERS; i++) {
+ struct slot *p;
+
+ if (! oss_mixer_names[i])
+ continue;
+ p = (struct slot *)mixer->slots[i].private_data;
+ snd_iprintf(buffer, "%s ", oss_mixer_names[i]);
+ if (p && p->assigned)
+ snd_iprintf(buffer, "\"%s\" %d\n",
+ p->assigned->name,
+ p->assigned->index);
+ else
+ snd_iprintf(buffer, "\"\" 0\n");
+ }
+ up(&mixer->reg_mutex);
+}
+
+static void snd_mixer_oss_proc_write(snd_info_entry_t *entry,
+ snd_info_buffer_t * buffer)
+{
+ snd_mixer_oss_t *mixer = entry->private_data;
+ char line[128], str[32], idxstr[16], *cptr;
+ int ch, idx;
+ struct snd_mixer_oss_assign_table *tbl;
+ struct slot *slot;
+
+ while (!snd_info_get_line(buffer, line, sizeof(line))) {
+ cptr = snd_info_get_str(str, line, sizeof(str));
+ for (ch = 0; ch < SNDRV_OSS_MAX_MIXERS; ch++)
+ if (oss_mixer_names[ch] && strcmp(oss_mixer_names[ch], str) == 0)
+ break;
+ if (ch >= SNDRV_OSS_MAX_MIXERS) {
+ snd_printk(KERN_ERR "mixer_oss: invalid OSS volume '%s'\n", str);
+ continue;
+ }
+ cptr = snd_info_get_str(str, cptr, sizeof(str));
+ if (! *str) {
+ /* remove the entry */
+ down(&mixer->reg_mutex);
+ mixer_slot_clear(&mixer->slots[ch]);
+ up(&mixer->reg_mutex);
+ continue;
+ }
+ snd_info_get_str(idxstr, cptr, sizeof(idxstr));
+ idx = simple_strtoul(idxstr, NULL, 10);
+ if (idx >= 0x4000) { /* too big */
+ snd_printk(KERN_ERR "mixer_oss: invalid index %d\n", idx);
+ continue;
+ }
+ down(&mixer->reg_mutex);
+ slot = (struct slot *)mixer->slots[ch].private_data;
+ if (slot && slot->assigned &&
+ slot->assigned->index == idx && ! strcmp(slot->assigned->name, str))
+ /* not changed */
+ goto __unlock;
+ tbl = kmalloc(sizeof(*tbl), GFP_KERNEL);
+ if (! tbl) {
+ snd_printk(KERN_ERR "mixer_oss: no memory\n");
+ goto __unlock;
+ }
+ tbl->oss_id = ch;
+ tbl->name = snd_kmalloc_strdup(str, GFP_KERNEL);
+ if (! tbl->name) {
+ kfree(tbl);
+ goto __unlock;
+ }
+ tbl->index = idx;
+ if (snd_mixer_oss_build_input(mixer, tbl, 1, 1) <= 0) {
+ kfree(tbl->name);
+ kfree(tbl);
+ }
+ __unlock:
+ up(&mixer->reg_mutex);
+ }
+}
+
+static void snd_mixer_oss_proc_init(snd_mixer_oss_t *mixer)
+{
+ snd_info_entry_t *entry;
+
+ entry = snd_info_create_card_entry(mixer->card, "oss_mixer",
+ mixer->card->proc_root);
+ if (! entry)
+ return;
+ entry->content = SNDRV_INFO_CONTENT_TEXT;
+ entry->mode = S_IFREG | S_IRUGO | S_IWUSR;
+ entry->c.text.read_size = 8192;
+ entry->c.text.read = snd_mixer_oss_proc_read;
+ entry->c.text.write_size = 8192;
+ entry->c.text.write = snd_mixer_oss_proc_write;
+ entry->private_data = mixer;
+ if (snd_info_register(entry) < 0) {
+ snd_info_free_entry(entry);
+ entry = NULL;
+ }
+ mixer->proc_entry = entry;
+}
+
+static void snd_mixer_oss_proc_done(snd_mixer_oss_t *mixer)
+{
+ if (mixer->proc_entry) {
+ snd_info_unregister(mixer->proc_entry);
+ mixer->proc_entry = NULL;
+ }
+}
+
+static void snd_mixer_oss_build(snd_mixer_oss_t *mixer)
+{
+ static struct snd_mixer_oss_assign_table table[] = {
+ { SOUND_MIXER_VOLUME, "Master", 0 },
+ { SOUND_MIXER_VOLUME, "Front", 0 }, /* fallback */
+ { SOUND_MIXER_BASS, "Tone Control - Bass", 0 },
+ { SOUND_MIXER_TREBLE, "Tone Control - Treble", 0 },
+ { SOUND_MIXER_SYNTH, "Synth", 0 },
+ { SOUND_MIXER_SYNTH, "FM", 0 }, /* fallback */
+ { SOUND_MIXER_SYNTH, "Music", 0 }, /* fallback */
+ { SOUND_MIXER_PCM, "PCM", 0 },
+ { SOUND_MIXER_SPEAKER, "PC Speaker", 0 },
+ { SOUND_MIXER_LINE, "Line", 0 },
+ { SOUND_MIXER_MIC, "Mic", 0 },
+ { SOUND_MIXER_CD, "CD", 0 },
+ { SOUND_MIXER_IMIX, "Monitor Mix", 0 },
+ { SOUND_MIXER_ALTPCM, "PCM", 1 },
+ { SOUND_MIXER_ALTPCM, "Headphone", 0 }, /* fallback */
+ { SOUND_MIXER_ALTPCM, "Wave", 0 }, /* fallback */
+ { SOUND_MIXER_RECLEV, "-- nothing --", 0 },
+ { SOUND_MIXER_IGAIN, "Capture", 0 },
+ { SOUND_MIXER_OGAIN, "Playback", 0 },
+ { SOUND_MIXER_LINE1, "Aux", 0 },
+ { SOUND_MIXER_LINE2, "Aux", 1 },
+ { SOUND_MIXER_LINE3, "Aux", 2 },
+ { SOUND_MIXER_DIGITAL1, "Digital", 0 },
+ { SOUND_MIXER_DIGITAL1, "IEC958", 0 }, /* fallback */
+ { SOUND_MIXER_DIGITAL1, "IEC958 Optical", 0 }, /* fallback */
+ { SOUND_MIXER_DIGITAL1, "IEC958 Coaxial", 0 }, /* fallback */
+ { SOUND_MIXER_DIGITAL2, "Digital", 1 },
+ { SOUND_MIXER_DIGITAL3, "Digital", 2 },
+ { SOUND_MIXER_PHONEIN, "Phone", 0 },
+ { SOUND_MIXER_PHONEOUT, "Master Mono", 0 },
+ { SOUND_MIXER_PHONEOUT, "Phone", 0 }, /* fallback */
+ { SOUND_MIXER_VIDEO, "Video", 0 },
+ { SOUND_MIXER_RADIO, "Radio", 0 },
+ { SOUND_MIXER_MONITOR, "Monitor", 0 }
+ };
+ unsigned int idx;
+
+ for (idx = 0; idx < ARRAY_SIZE(table); idx++)
+ snd_mixer_oss_build_input(mixer, &table[idx], 0, 0);
+ if (mixer->mask_recsrc) {
+ mixer->get_recsrc = snd_mixer_oss_get_recsrc2;
+ mixer->put_recsrc = snd_mixer_oss_put_recsrc2;
+ }
+}
+
+/*
+ *
+ */
+
+static int snd_mixer_oss_free1(void *private)
+{
+ snd_mixer_oss_t *mixer = private;
+ snd_card_t * card;
+ int idx;
+
+ snd_assert(mixer != NULL, return -ENXIO);
+ card = mixer->card;
+ snd_assert(mixer == card->mixer_oss, return -ENXIO);
+ card->mixer_oss = NULL;
+ for (idx = 0; idx < SNDRV_OSS_MAX_MIXERS; idx++) {
+ snd_mixer_oss_slot_t *chn = &mixer->slots[idx];
+ if (chn->private_free)
+ chn->private_free(chn);
+ }
+ kfree(mixer);
+ return 0;
+}
+
+static int snd_mixer_oss_notify_handler(snd_card_t * card, int cmd)
+{
+ snd_mixer_oss_t *mixer;
+
+ if (cmd == SND_MIXER_OSS_NOTIFY_REGISTER) {
+ char name[128];
+ int idx, err;
+
+ mixer = kcalloc(2, sizeof(*mixer), GFP_KERNEL);
+ if (mixer == NULL)
+ return -ENOMEM;
+ init_MUTEX(&mixer->reg_mutex);
+ sprintf(name, "mixer%i%i", card->number, 0);
+ if ((err = snd_register_oss_device(SNDRV_OSS_DEVICE_TYPE_MIXER,
+ card, 0,
+ &snd_mixer_oss_reg,
+ name)) < 0) {
+ snd_printk("unable to register OSS mixer device %i:%i\n", card->number, 0);
+ kfree(mixer);
+ return err;
+ }
+ mixer->oss_dev_alloc = 1;
+ mixer->card = card;
+ if (*card->mixername)
+ strlcpy(mixer->name, card->mixername, sizeof(mixer->name));
+ else
+ strlcpy(mixer->name, name, sizeof(mixer->name));
+#ifdef SNDRV_OSS_INFO_DEV_MIXERS
+ snd_oss_info_register(SNDRV_OSS_INFO_DEV_MIXERS,
+ card->number,
+ mixer->name);
+#endif
+ for (idx = 0; idx < SNDRV_OSS_MAX_MIXERS; idx++)
+ mixer->slots[idx].number = idx;
+ card->mixer_oss = mixer;
+ snd_mixer_oss_build(mixer);
+ snd_mixer_oss_proc_init(mixer);
+ } else if (cmd == SND_MIXER_OSS_NOTIFY_DISCONNECT) {
+ mixer = card->mixer_oss;
+ if (mixer == NULL || !mixer->oss_dev_alloc)
+ return 0;
+ snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_MIXER, mixer->card, 0);
+ mixer->oss_dev_alloc = 0;
+ } else { /* free */
+ mixer = card->mixer_oss;
+ if (mixer == NULL)
+ return 0;
+#ifdef SNDRV_OSS_INFO_DEV_MIXERS
+ snd_oss_info_unregister(SNDRV_OSS_INFO_DEV_MIXERS, mixer->card->number);
+#endif
+ if (mixer->oss_dev_alloc)
+ snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_MIXER, mixer->card, 0);
+ snd_mixer_oss_proc_done(mixer);
+ return snd_mixer_oss_free1(mixer);
+ }
+ return 0;
+}
+
+static int __init alsa_mixer_oss_init(void)
+{
+ int idx;
+
+ snd_mixer_oss_notify_callback = snd_mixer_oss_notify_handler;
+ for (idx = 0; idx < SNDRV_CARDS; idx++) {
+ if (snd_cards[idx])
+ snd_mixer_oss_notify_handler(snd_cards[idx], SND_MIXER_OSS_NOTIFY_REGISTER);
+ }
+ return 0;
+}
+
+static void __exit alsa_mixer_oss_exit(void)
+{
+ int idx;
+
+ snd_mixer_oss_notify_callback = NULL;
+ for (idx = 0; idx < SNDRV_CARDS; idx++) {
+ if (snd_cards[idx])
+ snd_mixer_oss_notify_handler(snd_cards[idx], SND_MIXER_OSS_NOTIFY_FREE);
+ }
+}
+
+module_init(alsa_mixer_oss_init)
+module_exit(alsa_mixer_oss_exit)
+
+EXPORT_SYMBOL(snd_mixer_oss_ioctl_card);
diff --git a/sound/core/oss/mulaw.c b/sound/core/oss/mulaw.c
new file mode 100644
index 00000000000..44ec4c66eb1
--- /dev/null
+++ b/sound/core/oss/mulaw.c
@@ -0,0 +1,308 @@
+/*
+ * Mu-Law conversion Plug-In Interface
+ * Copyright (c) 1999 by Jaroslav Kysela <perex@suse.cz>
+ * Uros Bizjak <uros@kss-loka.si>
+ *
+ * Based on reference implementation by Sun Microsystems, Inc.
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/time.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include "pcm_plugin.h"
+
+#define SIGN_BIT (0x80) /* Sign bit for a u-law byte. */
+#define QUANT_MASK (0xf) /* Quantization field mask. */
+#define NSEGS (8) /* Number of u-law segments. */
+#define SEG_SHIFT (4) /* Left shift for segment number. */
+#define SEG_MASK (0x70) /* Segment field mask. */
+
+static inline int val_seg(int val)
+{
+ int r = 0;
+ val >>= 7;
+ if (val & 0xf0) {
+ val >>= 4;
+ r += 4;
+ }
+ if (val & 0x0c) {
+ val >>= 2;
+ r += 2;
+ }
+ if (val & 0x02)
+ r += 1;
+ return r;
+}
+
+#define BIAS (0x84) /* Bias for linear code. */
+
+/*
+ * linear2ulaw() - Convert a linear PCM value to u-law
+ *
+ * In order to simplify the encoding process, the original linear magnitude
+ * is biased by adding 33 which shifts the encoding range from (0 - 8158) to
+ * (33 - 8191). The result can be seen in the following encoding table:
+ *
+ * Biased Linear Input Code Compressed Code
+ * ------------------------ ---------------
+ * 00000001wxyza 000wxyz
+ * 0000001wxyzab 001wxyz
+ * 000001wxyzabc 010wxyz
+ * 00001wxyzabcd 011wxyz
+ * 0001wxyzabcde 100wxyz
+ * 001wxyzabcdef 101wxyz
+ * 01wxyzabcdefg 110wxyz
+ * 1wxyzabcdefgh 111wxyz
+ *
+ * Each biased linear code has a leading 1 which identifies the segment
+ * number. The value of the segment number is equal to 7 minus the number
+ * of leading 0's. The quantization interval is directly available as the
+ * four bits wxyz. * The trailing bits (a - h) are ignored.
+ *
+ * Ordinarily the complement of the resulting code word is used for
+ * transmission, and so the code word is complemented before it is returned.
+ *
+ * For further information see John C. Bellamy's Digital Telephony, 1982,
+ * John Wiley & Sons, pps 98-111 and 472-476.
+ */
+static unsigned char linear2ulaw(int pcm_val) /* 2's complement (16-bit range) */
+{
+ int mask;
+ int seg;
+ unsigned char uval;
+
+ /* Get the sign and the magnitude of the value. */
+ if (pcm_val < 0) {
+ pcm_val = BIAS - pcm_val;
+ mask = 0x7F;
+ } else {
+ pcm_val += BIAS;
+ mask = 0xFF;
+ }
+ if (pcm_val > 0x7FFF)
+ pcm_val = 0x7FFF;
+
+ /* Convert the scaled magnitude to segment number. */
+ seg = val_seg(pcm_val);
+
+ /*
+ * Combine the sign, segment, quantization bits;
+ * and complement the code word.
+ */
+ uval = (seg << 4) | ((pcm_val >> (seg + 3)) & 0xF);
+ return uval ^ mask;
+}
+
+/*
+ * ulaw2linear() - Convert a u-law value to 16-bit linear PCM
+ *
+ * First, a biased linear code is derived from the code word. An unbiased
+ * output can then be obtained by subtracting 33 from the biased code.
+ *
+ * Note that this function expects to be passed the complement of the
+ * original code word. This is in keeping with ISDN conventions.
+ */
+static int ulaw2linear(unsigned char u_val)
+{
+ int t;
+
+ /* Complement to obtain normal u-law value. */
+ u_val = ~u_val;
+
+ /*
+ * Extract and bias the quantization bits. Then
+ * shift up by the segment number and subtract out the bias.
+ */
+ t = ((u_val & QUANT_MASK) << 3) + BIAS;
+ t <<= ((unsigned)u_val & SEG_MASK) >> SEG_SHIFT;
+
+ return ((u_val & SIGN_BIT) ? (BIAS - t) : (t - BIAS));
+}
+
+/*
+ * Basic Mu-Law plugin
+ */
+
+typedef void (*mulaw_f)(snd_pcm_plugin_t *plugin,
+ const snd_pcm_plugin_channel_t *src_channels,
+ snd_pcm_plugin_channel_t *dst_channels,
+ snd_pcm_uframes_t frames);
+
+typedef struct mulaw_private_data {
+ mulaw_f func;
+ int conv;
+} mulaw_t;
+
+static void mulaw_decode(snd_pcm_plugin_t *plugin,
+ const snd_pcm_plugin_channel_t *src_channels,
+ snd_pcm_plugin_channel_t *dst_channels,
+ snd_pcm_uframes_t frames)
+{
+#define PUT_S16_LABELS
+#include "plugin_ops.h"
+#undef PUT_S16_LABELS
+ mulaw_t *data = (mulaw_t *)plugin->extra_data;
+ void *put = put_s16_labels[data->conv];
+ int channel;
+ int nchannels = plugin->src_format.channels;
+ for (channel = 0; channel < nchannels; ++channel) {
+ char *src;
+ char *dst;
+ int src_step, dst_step;
+ snd_pcm_uframes_t frames1;
+ if (!src_channels[channel].enabled) {
+ if (dst_channels[channel].wanted)
+ snd_pcm_area_silence(&dst_channels[channel].area, 0, frames, plugin->dst_format.format);
+ dst_channels[channel].enabled = 0;
+ continue;
+ }
+ dst_channels[channel].enabled = 1;
+ src = src_channels[channel].area.addr + src_channels[channel].area.first / 8;
+ dst = dst_channels[channel].area.addr + dst_channels[channel].area.first / 8;
+ src_step = src_channels[channel].area.step / 8;
+ dst_step = dst_channels[channel].area.step / 8;
+ frames1 = frames;
+ while (frames1-- > 0) {
+ signed short sample = ulaw2linear(*src);
+ goto *put;
+#define PUT_S16_END after
+#include "plugin_ops.h"
+#undef PUT_S16_END
+ after:
+ src += src_step;
+ dst += dst_step;
+ }
+ }
+}
+
+static void mulaw_encode(snd_pcm_plugin_t *plugin,
+ const snd_pcm_plugin_channel_t *src_channels,
+ snd_pcm_plugin_channel_t *dst_channels,
+ snd_pcm_uframes_t frames)
+{
+#define GET_S16_LABELS
+#include "plugin_ops.h"
+#undef GET_S16_LABELS
+ mulaw_t *data = (mulaw_t *)plugin->extra_data;
+ void *get = get_s16_labels[data->conv];
+ int channel;
+ int nchannels = plugin->src_format.channels;
+ signed short sample = 0;
+ for (channel = 0; channel < nchannels; ++channel) {
+ char *src;
+ char *dst;
+ int src_step, dst_step;
+ snd_pcm_uframes_t frames1;
+ if (!src_channels[channel].enabled) {
+ if (dst_channels[channel].wanted)
+ snd_pcm_area_silence(&dst_channels[channel].area, 0, frames, plugin->dst_format.format);
+ dst_channels[channel].enabled = 0;
+ continue;
+ }
+ dst_channels[channel].enabled = 1;
+ src = src_channels[channel].area.addr + src_channels[channel].area.first / 8;
+ dst = dst_channels[channel].area.addr + dst_channels[channel].area.first / 8;
+ src_step = src_channels[channel].area.step / 8;
+ dst_step = dst_channels[channel].area.step / 8;
+ frames1 = frames;
+ while (frames1-- > 0) {
+ goto *get;
+#define GET_S16_END after
+#include "plugin_ops.h"
+#undef GET_S16_END
+ after:
+ *dst = linear2ulaw(sample);
+ src += src_step;
+ dst += dst_step;
+ }
+ }
+}
+
+static snd_pcm_sframes_t mulaw_transfer(snd_pcm_plugin_t *plugin,
+ const snd_pcm_plugin_channel_t *src_channels,
+ snd_pcm_plugin_channel_t *dst_channels,
+ snd_pcm_uframes_t frames)
+{
+ mulaw_t *data;
+
+ snd_assert(plugin != NULL && src_channels != NULL && dst_channels != NULL, return -ENXIO);
+ if (frames == 0)
+ return 0;
+#ifdef CONFIG_SND_DEBUG
+ {
+ unsigned int channel;
+ for (channel = 0; channel < plugin->src_format.channels; channel++) {
+ snd_assert(src_channels[channel].area.first % 8 == 0 &&
+ src_channels[channel].area.step % 8 == 0,
+ return -ENXIO);
+ snd_assert(dst_channels[channel].area.first % 8 == 0 &&
+ dst_channels[channel].area.step % 8 == 0,
+ return -ENXIO);
+ }
+ }
+#endif
+ data = (mulaw_t *)plugin->extra_data;
+ data->func(plugin, src_channels, dst_channels, frames);
+ return frames;
+}
+
+int snd_pcm_plugin_build_mulaw(snd_pcm_plug_t *plug,
+ snd_pcm_plugin_format_t *src_format,
+ snd_pcm_plugin_format_t *dst_format,
+ snd_pcm_plugin_t **r_plugin)
+{
+ int err;
+ mulaw_t *data;
+ snd_pcm_plugin_t *plugin;
+ snd_pcm_plugin_format_t *format;
+ mulaw_f func;
+
+ snd_assert(r_plugin != NULL, return -ENXIO);
+ *r_plugin = NULL;
+
+ snd_assert(src_format->rate == dst_format->rate, return -ENXIO);
+ snd_assert(src_format->channels == dst_format->channels, return -ENXIO);
+
+ if (dst_format->format == SNDRV_PCM_FORMAT_MU_LAW) {
+ format = src_format;
+ func = mulaw_encode;
+ }
+ else if (src_format->format == SNDRV_PCM_FORMAT_MU_LAW) {
+ format = dst_format;
+ func = mulaw_decode;
+ }
+ else {
+ snd_BUG();
+ return -EINVAL;
+ }
+ snd_assert(snd_pcm_format_linear(format->format) != 0, return -ENXIO);
+
+ err = snd_pcm_plugin_build(plug, "Mu-Law<->linear conversion",
+ src_format, dst_format,
+ sizeof(mulaw_t), &plugin);
+ if (err < 0)
+ return err;
+ data = (mulaw_t*)plugin->extra_data;
+ data->func = func;
+ data->conv = getput_index(format->format);
+ snd_assert(data->conv >= 0 && data->conv < 4*2*2, return -EINVAL);
+ plugin->transfer = mulaw_transfer;
+ *r_plugin = plugin;
+ return 0;
+}
diff --git a/sound/core/oss/pcm_oss.c b/sound/core/oss/pcm_oss.c
new file mode 100644
index 00000000000..1a805020f57
--- /dev/null
+++ b/sound/core/oss/pcm_oss.c
@@ -0,0 +1,2530 @@
+/*
+ * Digital Audio (PCM) abstract layer / OSS compatible
+ * Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#if 0
+#define PLUGIN_DEBUG
+#endif
+#if 0
+#define OSS_DEBUG
+#endif
+
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/smp_lock.h>
+#include <linux/slab.h>
+#include <linux/time.h>
+#include <linux/vmalloc.h>
+#include <linux/moduleparam.h>
+#include <sound/core.h>
+#include <sound/minors.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include "pcm_plugin.h"
+#include <sound/info.h>
+#include <linux/soundcard.h>
+#include <sound/initval.h>
+
+#define OSS_ALSAEMULVER _SIOR ('M', 249, int)
+
+static int dsp_map[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = 0};
+static int adsp_map[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = 1};
+static int nonblock_open = 1;
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>, Abramo Bagnara <abramo@alsa-project.org>");
+MODULE_DESCRIPTION("PCM OSS emulation for ALSA.");
+MODULE_LICENSE("GPL");
+module_param_array(dsp_map, int, NULL, 0444);
+MODULE_PARM_DESC(dsp_map, "PCM device number assigned to 1st OSS device.");
+module_param_array(adsp_map, int, NULL, 0444);
+MODULE_PARM_DESC(adsp_map, "PCM device number assigned to 2nd OSS device.");
+module_param(nonblock_open, bool, 0644);
+MODULE_PARM_DESC(nonblock_open, "Don't block opening busy PCM devices.");
+MODULE_ALIAS_SNDRV_MINOR(SNDRV_MINOR_OSS_PCM);
+MODULE_ALIAS_SNDRV_MINOR(SNDRV_MINOR_OSS_PCM1);
+
+extern int snd_mixer_oss_ioctl_card(snd_card_t *card, unsigned int cmd, unsigned long arg);
+static int snd_pcm_oss_get_rate(snd_pcm_oss_file_t *pcm_oss_file);
+static int snd_pcm_oss_get_channels(snd_pcm_oss_file_t *pcm_oss_file);
+static int snd_pcm_oss_get_format(snd_pcm_oss_file_t *pcm_oss_file);
+
+static inline mm_segment_t snd_enter_user(void)
+{
+ mm_segment_t fs = get_fs();
+ set_fs(get_ds());
+ return fs;
+}
+
+static inline void snd_leave_user(mm_segment_t fs)
+{
+ set_fs(fs);
+}
+
+static int snd_pcm_oss_plugin_clear(snd_pcm_substream_t *substream)
+{
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ snd_pcm_plugin_t *plugin, *next;
+
+ plugin = runtime->oss.plugin_first;
+ while (plugin) {
+ next = plugin->next;
+ snd_pcm_plugin_free(plugin);
+ plugin = next;
+ }
+ runtime->oss.plugin_first = runtime->oss.plugin_last = NULL;
+ return 0;
+}
+
+static int snd_pcm_plugin_insert(snd_pcm_plugin_t *plugin)
+{
+ snd_pcm_runtime_t *runtime = plugin->plug->runtime;
+ plugin->next = runtime->oss.plugin_first;
+ plugin->prev = NULL;
+ if (runtime->oss.plugin_first) {
+ runtime->oss.plugin_first->prev = plugin;
+ runtime->oss.plugin_first = plugin;
+ } else {
+ runtime->oss.plugin_last =
+ runtime->oss.plugin_first = plugin;
+ }
+ return 0;
+}
+
+int snd_pcm_plugin_append(snd_pcm_plugin_t *plugin)
+{
+ snd_pcm_runtime_t *runtime = plugin->plug->runtime;
+ plugin->next = NULL;
+ plugin->prev = runtime->oss.plugin_last;
+ if (runtime->oss.plugin_last) {
+ runtime->oss.plugin_last->next = plugin;
+ runtime->oss.plugin_last = plugin;
+ } else {
+ runtime->oss.plugin_last =
+ runtime->oss.plugin_first = plugin;
+ }
+ return 0;
+}
+
+static long snd_pcm_oss_bytes(snd_pcm_substream_t *substream, long frames)
+{
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ snd_pcm_uframes_t buffer_size = snd_pcm_lib_buffer_bytes(substream);
+ frames = frames_to_bytes(runtime, frames);
+ if (buffer_size == runtime->oss.buffer_bytes)
+ return frames;
+ return (runtime->oss.buffer_bytes * frames) / buffer_size;
+}
+
+static long snd_pcm_alsa_frames(snd_pcm_substream_t *substream, long bytes)
+{
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ snd_pcm_uframes_t buffer_size = snd_pcm_lib_buffer_bytes(substream);
+ if (buffer_size == runtime->oss.buffer_bytes)
+ return bytes_to_frames(runtime, bytes);
+ return bytes_to_frames(runtime, (buffer_size * bytes) / runtime->oss.buffer_bytes);
+}
+
+static int snd_pcm_oss_format_from(int format)
+{
+ switch (format) {
+ case AFMT_MU_LAW: return SNDRV_PCM_FORMAT_MU_LAW;
+ case AFMT_A_LAW: return SNDRV_PCM_FORMAT_A_LAW;
+ case AFMT_IMA_ADPCM: return SNDRV_PCM_FORMAT_IMA_ADPCM;
+ case AFMT_U8: return SNDRV_PCM_FORMAT_U8;
+ case AFMT_S16_LE: return SNDRV_PCM_FORMAT_S16_LE;
+ case AFMT_S16_BE: return SNDRV_PCM_FORMAT_S16_BE;
+ case AFMT_S8: return SNDRV_PCM_FORMAT_S8;
+ case AFMT_U16_LE: return SNDRV_PCM_FORMAT_U16_LE;
+ case AFMT_U16_BE: return SNDRV_PCM_FORMAT_U16_BE;
+ case AFMT_MPEG: return SNDRV_PCM_FORMAT_MPEG;
+ default: return SNDRV_PCM_FORMAT_U8;
+ }
+}
+
+static int snd_pcm_oss_format_to(int format)
+{
+ switch (format) {
+ case SNDRV_PCM_FORMAT_MU_LAW: return AFMT_MU_LAW;
+ case SNDRV_PCM_FORMAT_A_LAW: return AFMT_A_LAW;
+ case SNDRV_PCM_FORMAT_IMA_ADPCM: return AFMT_IMA_ADPCM;
+ case SNDRV_PCM_FORMAT_U8: return AFMT_U8;
+ case SNDRV_PCM_FORMAT_S16_LE: return AFMT_S16_LE;
+ case SNDRV_PCM_FORMAT_S16_BE: return AFMT_S16_BE;
+ case SNDRV_PCM_FORMAT_S8: return AFMT_S8;
+ case SNDRV_PCM_FORMAT_U16_LE: return AFMT_U16_LE;
+ case SNDRV_PCM_FORMAT_U16_BE: return AFMT_U16_BE;
+ case SNDRV_PCM_FORMAT_MPEG: return AFMT_MPEG;
+ default: return -EINVAL;
+ }
+}
+
+static int snd_pcm_oss_period_size(snd_pcm_substream_t *substream,
+ snd_pcm_hw_params_t *oss_params,
+ snd_pcm_hw_params_t *slave_params)
+{
+ size_t s;
+ size_t oss_buffer_size, oss_period_size, oss_periods;
+ size_t min_period_size, max_period_size;
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ size_t oss_frame_size;
+
+ oss_frame_size = snd_pcm_format_physical_width(params_format(oss_params)) *
+ params_channels(oss_params) / 8;
+
+ oss_buffer_size = snd_pcm_plug_client_size(substream,
+ snd_pcm_hw_param_value_max(slave_params, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, NULL)) * oss_frame_size;
+ oss_buffer_size = 1 << ld2(oss_buffer_size);
+ if (atomic_read(&runtime->mmap_count)) {
+ if (oss_buffer_size > runtime->oss.mmap_bytes)
+ oss_buffer_size = runtime->oss.mmap_bytes;
+ }
+
+ if (substream->oss.setup &&
+ substream->oss.setup->period_size > 16)
+ oss_period_size = substream->oss.setup->period_size;
+ else if (runtime->oss.fragshift) {
+ oss_period_size = 1 << runtime->oss.fragshift;
+ if (oss_period_size > oss_buffer_size / 2)
+ oss_period_size = oss_buffer_size / 2;
+ } else {
+ int sd;
+ size_t bytes_per_sec = params_rate(oss_params) * snd_pcm_format_physical_width(params_format(oss_params)) * params_channels(oss_params) / 8;
+
+ oss_period_size = oss_buffer_size;
+ do {
+ oss_period_size /= 2;
+ } while (oss_period_size > bytes_per_sec);
+ if (runtime->oss.subdivision == 0) {
+ sd = 4;
+ if (oss_period_size / sd > 4096)
+ sd *= 2;
+ if (oss_period_size / sd < 4096)
+ sd = 1;
+ } else
+ sd = runtime->oss.subdivision;
+ oss_period_size /= sd;
+ if (oss_period_size < 16)
+ oss_period_size = 16;
+ }
+
+ min_period_size = snd_pcm_plug_client_size(substream,
+ snd_pcm_hw_param_value_min(slave_params, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, NULL));
+ min_period_size *= oss_frame_size;
+ min_period_size = 1 << (ld2(min_period_size - 1) + 1);
+ if (oss_period_size < min_period_size)
+ oss_period_size = min_period_size;
+
+ max_period_size = snd_pcm_plug_client_size(substream,
+ snd_pcm_hw_param_value_max(slave_params, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, NULL));
+ max_period_size *= oss_frame_size;
+ max_period_size = 1 << ld2(max_period_size);
+ if (oss_period_size > max_period_size)
+ oss_period_size = max_period_size;
+
+ oss_periods = oss_buffer_size / oss_period_size;
+
+ if (substream->oss.setup) {
+ if (substream->oss.setup->periods > 1)
+ oss_periods = substream->oss.setup->periods;
+ }
+
+ s = snd_pcm_hw_param_value_max(slave_params, SNDRV_PCM_HW_PARAM_PERIODS, NULL);
+ if (runtime->oss.maxfrags && s > runtime->oss.maxfrags)
+ s = runtime->oss.maxfrags;
+ if (oss_periods > s)
+ oss_periods = s;
+
+ s = snd_pcm_hw_param_value_min(slave_params, SNDRV_PCM_HW_PARAM_PERIODS, NULL);
+ if (s < 2)
+ s = 2;
+ if (oss_periods < s)
+ oss_periods = s;
+
+ while (oss_period_size * oss_periods > oss_buffer_size)
+ oss_period_size /= 2;
+
+ snd_assert(oss_period_size >= 16, return -EINVAL);
+ runtime->oss.period_bytes = oss_period_size;
+ runtime->oss.period_frames = 1;
+ runtime->oss.periods = oss_periods;
+ return 0;
+}
+
+static int choose_rate(snd_pcm_substream_t *substream,
+ snd_pcm_hw_params_t *params, unsigned int best_rate)
+{
+ snd_interval_t *it;
+ snd_pcm_hw_params_t *save;
+ unsigned int rate, prev;
+
+ save = kmalloc(sizeof(*save), GFP_KERNEL);
+ if (save == NULL)
+ return -ENOMEM;
+ *save = *params;
+ it = hw_param_interval(save, SNDRV_PCM_HW_PARAM_RATE);
+
+ /* try multiples of the best rate */
+ rate = best_rate;
+ for (;;) {
+ if (it->max < rate || (it->max == rate && it->openmax))
+ break;
+ if (it->min < rate || (it->min == rate && !it->openmin)) {
+ int ret;
+ ret = snd_pcm_hw_param_set(substream, params,
+ SNDRV_PCM_HW_PARAM_RATE,
+ rate, 0);
+ if (ret == (int)rate) {
+ kfree(save);
+ return rate;
+ }
+ *params = *save;
+ }
+ prev = rate;
+ rate += best_rate;
+ if (rate <= prev)
+ break;
+ }
+
+ /* not found, use the nearest rate */
+ kfree(save);
+ return snd_pcm_hw_param_near(substream, params, SNDRV_PCM_HW_PARAM_RATE, best_rate, NULL);
+}
+
+static int snd_pcm_oss_change_params(snd_pcm_substream_t *substream)
+{
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ snd_pcm_hw_params_t *params, *sparams;
+ snd_pcm_sw_params_t *sw_params;
+ ssize_t oss_buffer_size, oss_period_size;
+ size_t oss_frame_size;
+ int err;
+ int direct;
+ int format, sformat, n;
+ snd_mask_t sformat_mask;
+ snd_mask_t mask;
+
+ sw_params = kmalloc(sizeof(*sw_params), GFP_KERNEL);
+ params = kmalloc(sizeof(*params), GFP_KERNEL);
+ sparams = kmalloc(sizeof(*sparams), GFP_KERNEL);
+ if (!sw_params || !params || !sparams) {
+ snd_printd("No memory\n");
+ err = -ENOMEM;
+ goto failure;
+ }
+
+ if (atomic_read(&runtime->mmap_count)) {
+ direct = 1;
+ } else {
+ snd_pcm_oss_setup_t *setup = substream->oss.setup;
+ direct = (setup != NULL && setup->direct);
+ }
+
+ _snd_pcm_hw_params_any(sparams);
+ _snd_pcm_hw_param_setinteger(sparams, SNDRV_PCM_HW_PARAM_PERIODS);
+ _snd_pcm_hw_param_min(sparams, SNDRV_PCM_HW_PARAM_PERIODS, 2, 0);
+ snd_mask_none(&mask);
+ if (atomic_read(&runtime->mmap_count))
+ snd_mask_set(&mask, SNDRV_PCM_ACCESS_MMAP_INTERLEAVED);
+ else {
+ snd_mask_set(&mask, SNDRV_PCM_ACCESS_RW_INTERLEAVED);
+ if (!direct)
+ snd_mask_set(&mask, SNDRV_PCM_ACCESS_RW_NONINTERLEAVED);
+ }
+ err = snd_pcm_hw_param_mask(substream, sparams, SNDRV_PCM_HW_PARAM_ACCESS, &mask);
+ if (err < 0) {
+ snd_printd("No usable accesses\n");
+ err = -EINVAL;
+ goto failure;
+ }
+ choose_rate(substream, sparams, runtime->oss.rate);
+ snd_pcm_hw_param_near(substream, sparams, SNDRV_PCM_HW_PARAM_CHANNELS, runtime->oss.channels, NULL);
+
+ format = snd_pcm_oss_format_from(runtime->oss.format);
+
+ sformat_mask = *hw_param_mask(sparams, SNDRV_PCM_HW_PARAM_FORMAT);
+ if (direct)
+ sformat = format;
+ else
+ sformat = snd_pcm_plug_slave_format(format, &sformat_mask);
+
+ if (sformat < 0 || !snd_mask_test(&sformat_mask, sformat)) {
+ for (sformat = 0; sformat <= SNDRV_PCM_FORMAT_LAST; sformat++) {
+ if (snd_mask_test(&sformat_mask, sformat) &&
+ snd_pcm_oss_format_to(sformat) >= 0)
+ break;
+ }
+ if (sformat > SNDRV_PCM_FORMAT_LAST) {
+ snd_printd("Cannot find a format!!!\n");
+ err = -EINVAL;
+ goto failure;
+ }
+ }
+ err = _snd_pcm_hw_param_set(sparams, SNDRV_PCM_HW_PARAM_FORMAT, sformat, 0);
+ snd_assert(err >= 0, goto failure);
+
+ if (direct) {
+ memcpy(params, sparams, sizeof(*params));
+ } else {
+ _snd_pcm_hw_params_any(params);
+ _snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_ACCESS,
+ SNDRV_PCM_ACCESS_RW_INTERLEAVED, 0);
+ _snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ snd_pcm_oss_format_from(runtime->oss.format), 0);
+ _snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_CHANNELS,
+ runtime->oss.channels, 0);
+ _snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_RATE,
+ runtime->oss.rate, 0);
+ pdprintf("client: access = %i, format = %i, channels = %i, rate = %i\n",
+ params_access(params), params_format(params),
+ params_channels(params), params_rate(params));
+ }
+ pdprintf("slave: access = %i, format = %i, channels = %i, rate = %i\n",
+ params_access(sparams), params_format(sparams),
+ params_channels(sparams), params_rate(sparams));
+
+ oss_frame_size = snd_pcm_format_physical_width(params_format(params)) *
+ params_channels(params) / 8;
+
+ snd_pcm_oss_plugin_clear(substream);
+ if (!direct) {
+ /* add necessary plugins */
+ snd_pcm_oss_plugin_clear(substream);
+ if ((err = snd_pcm_plug_format_plugins(substream,
+ params,
+ sparams)) < 0) {
+ snd_printd("snd_pcm_plug_format_plugins failed: %i\n", err);
+ snd_pcm_oss_plugin_clear(substream);
+ goto failure;
+ }
+ if (runtime->oss.plugin_first) {
+ snd_pcm_plugin_t *plugin;
+ if ((err = snd_pcm_plugin_build_io(substream, sparams, &plugin)) < 0) {
+ snd_printd("snd_pcm_plugin_build_io failed: %i\n", err);
+ snd_pcm_oss_plugin_clear(substream);
+ goto failure;
+ }
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ err = snd_pcm_plugin_append(plugin);
+ } else {
+ err = snd_pcm_plugin_insert(plugin);
+ }
+ if (err < 0) {
+ snd_pcm_oss_plugin_clear(substream);
+ goto failure;
+ }
+ }
+ }
+
+ err = snd_pcm_oss_period_size(substream, params, sparams);
+ if (err < 0)
+ goto failure;
+
+ n = snd_pcm_plug_slave_size(substream, runtime->oss.period_bytes / oss_frame_size);
+ err = snd_pcm_hw_param_near(substream, sparams, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, n, NULL);
+ snd_assert(err >= 0, goto failure);
+
+ err = snd_pcm_hw_param_near(substream, sparams, SNDRV_PCM_HW_PARAM_PERIODS,
+ runtime->oss.periods, NULL);
+ snd_assert(err >= 0, goto failure);
+
+ snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DROP, NULL);
+
+ if ((err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_HW_PARAMS, sparams)) < 0) {
+ snd_printd("HW_PARAMS failed: %i\n", err);
+ goto failure;
+ }
+
+ memset(sw_params, 0, sizeof(*sw_params));
+ if (runtime->oss.trigger) {
+ sw_params->start_threshold = 1;
+ } else {
+ sw_params->start_threshold = runtime->boundary;
+ }
+ if (atomic_read(&runtime->mmap_count) || substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ sw_params->stop_threshold = runtime->boundary;
+ else
+ sw_params->stop_threshold = runtime->buffer_size;
+ sw_params->tstamp_mode = SNDRV_PCM_TSTAMP_NONE;
+ sw_params->period_step = 1;
+ sw_params->sleep_min = 0;
+ sw_params->avail_min = 1;
+ sw_params->xfer_align = 1;
+ if (atomic_read(&runtime->mmap_count) ||
+ (substream->oss.setup && substream->oss.setup->nosilence)) {
+ sw_params->silence_threshold = 0;
+ sw_params->silence_size = 0;
+ } else {
+ snd_pcm_uframes_t frames;
+ frames = runtime->period_size + 16;
+ if (frames > runtime->buffer_size)
+ frames = runtime->buffer_size;
+ sw_params->silence_threshold = frames;
+ sw_params->silence_size = frames;
+ }
+
+ if ((err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_SW_PARAMS, sw_params)) < 0) {
+ snd_printd("SW_PARAMS failed: %i\n", err);
+ goto failure;
+ }
+
+ runtime->oss.periods = params_periods(sparams);
+ oss_period_size = snd_pcm_plug_client_size(substream, params_period_size(sparams));
+ snd_assert(oss_period_size >= 0, err = -EINVAL; goto failure);
+ if (runtime->oss.plugin_first) {
+ err = snd_pcm_plug_alloc(substream, oss_period_size);
+ if (err < 0)
+ goto failure;
+ }
+ oss_period_size *= oss_frame_size;
+
+ oss_buffer_size = oss_period_size * runtime->oss.periods;
+ snd_assert(oss_buffer_size >= 0, err = -EINVAL; goto failure);
+
+ runtime->oss.period_bytes = oss_period_size;
+ runtime->oss.buffer_bytes = oss_buffer_size;
+
+ pdprintf("oss: period bytes = %i, buffer bytes = %i\n",
+ runtime->oss.period_bytes,
+ runtime->oss.buffer_bytes);
+ pdprintf("slave: period_size = %i, buffer_size = %i\n",
+ params_period_size(sparams),
+ params_buffer_size(sparams));
+
+ runtime->oss.format = snd_pcm_oss_format_to(params_format(params));
+ runtime->oss.channels = params_channels(params);
+ runtime->oss.rate = params_rate(params);
+
+ runtime->oss.params = 0;
+ runtime->oss.prepare = 1;
+ vfree(runtime->oss.buffer);
+ runtime->oss.buffer = vmalloc(runtime->oss.period_bytes);
+ runtime->oss.buffer_used = 0;
+ if (runtime->dma_area)
+ snd_pcm_format_set_silence(runtime->format, runtime->dma_area, bytes_to_samples(runtime, runtime->dma_bytes));
+
+ runtime->oss.period_frames = snd_pcm_alsa_frames(substream, oss_period_size);
+
+ err = 0;
+failure:
+ kfree(sw_params);
+ kfree(params);
+ kfree(sparams);
+ return err;
+}
+
+static int snd_pcm_oss_get_active_substream(snd_pcm_oss_file_t *pcm_oss_file, snd_pcm_substream_t **r_substream)
+{
+ int idx, err;
+ snd_pcm_substream_t *asubstream = NULL, *substream;
+
+ for (idx = 0; idx < 2; idx++) {
+ substream = pcm_oss_file->streams[idx];
+ if (substream == NULL)
+ continue;
+ if (asubstream == NULL)
+ asubstream = substream;
+ if (substream->runtime->oss.params) {
+ err = snd_pcm_oss_change_params(substream);
+ if (err < 0)
+ return err;
+ }
+ }
+ snd_assert(asubstream != NULL, return -EIO);
+ if (r_substream)
+ *r_substream = asubstream;
+ return 0;
+}
+
+static int snd_pcm_oss_prepare(snd_pcm_substream_t *substream)
+{
+ int err;
+ snd_pcm_runtime_t *runtime = substream->runtime;
+
+ err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_PREPARE, NULL);
+ if (err < 0) {
+ snd_printd("snd_pcm_oss_prepare: SNDRV_PCM_IOCTL_PREPARE failed\n");
+ return err;
+ }
+ runtime->oss.prepare = 0;
+ runtime->oss.prev_hw_ptr_interrupt = 0;
+ runtime->oss.period_ptr = 0;
+ runtime->oss.buffer_used = 0;
+
+ return 0;
+}
+
+static int snd_pcm_oss_make_ready(snd_pcm_substream_t *substream)
+{
+ snd_pcm_runtime_t *runtime;
+ int err;
+
+ if (substream == NULL)
+ return 0;
+ runtime = substream->runtime;
+ if (runtime->oss.params) {
+ err = snd_pcm_oss_change_params(substream);
+ if (err < 0)
+ return err;
+ }
+ if (runtime->oss.prepare) {
+ err = snd_pcm_oss_prepare(substream);
+ if (err < 0)
+ return err;
+ }
+ return 0;
+}
+
+static int snd_pcm_oss_capture_position_fixup(snd_pcm_substream_t *substream, snd_pcm_sframes_t *delay)
+{
+ snd_pcm_runtime_t *runtime;
+ snd_pcm_uframes_t frames;
+ int err = 0;
+
+ while (1) {
+ err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DELAY, delay);
+ if (err < 0)
+ break;
+ runtime = substream->runtime;
+ if (*delay <= (snd_pcm_sframes_t)runtime->buffer_size)
+ break;
+ /* in case of overrun, skip whole periods like OSS/Linux driver does */
+ /* until avail(delay) <= buffer_size */
+ frames = (*delay - runtime->buffer_size) + runtime->period_size - 1;
+ frames /= runtime->period_size;
+ frames *= runtime->period_size;
+ err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_FORWARD, &frames);
+ if (err < 0)
+ break;
+ }
+ return err;
+}
+
+snd_pcm_sframes_t snd_pcm_oss_write3(snd_pcm_substream_t *substream, const char *ptr, snd_pcm_uframes_t frames, int in_kernel)
+{
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ int ret;
+ while (1) {
+ if (runtime->status->state == SNDRV_PCM_STATE_XRUN ||
+ runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) {
+#ifdef OSS_DEBUG
+ if (runtime->status->state == SNDRV_PCM_STATE_XRUN)
+ printk("pcm_oss: write: recovering from XRUN\n");
+ else
+ printk("pcm_oss: write: recovering from SUSPEND\n");
+#endif
+ ret = snd_pcm_oss_prepare(substream);
+ if (ret < 0)
+ break;
+ }
+ if (in_kernel) {
+ mm_segment_t fs;
+ fs = snd_enter_user();
+ ret = snd_pcm_lib_write(substream, (void __user *)ptr, frames);
+ snd_leave_user(fs);
+ } else {
+ ret = snd_pcm_lib_write(substream, (void __user *)ptr, frames);
+ }
+ if (ret != -EPIPE && ret != -ESTRPIPE)
+ break;
+ /* test, if we can't store new data, because the stream */
+ /* has not been started */
+ if (runtime->status->state == SNDRV_PCM_STATE_PREPARED)
+ return -EAGAIN;
+ }
+ return ret;
+}
+
+snd_pcm_sframes_t snd_pcm_oss_read3(snd_pcm_substream_t *substream, char *ptr, snd_pcm_uframes_t frames, int in_kernel)
+{
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ snd_pcm_sframes_t delay;
+ int ret;
+ while (1) {
+ if (runtime->status->state == SNDRV_PCM_STATE_XRUN ||
+ runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) {
+#ifdef OSS_DEBUG
+ if (runtime->status->state == SNDRV_PCM_STATE_XRUN)
+ printk("pcm_oss: read: recovering from XRUN\n");
+ else
+ printk("pcm_oss: read: recovering from SUSPEND\n");
+#endif
+ ret = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DRAIN, NULL);
+ if (ret < 0)
+ break;
+ } else if (runtime->status->state == SNDRV_PCM_STATE_SETUP) {
+ ret = snd_pcm_oss_prepare(substream);
+ if (ret < 0)
+ break;
+ }
+ ret = snd_pcm_oss_capture_position_fixup(substream, &delay);
+ if (ret < 0)
+ break;
+ if (in_kernel) {
+ mm_segment_t fs;
+ fs = snd_enter_user();
+ ret = snd_pcm_lib_read(substream, (void __user *)ptr, frames);
+ snd_leave_user(fs);
+ } else {
+ ret = snd_pcm_lib_read(substream, (void __user *)ptr, frames);
+ }
+ if (ret == -EPIPE) {
+ if (runtime->status->state == SNDRV_PCM_STATE_DRAINING) {
+ ret = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DROP, NULL);
+ if (ret < 0)
+ break;
+ }
+ continue;
+ }
+ if (ret != -ESTRPIPE)
+ break;
+ }
+ return ret;
+}
+
+snd_pcm_sframes_t snd_pcm_oss_writev3(snd_pcm_substream_t *substream, void **bufs, snd_pcm_uframes_t frames, int in_kernel)
+{
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ int ret;
+ while (1) {
+ if (runtime->status->state == SNDRV_PCM_STATE_XRUN ||
+ runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) {
+#ifdef OSS_DEBUG
+ if (runtime->status->state == SNDRV_PCM_STATE_XRUN)
+ printk("pcm_oss: writev: recovering from XRUN\n");
+ else
+ printk("pcm_oss: writev: recovering from SUSPEND\n");
+#endif
+ ret = snd_pcm_oss_prepare(substream);
+ if (ret < 0)
+ break;
+ }
+ if (in_kernel) {
+ mm_segment_t fs;
+ fs = snd_enter_user();
+ ret = snd_pcm_lib_writev(substream, (void __user **)bufs, frames);
+ snd_leave_user(fs);
+ } else {
+ ret = snd_pcm_lib_writev(substream, (void __user **)bufs, frames);
+ }
+ if (ret != -EPIPE && ret != -ESTRPIPE)
+ break;
+
+ /* test, if we can't store new data, because the stream */
+ /* has not been started */
+ if (runtime->status->state == SNDRV_PCM_STATE_PREPARED)
+ return -EAGAIN;
+ }
+ return ret;
+}
+
+snd_pcm_sframes_t snd_pcm_oss_readv3(snd_pcm_substream_t *substream, void **bufs, snd_pcm_uframes_t frames, int in_kernel)
+{
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ int ret;
+ while (1) {
+ if (runtime->status->state == SNDRV_PCM_STATE_XRUN ||
+ runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) {
+#ifdef OSS_DEBUG
+ if (runtime->status->state == SNDRV_PCM_STATE_XRUN)
+ printk("pcm_oss: readv: recovering from XRUN\n");
+ else
+ printk("pcm_oss: readv: recovering from SUSPEND\n");
+#endif
+ ret = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DRAIN, NULL);
+ if (ret < 0)
+ break;
+ } else if (runtime->status->state == SNDRV_PCM_STATE_SETUP) {
+ ret = snd_pcm_oss_prepare(substream);
+ if (ret < 0)
+ break;
+ }
+ if (in_kernel) {
+ mm_segment_t fs;
+ fs = snd_enter_user();
+ ret = snd_pcm_lib_readv(substream, (void __user **)bufs, frames);
+ snd_leave_user(fs);
+ } else {
+ ret = snd_pcm_lib_readv(substream, (void __user **)bufs, frames);
+ }
+ if (ret != -EPIPE && ret != -ESTRPIPE)
+ break;
+ }
+ return ret;
+}
+
+static ssize_t snd_pcm_oss_write2(snd_pcm_substream_t *substream, const char *buf, size_t bytes, int in_kernel)
+{
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ snd_pcm_sframes_t frames, frames1;
+ if (runtime->oss.plugin_first) {
+ snd_pcm_plugin_channel_t *channels;
+ size_t oss_frame_bytes = (runtime->oss.plugin_first->src_width * runtime->oss.plugin_first->src_format.channels) / 8;
+ if (!in_kernel) {
+ if (copy_from_user(runtime->oss.buffer, (const char __user *)buf, bytes))
+ return -EFAULT;
+ buf = runtime->oss.buffer;
+ }
+ frames = bytes / oss_frame_bytes;
+ frames1 = snd_pcm_plug_client_channels_buf(substream, (char *)buf, frames, &channels);
+ if (frames1 < 0)
+ return frames1;
+ frames1 = snd_pcm_plug_write_transfer(substream, channels, frames1);
+ if (frames1 <= 0)
+ return frames1;
+ bytes = frames1 * oss_frame_bytes;
+ } else {
+ frames = bytes_to_frames(runtime, bytes);
+ frames1 = snd_pcm_oss_write3(substream, buf, frames, in_kernel);
+ if (frames1 <= 0)
+ return frames1;
+ bytes = frames_to_bytes(runtime, frames1);
+ }
+ return bytes;
+}
+
+static ssize_t snd_pcm_oss_write1(snd_pcm_substream_t *substream, const char __user *buf, size_t bytes)
+{
+ size_t xfer = 0;
+ ssize_t tmp;
+ snd_pcm_runtime_t *runtime = substream->runtime;
+
+ if (atomic_read(&runtime->mmap_count))
+ return -ENXIO;
+
+ if ((tmp = snd_pcm_oss_make_ready(substream)) < 0)
+ return tmp;
+ while (bytes > 0) {
+ if (bytes < runtime->oss.period_bytes || runtime->oss.buffer_used > 0) {
+ tmp = bytes;
+ if (tmp + runtime->oss.buffer_used > runtime->oss.period_bytes)
+ tmp = runtime->oss.period_bytes - runtime->oss.buffer_used;
+ if (tmp > 0) {
+ if (copy_from_user(runtime->oss.buffer + runtime->oss.buffer_used, buf, tmp))
+ return xfer > 0 ? (snd_pcm_sframes_t)xfer : -EFAULT;
+ }
+ runtime->oss.buffer_used += tmp;
+ buf += tmp;
+ bytes -= tmp;
+ xfer += tmp;
+ if ((substream->oss.setup != NULL && substream->oss.setup->partialfrag) ||
+ runtime->oss.buffer_used == runtime->oss.period_bytes) {
+ tmp = snd_pcm_oss_write2(substream, runtime->oss.buffer + runtime->oss.period_ptr,
+ runtime->oss.buffer_used - runtime->oss.period_ptr, 1);
+ if (tmp <= 0)
+ return xfer > 0 ? (snd_pcm_sframes_t)xfer : tmp;
+ runtime->oss.bytes += tmp;
+ runtime->oss.period_ptr += tmp;
+ runtime->oss.period_ptr %= runtime->oss.period_bytes;
+ if (runtime->oss.period_ptr == 0 ||
+ runtime->oss.period_ptr == runtime->oss.buffer_used)
+ runtime->oss.buffer_used = 0;
+ else if ((substream->ffile->f_flags & O_NONBLOCK) != 0)
+ return xfer > 0 ? xfer : -EAGAIN;
+ }
+ } else {
+ tmp = snd_pcm_oss_write2(substream, (const char *)buf, runtime->oss.period_bytes, 0);
+ if (tmp <= 0)
+ return xfer > 0 ? (snd_pcm_sframes_t)xfer : tmp;
+ runtime->oss.bytes += tmp;
+ buf += tmp;
+ bytes -= tmp;
+ xfer += tmp;
+ if ((substream->ffile->f_flags & O_NONBLOCK) != 0 &&
+ tmp != runtime->oss.period_bytes)
+ break;
+ }
+ }
+ return xfer;
+}
+
+static ssize_t snd_pcm_oss_read2(snd_pcm_substream_t *substream, char *buf, size_t bytes, int in_kernel)
+{
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ snd_pcm_sframes_t frames, frames1;
+ char __user *final_dst = (char __user *)buf;
+ if (runtime->oss.plugin_first) {
+ snd_pcm_plugin_channel_t *channels;
+ size_t oss_frame_bytes = (runtime->oss.plugin_last->dst_width * runtime->oss.plugin_last->dst_format.channels) / 8;
+ if (!in_kernel)
+ buf = runtime->oss.buffer;
+ frames = bytes / oss_frame_bytes;
+ frames1 = snd_pcm_plug_client_channels_buf(substream, buf, frames, &channels);
+ if (frames1 < 0)
+ return frames1;
+ frames1 = snd_pcm_plug_read_transfer(substream, channels, frames1);
+ if (frames1 <= 0)
+ return frames1;
+ bytes = frames1 * oss_frame_bytes;
+ if (!in_kernel && copy_to_user(final_dst, buf, bytes))
+ return -EFAULT;
+ } else {
+ frames = bytes_to_frames(runtime, bytes);
+ frames1 = snd_pcm_oss_read3(substream, buf, frames, in_kernel);
+ if (frames1 <= 0)
+ return frames1;
+ bytes = frames_to_bytes(runtime, frames1);
+ }
+ return bytes;
+}
+
+static ssize_t snd_pcm_oss_read1(snd_pcm_substream_t *substream, char __user *buf, size_t bytes)
+{
+ size_t xfer = 0;
+ ssize_t tmp;
+ snd_pcm_runtime_t *runtime = substream->runtime;
+
+ if (atomic_read(&runtime->mmap_count))
+ return -ENXIO;
+
+ if ((tmp = snd_pcm_oss_make_ready(substream)) < 0)
+ return tmp;
+ while (bytes > 0) {
+ if (bytes < runtime->oss.period_bytes || runtime->oss.buffer_used > 0) {
+ if (runtime->oss.buffer_used == 0) {
+ tmp = snd_pcm_oss_read2(substream, runtime->oss.buffer, runtime->oss.period_bytes, 1);
+ if (tmp <= 0)
+ return xfer > 0 ? (snd_pcm_sframes_t)xfer : tmp;
+ runtime->oss.bytes += tmp;
+ runtime->oss.period_ptr = tmp;
+ runtime->oss.buffer_used = tmp;
+ }
+ tmp = bytes;
+ if ((size_t) tmp > runtime->oss.buffer_used)
+ tmp = runtime->oss.buffer_used;
+ if (copy_to_user(buf, runtime->oss.buffer + (runtime->oss.period_ptr - runtime->oss.buffer_used), tmp))
+ return xfer > 0 ? (snd_pcm_sframes_t)xfer : -EFAULT;
+ buf += tmp;
+ bytes -= tmp;
+ xfer += tmp;
+ runtime->oss.buffer_used -= tmp;
+ } else {
+ tmp = snd_pcm_oss_read2(substream, (char *)buf, runtime->oss.period_bytes, 0);
+ if (tmp <= 0)
+ return xfer > 0 ? (snd_pcm_sframes_t)xfer : tmp;
+ runtime->oss.bytes += tmp;
+ buf += tmp;
+ bytes -= tmp;
+ xfer += tmp;
+ }
+ }
+ return xfer;
+}
+
+static int snd_pcm_oss_reset(snd_pcm_oss_file_t *pcm_oss_file)
+{
+ snd_pcm_substream_t *substream;
+
+ substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK];
+ if (substream != NULL) {
+ snd_pcm_kernel_playback_ioctl(substream, SNDRV_PCM_IOCTL_DROP, NULL);
+ substream->runtime->oss.prepare = 1;
+ }
+ substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE];
+ if (substream != NULL) {
+ snd_pcm_kernel_capture_ioctl(substream, SNDRV_PCM_IOCTL_DROP, NULL);
+ substream->runtime->oss.prepare = 1;
+ }
+ return 0;
+}
+
+static int snd_pcm_oss_post(snd_pcm_oss_file_t *pcm_oss_file)
+{
+ snd_pcm_substream_t *substream;
+ int err;
+
+ substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK];
+ if (substream != NULL) {
+ if ((err = snd_pcm_oss_make_ready(substream)) < 0)
+ return err;
+ snd_pcm_kernel_playback_ioctl(substream, SNDRV_PCM_IOCTL_START, NULL);
+ }
+ /* note: all errors from the start action are ignored */
+ /* OSS apps do not know, how to handle them */
+ return 0;
+}
+
+static int snd_pcm_oss_sync1(snd_pcm_substream_t *substream, size_t size)
+{
+ snd_pcm_runtime_t *runtime;
+ ssize_t result = 0;
+ long res;
+ wait_queue_t wait;
+
+ runtime = substream->runtime;
+ init_waitqueue_entry(&wait, current);
+ add_wait_queue(&runtime->sleep, &wait);
+#ifdef OSS_DEBUG
+ printk("sync1: size = %li\n", size);
+#endif
+ while (1) {
+ result = snd_pcm_oss_write2(substream, runtime->oss.buffer, size, 1);
+ if (result > 0) {
+ runtime->oss.buffer_used = 0;
+ result = 0;
+ break;
+ }
+ if (result != 0 && result != -EAGAIN)
+ break;
+ result = 0;
+ set_current_state(TASK_INTERRUPTIBLE);
+ snd_pcm_stream_lock_irq(substream);
+ res = runtime->status->state;
+ snd_pcm_stream_unlock_irq(substream);
+ if (res != SNDRV_PCM_STATE_RUNNING) {
+ set_current_state(TASK_RUNNING);
+ break;
+ }
+ res = schedule_timeout(10 * HZ);
+ if (signal_pending(current)) {
+ result = -ERESTARTSYS;
+ break;
+ }
+ if (res == 0) {
+ snd_printk(KERN_ERR "OSS sync error - DMA timeout\n");
+ result = -EIO;
+ break;
+ }
+ }
+ remove_wait_queue(&runtime->sleep, &wait);
+ return result;
+}
+
+static int snd_pcm_oss_sync(snd_pcm_oss_file_t *pcm_oss_file)
+{
+ int err = 0;
+ unsigned int saved_f_flags;
+ snd_pcm_substream_t *substream;
+ snd_pcm_runtime_t *runtime;
+ snd_pcm_format_t format;
+ unsigned long width;
+ size_t size;
+
+ substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK];
+ if (substream != NULL) {
+ runtime = substream->runtime;
+ if (atomic_read(&runtime->mmap_count))
+ goto __direct;
+ if ((err = snd_pcm_oss_make_ready(substream)) < 0)
+ return err;
+ format = snd_pcm_oss_format_from(runtime->oss.format);
+ width = snd_pcm_format_physical_width(format);
+ if (runtime->oss.buffer_used > 0) {
+#ifdef OSS_DEBUG
+ printk("sync: buffer_used\n");
+#endif
+ size = (8 * (runtime->oss.period_bytes - runtime->oss.buffer_used) + 7) / width;
+ snd_pcm_format_set_silence(format,
+ runtime->oss.buffer + runtime->oss.buffer_used,
+ size);
+ err = snd_pcm_oss_sync1(substream, runtime->oss.period_bytes);
+ if (err < 0)
+ return err;
+ } else if (runtime->oss.period_ptr > 0) {
+#ifdef OSS_DEBUG
+ printk("sync: period_ptr\n");
+#endif
+ size = runtime->oss.period_bytes - runtime->oss.period_ptr;
+ snd_pcm_format_set_silence(format,
+ runtime->oss.buffer,
+ size * 8 / width);
+ err = snd_pcm_oss_sync1(substream, size);
+ if (err < 0)
+ return err;
+ }
+ /*
+ * The ALSA's period might be a bit large than OSS one.
+ * Fill the remain portion of ALSA period with zeros.
+ */
+ size = runtime->control->appl_ptr % runtime->period_size;
+ if (size > 0) {
+ size = runtime->period_size - size;
+ if (runtime->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED) {
+ size = (runtime->frame_bits * size) / 8;
+ while (size > 0) {
+ mm_segment_t fs;
+ size_t size1 = size < runtime->oss.period_bytes ? size : runtime->oss.period_bytes;
+ size -= size1;
+ size1 *= 8;
+ size1 /= runtime->sample_bits;
+ snd_pcm_format_set_silence(runtime->format,
+ runtime->oss.buffer,
+ size1);
+ fs = snd_enter_user();
+ snd_pcm_lib_write(substream, (void __user *)runtime->oss.buffer, size1);
+ snd_leave_user(fs);
+ }
+ } else if (runtime->access == SNDRV_PCM_ACCESS_RW_NONINTERLEAVED) {
+ void __user *buffers[runtime->channels];
+ memset(buffers, 0, runtime->channels * sizeof(void *));
+ snd_pcm_lib_writev(substream, buffers, size);
+ }
+ }
+ /*
+ * finish sync: drain the buffer
+ */
+ __direct:
+ saved_f_flags = substream->ffile->f_flags;
+ substream->ffile->f_flags &= ~O_NONBLOCK;
+ err = snd_pcm_kernel_playback_ioctl(substream, SNDRV_PCM_IOCTL_DRAIN, NULL);
+ substream->ffile->f_flags = saved_f_flags;
+ if (err < 0)
+ return err;
+ runtime->oss.prepare = 1;
+ }
+
+ substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE];
+ if (substream != NULL) {
+ if ((err = snd_pcm_oss_make_ready(substream)) < 0)
+ return err;
+ runtime = substream->runtime;
+ err = snd_pcm_kernel_capture_ioctl(substream, SNDRV_PCM_IOCTL_DROP, NULL);
+ if (err < 0)
+ return err;
+ runtime->oss.buffer_used = 0;
+ runtime->oss.prepare = 1;
+ }
+ return 0;
+}
+
+static int snd_pcm_oss_set_rate(snd_pcm_oss_file_t *pcm_oss_file, int rate)
+{
+ int idx;
+
+ for (idx = 1; idx >= 0; --idx) {
+ snd_pcm_substream_t *substream = pcm_oss_file->streams[idx];
+ snd_pcm_runtime_t *runtime;
+ if (substream == NULL)
+ continue;
+ runtime = substream->runtime;
+ if (rate < 1000)
+ rate = 1000;
+ else if (rate > 192000)
+ rate = 192000;
+ if (runtime->oss.rate != rate) {
+ runtime->oss.params = 1;
+ runtime->oss.rate = rate;
+ }
+ }
+ return snd_pcm_oss_get_rate(pcm_oss_file);
+}
+
+static int snd_pcm_oss_get_rate(snd_pcm_oss_file_t *pcm_oss_file)
+{
+ snd_pcm_substream_t *substream;
+ int err;
+
+ if ((err = snd_pcm_oss_get_active_substream(pcm_oss_file, &substream)) < 0)
+ return err;
+ return substream->runtime->oss.rate;
+}
+
+static int snd_pcm_oss_set_channels(snd_pcm_oss_file_t *pcm_oss_file, unsigned int channels)
+{
+ int idx;
+ if (channels < 1)
+ channels = 1;
+ if (channels > 128)
+ return -EINVAL;
+ for (idx = 1; idx >= 0; --idx) {
+ snd_pcm_substream_t *substream = pcm_oss_file->streams[idx];
+ snd_pcm_runtime_t *runtime;
+ if (substream == NULL)
+ continue;
+ runtime = substream->runtime;
+ if (runtime->oss.channels != channels) {
+ runtime->oss.params = 1;
+ runtime->oss.channels = channels;
+ }
+ }
+ return snd_pcm_oss_get_channels(pcm_oss_file);
+}
+
+static int snd_pcm_oss_get_channels(snd_pcm_oss_file_t *pcm_oss_file)
+{
+ snd_pcm_substream_t *substream;
+ int err;
+
+ if ((err = snd_pcm_oss_get_active_substream(pcm_oss_file, &substream)) < 0)
+ return err;
+ return substream->runtime->oss.channels;
+}
+
+static int snd_pcm_oss_get_block_size(snd_pcm_oss_file_t *pcm_oss_file)
+{
+ snd_pcm_substream_t *substream;
+ int err;
+
+ if ((err = snd_pcm_oss_get_active_substream(pcm_oss_file, &substream)) < 0)
+ return err;
+ return substream->runtime->oss.period_bytes;
+}
+
+static int snd_pcm_oss_get_formats(snd_pcm_oss_file_t *pcm_oss_file)
+{
+ snd_pcm_substream_t *substream;
+ int err;
+ int direct;
+ snd_pcm_hw_params_t *params;
+ unsigned int formats = 0;
+ snd_mask_t format_mask;
+ int fmt;
+
+ if ((err = snd_pcm_oss_get_active_substream(pcm_oss_file, &substream)) < 0)
+ return err;
+ if (atomic_read(&substream->runtime->mmap_count)) {
+ direct = 1;
+ } else {
+ snd_pcm_oss_setup_t *setup = substream->oss.setup;
+ direct = (setup != NULL && setup->direct);
+ }
+ if (!direct)
+ return AFMT_MU_LAW | AFMT_U8 |
+ AFMT_S16_LE | AFMT_S16_BE |
+ AFMT_S8 | AFMT_U16_LE |
+ AFMT_U16_BE;
+ params = kmalloc(sizeof(*params), GFP_KERNEL);
+ if (!params)
+ return -ENOMEM;
+ _snd_pcm_hw_params_any(params);
+ err = snd_pcm_hw_refine(substream, params);
+ format_mask = *hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
+ kfree(params);
+ snd_assert(err >= 0, return err);
+ for (fmt = 0; fmt < 32; ++fmt) {
+ if (snd_mask_test(&format_mask, fmt)) {
+ int f = snd_pcm_oss_format_to(fmt);
+ if (f >= 0)
+ formats |= f;
+ }
+ }
+ return formats;
+}
+
+static int snd_pcm_oss_set_format(snd_pcm_oss_file_t *pcm_oss_file, int format)
+{
+ int formats, idx;
+
+ if (format != AFMT_QUERY) {
+ formats = snd_pcm_oss_get_formats(pcm_oss_file);
+ if (!(formats & format))
+ format = AFMT_U8;
+ for (idx = 1; idx >= 0; --idx) {
+ snd_pcm_substream_t *substream = pcm_oss_file->streams[idx];
+ snd_pcm_runtime_t *runtime;
+ if (substream == NULL)
+ continue;
+ runtime = substream->runtime;
+ if (runtime->oss.format != format) {
+ runtime->oss.params = 1;
+ runtime->oss.format = format;
+ }
+ }
+ }
+ return snd_pcm_oss_get_format(pcm_oss_file);
+}
+
+static int snd_pcm_oss_get_format(snd_pcm_oss_file_t *pcm_oss_file)
+{
+ snd_pcm_substream_t *substream;
+ int err;
+
+ if ((err = snd_pcm_oss_get_active_substream(pcm_oss_file, &substream)) < 0)
+ return err;
+ return substream->runtime->oss.format;
+}
+
+static int snd_pcm_oss_set_subdivide1(snd_pcm_substream_t *substream, int subdivide)
+{
+ snd_pcm_runtime_t *runtime;
+
+ if (substream == NULL)
+ return 0;
+ runtime = substream->runtime;
+ if (subdivide == 0) {
+ subdivide = runtime->oss.subdivision;
+ if (subdivide == 0)
+ subdivide = 1;
+ return subdivide;
+ }
+ if (runtime->oss.subdivision || runtime->oss.fragshift)
+ return -EINVAL;
+ if (subdivide != 1 && subdivide != 2 && subdivide != 4 &&
+ subdivide != 8 && subdivide != 16)
+ return -EINVAL;
+ runtime->oss.subdivision = subdivide;
+ runtime->oss.params = 1;
+ return subdivide;
+}
+
+static int snd_pcm_oss_set_subdivide(snd_pcm_oss_file_t *pcm_oss_file, int subdivide)
+{
+ int err = -EINVAL, idx;
+
+ for (idx = 1; idx >= 0; --idx) {
+ snd_pcm_substream_t *substream = pcm_oss_file->streams[idx];
+ if (substream == NULL)
+ continue;
+ if ((err = snd_pcm_oss_set_subdivide1(substream, subdivide)) < 0)
+ return err;
+ }
+ return err;
+}
+
+static int snd_pcm_oss_set_fragment1(snd_pcm_substream_t *substream, unsigned int val)
+{
+ snd_pcm_runtime_t *runtime;
+
+ if (substream == NULL)
+ return 0;
+ runtime = substream->runtime;
+ if (runtime->oss.subdivision || runtime->oss.fragshift)
+ return -EINVAL;
+ runtime->oss.fragshift = val & 0xffff;
+ runtime->oss.maxfrags = (val >> 16) & 0xffff;
+ if (runtime->oss.fragshift < 4) /* < 16 */
+ runtime->oss.fragshift = 4;
+ if (runtime->oss.maxfrags < 2)
+ runtime->oss.maxfrags = 2;
+ runtime->oss.params = 1;
+ return 0;
+}
+
+static int snd_pcm_oss_set_fragment(snd_pcm_oss_file_t *pcm_oss_file, unsigned int val)
+{
+ int err = -EINVAL, idx;
+
+ for (idx = 1; idx >= 0; --idx) {
+ snd_pcm_substream_t *substream = pcm_oss_file->streams[idx];
+ if (substream == NULL)
+ continue;
+ if ((err = snd_pcm_oss_set_fragment1(substream, val)) < 0)
+ return err;
+ }
+ return err;
+}
+
+static int snd_pcm_oss_nonblock(struct file * file)
+{
+ file->f_flags |= O_NONBLOCK;
+ return 0;
+}
+
+static int snd_pcm_oss_get_caps1(snd_pcm_substream_t *substream, int res)
+{
+
+ if (substream == NULL) {
+ res &= ~DSP_CAP_DUPLEX;
+ return res;
+ }
+#ifdef DSP_CAP_MULTI
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ if (substream->pstr->substream_count > 1)
+ res |= DSP_CAP_MULTI;
+#endif
+ /* DSP_CAP_REALTIME is set all times: */
+ /* all ALSA drivers can return actual pointer in ring buffer */
+#if defined(DSP_CAP_REALTIME) && 0
+ {
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ if (runtime->info & (SNDRV_PCM_INFO_BLOCK_TRANSFER|SNDRV_PCM_INFO_BATCH))
+ res &= ~DSP_CAP_REALTIME;
+ }
+#endif
+ return res;
+}
+
+static int snd_pcm_oss_get_caps(snd_pcm_oss_file_t *pcm_oss_file)
+{
+ int result, idx;
+
+ result = DSP_CAP_TRIGGER | DSP_CAP_MMAP | DSP_CAP_DUPLEX | DSP_CAP_REALTIME;
+ for (idx = 0; idx < 2; idx++) {
+ snd_pcm_substream_t *substream = pcm_oss_file->streams[idx];
+ result = snd_pcm_oss_get_caps1(substream, result);
+ }
+ result |= 0x0001; /* revision - same as SB AWE 64 */
+ return result;
+}
+
+static void snd_pcm_oss_simulate_fill(snd_pcm_substream_t *substream, snd_pcm_uframes_t hw_ptr)
+{
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ snd_pcm_uframes_t appl_ptr;
+ appl_ptr = hw_ptr + runtime->buffer_size;
+ appl_ptr %= runtime->boundary;
+ runtime->control->appl_ptr = appl_ptr;
+}
+
+static int snd_pcm_oss_set_trigger(snd_pcm_oss_file_t *pcm_oss_file, int trigger)
+{
+ snd_pcm_runtime_t *runtime;
+ snd_pcm_substream_t *psubstream = NULL, *csubstream = NULL;
+ int err, cmd;
+
+#ifdef OSS_DEBUG
+ printk("pcm_oss: trigger = 0x%x\n", trigger);
+#endif
+
+ psubstream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK];
+ csubstream = pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE];
+
+ if (psubstream) {
+ if ((err = snd_pcm_oss_make_ready(psubstream)) < 0)
+ return err;
+ }
+ if (csubstream) {
+ if ((err = snd_pcm_oss_make_ready(csubstream)) < 0)
+ return err;
+ }
+ if (psubstream) {
+ runtime = psubstream->runtime;
+ if (trigger & PCM_ENABLE_OUTPUT) {
+ if (runtime->oss.trigger)
+ goto _skip1;
+ if (atomic_read(&psubstream->runtime->mmap_count))
+ snd_pcm_oss_simulate_fill(psubstream, runtime->hw_ptr_interrupt);
+ runtime->oss.trigger = 1;
+ runtime->start_threshold = 1;
+ cmd = SNDRV_PCM_IOCTL_START;
+ } else {
+ if (!runtime->oss.trigger)
+ goto _skip1;
+ runtime->oss.trigger = 0;
+ runtime->start_threshold = runtime->boundary;
+ cmd = SNDRV_PCM_IOCTL_DROP;
+ runtime->oss.prepare = 1;
+ }
+ err = snd_pcm_kernel_playback_ioctl(psubstream, cmd, NULL);
+ if (err < 0)
+ return err;
+ }
+ _skip1:
+ if (csubstream) {
+ runtime = csubstream->runtime;
+ if (trigger & PCM_ENABLE_INPUT) {
+ if (runtime->oss.trigger)
+ goto _skip2;
+ runtime->oss.trigger = 1;
+ runtime->start_threshold = 1;
+ cmd = SNDRV_PCM_IOCTL_START;
+ } else {
+ if (!runtime->oss.trigger)
+ goto _skip2;
+ runtime->oss.trigger = 0;
+ runtime->start_threshold = runtime->boundary;
+ cmd = SNDRV_PCM_IOCTL_DROP;
+ runtime->oss.prepare = 1;
+ }
+ err = snd_pcm_kernel_capture_ioctl(csubstream, cmd, NULL);
+ if (err < 0)
+ return err;
+ }
+ _skip2:
+ return 0;
+}
+
+static int snd_pcm_oss_get_trigger(snd_pcm_oss_file_t *pcm_oss_file)
+{
+ snd_pcm_substream_t *psubstream = NULL, *csubstream = NULL;
+ int result = 0;
+
+ psubstream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK];
+ csubstream = pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE];
+ if (psubstream && psubstream->runtime && psubstream->runtime->oss.trigger)
+ result |= PCM_ENABLE_OUTPUT;
+ if (csubstream && csubstream->runtime && csubstream->runtime->oss.trigger)
+ result |= PCM_ENABLE_INPUT;
+ return result;
+}
+
+static int snd_pcm_oss_get_odelay(snd_pcm_oss_file_t *pcm_oss_file)
+{
+ snd_pcm_substream_t *substream;
+ snd_pcm_runtime_t *runtime;
+ snd_pcm_sframes_t delay;
+ int err;
+
+ substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK];
+ if (substream == NULL)
+ return -EINVAL;
+ if ((err = snd_pcm_oss_make_ready(substream)) < 0)
+ return err;
+ runtime = substream->runtime;
+ if (runtime->oss.params || runtime->oss.prepare)
+ return 0;
+ err = snd_pcm_kernel_playback_ioctl(substream, SNDRV_PCM_IOCTL_DELAY, &delay);
+ if (err == -EPIPE)
+ delay = 0; /* hack for broken OSS applications */
+ else if (err < 0)
+ return err;
+ return snd_pcm_oss_bytes(substream, delay);
+}
+
+static int snd_pcm_oss_get_ptr(snd_pcm_oss_file_t *pcm_oss_file, int stream, struct count_info __user * _info)
+{
+ snd_pcm_substream_t *substream;
+ snd_pcm_runtime_t *runtime;
+ snd_pcm_sframes_t delay;
+ int fixup;
+ struct count_info info;
+ int err;
+
+ if (_info == NULL)
+ return -EFAULT;
+ substream = pcm_oss_file->streams[stream];
+ if (substream == NULL)
+ return -EINVAL;
+ if ((err = snd_pcm_oss_make_ready(substream)) < 0)
+ return err;
+ runtime = substream->runtime;
+ if (runtime->oss.params || runtime->oss.prepare) {
+ memset(&info, 0, sizeof(info));
+ if (copy_to_user(_info, &info, sizeof(info)))
+ return -EFAULT;
+ return 0;
+ }
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DELAY, &delay);
+ if (err == -EPIPE || err == -ESTRPIPE || (! err && delay < 0)) {
+ err = 0;
+ delay = 0;
+ fixup = 0;
+ } else {
+ fixup = runtime->oss.buffer_used;
+ }
+ } else {
+ err = snd_pcm_oss_capture_position_fixup(substream, &delay);
+ fixup = -runtime->oss.buffer_used;
+ }
+ if (err < 0)
+ return err;
+ info.ptr = snd_pcm_oss_bytes(substream, runtime->status->hw_ptr % runtime->buffer_size);
+ if (atomic_read(&runtime->mmap_count)) {
+ snd_pcm_sframes_t n;
+ n = (delay = runtime->hw_ptr_interrupt) - runtime->oss.prev_hw_ptr_interrupt;
+ if (n < 0)
+ n += runtime->boundary;
+ info.blocks = n / runtime->period_size;
+ runtime->oss.prev_hw_ptr_interrupt = delay;
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ snd_pcm_oss_simulate_fill(substream, delay);
+ info.bytes = snd_pcm_oss_bytes(substream, runtime->status->hw_ptr) & INT_MAX;
+ } else {
+ delay = snd_pcm_oss_bytes(substream, delay) + fixup;
+ info.blocks = delay / runtime->oss.period_bytes;
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+ info.bytes = (runtime->oss.bytes - delay) & INT_MAX;
+ else
+ info.bytes = (runtime->oss.bytes + delay) & INT_MAX;
+ }
+ if (copy_to_user(_info, &info, sizeof(info)))
+ return -EFAULT;
+ return 0;
+}
+
+static int snd_pcm_oss_get_space(snd_pcm_oss_file_t *pcm_oss_file, int stream, struct audio_buf_info __user *_info)
+{
+ snd_pcm_substream_t *substream;
+ snd_pcm_runtime_t *runtime;
+ snd_pcm_sframes_t avail;
+ int fixup;
+ struct audio_buf_info info;
+ int err;
+
+ if (_info == NULL)
+ return -EFAULT;
+ substream = pcm_oss_file->streams[stream];
+ if (substream == NULL)
+ return -EINVAL;
+ runtime = substream->runtime;
+
+ if (runtime->oss.params &&
+ (err = snd_pcm_oss_change_params(substream)) < 0)
+ return err;
+
+ info.fragsize = runtime->oss.period_bytes;
+ info.fragstotal = runtime->periods;
+ if (runtime->oss.prepare) {
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ info.bytes = runtime->oss.period_bytes * runtime->oss.periods;
+ info.fragments = runtime->oss.periods;
+ } else {
+ info.bytes = 0;
+ info.fragments = 0;
+ }
+ } else {
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DELAY, &avail);
+ if (err == -EPIPE || err == -ESTRPIPE || (! err && avail < 0)) {
+ avail = runtime->buffer_size;
+ err = 0;
+ fixup = 0;
+ } else {
+ avail = runtime->buffer_size - avail;
+ fixup = -runtime->oss.buffer_used;
+ }
+ } else {
+ err = snd_pcm_oss_capture_position_fixup(substream, &avail);
+ fixup = runtime->oss.buffer_used;
+ }
+ if (err < 0)
+ return err;
+ info.bytes = snd_pcm_oss_bytes(substream, avail) + fixup;
+ info.fragments = info.bytes / runtime->oss.period_bytes;
+ }
+
+#ifdef OSS_DEBUG
+ printk("pcm_oss: space: bytes = %i, fragments = %i, fragstotal = %i, fragsize = %i\n", info.bytes, info.fragments, info.fragstotal, info.fragsize);
+#endif
+ if (copy_to_user(_info, &info, sizeof(info)))
+ return -EFAULT;
+ return 0;
+}
+
+static int snd_pcm_oss_get_mapbuf(snd_pcm_oss_file_t *pcm_oss_file, int stream, struct buffmem_desc __user * _info)
+{
+ // it won't be probably implemented
+ // snd_printd("TODO: snd_pcm_oss_get_mapbuf\n");
+ return -EINVAL;
+}
+
+static snd_pcm_oss_setup_t *snd_pcm_oss_look_for_setup(snd_pcm_t *pcm, int stream, const char *task_name)
+{
+ const char *ptr, *ptrl;
+ snd_pcm_oss_setup_t *setup;
+
+ down(&pcm->streams[stream].oss.setup_mutex);
+ for (setup = pcm->streams[stream].oss.setup_list; setup; setup = setup->next) {
+ if (!strcmp(setup->task_name, task_name)) {
+ up(&pcm->streams[stream].oss.setup_mutex);
+ return setup;
+ }
+ }
+ ptr = ptrl = task_name;
+ while (*ptr) {
+ if (*ptr == '/')
+ ptrl = ptr + 1;
+ ptr++;
+ }
+ if (ptrl == task_name) {
+ goto __not_found;
+ return NULL;
+ }
+ for (setup = pcm->streams[stream].oss.setup_list; setup; setup = setup->next) {
+ if (!strcmp(setup->task_name, ptrl)) {
+ up(&pcm->streams[stream].oss.setup_mutex);
+ return setup;
+ }
+ }
+ __not_found:
+ up(&pcm->streams[stream].oss.setup_mutex);
+ return NULL;
+}
+
+static void snd_pcm_oss_init_substream(snd_pcm_substream_t *substream,
+ snd_pcm_oss_setup_t *setup,
+ int minor)
+{
+ snd_pcm_runtime_t *runtime;
+
+ substream->oss.oss = 1;
+ substream->oss.setup = setup;
+ runtime = substream->runtime;
+ runtime->oss.params = 1;
+ runtime->oss.trigger = 1;
+ runtime->oss.rate = 8000;
+ switch (SNDRV_MINOR_OSS_DEVICE(minor)) {
+ case SNDRV_MINOR_OSS_PCM_8:
+ runtime->oss.format = AFMT_U8;
+ break;
+ case SNDRV_MINOR_OSS_PCM_16:
+ runtime->oss.format = AFMT_S16_LE;
+ break;
+ default:
+ runtime->oss.format = AFMT_MU_LAW;
+ }
+ runtime->oss.channels = 1;
+ runtime->oss.fragshift = 0;
+ runtime->oss.maxfrags = 0;
+ runtime->oss.subdivision = 0;
+}
+
+static void snd_pcm_oss_release_substream(snd_pcm_substream_t *substream)
+{
+ snd_pcm_runtime_t *runtime;
+ runtime = substream->runtime;
+ vfree(runtime->oss.buffer);
+ snd_pcm_oss_plugin_clear(substream);
+ substream->oss.file = NULL;
+ substream->oss.oss = 0;
+}
+
+static int snd_pcm_oss_release_file(snd_pcm_oss_file_t *pcm_oss_file)
+{
+ int cidx;
+ snd_assert(pcm_oss_file != NULL, return -ENXIO);
+ for (cidx = 0; cidx < 2; ++cidx) {
+ snd_pcm_substream_t *substream = pcm_oss_file->streams[cidx];
+ snd_pcm_runtime_t *runtime;
+ if (substream == NULL)
+ continue;
+ runtime = substream->runtime;
+
+ snd_pcm_stream_lock_irq(substream);
+ if (snd_pcm_running(substream))
+ snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP);
+ snd_pcm_stream_unlock_irq(substream);
+ if (substream->open_flag) {
+ if (substream->ops->hw_free != NULL)
+ substream->ops->hw_free(substream);
+ substream->ops->close(substream);
+ substream->open_flag = 0;
+ }
+ substream->ffile = NULL;
+ snd_pcm_oss_release_substream(substream);
+ snd_pcm_release_substream(substream);
+ }
+ kfree(pcm_oss_file);
+ return 0;
+}
+
+static int snd_pcm_oss_open_file(struct file *file,
+ snd_pcm_t *pcm,
+ snd_pcm_oss_file_t **rpcm_oss_file,
+ int minor,
+ snd_pcm_oss_setup_t *psetup,
+ snd_pcm_oss_setup_t *csetup)
+{
+ int err = 0;
+ snd_pcm_oss_file_t *pcm_oss_file;
+ snd_pcm_substream_t *psubstream = NULL, *csubstream = NULL;
+ unsigned int f_mode = file->f_mode;
+
+ snd_assert(rpcm_oss_file != NULL, return -EINVAL);
+ *rpcm_oss_file = NULL;
+
+ pcm_oss_file = kcalloc(1, sizeof(*pcm_oss_file), GFP_KERNEL);
+ if (pcm_oss_file == NULL)
+ return -ENOMEM;
+
+ if ((f_mode & (FMODE_WRITE|FMODE_READ)) == (FMODE_WRITE|FMODE_READ) &&
+ (pcm->info_flags & SNDRV_PCM_INFO_HALF_DUPLEX))
+ f_mode = FMODE_WRITE;
+ if ((f_mode & FMODE_WRITE) && !(psetup && psetup->disable)) {
+ if ((err = snd_pcm_open_substream(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+ &psubstream)) < 0) {
+ snd_pcm_oss_release_file(pcm_oss_file);
+ return err;
+ }
+ pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK] = psubstream;
+ }
+ if ((f_mode & FMODE_READ) && !(csetup && csetup->disable)) {
+ if ((err = snd_pcm_open_substream(pcm, SNDRV_PCM_STREAM_CAPTURE,
+ &csubstream)) < 0) {
+ if (!(f_mode & FMODE_WRITE) || err != -ENODEV) {
+ snd_pcm_oss_release_file(pcm_oss_file);
+ return err;
+ } else {
+ csubstream = NULL;
+ }
+ }
+ pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE] = csubstream;
+ }
+
+ if (psubstream == NULL && csubstream == NULL) {
+ snd_pcm_oss_release_file(pcm_oss_file);
+ return -EINVAL;
+ }
+ if (psubstream != NULL) {
+ psubstream->oss.file = pcm_oss_file;
+ err = snd_pcm_hw_constraints_init(psubstream);
+ if (err < 0) {
+ snd_printd("snd_pcm_hw_constraint_init failed\n");
+ snd_pcm_oss_release_file(pcm_oss_file);
+ return err;
+ }
+ if ((err = psubstream->ops->open(psubstream)) < 0) {
+ snd_pcm_oss_release_file(pcm_oss_file);
+ return err;
+ }
+ psubstream->open_flag = 1;
+ err = snd_pcm_hw_constraints_complete(psubstream);
+ if (err < 0) {
+ snd_printd("snd_pcm_hw_constraint_complete failed\n");
+ snd_pcm_oss_release_file(pcm_oss_file);
+ return err;
+ }
+ psubstream->ffile = file;
+ snd_pcm_oss_init_substream(psubstream, psetup, minor);
+ }
+ if (csubstream != NULL) {
+ csubstream->oss.file = pcm_oss_file;
+ err = snd_pcm_hw_constraints_init(csubstream);
+ if (err < 0) {
+ snd_printd("snd_pcm_hw_constraint_init failed\n");
+ snd_pcm_oss_release_file(pcm_oss_file);
+ return err;
+ }
+ if ((err = csubstream->ops->open(csubstream)) < 0) {
+ snd_pcm_oss_release_file(pcm_oss_file);
+ return err;
+ }
+ csubstream->open_flag = 1;
+ err = snd_pcm_hw_constraints_complete(csubstream);
+ if (err < 0) {
+ snd_printd("snd_pcm_hw_constraint_complete failed\n");
+ snd_pcm_oss_release_file(pcm_oss_file);
+ return err;
+ }
+ csubstream->ffile = file;
+ snd_pcm_oss_init_substream(csubstream, csetup, minor);
+ }
+
+ file->private_data = pcm_oss_file;
+ *rpcm_oss_file = pcm_oss_file;
+ return 0;
+}
+
+
+static int snd_pcm_oss_open(struct inode *inode, struct file *file)
+{
+ int minor = iminor(inode);
+ int cardnum = SNDRV_MINOR_OSS_CARD(minor);
+ int device;
+ int err;
+ char task_name[32];
+ snd_pcm_t *pcm;
+ snd_pcm_oss_file_t *pcm_oss_file;
+ snd_pcm_oss_setup_t *psetup = NULL, *csetup = NULL;
+ int nonblock;
+ wait_queue_t wait;
+
+ snd_assert(cardnum >= 0 && cardnum < SNDRV_CARDS, return -ENXIO);
+ device = SNDRV_MINOR_OSS_DEVICE(minor) == SNDRV_MINOR_OSS_PCM1 ?
+ adsp_map[cardnum] : dsp_map[cardnum];
+
+ pcm = snd_pcm_devices[(cardnum * SNDRV_PCM_DEVICES) + device];
+ if (pcm == NULL) {
+ err = -ENODEV;
+ goto __error1;
+ }
+ err = snd_card_file_add(pcm->card, file);
+ if (err < 0)
+ goto __error1;
+ if (!try_module_get(pcm->card->module)) {
+ err = -EFAULT;
+ goto __error2;
+ }
+ if (snd_task_name(current, task_name, sizeof(task_name)) < 0) {
+ err = -EFAULT;
+ goto __error;
+ }
+ if (file->f_mode & FMODE_WRITE)
+ psetup = snd_pcm_oss_look_for_setup(pcm, SNDRV_PCM_STREAM_PLAYBACK, task_name);
+ if (file->f_mode & FMODE_READ)
+ csetup = snd_pcm_oss_look_for_setup(pcm, SNDRV_PCM_STREAM_CAPTURE, task_name);
+
+ nonblock = !!(file->f_flags & O_NONBLOCK);
+ if (psetup && !psetup->disable) {
+ if (psetup->nonblock)
+ nonblock = 1;
+ else if (psetup->block)
+ nonblock = 0;
+ } else if (csetup && !csetup->disable) {
+ if (csetup->nonblock)
+ nonblock = 1;
+ else if (csetup->block)
+ nonblock = 0;
+ }
+ if (!nonblock)
+ nonblock = nonblock_open;
+
+ init_waitqueue_entry(&wait, current);
+ add_wait_queue(&pcm->open_wait, &wait);
+ down(&pcm->open_mutex);
+ while (1) {
+ err = snd_pcm_oss_open_file(file, pcm, &pcm_oss_file,
+ minor, psetup, csetup);
+ if (err >= 0)
+ break;
+ if (err == -EAGAIN) {
+ if (nonblock) {
+ err = -EBUSY;
+ break;
+ }
+ } else
+ break;
+ set_current_state(TASK_INTERRUPTIBLE);
+ up(&pcm->open_mutex);
+ schedule();
+ down(&pcm->open_mutex);
+ if (signal_pending(current)) {
+ err = -ERESTARTSYS;
+ break;
+ }
+ }
+ remove_wait_queue(&pcm->open_wait, &wait);
+ up(&pcm->open_mutex);
+ if (err < 0)
+ goto __error;
+ return err;
+
+ __error:
+ module_put(pcm->card->module);
+ __error2:
+ snd_card_file_remove(pcm->card, file);
+ __error1:
+ return err;
+}
+
+static int snd_pcm_oss_release(struct inode *inode, struct file *file)
+{
+ snd_pcm_t *pcm;
+ snd_pcm_substream_t *substream;
+ snd_pcm_oss_file_t *pcm_oss_file;
+
+ pcm_oss_file = file->private_data;
+ substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK];
+ if (substream == NULL)
+ substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE];
+ snd_assert(substream != NULL, return -ENXIO);
+ pcm = substream->pcm;
+ snd_pcm_oss_sync(pcm_oss_file);
+ down(&pcm->open_mutex);
+ snd_pcm_oss_release_file(pcm_oss_file);
+ up(&pcm->open_mutex);
+ wake_up(&pcm->open_wait);
+ module_put(pcm->card->module);
+ snd_card_file_remove(pcm->card, file);
+ return 0;
+}
+
+static long snd_pcm_oss_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ snd_pcm_oss_file_t *pcm_oss_file;
+ int __user *p = (int __user *)arg;
+ int res;
+
+ pcm_oss_file = file->private_data;
+ if (cmd == OSS_GETVERSION)
+ return put_user(SNDRV_OSS_VERSION, p);
+ if (cmd == OSS_ALSAEMULVER)
+ return put_user(1, p);
+#if defined(CONFIG_SND_MIXER_OSS) || (defined(MODULE) && defined(CONFIG_SND_MIXER_OSS_MODULE))
+ if (((cmd >> 8) & 0xff) == 'M') { /* mixer ioctl - for OSS compatibility */
+ snd_pcm_substream_t *substream;
+ int idx;
+ for (idx = 0; idx < 2; ++idx) {
+ substream = pcm_oss_file->streams[idx];
+ if (substream != NULL)
+ break;
+ }
+ snd_assert(substream != NULL, return -ENXIO);
+ return snd_mixer_oss_ioctl_card(substream->pcm->card, cmd, arg);
+ }
+#endif
+ if (((cmd >> 8) & 0xff) != 'P')
+ return -EINVAL;
+#ifdef OSS_DEBUG
+ printk("pcm_oss: ioctl = 0x%x\n", cmd);
+#endif
+ switch (cmd) {
+ case SNDCTL_DSP_RESET:
+ return snd_pcm_oss_reset(pcm_oss_file);
+ case SNDCTL_DSP_SYNC:
+ return snd_pcm_oss_sync(pcm_oss_file);
+ case SNDCTL_DSP_SPEED:
+ if (get_user(res, p))
+ return -EFAULT;
+ if ((res = snd_pcm_oss_set_rate(pcm_oss_file, res))<0)
+ return res;
+ return put_user(res, p);
+ case SOUND_PCM_READ_RATE:
+ res = snd_pcm_oss_get_rate(pcm_oss_file);
+ if (res < 0)
+ return res;
+ return put_user(res, p);
+ case SNDCTL_DSP_STEREO:
+ if (get_user(res, p))
+ return -EFAULT;
+ res = res > 0 ? 2 : 1;
+ if ((res = snd_pcm_oss_set_channels(pcm_oss_file, res)) < 0)
+ return res;
+ return put_user(--res, p);
+ case SNDCTL_DSP_GETBLKSIZE:
+ res = snd_pcm_oss_get_block_size(pcm_oss_file);
+ if (res < 0)
+ return res;
+ return put_user(res, p);
+ case SNDCTL_DSP_SETFMT:
+ if (get_user(res, p))
+ return -EFAULT;
+ res = snd_pcm_oss_set_format(pcm_oss_file, res);
+ if (res < 0)
+ return res;
+ return put_user(res, p);
+ case SOUND_PCM_READ_BITS:
+ res = snd_pcm_oss_get_format(pcm_oss_file);
+ if (res < 0)
+ return res;
+ return put_user(res, p);
+ case SNDCTL_DSP_CHANNELS:
+ if (get_user(res, p))
+ return -EFAULT;
+ res = snd_pcm_oss_set_channels(pcm_oss_file, res);
+ if (res < 0)
+ return res;
+ return put_user(res, p);
+ case SOUND_PCM_READ_CHANNELS:
+ res = snd_pcm_oss_get_channels(pcm_oss_file);
+ if (res < 0)
+ return res;
+ return put_user(res, p);
+ case SOUND_PCM_WRITE_FILTER:
+ case SOUND_PCM_READ_FILTER:
+ return -EIO;
+ case SNDCTL_DSP_POST:
+ return snd_pcm_oss_post(pcm_oss_file);
+ case SNDCTL_DSP_SUBDIVIDE:
+ if (get_user(res, p))
+ return -EFAULT;
+ res = snd_pcm_oss_set_subdivide(pcm_oss_file, res);
+ if (res < 0)
+ return res;
+ return put_user(res, p);
+ case SNDCTL_DSP_SETFRAGMENT:
+ if (get_user(res, p))
+ return -EFAULT;
+ return snd_pcm_oss_set_fragment(pcm_oss_file, res);
+ case SNDCTL_DSP_GETFMTS:
+ res = snd_pcm_oss_get_formats(pcm_oss_file);
+ if (res < 0)
+ return res;
+ return put_user(res, p);
+ case SNDCTL_DSP_GETOSPACE:
+ case SNDCTL_DSP_GETISPACE:
+ return snd_pcm_oss_get_space(pcm_oss_file,
+ cmd == SNDCTL_DSP_GETISPACE ?
+ SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK,
+ (struct audio_buf_info __user *) arg);
+ case SNDCTL_DSP_NONBLOCK:
+ return snd_pcm_oss_nonblock(file);
+ case SNDCTL_DSP_GETCAPS:
+ res = snd_pcm_oss_get_caps(pcm_oss_file);
+ if (res < 0)
+ return res;
+ return put_user(res, p);
+ case SNDCTL_DSP_GETTRIGGER:
+ res = snd_pcm_oss_get_trigger(pcm_oss_file);
+ if (res < 0)
+ return res;
+ return put_user(res, p);
+ case SNDCTL_DSP_SETTRIGGER:
+ if (get_user(res, p))
+ return -EFAULT;
+ return snd_pcm_oss_set_trigger(pcm_oss_file, res);
+ case SNDCTL_DSP_GETIPTR:
+ case SNDCTL_DSP_GETOPTR:
+ return snd_pcm_oss_get_ptr(pcm_oss_file,
+ cmd == SNDCTL_DSP_GETIPTR ?
+ SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK,
+ (struct count_info __user *) arg);
+ case SNDCTL_DSP_MAPINBUF:
+ case SNDCTL_DSP_MAPOUTBUF:
+ return snd_pcm_oss_get_mapbuf(pcm_oss_file,
+ cmd == SNDCTL_DSP_MAPINBUF ?
+ SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK,
+ (struct buffmem_desc __user *) arg);
+ case SNDCTL_DSP_SETSYNCRO:
+ /* stop DMA now.. */
+ return 0;
+ case SNDCTL_DSP_SETDUPLEX:
+ if (snd_pcm_oss_get_caps(pcm_oss_file) & DSP_CAP_DUPLEX)
+ return 0;
+ return -EIO;
+ case SNDCTL_DSP_GETODELAY:
+ res = snd_pcm_oss_get_odelay(pcm_oss_file);
+ if (res < 0) {
+ /* it's for sure, some broken apps don't check for error codes */
+ put_user(0, p);
+ return res;
+ }
+ return put_user(res, p);
+ case SNDCTL_DSP_PROFILE:
+ return 0; /* silently ignore */
+ default:
+ snd_printd("pcm_oss: unknown command = 0x%x\n", cmd);
+ }
+ return -EINVAL;
+}
+
+#ifdef CONFIG_COMPAT
+/* all compatible */
+#define snd_pcm_oss_ioctl_compat snd_pcm_oss_ioctl
+#else
+#define snd_pcm_oss_ioctl_compat NULL
+#endif
+
+static ssize_t snd_pcm_oss_read(struct file *file, char __user *buf, size_t count, loff_t *offset)
+{
+ snd_pcm_oss_file_t *pcm_oss_file;
+ snd_pcm_substream_t *substream;
+
+ pcm_oss_file = file->private_data;
+ substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE];
+ if (substream == NULL)
+ return -ENXIO;
+#ifndef OSS_DEBUG
+ return snd_pcm_oss_read1(substream, buf, count);
+#else
+ {
+ ssize_t res = snd_pcm_oss_read1(substream, buf, count);
+ printk("pcm_oss: read %li bytes (returned %li bytes)\n", (long)count, (long)res);
+ return res;
+ }
+#endif
+}
+
+static ssize_t snd_pcm_oss_write(struct file *file, const char __user *buf, size_t count, loff_t *offset)
+{
+ snd_pcm_oss_file_t *pcm_oss_file;
+ snd_pcm_substream_t *substream;
+ long result;
+
+ pcm_oss_file = file->private_data;
+ substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK];
+ if (substream == NULL)
+ return -ENXIO;
+ up(&file->f_dentry->d_inode->i_sem);
+ result = snd_pcm_oss_write1(substream, buf, count);
+ down(&file->f_dentry->d_inode->i_sem);
+#ifdef OSS_DEBUG
+ printk("pcm_oss: write %li bytes (wrote %li bytes)\n", (long)count, (long)result);
+#endif
+ return result;
+}
+
+static int snd_pcm_oss_playback_ready(snd_pcm_substream_t *substream)
+{
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ if (atomic_read(&runtime->mmap_count))
+ return runtime->oss.prev_hw_ptr_interrupt != runtime->hw_ptr_interrupt;
+ else
+ return snd_pcm_playback_avail(runtime) >= runtime->oss.period_frames;
+}
+
+static int snd_pcm_oss_capture_ready(snd_pcm_substream_t *substream)
+{
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ if (atomic_read(&runtime->mmap_count))
+ return runtime->oss.prev_hw_ptr_interrupt != runtime->hw_ptr_interrupt;
+ else
+ return snd_pcm_capture_avail(runtime) >= runtime->oss.period_frames;
+}
+
+static unsigned int snd_pcm_oss_poll(struct file *file, poll_table * wait)
+{
+ snd_pcm_oss_file_t *pcm_oss_file;
+ unsigned int mask;
+ snd_pcm_substream_t *psubstream = NULL, *csubstream = NULL;
+
+ pcm_oss_file = file->private_data;
+
+ psubstream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK];
+ csubstream = pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE];
+
+ mask = 0;
+ if (psubstream != NULL) {
+ snd_pcm_runtime_t *runtime = psubstream->runtime;
+ poll_wait(file, &runtime->sleep, wait);
+ snd_pcm_stream_lock_irq(psubstream);
+ if (runtime->status->state != SNDRV_PCM_STATE_DRAINING &&
+ (runtime->status->state != SNDRV_PCM_STATE_RUNNING ||
+ snd_pcm_oss_playback_ready(psubstream)))
+ mask |= POLLOUT | POLLWRNORM;
+ snd_pcm_stream_unlock_irq(psubstream);
+ }
+ if (csubstream != NULL) {
+ snd_pcm_runtime_t *runtime = csubstream->runtime;
+ enum sndrv_pcm_state ostate;
+ poll_wait(file, &runtime->sleep, wait);
+ snd_pcm_stream_lock_irq(csubstream);
+ if ((ostate = runtime->status->state) != SNDRV_PCM_STATE_RUNNING ||
+ snd_pcm_oss_capture_ready(csubstream))
+ mask |= POLLIN | POLLRDNORM;
+ snd_pcm_stream_unlock_irq(csubstream);
+ if (ostate != SNDRV_PCM_STATE_RUNNING && runtime->oss.trigger) {
+ snd_pcm_oss_file_t ofile;
+ memset(&ofile, 0, sizeof(ofile));
+ ofile.streams[SNDRV_PCM_STREAM_CAPTURE] = pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE];
+ runtime->oss.trigger = 0;
+ snd_pcm_oss_set_trigger(&ofile, PCM_ENABLE_INPUT);
+ }
+ }
+
+ return mask;
+}
+
+static int snd_pcm_oss_mmap(struct file *file, struct vm_area_struct *area)
+{
+ snd_pcm_oss_file_t *pcm_oss_file;
+ snd_pcm_substream_t *substream = NULL;
+ snd_pcm_runtime_t *runtime;
+ int err;
+
+#ifdef OSS_DEBUG
+ printk("pcm_oss: mmap begin\n");
+#endif
+ pcm_oss_file = file->private_data;
+ switch ((area->vm_flags & (VM_READ | VM_WRITE))) {
+ case VM_READ | VM_WRITE:
+ substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK];
+ if (substream)
+ break;
+ /* Fall through */
+ case VM_READ:
+ substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE];
+ break;
+ case VM_WRITE:
+ substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK];
+ break;
+ default:
+ return -EINVAL;
+ }
+ /* set VM_READ access as well to fix memset() routines that do
+ reads before writes (to improve performance) */
+ area->vm_flags |= VM_READ;
+ if (substream == NULL)
+ return -ENXIO;
+ runtime = substream->runtime;
+ if (!(runtime->info & SNDRV_PCM_INFO_MMAP_VALID))
+ return -EIO;
+ if (runtime->info & SNDRV_PCM_INFO_INTERLEAVED)
+ runtime->access = SNDRV_PCM_ACCESS_MMAP_INTERLEAVED;
+ else
+ return -EIO;
+
+ if (runtime->oss.params) {
+ if ((err = snd_pcm_oss_change_params(substream)) < 0)
+ return err;
+ }
+ if (runtime->oss.plugin_first != NULL)
+ return -EIO;
+
+ if (area->vm_pgoff != 0)
+ return -EINVAL;
+
+ err = snd_pcm_mmap_data(substream, file, area);
+ if (err < 0)
+ return err;
+ runtime->oss.mmap_bytes = area->vm_end - area->vm_start;
+ runtime->silence_threshold = 0;
+ runtime->silence_size = 0;
+#ifdef OSS_DEBUG
+ printk("pcm_oss: mmap ok, bytes = 0x%x\n", runtime->oss.mmap_bytes);
+#endif
+ /* In mmap mode we never stop */
+ runtime->stop_threshold = runtime->boundary;
+
+ return 0;
+}
+
+/*
+ * /proc interface
+ */
+
+static void snd_pcm_oss_proc_read(snd_info_entry_t *entry,
+ snd_info_buffer_t * buffer)
+{
+ snd_pcm_str_t *pstr = (snd_pcm_str_t *)entry->private_data;
+ snd_pcm_oss_setup_t *setup = pstr->oss.setup_list;
+ down(&pstr->oss.setup_mutex);
+ while (setup) {
+ snd_iprintf(buffer, "%s %u %u%s%s%s%s%s%s\n",
+ setup->task_name,
+ setup->periods,
+ setup->period_size,
+ setup->disable ? " disable" : "",
+ setup->direct ? " direct" : "",
+ setup->block ? " block" : "",
+ setup->nonblock ? " non-block" : "",
+ setup->partialfrag ? " partial-frag" : "",
+ setup->nosilence ? " no-silence" : "");
+ setup = setup->next;
+ }
+ up(&pstr->oss.setup_mutex);
+}
+
+static void snd_pcm_oss_proc_free_setup_list(snd_pcm_str_t * pstr)
+{
+ unsigned int idx;
+ snd_pcm_substream_t *substream;
+ snd_pcm_oss_setup_t *setup, *setupn;
+
+ for (idx = 0, substream = pstr->substream;
+ idx < pstr->substream_count; idx++, substream = substream->next)
+ substream->oss.setup = NULL;
+ for (setup = pstr->oss.setup_list, pstr->oss.setup_list = NULL;
+ setup; setup = setupn) {
+ setupn = setup->next;
+ kfree(setup->task_name);
+ kfree(setup);
+ }
+ pstr->oss.setup_list = NULL;
+}
+
+static void snd_pcm_oss_proc_write(snd_info_entry_t *entry,
+ snd_info_buffer_t * buffer)
+{
+ snd_pcm_str_t *pstr = (snd_pcm_str_t *)entry->private_data;
+ char line[128], str[32], task_name[32], *ptr;
+ int idx1;
+ snd_pcm_oss_setup_t *setup, *setup1, template;
+
+ while (!snd_info_get_line(buffer, line, sizeof(line))) {
+ down(&pstr->oss.setup_mutex);
+ memset(&template, 0, sizeof(template));
+ ptr = snd_info_get_str(task_name, line, sizeof(task_name));
+ if (!strcmp(task_name, "clear") || !strcmp(task_name, "erase")) {
+ snd_pcm_oss_proc_free_setup_list(pstr);
+ up(&pstr->oss.setup_mutex);
+ continue;
+ }
+ for (setup = pstr->oss.setup_list; setup; setup = setup->next) {
+ if (!strcmp(setup->task_name, task_name)) {
+ template = *setup;
+ break;
+ }
+ }
+ ptr = snd_info_get_str(str, ptr, sizeof(str));
+ template.periods = simple_strtoul(str, NULL, 10);
+ ptr = snd_info_get_str(str, ptr, sizeof(str));
+ template.period_size = simple_strtoul(str, NULL, 10);
+ for (idx1 = 31; idx1 >= 0; idx1--)
+ if (template.period_size & (1 << idx1))
+ break;
+ for (idx1--; idx1 >= 0; idx1--)
+ template.period_size &= ~(1 << idx1);
+ do {
+ ptr = snd_info_get_str(str, ptr, sizeof(str));
+ if (!strcmp(str, "disable")) {
+ template.disable = 1;
+ } else if (!strcmp(str, "direct")) {
+ template.direct = 1;
+ } else if (!strcmp(str, "block")) {
+ template.block = 1;
+ } else if (!strcmp(str, "non-block")) {
+ template.nonblock = 1;
+ } else if (!strcmp(str, "partial-frag")) {
+ template.partialfrag = 1;
+ } else if (!strcmp(str, "no-silence")) {
+ template.nosilence = 1;
+ }
+ } while (*str);
+ if (setup == NULL) {
+ setup = (snd_pcm_oss_setup_t *) kmalloc(sizeof(snd_pcm_oss_setup_t), GFP_KERNEL);
+ if (setup) {
+ if (pstr->oss.setup_list == NULL) {
+ pstr->oss.setup_list = setup;
+ } else {
+ for (setup1 = pstr->oss.setup_list; setup1->next; setup1 = setup1->next);
+ setup1->next = setup;
+ }
+ template.task_name = snd_kmalloc_strdup(task_name, GFP_KERNEL);
+ } else {
+ buffer->error = -ENOMEM;
+ }
+ }
+ if (setup)
+ *setup = template;
+ up(&pstr->oss.setup_mutex);
+ }
+}
+
+static void snd_pcm_oss_proc_init(snd_pcm_t *pcm)
+{
+ int stream;
+ for (stream = 0; stream < 2; ++stream) {
+ snd_info_entry_t *entry;
+ snd_pcm_str_t *pstr = &pcm->streams[stream];
+ if (pstr->substream_count == 0)
+ continue;
+ if ((entry = snd_info_create_card_entry(pcm->card, "oss", pstr->proc_root)) != NULL) {
+ entry->content = SNDRV_INFO_CONTENT_TEXT;
+ entry->mode = S_IFREG | S_IRUGO | S_IWUSR;
+ entry->c.text.read_size = 8192;
+ entry->c.text.read = snd_pcm_oss_proc_read;
+ entry->c.text.write_size = 8192;
+ entry->c.text.write = snd_pcm_oss_proc_write;
+ entry->private_data = pstr;
+ if (snd_info_register(entry) < 0) {
+ snd_info_free_entry(entry);
+ entry = NULL;
+ }
+ }
+ pstr->oss.proc_entry = entry;
+ }
+}
+
+static void snd_pcm_oss_proc_done(snd_pcm_t *pcm)
+{
+ int stream;
+ for (stream = 0; stream < 2; ++stream) {
+ snd_pcm_str_t *pstr = &pcm->streams[stream];
+ if (pstr->oss.proc_entry) {
+ snd_info_unregister(pstr->oss.proc_entry);
+ pstr->oss.proc_entry = NULL;
+ snd_pcm_oss_proc_free_setup_list(pstr);
+ }
+ }
+}
+
+/*
+ * ENTRY functions
+ */
+
+static struct file_operations snd_pcm_oss_f_reg =
+{
+ .owner = THIS_MODULE,
+ .read = snd_pcm_oss_read,
+ .write = snd_pcm_oss_write,
+ .open = snd_pcm_oss_open,
+ .release = snd_pcm_oss_release,
+ .poll = snd_pcm_oss_poll,
+ .unlocked_ioctl = snd_pcm_oss_ioctl,
+ .compat_ioctl = snd_pcm_oss_ioctl_compat,
+ .mmap = snd_pcm_oss_mmap,
+};
+
+static snd_minor_t snd_pcm_oss_reg =
+{
+ .comment = "digital audio",
+ .f_ops = &snd_pcm_oss_f_reg,
+};
+
+static void register_oss_dsp(snd_pcm_t *pcm, int index)
+{
+ char name[128];
+ sprintf(name, "dsp%i%i", pcm->card->number, pcm->device);
+ if (snd_register_oss_device(SNDRV_OSS_DEVICE_TYPE_PCM,
+ pcm->card, index, &snd_pcm_oss_reg,
+ name) < 0) {
+ snd_printk("unable to register OSS PCM device %i:%i\n", pcm->card->number, pcm->device);
+ }
+}
+
+static int snd_pcm_oss_register_minor(snd_pcm_t * pcm)
+{
+ pcm->oss.reg = 0;
+ if (dsp_map[pcm->card->number] == (int)pcm->device) {
+ char name[128];
+ int duplex;
+ register_oss_dsp(pcm, 0);
+ duplex = (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream_count > 0 &&
+ pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream_count &&
+ !(pcm->info_flags & SNDRV_PCM_INFO_HALF_DUPLEX));
+ sprintf(name, "%s%s", pcm->name, duplex ? " (DUPLEX)" : "");
+#ifdef SNDRV_OSS_INFO_DEV_AUDIO
+ snd_oss_info_register(SNDRV_OSS_INFO_DEV_AUDIO,
+ pcm->card->number,
+ name);
+#endif
+ pcm->oss.reg++;
+ pcm->oss.reg_mask |= 1;
+ }
+ if (adsp_map[pcm->card->number] == (int)pcm->device) {
+ register_oss_dsp(pcm, 1);
+ pcm->oss.reg++;
+ pcm->oss.reg_mask |= 2;
+ }
+
+ if (pcm->oss.reg)
+ snd_pcm_oss_proc_init(pcm);
+
+ return 0;
+}
+
+static int snd_pcm_oss_disconnect_minor(snd_pcm_t * pcm)
+{
+ if (pcm->oss.reg) {
+ if (pcm->oss.reg_mask & 1) {
+ pcm->oss.reg_mask &= ~1;
+ snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_PCM,
+ pcm->card, 0);
+ }
+ if (pcm->oss.reg_mask & 2) {
+ pcm->oss.reg_mask &= ~2;
+ snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_PCM,
+ pcm->card, 1);
+ }
+ }
+ return 0;
+}
+
+static int snd_pcm_oss_unregister_minor(snd_pcm_t * pcm)
+{
+ snd_pcm_oss_disconnect_minor(pcm);
+ if (pcm->oss.reg) {
+ if (dsp_map[pcm->card->number] == (int)pcm->device) {
+#ifdef SNDRV_OSS_INFO_DEV_AUDIO
+ snd_oss_info_unregister(SNDRV_OSS_INFO_DEV_AUDIO, pcm->card->number);
+#endif
+ }
+ pcm->oss.reg = 0;
+ snd_pcm_oss_proc_done(pcm);
+ }
+ return 0;
+}
+
+static snd_pcm_notify_t snd_pcm_oss_notify =
+{
+ .n_register = snd_pcm_oss_register_minor,
+ .n_disconnect = snd_pcm_oss_disconnect_minor,
+ .n_unregister = snd_pcm_oss_unregister_minor,
+};
+
+static int __init alsa_pcm_oss_init(void)
+{
+ int i;
+ int err;
+
+ /* check device map table */
+ for (i = 0; i < SNDRV_CARDS; i++) {
+ if (dsp_map[i] < 0 || dsp_map[i] >= SNDRV_PCM_DEVICES) {
+ snd_printk("invalid dsp_map[%d] = %d\n", i, dsp_map[i]);
+ dsp_map[i] = 0;
+ }
+ if (adsp_map[i] < 0 || adsp_map[i] >= SNDRV_PCM_DEVICES) {
+ snd_printk("invalid adsp_map[%d] = %d\n", i, adsp_map[i]);
+ adsp_map[i] = 1;
+ }
+ }
+ if ((err = snd_pcm_notify(&snd_pcm_oss_notify, 0)) < 0)
+ return err;
+ return 0;
+}
+
+static void __exit alsa_pcm_oss_exit(void)
+{
+ snd_pcm_notify(&snd_pcm_oss_notify, 1);
+}
+
+module_init(alsa_pcm_oss_init)
+module_exit(alsa_pcm_oss_exit)
diff --git a/sound/core/oss/pcm_plugin.c b/sound/core/oss/pcm_plugin.c
new file mode 100644
index 00000000000..6bb31009f0b
--- /dev/null
+++ b/sound/core/oss/pcm_plugin.c
@@ -0,0 +1,921 @@
+/*
+ * PCM Plug-In shared (kernel/library) code
+ * Copyright (c) 1999 by Jaroslav Kysela <perex@suse.cz>
+ * Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org>
+ *
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#if 0
+#define PLUGIN_DEBUG
+#endif
+
+#include <sound/driver.h>
+#include <linux/slab.h>
+#include <linux/time.h>
+#include <linux/vmalloc.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include "pcm_plugin.h"
+
+#define snd_pcm_plug_first(plug) ((plug)->runtime->oss.plugin_first)
+#define snd_pcm_plug_last(plug) ((plug)->runtime->oss.plugin_last)
+
+static int snd_pcm_plugin_src_channels_mask(snd_pcm_plugin_t *plugin,
+ bitset_t *dst_vmask,
+ bitset_t **src_vmask)
+{
+ bitset_t *vmask = plugin->src_vmask;
+ bitset_copy(vmask, dst_vmask, plugin->src_format.channels);
+ *src_vmask = vmask;
+ return 0;
+}
+
+static int snd_pcm_plugin_dst_channels_mask(snd_pcm_plugin_t *plugin,
+ bitset_t *src_vmask,
+ bitset_t **dst_vmask)
+{
+ bitset_t *vmask = plugin->dst_vmask;
+ bitset_copy(vmask, src_vmask, plugin->dst_format.channels);
+ *dst_vmask = vmask;
+ return 0;
+}
+
+/*
+ * because some cards might have rates "very close", we ignore
+ * all "resampling" requests within +-5%
+ */
+static int rate_match(unsigned int src_rate, unsigned int dst_rate)
+{
+ unsigned int low = (src_rate * 95) / 100;
+ unsigned int high = (src_rate * 105) / 100;
+ return dst_rate >= low && dst_rate <= high;
+}
+
+static int snd_pcm_plugin_alloc(snd_pcm_plugin_t *plugin, snd_pcm_uframes_t frames)
+{
+ snd_pcm_plugin_format_t *format;
+ ssize_t width;
+ size_t size;
+ unsigned int channel;
+ snd_pcm_plugin_channel_t *c;
+
+ if (plugin->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ format = &plugin->src_format;
+ } else {
+ format = &plugin->dst_format;
+ }
+ if ((width = snd_pcm_format_physical_width(format->format)) < 0)
+ return width;
+ size = frames * format->channels * width;
+ snd_assert((size % 8) == 0, return -ENXIO);
+ size /= 8;
+ if (plugin->buf_frames < frames) {
+ vfree(plugin->buf);
+ plugin->buf = vmalloc(size);
+ plugin->buf_frames = frames;
+ }
+ if (!plugin->buf) {
+ plugin->buf_frames = 0;
+ return -ENOMEM;
+ }
+ c = plugin->buf_channels;
+ if (plugin->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED) {
+ for (channel = 0; channel < format->channels; channel++, c++) {
+ c->frames = frames;
+ c->enabled = 1;
+ c->wanted = 0;
+ c->area.addr = plugin->buf;
+ c->area.first = channel * width;
+ c->area.step = format->channels * width;
+ }
+ } else if (plugin->access == SNDRV_PCM_ACCESS_RW_NONINTERLEAVED) {
+ snd_assert((size % format->channels) == 0,);
+ size /= format->channels;
+ for (channel = 0; channel < format->channels; channel++, c++) {
+ c->frames = frames;
+ c->enabled = 1;
+ c->wanted = 0;
+ c->area.addr = plugin->buf + (channel * size);
+ c->area.first = 0;
+ c->area.step = width;
+ }
+ } else
+ return -EINVAL;
+ return 0;
+}
+
+int snd_pcm_plug_alloc(snd_pcm_plug_t *plug, snd_pcm_uframes_t frames)
+{
+ int err;
+ snd_assert(snd_pcm_plug_first(plug) != NULL, return -ENXIO);
+ if (snd_pcm_plug_stream(plug) == SNDRV_PCM_STREAM_PLAYBACK) {
+ snd_pcm_plugin_t *plugin = snd_pcm_plug_first(plug);
+ while (plugin->next) {
+ if (plugin->dst_frames)
+ frames = plugin->dst_frames(plugin, frames);
+ snd_assert(frames > 0, return -ENXIO);
+ plugin = plugin->next;
+ err = snd_pcm_plugin_alloc(plugin, frames);
+ if (err < 0)
+ return err;
+ }
+ } else {
+ snd_pcm_plugin_t *plugin = snd_pcm_plug_last(plug);
+ while (plugin->prev) {
+ if (plugin->src_frames)
+ frames = plugin->src_frames(plugin, frames);
+ snd_assert(frames > 0, return -ENXIO);
+ plugin = plugin->prev;
+ err = snd_pcm_plugin_alloc(plugin, frames);
+ if (err < 0)
+ return err;
+ }
+ }
+ return 0;
+}
+
+
+snd_pcm_sframes_t snd_pcm_plugin_client_channels(snd_pcm_plugin_t *plugin,
+ snd_pcm_uframes_t frames,
+ snd_pcm_plugin_channel_t **channels)
+{
+ *channels = plugin->buf_channels;
+ return frames;
+}
+
+int snd_pcm_plugin_build(snd_pcm_plug_t *plug,
+ const char *name,
+ snd_pcm_plugin_format_t *src_format,
+ snd_pcm_plugin_format_t *dst_format,
+ size_t extra,
+ snd_pcm_plugin_t **ret)
+{
+ snd_pcm_plugin_t *plugin;
+ unsigned int channels;
+
+ snd_assert(plug != NULL, return -ENXIO);
+ snd_assert(src_format != NULL && dst_format != NULL, return -ENXIO);
+ plugin = kcalloc(1, sizeof(*plugin) + extra, GFP_KERNEL);
+ if (plugin == NULL)
+ return -ENOMEM;
+ plugin->name = name;
+ plugin->plug = plug;
+ plugin->stream = snd_pcm_plug_stream(plug);
+ plugin->access = SNDRV_PCM_ACCESS_RW_INTERLEAVED;
+ plugin->src_format = *src_format;
+ plugin->src_width = snd_pcm_format_physical_width(src_format->format);
+ snd_assert(plugin->src_width > 0, );
+ plugin->dst_format = *dst_format;
+ plugin->dst_width = snd_pcm_format_physical_width(dst_format->format);
+ snd_assert(plugin->dst_width > 0, );
+ if (plugin->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ channels = src_format->channels;
+ else
+ channels = dst_format->channels;
+ plugin->buf_channels = kcalloc(channels, sizeof(*plugin->buf_channels), GFP_KERNEL);
+ if (plugin->buf_channels == NULL) {
+ snd_pcm_plugin_free(plugin);
+ return -ENOMEM;
+ }
+ plugin->src_vmask = bitset_alloc(src_format->channels);
+ if (plugin->src_vmask == NULL) {
+ snd_pcm_plugin_free(plugin);
+ return -ENOMEM;
+ }
+ plugin->dst_vmask = bitset_alloc(dst_format->channels);
+ if (plugin->dst_vmask == NULL) {
+ snd_pcm_plugin_free(plugin);
+ return -ENOMEM;
+ }
+ plugin->client_channels = snd_pcm_plugin_client_channels;
+ plugin->src_channels_mask = snd_pcm_plugin_src_channels_mask;
+ plugin->dst_channels_mask = snd_pcm_plugin_dst_channels_mask;
+ *ret = plugin;
+ return 0;
+}
+
+int snd_pcm_plugin_free(snd_pcm_plugin_t *plugin)
+{
+ if (! plugin)
+ return 0;
+ if (plugin->private_free)
+ plugin->private_free(plugin);
+ kfree(plugin->buf_channels);
+ vfree(plugin->buf);
+ kfree(plugin->src_vmask);
+ kfree(plugin->dst_vmask);
+ kfree(plugin);
+ return 0;
+}
+
+snd_pcm_sframes_t snd_pcm_plug_client_size(snd_pcm_plug_t *plug, snd_pcm_uframes_t drv_frames)
+{
+ snd_pcm_plugin_t *plugin, *plugin_prev, *plugin_next;
+ int stream = snd_pcm_plug_stream(plug);
+
+ snd_assert(plug != NULL, return -ENXIO);
+ if (drv_frames == 0)
+ return 0;
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ plugin = snd_pcm_plug_last(plug);
+ while (plugin && drv_frames > 0) {
+ plugin_prev = plugin->prev;
+ if (plugin->src_frames)
+ drv_frames = plugin->src_frames(plugin, drv_frames);
+ plugin = plugin_prev;
+ }
+ } else if (stream == SNDRV_PCM_STREAM_CAPTURE) {
+ plugin = snd_pcm_plug_first(plug);
+ while (plugin && drv_frames > 0) {
+ plugin_next = plugin->next;
+ if (plugin->dst_frames)
+ drv_frames = plugin->dst_frames(plugin, drv_frames);
+ plugin = plugin_next;
+ }
+ } else
+ snd_BUG();
+ return drv_frames;
+}
+
+snd_pcm_sframes_t snd_pcm_plug_slave_size(snd_pcm_plug_t *plug, snd_pcm_uframes_t clt_frames)
+{
+ snd_pcm_plugin_t *plugin, *plugin_prev, *plugin_next;
+ snd_pcm_sframes_t frames;
+ int stream = snd_pcm_plug_stream(plug);
+
+ snd_assert(plug != NULL, return -ENXIO);
+ if (clt_frames == 0)
+ return 0;
+ frames = clt_frames;
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ plugin = snd_pcm_plug_first(plug);
+ while (plugin && frames > 0) {
+ plugin_next = plugin->next;
+ if (plugin->dst_frames) {
+ frames = plugin->dst_frames(plugin, frames);
+ if (frames < 0)
+ return frames;
+ }
+ plugin = plugin_next;
+ }
+ } else if (stream == SNDRV_PCM_STREAM_CAPTURE) {
+ plugin = snd_pcm_plug_last(plug);
+ while (plugin) {
+ plugin_prev = plugin->prev;
+ if (plugin->src_frames) {
+ frames = plugin->src_frames(plugin, frames);
+ if (frames < 0)
+ return frames;
+ }
+ plugin = plugin_prev;
+ }
+ } else
+ snd_BUG();
+ return frames;
+}
+
+static int snd_pcm_plug_formats(snd_mask_t *mask, int format)
+{
+ snd_mask_t formats = *mask;
+ u64 linfmts = (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_U16_BE | SNDRV_PCM_FMTBIT_S16_BE |
+ SNDRV_PCM_FMTBIT_U24_LE | SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_U24_BE | SNDRV_PCM_FMTBIT_S24_BE |
+ SNDRV_PCM_FMTBIT_U32_LE | SNDRV_PCM_FMTBIT_S32_LE |
+ SNDRV_PCM_FMTBIT_U32_BE | SNDRV_PCM_FMTBIT_S32_BE);
+ snd_mask_set(&formats, SNDRV_PCM_FORMAT_MU_LAW);
+
+ if (formats.bits[0] & (u32)linfmts)
+ formats.bits[0] |= (u32)linfmts;
+ if (formats.bits[1] & (u32)(linfmts >> 32))
+ formats.bits[1] |= (u32)(linfmts >> 32);
+ return snd_mask_test(&formats, format);
+}
+
+static int preferred_formats[] = {
+ SNDRV_PCM_FORMAT_S16_LE,
+ SNDRV_PCM_FORMAT_S16_BE,
+ SNDRV_PCM_FORMAT_U16_LE,
+ SNDRV_PCM_FORMAT_U16_BE,
+ SNDRV_PCM_FORMAT_S24_LE,
+ SNDRV_PCM_FORMAT_S24_BE,
+ SNDRV_PCM_FORMAT_U24_LE,
+ SNDRV_PCM_FORMAT_U24_BE,
+ SNDRV_PCM_FORMAT_S32_LE,
+ SNDRV_PCM_FORMAT_S32_BE,
+ SNDRV_PCM_FORMAT_U32_LE,
+ SNDRV_PCM_FORMAT_U32_BE,
+ SNDRV_PCM_FORMAT_S8,
+ SNDRV_PCM_FORMAT_U8
+};
+
+int snd_pcm_plug_slave_format(int format, snd_mask_t *format_mask)
+{
+ if (snd_mask_test(format_mask, format))
+ return format;
+ if (! snd_pcm_plug_formats(format_mask, format))
+ return -EINVAL;
+ if (snd_pcm_format_linear(format)) {
+ int width = snd_pcm_format_width(format);
+ int unsignd = snd_pcm_format_unsigned(format);
+ int big = snd_pcm_format_big_endian(format);
+ int format1;
+ int wid, width1=width;
+ int dwidth1 = 8;
+ for (wid = 0; wid < 4; ++wid) {
+ int end, big1 = big;
+ for (end = 0; end < 2; ++end) {
+ int sgn, unsignd1 = unsignd;
+ for (sgn = 0; sgn < 2; ++sgn) {
+ format1 = snd_pcm_build_linear_format(width1, unsignd1, big1);
+ if (format1 >= 0 &&
+ snd_mask_test(format_mask, format1))
+ goto _found;
+ unsignd1 = !unsignd1;
+ }
+ big1 = !big1;
+ }
+ if (width1 == 32) {
+ dwidth1 = -dwidth1;
+ width1 = width;
+ }
+ width1 += dwidth1;
+ }
+ return -EINVAL;
+ _found:
+ return format1;
+ } else {
+ unsigned int i;
+ switch (format) {
+ case SNDRV_PCM_FORMAT_MU_LAW:
+ for (i = 0; i < ARRAY_SIZE(preferred_formats); ++i) {
+ int format1 = preferred_formats[i];
+ if (snd_mask_test(format_mask, format1))
+ return format1;
+ }
+ default:
+ return -EINVAL;
+ }
+ }
+}
+
+int snd_pcm_plug_format_plugins(snd_pcm_plug_t *plug,
+ snd_pcm_hw_params_t *params,
+ snd_pcm_hw_params_t *slave_params)
+{
+ snd_pcm_plugin_format_t tmpformat;
+ snd_pcm_plugin_format_t dstformat;
+ snd_pcm_plugin_format_t srcformat;
+ int src_access, dst_access;
+ snd_pcm_plugin_t *plugin = NULL;
+ int err;
+ int stream = snd_pcm_plug_stream(plug);
+ int slave_interleaved = (params_channels(slave_params) == 1 ||
+ params_access(slave_params) == SNDRV_PCM_ACCESS_RW_INTERLEAVED);
+
+ switch (stream) {
+ case SNDRV_PCM_STREAM_PLAYBACK:
+ dstformat.format = params_format(slave_params);
+ dstformat.rate = params_rate(slave_params);
+ dstformat.channels = params_channels(slave_params);
+ srcformat.format = params_format(params);
+ srcformat.rate = params_rate(params);
+ srcformat.channels = params_channels(params);
+ src_access = SNDRV_PCM_ACCESS_RW_INTERLEAVED;
+ dst_access = (slave_interleaved ? SNDRV_PCM_ACCESS_RW_INTERLEAVED :
+ SNDRV_PCM_ACCESS_RW_NONINTERLEAVED);
+ break;
+ case SNDRV_PCM_STREAM_CAPTURE:
+ dstformat.format = params_format(params);
+ dstformat.rate = params_rate(params);
+ dstformat.channels = params_channels(params);
+ srcformat.format = params_format(slave_params);
+ srcformat.rate = params_rate(slave_params);
+ srcformat.channels = params_channels(slave_params);
+ src_access = (slave_interleaved ? SNDRV_PCM_ACCESS_RW_INTERLEAVED :
+ SNDRV_PCM_ACCESS_RW_NONINTERLEAVED);
+ dst_access = SNDRV_PCM_ACCESS_RW_INTERLEAVED;
+ break;
+ default:
+ snd_BUG();
+ return -EINVAL;
+ }
+ tmpformat = srcformat;
+
+ pdprintf("srcformat: format=%i, rate=%i, channels=%i\n",
+ srcformat.format,
+ srcformat.rate,
+ srcformat.channels);
+ pdprintf("dstformat: format=%i, rate=%i, channels=%i\n",
+ dstformat.format,
+ dstformat.rate,
+ dstformat.channels);
+
+ /* Format change (linearization) */
+ if ((srcformat.format != dstformat.format ||
+ !rate_match(srcformat.rate, dstformat.rate) ||
+ srcformat.channels != dstformat.channels) &&
+ !snd_pcm_format_linear(srcformat.format)) {
+ if (snd_pcm_format_linear(dstformat.format))
+ tmpformat.format = dstformat.format;
+ else
+ tmpformat.format = SNDRV_PCM_FORMAT_S16;
+ switch (srcformat.format) {
+ case SNDRV_PCM_FORMAT_MU_LAW:
+ err = snd_pcm_plugin_build_mulaw(plug,
+ &srcformat, &tmpformat,
+ &plugin);
+ break;
+ default:
+ return -EINVAL;
+ }
+ pdprintf("format change: src=%i, dst=%i returns %i\n", srcformat.format, tmpformat.format, err);
+ if (err < 0)
+ return err;
+ err = snd_pcm_plugin_append(plugin);
+ if (err < 0) {
+ snd_pcm_plugin_free(plugin);
+ return err;
+ }
+ srcformat = tmpformat;
+ src_access = dst_access;
+ }
+
+ /* channels reduction */
+ if (srcformat.channels > dstformat.channels) {
+ int sv = srcformat.channels;
+ int dv = dstformat.channels;
+ route_ttable_entry_t *ttable = kcalloc(dv * sv, sizeof(*ttable), GFP_KERNEL);
+ if (ttable == NULL)
+ return -ENOMEM;
+#if 1
+ if (sv == 2 && dv == 1) {
+ ttable[0] = HALF;
+ ttable[1] = HALF;
+ } else
+#endif
+ {
+ int v;
+ for (v = 0; v < dv; ++v)
+ ttable[v * sv + v] = FULL;
+ }
+ tmpformat.channels = dstformat.channels;
+ if (rate_match(srcformat.rate, dstformat.rate) &&
+ snd_pcm_format_linear(dstformat.format))
+ tmpformat.format = dstformat.format;
+ err = snd_pcm_plugin_build_route(plug,
+ &srcformat, &tmpformat,
+ ttable, &plugin);
+ kfree(ttable);
+ pdprintf("channels reduction: src=%i, dst=%i returns %i\n", srcformat.channels, tmpformat.channels, err);
+ if (err < 0) {
+ snd_pcm_plugin_free(plugin);
+ return err;
+ }
+ err = snd_pcm_plugin_append(plugin);
+ if (err < 0) {
+ snd_pcm_plugin_free(plugin);
+ return err;
+ }
+ srcformat = tmpformat;
+ src_access = dst_access;
+ }
+
+ /* rate resampling */
+ if (!rate_match(srcformat.rate, dstformat.rate)) {
+ tmpformat.rate = dstformat.rate;
+ if (srcformat.channels == dstformat.channels &&
+ snd_pcm_format_linear(dstformat.format))
+ tmpformat.format = dstformat.format;
+ err = snd_pcm_plugin_build_rate(plug,
+ &srcformat, &tmpformat,
+ &plugin);
+ pdprintf("rate down resampling: src=%i, dst=%i returns %i\n", srcformat.rate, tmpformat.rate, err);
+ if (err < 0) {
+ snd_pcm_plugin_free(plugin);
+ return err;
+ }
+ err = snd_pcm_plugin_append(plugin);
+ if (err < 0) {
+ snd_pcm_plugin_free(plugin);
+ return err;
+ }
+ srcformat = tmpformat;
+ src_access = dst_access;
+ }
+
+ /* channels extension */
+ if (srcformat.channels < dstformat.channels) {
+ int sv = srcformat.channels;
+ int dv = dstformat.channels;
+ route_ttable_entry_t *ttable = kcalloc(dv * sv, sizeof(*ttable), GFP_KERNEL);
+ if (ttable == NULL)
+ return -ENOMEM;
+#if 0
+ {
+ int v;
+ for (v = 0; v < sv; ++v)
+ ttable[v * sv + v] = FULL;
+ }
+#else
+ {
+ /* Playback is spreaded on all channels */
+ int vd, vs;
+ for (vd = 0, vs = 0; vd < dv; ++vd) {
+ ttable[vd * sv + vs] = FULL;
+ vs++;
+ if (vs == sv)
+ vs = 0;
+ }
+ }
+#endif
+ tmpformat.channels = dstformat.channels;
+ if (snd_pcm_format_linear(dstformat.format))
+ tmpformat.format = dstformat.format;
+ err = snd_pcm_plugin_build_route(plug,
+ &srcformat, &tmpformat,
+ ttable, &plugin);
+ kfree(ttable);
+ pdprintf("channels extension: src=%i, dst=%i returns %i\n", srcformat.channels, tmpformat.channels, err);
+ if (err < 0) {
+ snd_pcm_plugin_free(plugin);
+ return err;
+ }
+ err = snd_pcm_plugin_append(plugin);
+ if (err < 0) {
+ snd_pcm_plugin_free(plugin);
+ return err;
+ }
+ srcformat = tmpformat;
+ src_access = dst_access;
+ }
+
+ /* format change */
+ if (srcformat.format != dstformat.format) {
+ tmpformat.format = dstformat.format;
+ if (tmpformat.format == SNDRV_PCM_FORMAT_MU_LAW) {
+ err = snd_pcm_plugin_build_mulaw(plug,
+ &srcformat, &tmpformat,
+ &plugin);
+ }
+ else if (snd_pcm_format_linear(srcformat.format) &&
+ snd_pcm_format_linear(tmpformat.format)) {
+ err = snd_pcm_plugin_build_linear(plug,
+ &srcformat, &tmpformat,
+ &plugin);
+ }
+ else
+ return -EINVAL;
+ pdprintf("format change: src=%i, dst=%i returns %i\n", srcformat.format, tmpformat.format, err);
+ if (err < 0)
+ return err;
+ err = snd_pcm_plugin_append(plugin);
+ if (err < 0) {
+ snd_pcm_plugin_free(plugin);
+ return err;
+ }
+ srcformat = tmpformat;
+ src_access = dst_access;
+ }
+
+ /* de-interleave */
+ if (src_access != dst_access) {
+ err = snd_pcm_plugin_build_copy(plug,
+ &srcformat,
+ &tmpformat,
+ &plugin);
+ pdprintf("interleave change (copy: returns %i)\n", err);
+ if (err < 0)
+ return err;
+ err = snd_pcm_plugin_append(plugin);
+ if (err < 0) {
+ snd_pcm_plugin_free(plugin);
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+snd_pcm_sframes_t snd_pcm_plug_client_channels_buf(snd_pcm_plug_t *plug,
+ char *buf,
+ snd_pcm_uframes_t count,
+ snd_pcm_plugin_channel_t **channels)
+{
+ snd_pcm_plugin_t *plugin;
+ snd_pcm_plugin_channel_t *v;
+ snd_pcm_plugin_format_t *format;
+ int width, nchannels, channel;
+ int stream = snd_pcm_plug_stream(plug);
+
+ snd_assert(buf != NULL, return -ENXIO);
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ plugin = snd_pcm_plug_first(plug);
+ format = &plugin->src_format;
+ } else {
+ plugin = snd_pcm_plug_last(plug);
+ format = &plugin->dst_format;
+ }
+ v = plugin->buf_channels;
+ *channels = v;
+ if ((width = snd_pcm_format_physical_width(format->format)) < 0)
+ return width;
+ nchannels = format->channels;
+ snd_assert(plugin->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED || format->channels <= 1, return -ENXIO);
+ for (channel = 0; channel < nchannels; channel++, v++) {
+ v->frames = count;
+ v->enabled = 1;
+ v->wanted = (stream == SNDRV_PCM_STREAM_CAPTURE);
+ v->area.addr = buf;
+ v->area.first = channel * width;
+ v->area.step = nchannels * width;
+ }
+ return count;
+}
+
+static int snd_pcm_plug_playback_channels_mask(snd_pcm_plug_t *plug,
+ bitset_t *client_vmask)
+{
+ snd_pcm_plugin_t *plugin = snd_pcm_plug_last(plug);
+ if (plugin == NULL) {
+ return 0;
+ } else {
+ int schannels = plugin->dst_format.channels;
+ bitset_t bs[bitset_size(schannels)];
+ bitset_t *srcmask;
+ bitset_t *dstmask = bs;
+ int err;
+ bitset_one(dstmask, schannels);
+ if (plugin == NULL) {
+ bitset_and(client_vmask, dstmask, schannels);
+ return 0;
+ }
+ while (1) {
+ err = plugin->src_channels_mask(plugin, dstmask, &srcmask);
+ if (err < 0)
+ return err;
+ dstmask = srcmask;
+ if (plugin->prev == NULL)
+ break;
+ plugin = plugin->prev;
+ }
+ bitset_and(client_vmask, dstmask, plugin->src_format.channels);
+ return 0;
+ }
+}
+
+static int snd_pcm_plug_playback_disable_useless_channels(snd_pcm_plug_t *plug,
+ snd_pcm_plugin_channel_t *src_channels)
+{
+ snd_pcm_plugin_t *plugin = snd_pcm_plug_first(plug);
+ unsigned int nchannels = plugin->src_format.channels;
+ bitset_t bs[bitset_size(nchannels)];
+ bitset_t *srcmask = bs;
+ int err;
+ unsigned int channel;
+ for (channel = 0; channel < nchannels; channel++) {
+ if (src_channels[channel].enabled)
+ bitset_set(srcmask, channel);
+ else
+ bitset_reset(srcmask, channel);
+ }
+ err = snd_pcm_plug_playback_channels_mask(plug, srcmask);
+ if (err < 0)
+ return err;
+ for (channel = 0; channel < nchannels; channel++) {
+ if (!bitset_get(srcmask, channel))
+ src_channels[channel].enabled = 0;
+ }
+ return 0;
+}
+
+static int snd_pcm_plug_capture_disable_useless_channels(snd_pcm_plug_t *plug,
+ snd_pcm_plugin_channel_t *src_channels,
+ snd_pcm_plugin_channel_t *client_channels)
+{
+ snd_pcm_plugin_t *plugin = snd_pcm_plug_last(plug);
+ unsigned int nchannels = plugin->dst_format.channels;
+ bitset_t bs[bitset_size(nchannels)];
+ bitset_t *dstmask = bs;
+ bitset_t *srcmask;
+ int err;
+ unsigned int channel;
+ for (channel = 0; channel < nchannels; channel++) {
+ if (client_channels[channel].enabled)
+ bitset_set(dstmask, channel);
+ else
+ bitset_reset(dstmask, channel);
+ }
+ while (plugin) {
+ err = plugin->src_channels_mask(plugin, dstmask, &srcmask);
+ if (err < 0)
+ return err;
+ dstmask = srcmask;
+ plugin = plugin->prev;
+ }
+ plugin = snd_pcm_plug_first(plug);
+ nchannels = plugin->src_format.channels;
+ for (channel = 0; channel < nchannels; channel++) {
+ if (!bitset_get(dstmask, channel))
+ src_channels[channel].enabled = 0;
+ }
+ return 0;
+}
+
+snd_pcm_sframes_t snd_pcm_plug_write_transfer(snd_pcm_plug_t *plug, snd_pcm_plugin_channel_t *src_channels, snd_pcm_uframes_t size)
+{
+ snd_pcm_plugin_t *plugin, *next;
+ snd_pcm_plugin_channel_t *dst_channels;
+ int err;
+ snd_pcm_sframes_t frames = size;
+
+ if ((err = snd_pcm_plug_playback_disable_useless_channels(plug, src_channels)) < 0)
+ return err;
+
+ plugin = snd_pcm_plug_first(plug);
+ while (plugin && frames > 0) {
+ if ((next = plugin->next) != NULL) {
+ snd_pcm_sframes_t frames1 = frames;
+ if (plugin->dst_frames)
+ frames1 = plugin->dst_frames(plugin, frames);
+ if ((err = next->client_channels(next, frames1, &dst_channels)) < 0) {
+ return err;
+ }
+ if (err != frames1) {
+ frames = err;
+ if (plugin->src_frames)
+ frames = plugin->src_frames(plugin, frames1);
+ }
+ } else
+ dst_channels = NULL;
+ pdprintf("write plugin: %s, %li\n", plugin->name, frames);
+ if ((frames = plugin->transfer(plugin, src_channels, dst_channels, frames)) < 0)
+ return frames;
+ src_channels = dst_channels;
+ plugin = next;
+ }
+ return snd_pcm_plug_client_size(plug, frames);
+}
+
+snd_pcm_sframes_t snd_pcm_plug_read_transfer(snd_pcm_plug_t *plug, snd_pcm_plugin_channel_t *dst_channels_final, snd_pcm_uframes_t size)
+{
+ snd_pcm_plugin_t *plugin, *next;
+ snd_pcm_plugin_channel_t *src_channels, *dst_channels;
+ snd_pcm_sframes_t frames = size;
+ int err;
+
+ frames = snd_pcm_plug_slave_size(plug, frames);
+ if (frames < 0)
+ return frames;
+
+ src_channels = NULL;
+ plugin = snd_pcm_plug_first(plug);
+ while (plugin && frames > 0) {
+ if ((next = plugin->next) != NULL) {
+ if ((err = plugin->client_channels(plugin, frames, &dst_channels)) < 0) {
+ return err;
+ }
+ frames = err;
+ if (!plugin->prev) {
+ if ((err = snd_pcm_plug_capture_disable_useless_channels(plug, dst_channels, dst_channels_final)) < 0)
+ return err;
+ }
+ } else {
+ dst_channels = dst_channels_final;
+ }
+ pdprintf("read plugin: %s, %li\n", plugin->name, frames);
+ if ((frames = plugin->transfer(plugin, src_channels, dst_channels, frames)) < 0)
+ return frames;
+ plugin = next;
+ src_channels = dst_channels;
+ }
+ return frames;
+}
+
+int snd_pcm_area_silence(const snd_pcm_channel_area_t *dst_area, size_t dst_offset,
+ size_t samples, int format)
+{
+ /* FIXME: sub byte resolution and odd dst_offset */
+ unsigned char *dst;
+ unsigned int dst_step;
+ int width;
+ const unsigned char *silence;
+ if (!dst_area->addr)
+ return 0;
+ dst = dst_area->addr + (dst_area->first + dst_area->step * dst_offset) / 8;
+ width = snd_pcm_format_physical_width(format);
+ if (width <= 0)
+ return -EINVAL;
+ if (dst_area->step == (unsigned int) width && width >= 8)
+ return snd_pcm_format_set_silence(format, dst, samples);
+ silence = snd_pcm_format_silence_64(format);
+ if (! silence)
+ return -EINVAL;
+ dst_step = dst_area->step / 8;
+ if (width == 4) {
+ /* Ima ADPCM */
+ int dstbit = dst_area->first % 8;
+ int dstbit_step = dst_area->step % 8;
+ while (samples-- > 0) {
+ if (dstbit)
+ *dst &= 0xf0;
+ else
+ *dst &= 0x0f;
+ dst += dst_step;
+ dstbit += dstbit_step;
+ if (dstbit == 8) {
+ dst++;
+ dstbit = 0;
+ }
+ }
+ } else {
+ width /= 8;
+ while (samples-- > 0) {
+ memcpy(dst, silence, width);
+ dst += dst_step;
+ }
+ }
+ return 0;
+}
+
+int snd_pcm_area_copy(const snd_pcm_channel_area_t *src_area, size_t src_offset,
+ const snd_pcm_channel_area_t *dst_area, size_t dst_offset,
+ size_t samples, int format)
+{
+ /* FIXME: sub byte resolution and odd dst_offset */
+ char *src, *dst;
+ int width;
+ int src_step, dst_step;
+ src = src_area->addr + (src_area->first + src_area->step * src_offset) / 8;
+ if (!src_area->addr)
+ return snd_pcm_area_silence(dst_area, dst_offset, samples, format);
+ dst = dst_area->addr + (dst_area->first + dst_area->step * dst_offset) / 8;
+ if (!dst_area->addr)
+ return 0;
+ width = snd_pcm_format_physical_width(format);
+ if (width <= 0)
+ return -EINVAL;
+ if (src_area->step == (unsigned int) width &&
+ dst_area->step == (unsigned int) width && width >= 8) {
+ size_t bytes = samples * width / 8;
+ memcpy(dst, src, bytes);
+ return 0;
+ }
+ src_step = src_area->step / 8;
+ dst_step = dst_area->step / 8;
+ if (width == 4) {
+ /* Ima ADPCM */
+ int srcbit = src_area->first % 8;
+ int srcbit_step = src_area->step % 8;
+ int dstbit = dst_area->first % 8;
+ int dstbit_step = dst_area->step % 8;
+ while (samples-- > 0) {
+ unsigned char srcval;
+ if (srcbit)
+ srcval = *src & 0x0f;
+ else
+ srcval = (*src & 0xf0) >> 4;
+ if (dstbit)
+ *dst = (*dst & 0xf0) | srcval;
+ else
+ *dst = (*dst & 0x0f) | (srcval << 4);
+ src += src_step;
+ srcbit += srcbit_step;
+ if (srcbit == 8) {
+ src++;
+ srcbit = 0;
+ }
+ dst += dst_step;
+ dstbit += dstbit_step;
+ if (dstbit == 8) {
+ dst++;
+ dstbit = 0;
+ }
+ }
+ } else {
+ width /= 8;
+ while (samples-- > 0) {
+ memcpy(dst, src, width);
+ src += src_step;
+ dst += dst_step;
+ }
+ }
+ return 0;
+}
diff --git a/sound/core/oss/pcm_plugin.h b/sound/core/oss/pcm_plugin.h
new file mode 100644
index 00000000000..0f86ce47749
--- /dev/null
+++ b/sound/core/oss/pcm_plugin.h
@@ -0,0 +1,250 @@
+#ifndef __PCM_PLUGIN_H
+#define __PCM_PLUGIN_H
+
+/*
+ * Digital Audio (Plugin interface) abstract layer
+ * Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef ATTRIBUTE_UNUSED
+#define ATTRIBUTE_UNUSED __attribute__ ((__unused__))
+#endif
+
+typedef unsigned int bitset_t;
+
+static inline size_t bitset_size(int nbits)
+{
+ return (nbits + sizeof(bitset_t) * 8 - 1) / (sizeof(bitset_t) * 8);
+}
+
+static inline bitset_t *bitset_alloc(int nbits)
+{
+ return kcalloc(bitset_size(nbits), sizeof(bitset_t), GFP_KERNEL);
+}
+
+static inline void bitset_set(bitset_t *bitmap, unsigned int pos)
+{
+ size_t bits = sizeof(*bitmap) * 8;
+ bitmap[pos / bits] |= 1 << (pos % bits);
+}
+
+static inline void bitset_reset(bitset_t *bitmap, unsigned int pos)
+{
+ size_t bits = sizeof(*bitmap) * 8;
+ bitmap[pos / bits] &= ~(1 << (pos % bits));
+}
+
+static inline int bitset_get(bitset_t *bitmap, unsigned int pos)
+{
+ size_t bits = sizeof(*bitmap) * 8;
+ return !!(bitmap[pos / bits] & (1 << (pos % bits)));
+}
+
+static inline void bitset_copy(bitset_t *dst, bitset_t *src, unsigned int nbits)
+{
+ memcpy(dst, src, bitset_size(nbits) * sizeof(bitset_t));
+}
+
+static inline void bitset_and(bitset_t *dst, bitset_t *bs, unsigned int nbits)
+{
+ bitset_t *end = dst + bitset_size(nbits);
+ while (dst < end)
+ *dst++ &= *bs++;
+}
+
+static inline void bitset_or(bitset_t *dst, bitset_t *bs, unsigned int nbits)
+{
+ bitset_t *end = dst + bitset_size(nbits);
+ while (dst < end)
+ *dst++ |= *bs++;
+}
+
+static inline void bitset_zero(bitset_t *dst, unsigned int nbits)
+{
+ bitset_t *end = dst + bitset_size(nbits);
+ while (dst < end)
+ *dst++ = 0;
+}
+
+static inline void bitset_one(bitset_t *dst, unsigned int nbits)
+{
+ bitset_t *end = dst + bitset_size(nbits);
+ while (dst < end)
+ *dst++ = ~(bitset_t)0;
+}
+
+#define snd_pcm_plug_t snd_pcm_substream_t
+#define snd_pcm_plug_stream(plug) ((plug)->stream)
+
+typedef enum {
+ INIT = 0,
+ PREPARE = 1,
+} snd_pcm_plugin_action_t;
+
+typedef struct _snd_pcm_channel_area {
+ void *addr; /* base address of channel samples */
+ unsigned int first; /* offset to first sample in bits */
+ unsigned int step; /* samples distance in bits */
+} snd_pcm_channel_area_t;
+
+typedef struct _snd_pcm_plugin_channel {
+ void *aptr; /* pointer to the allocated area */
+ snd_pcm_channel_area_t area;
+ snd_pcm_uframes_t frames; /* allocated frames */
+ unsigned int enabled:1; /* channel need to be processed */
+ unsigned int wanted:1; /* channel is wanted */
+} snd_pcm_plugin_channel_t;
+
+typedef struct _snd_pcm_plugin_format {
+ int format;
+ unsigned int rate;
+ unsigned int channels;
+} snd_pcm_plugin_format_t;
+
+struct _snd_pcm_plugin {
+ const char *name; /* plug-in name */
+ int stream;
+ snd_pcm_plugin_format_t src_format; /* source format */
+ snd_pcm_plugin_format_t dst_format; /* destination format */
+ int src_width; /* sample width in bits */
+ int dst_width; /* sample width in bits */
+ int access;
+ snd_pcm_sframes_t (*src_frames)(snd_pcm_plugin_t *plugin, snd_pcm_uframes_t dst_frames);
+ snd_pcm_sframes_t (*dst_frames)(snd_pcm_plugin_t *plugin, snd_pcm_uframes_t src_frames);
+ snd_pcm_sframes_t (*client_channels)(snd_pcm_plugin_t *plugin,
+ snd_pcm_uframes_t frames,
+ snd_pcm_plugin_channel_t **channels);
+ int (*src_channels_mask)(snd_pcm_plugin_t *plugin,
+ bitset_t *dst_vmask,
+ bitset_t **src_vmask);
+ int (*dst_channels_mask)(snd_pcm_plugin_t *plugin,
+ bitset_t *src_vmask,
+ bitset_t **dst_vmask);
+ snd_pcm_sframes_t (*transfer)(snd_pcm_plugin_t *plugin,
+ const snd_pcm_plugin_channel_t *src_channels,
+ snd_pcm_plugin_channel_t *dst_channels,
+ snd_pcm_uframes_t frames);
+ int (*action)(snd_pcm_plugin_t *plugin,
+ snd_pcm_plugin_action_t action,
+ unsigned long data);
+ snd_pcm_plugin_t *prev;
+ snd_pcm_plugin_t *next;
+ snd_pcm_plug_t *plug;
+ void *private_data;
+ void (*private_free)(snd_pcm_plugin_t *plugin);
+ char *buf;
+ snd_pcm_uframes_t buf_frames;
+ snd_pcm_plugin_channel_t *buf_channels;
+ bitset_t *src_vmask;
+ bitset_t *dst_vmask;
+ char extra_data[0];
+};
+
+int snd_pcm_plugin_build(snd_pcm_plug_t *handle,
+ const char *name,
+ snd_pcm_plugin_format_t *src_format,
+ snd_pcm_plugin_format_t *dst_format,
+ size_t extra,
+ snd_pcm_plugin_t **ret);
+int snd_pcm_plugin_free(snd_pcm_plugin_t *plugin);
+int snd_pcm_plugin_clear(snd_pcm_plugin_t **first);
+int snd_pcm_plug_alloc(snd_pcm_plug_t *plug, snd_pcm_uframes_t frames);
+snd_pcm_sframes_t snd_pcm_plug_client_size(snd_pcm_plug_t *handle, snd_pcm_uframes_t drv_size);
+snd_pcm_sframes_t snd_pcm_plug_slave_size(snd_pcm_plug_t *handle, snd_pcm_uframes_t clt_size);
+
+#define FULL ROUTE_PLUGIN_RESOLUTION
+#define HALF ROUTE_PLUGIN_RESOLUTION / 2
+typedef int route_ttable_entry_t;
+
+int snd_pcm_plugin_build_io(snd_pcm_plug_t *handle,
+ snd_pcm_hw_params_t *params,
+ snd_pcm_plugin_t **r_plugin);
+int snd_pcm_plugin_build_linear(snd_pcm_plug_t *handle,
+ snd_pcm_plugin_format_t *src_format,
+ snd_pcm_plugin_format_t *dst_format,
+ snd_pcm_plugin_t **r_plugin);
+int snd_pcm_plugin_build_mulaw(snd_pcm_plug_t *handle,
+ snd_pcm_plugin_format_t *src_format,
+ snd_pcm_plugin_format_t *dst_format,
+ snd_pcm_plugin_t **r_plugin);
+int snd_pcm_plugin_build_rate(snd_pcm_plug_t *handle,
+ snd_pcm_plugin_format_t *src_format,
+ snd_pcm_plugin_format_t *dst_format,
+ snd_pcm_plugin_t **r_plugin);
+int snd_pcm_plugin_build_route(snd_pcm_plug_t *handle,
+ snd_pcm_plugin_format_t *src_format,
+ snd_pcm_plugin_format_t *dst_format,
+ route_ttable_entry_t *ttable,
+ snd_pcm_plugin_t **r_plugin);
+int snd_pcm_plugin_build_copy(snd_pcm_plug_t *handle,
+ snd_pcm_plugin_format_t *src_format,
+ snd_pcm_plugin_format_t *dst_format,
+ snd_pcm_plugin_t **r_plugin);
+
+int snd_pcm_plug_format_plugins(snd_pcm_plug_t *substream,
+ snd_pcm_hw_params_t *params,
+ snd_pcm_hw_params_t *slave_params);
+
+int snd_pcm_plug_slave_format(int format, snd_mask_t *format_mask);
+
+int snd_pcm_plugin_append(snd_pcm_plugin_t *plugin);
+
+snd_pcm_sframes_t snd_pcm_plug_write_transfer(snd_pcm_plug_t *handle, snd_pcm_plugin_channel_t *src_channels, snd_pcm_uframes_t size);
+snd_pcm_sframes_t snd_pcm_plug_read_transfer(snd_pcm_plug_t *handle, snd_pcm_plugin_channel_t *dst_channels_final, snd_pcm_uframes_t size);
+
+snd_pcm_sframes_t snd_pcm_plug_client_channels_buf(snd_pcm_plug_t *handle,
+ char *buf, snd_pcm_uframes_t count,
+ snd_pcm_plugin_channel_t **channels);
+
+snd_pcm_sframes_t snd_pcm_plugin_client_channels(snd_pcm_plugin_t *plugin,
+ snd_pcm_uframes_t frames,
+ snd_pcm_plugin_channel_t **channels);
+
+int snd_pcm_area_silence(const snd_pcm_channel_area_t *dst_channel, size_t dst_offset,
+ size_t samples, int format);
+int snd_pcm_area_copy(const snd_pcm_channel_area_t *src_channel, size_t src_offset,
+ const snd_pcm_channel_area_t *dst_channel, size_t dst_offset,
+ size_t samples, int format);
+
+void *snd_pcm_plug_buf_alloc(snd_pcm_plug_t *plug, snd_pcm_uframes_t size);
+void snd_pcm_plug_buf_unlock(snd_pcm_plug_t *plug, void *ptr);
+snd_pcm_sframes_t snd_pcm_oss_write3(snd_pcm_substream_t *substream, const char *ptr, snd_pcm_uframes_t size, int in_kernel);
+snd_pcm_sframes_t snd_pcm_oss_read3(snd_pcm_substream_t *substream, char *ptr, snd_pcm_uframes_t size, int in_kernel);
+snd_pcm_sframes_t snd_pcm_oss_writev3(snd_pcm_substream_t *substream, void **bufs, snd_pcm_uframes_t frames, int in_kernel);
+snd_pcm_sframes_t snd_pcm_oss_readv3(snd_pcm_substream_t *substream, void **bufs, snd_pcm_uframes_t frames, int in_kernel);
+
+
+
+#define ROUTE_PLUGIN_RESOLUTION 16
+
+int getput_index(int format);
+int copy_index(int format);
+int conv_index(int src_format, int dst_format);
+
+void zero_channel(snd_pcm_plugin_t *plugin,
+ const snd_pcm_plugin_channel_t *dst_channel,
+ size_t samples);
+
+#ifdef PLUGIN_DEBUG
+#define pdprintf( fmt, args... ) printk( "plugin: " fmt, ##args)
+#else
+#define pdprintf( fmt, args... )
+#endif
+
+#endif /* __PCM_PLUGIN_H */
diff --git a/sound/core/oss/plugin_ops.h b/sound/core/oss/plugin_ops.h
new file mode 100644
index 00000000000..0607e956608
--- /dev/null
+++ b/sound/core/oss/plugin_ops.h
@@ -0,0 +1,536 @@
+/*
+ * Plugin sample operators with fast switch
+ * Copyright (c) 2000 by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+
+#define as_u8(ptr) (*(u_int8_t*)(ptr))
+#define as_u16(ptr) (*(u_int16_t*)(ptr))
+#define as_u32(ptr) (*(u_int32_t*)(ptr))
+#define as_u64(ptr) (*(u_int64_t*)(ptr))
+#define as_s8(ptr) (*(int8_t*)(ptr))
+#define as_s16(ptr) (*(int16_t*)(ptr))
+#define as_s32(ptr) (*(int32_t*)(ptr))
+#define as_s64(ptr) (*(int64_t*)(ptr))
+
+#ifdef COPY_LABELS
+static void *copy_labels[4] = {
+ &&copy_8,
+ &&copy_16,
+ &&copy_32,
+ &&copy_64
+};
+#endif
+
+#ifdef COPY_END
+while(0) {
+copy_8: as_s8(dst) = as_s8(src); goto COPY_END;
+copy_16: as_s16(dst) = as_s16(src); goto COPY_END;
+copy_32: as_s32(dst) = as_s32(src); goto COPY_END;
+copy_64: as_s64(dst) = as_s64(src); goto COPY_END;
+}
+#endif
+
+#ifdef CONV_LABELS
+/* src_wid src_endswap sign_toggle dst_wid dst_endswap */
+static void *conv_labels[4 * 2 * 2 * 4 * 2] = {
+ &&conv_xxx1_xxx1, /* 8h -> 8h */
+ &&conv_xxx1_xxx1, /* 8h -> 8s */
+ &&conv_xxx1_xx10, /* 8h -> 16h */
+ &&conv_xxx1_xx01, /* 8h -> 16s */
+ &&conv_xxx1_x100, /* 8h -> 24h */
+ &&conv_xxx1_001x, /* 8h -> 24s */
+ &&conv_xxx1_1000, /* 8h -> 32h */
+ &&conv_xxx1_0001, /* 8h -> 32s */
+ &&conv_xxx1_xxx9, /* 8h ^> 8h */
+ &&conv_xxx1_xxx9, /* 8h ^> 8s */
+ &&conv_xxx1_xx90, /* 8h ^> 16h */
+ &&conv_xxx1_xx09, /* 8h ^> 16s */
+ &&conv_xxx1_x900, /* 8h ^> 24h */
+ &&conv_xxx1_009x, /* 8h ^> 24s */
+ &&conv_xxx1_9000, /* 8h ^> 32h */
+ &&conv_xxx1_0009, /* 8h ^> 32s */
+ &&conv_xxx1_xxx1, /* 8s -> 8h */
+ &&conv_xxx1_xxx1, /* 8s -> 8s */
+ &&conv_xxx1_xx10, /* 8s -> 16h */
+ &&conv_xxx1_xx01, /* 8s -> 16s */
+ &&conv_xxx1_x100, /* 8s -> 24h */
+ &&conv_xxx1_001x, /* 8s -> 24s */
+ &&conv_xxx1_1000, /* 8s -> 32h */
+ &&conv_xxx1_0001, /* 8s -> 32s */
+ &&conv_xxx1_xxx9, /* 8s ^> 8h */
+ &&conv_xxx1_xxx9, /* 8s ^> 8s */
+ &&conv_xxx1_xx90, /* 8s ^> 16h */
+ &&conv_xxx1_xx09, /* 8s ^> 16s */
+ &&conv_xxx1_x900, /* 8s ^> 24h */
+ &&conv_xxx1_009x, /* 8s ^> 24s */
+ &&conv_xxx1_9000, /* 8s ^> 32h */
+ &&conv_xxx1_0009, /* 8s ^> 32s */
+ &&conv_xx12_xxx1, /* 16h -> 8h */
+ &&conv_xx12_xxx1, /* 16h -> 8s */
+ &&conv_xx12_xx12, /* 16h -> 16h */
+ &&conv_xx12_xx21, /* 16h -> 16s */
+ &&conv_xx12_x120, /* 16h -> 24h */
+ &&conv_xx12_021x, /* 16h -> 24s */
+ &&conv_xx12_1200, /* 16h -> 32h */
+ &&conv_xx12_0021, /* 16h -> 32s */
+ &&conv_xx12_xxx9, /* 16h ^> 8h */
+ &&conv_xx12_xxx9, /* 16h ^> 8s */
+ &&conv_xx12_xx92, /* 16h ^> 16h */
+ &&conv_xx12_xx29, /* 16h ^> 16s */
+ &&conv_xx12_x920, /* 16h ^> 24h */
+ &&conv_xx12_029x, /* 16h ^> 24s */
+ &&conv_xx12_9200, /* 16h ^> 32h */
+ &&conv_xx12_0029, /* 16h ^> 32s */
+ &&conv_xx12_xxx2, /* 16s -> 8h */
+ &&conv_xx12_xxx2, /* 16s -> 8s */
+ &&conv_xx12_xx21, /* 16s -> 16h */
+ &&conv_xx12_xx12, /* 16s -> 16s */
+ &&conv_xx12_x210, /* 16s -> 24h */
+ &&conv_xx12_012x, /* 16s -> 24s */
+ &&conv_xx12_2100, /* 16s -> 32h */
+ &&conv_xx12_0012, /* 16s -> 32s */
+ &&conv_xx12_xxxA, /* 16s ^> 8h */
+ &&conv_xx12_xxxA, /* 16s ^> 8s */
+ &&conv_xx12_xxA1, /* 16s ^> 16h */
+ &&conv_xx12_xx1A, /* 16s ^> 16s */
+ &&conv_xx12_xA10, /* 16s ^> 24h */
+ &&conv_xx12_01Ax, /* 16s ^> 24s */
+ &&conv_xx12_A100, /* 16s ^> 32h */
+ &&conv_xx12_001A, /* 16s ^> 32s */
+ &&conv_x123_xxx1, /* 24h -> 8h */
+ &&conv_x123_xxx1, /* 24h -> 8s */
+ &&conv_x123_xx12, /* 24h -> 16h */
+ &&conv_x123_xx21, /* 24h -> 16s */
+ &&conv_x123_x123, /* 24h -> 24h */
+ &&conv_x123_321x, /* 24h -> 24s */
+ &&conv_x123_1230, /* 24h -> 32h */
+ &&conv_x123_0321, /* 24h -> 32s */
+ &&conv_x123_xxx9, /* 24h ^> 8h */
+ &&conv_x123_xxx9, /* 24h ^> 8s */
+ &&conv_x123_xx92, /* 24h ^> 16h */
+ &&conv_x123_xx29, /* 24h ^> 16s */
+ &&conv_x123_x923, /* 24h ^> 24h */
+ &&conv_x123_329x, /* 24h ^> 24s */
+ &&conv_x123_9230, /* 24h ^> 32h */
+ &&conv_x123_0329, /* 24h ^> 32s */
+ &&conv_123x_xxx3, /* 24s -> 8h */
+ &&conv_123x_xxx3, /* 24s -> 8s */
+ &&conv_123x_xx32, /* 24s -> 16h */
+ &&conv_123x_xx23, /* 24s -> 16s */
+ &&conv_123x_x321, /* 24s -> 24h */
+ &&conv_123x_123x, /* 24s -> 24s */
+ &&conv_123x_3210, /* 24s -> 32h */
+ &&conv_123x_0123, /* 24s -> 32s */
+ &&conv_123x_xxxB, /* 24s ^> 8h */
+ &&conv_123x_xxxB, /* 24s ^> 8s */
+ &&conv_123x_xxB2, /* 24s ^> 16h */
+ &&conv_123x_xx2B, /* 24s ^> 16s */
+ &&conv_123x_xB21, /* 24s ^> 24h */
+ &&conv_123x_12Bx, /* 24s ^> 24s */
+ &&conv_123x_B210, /* 24s ^> 32h */
+ &&conv_123x_012B, /* 24s ^> 32s */
+ &&conv_1234_xxx1, /* 32h -> 8h */
+ &&conv_1234_xxx1, /* 32h -> 8s */
+ &&conv_1234_xx12, /* 32h -> 16h */
+ &&conv_1234_xx21, /* 32h -> 16s */
+ &&conv_1234_x123, /* 32h -> 24h */
+ &&conv_1234_321x, /* 32h -> 24s */
+ &&conv_1234_1234, /* 32h -> 32h */
+ &&conv_1234_4321, /* 32h -> 32s */
+ &&conv_1234_xxx9, /* 32h ^> 8h */
+ &&conv_1234_xxx9, /* 32h ^> 8s */
+ &&conv_1234_xx92, /* 32h ^> 16h */
+ &&conv_1234_xx29, /* 32h ^> 16s */
+ &&conv_1234_x923, /* 32h ^> 24h */
+ &&conv_1234_329x, /* 32h ^> 24s */
+ &&conv_1234_9234, /* 32h ^> 32h */
+ &&conv_1234_4329, /* 32h ^> 32s */
+ &&conv_1234_xxx4, /* 32s -> 8h */
+ &&conv_1234_xxx4, /* 32s -> 8s */
+ &&conv_1234_xx43, /* 32s -> 16h */
+ &&conv_1234_xx34, /* 32s -> 16s */
+ &&conv_1234_x432, /* 32s -> 24h */
+ &&conv_1234_234x, /* 32s -> 24s */
+ &&conv_1234_4321, /* 32s -> 32h */
+ &&conv_1234_1234, /* 32s -> 32s */
+ &&conv_1234_xxxC, /* 32s ^> 8h */
+ &&conv_1234_xxxC, /* 32s ^> 8s */
+ &&conv_1234_xxC3, /* 32s ^> 16h */
+ &&conv_1234_xx3C, /* 32s ^> 16s */
+ &&conv_1234_xC32, /* 32s ^> 24h */
+ &&conv_1234_23Cx, /* 32s ^> 24s */
+ &&conv_1234_C321, /* 32s ^> 32h */
+ &&conv_1234_123C, /* 32s ^> 32s */
+};
+#endif
+
+#ifdef CONV_END
+while(0) {
+conv_xxx1_xxx1: as_u8(dst) = as_u8(src); goto CONV_END;
+conv_xxx1_xx10: as_u16(dst) = (u_int16_t)as_u8(src) << 8; goto CONV_END;
+conv_xxx1_xx01: as_u16(dst) = (u_int16_t)as_u8(src); goto CONV_END;
+conv_xxx1_x100: as_u32(dst) = (u_int32_t)as_u8(src) << 16; goto CONV_END;
+conv_xxx1_001x: as_u32(dst) = (u_int32_t)as_u8(src) << 8; goto CONV_END;
+conv_xxx1_1000: as_u32(dst) = (u_int32_t)as_u8(src) << 24; goto CONV_END;
+conv_xxx1_0001: as_u32(dst) = (u_int32_t)as_u8(src); goto CONV_END;
+conv_xxx1_xxx9: as_u8(dst) = as_u8(src) ^ 0x80; goto CONV_END;
+conv_xxx1_xx90: as_u16(dst) = (u_int16_t)(as_u8(src) ^ 0x80) << 8; goto CONV_END;
+conv_xxx1_xx09: as_u16(dst) = (u_int16_t)(as_u8(src) ^ 0x80); goto CONV_END;
+conv_xxx1_x900: as_u32(dst) = (u_int32_t)(as_u8(src) ^ 0x80) << 16; goto CONV_END;
+conv_xxx1_009x: as_u32(dst) = (u_int32_t)(as_u8(src) ^ 0x80) << 8; goto CONV_END;
+conv_xxx1_9000: as_u32(dst) = (u_int32_t)(as_u8(src) ^ 0x80) << 24; goto CONV_END;
+conv_xxx1_0009: as_u32(dst) = (u_int32_t)(as_u8(src) ^ 0x80); goto CONV_END;
+conv_xx12_xxx1: as_u8(dst) = as_u16(src) >> 8; goto CONV_END;
+conv_xx12_xx12: as_u16(dst) = as_u16(src); goto CONV_END;
+conv_xx12_xx21: as_u16(dst) = swab16(as_u16(src)); goto CONV_END;
+conv_xx12_x120: as_u32(dst) = (u_int32_t)as_u16(src) << 8; goto CONV_END;
+conv_xx12_021x: as_u32(dst) = (u_int32_t)swab16(as_u16(src)) << 8; goto CONV_END;
+conv_xx12_1200: as_u32(dst) = (u_int32_t)as_u16(src) << 16; goto CONV_END;
+conv_xx12_0021: as_u32(dst) = (u_int32_t)swab16(as_u16(src)); goto CONV_END;
+conv_xx12_xxx9: as_u8(dst) = (as_u16(src) >> 8) ^ 0x80; goto CONV_END;
+conv_xx12_xx92: as_u16(dst) = as_u16(src) ^ 0x8000; goto CONV_END;
+conv_xx12_xx29: as_u16(dst) = swab16(as_u16(src)) ^ 0x80; goto CONV_END;
+conv_xx12_x920: as_u32(dst) = (u_int32_t)(as_u16(src) ^ 0x8000) << 8; goto CONV_END;
+conv_xx12_029x: as_u32(dst) = (u_int32_t)(swab16(as_u16(src)) ^ 0x80) << 8; goto CONV_END;
+conv_xx12_9200: as_u32(dst) = (u_int32_t)(as_u16(src) ^ 0x8000) << 16; goto CONV_END;
+conv_xx12_0029: as_u32(dst) = (u_int32_t)(swab16(as_u16(src)) ^ 0x80); goto CONV_END;
+conv_xx12_xxx2: as_u8(dst) = as_u16(src) & 0xff; goto CONV_END;
+conv_xx12_x210: as_u32(dst) = (u_int32_t)swab16(as_u16(src)) << 8; goto CONV_END;
+conv_xx12_012x: as_u32(dst) = (u_int32_t)as_u16(src) << 8; goto CONV_END;
+conv_xx12_2100: as_u32(dst) = (u_int32_t)swab16(as_u16(src)) << 16; goto CONV_END;
+conv_xx12_0012: as_u32(dst) = (u_int32_t)as_u16(src); goto CONV_END;
+conv_xx12_xxxA: as_u8(dst) = (as_u16(src) ^ 0x80) & 0xff; goto CONV_END;
+conv_xx12_xxA1: as_u16(dst) = swab16(as_u16(src) ^ 0x80); goto CONV_END;
+conv_xx12_xx1A: as_u16(dst) = as_u16(src) ^ 0x80; goto CONV_END;
+conv_xx12_xA10: as_u32(dst) = (u_int32_t)swab16(as_u16(src) ^ 0x80) << 8; goto CONV_END;
+conv_xx12_01Ax: as_u32(dst) = (u_int32_t)(as_u16(src) ^ 0x80) << 8; goto CONV_END;
+conv_xx12_A100: as_u32(dst) = (u_int32_t)swab16(as_u16(src) ^ 0x80) << 16; goto CONV_END;
+conv_xx12_001A: as_u32(dst) = (u_int32_t)(as_u16(src) ^ 0x80); goto CONV_END;
+conv_x123_xxx1: as_u8(dst) = as_u32(src) >> 16; goto CONV_END;
+conv_x123_xx12: as_u16(dst) = as_u32(src) >> 8; goto CONV_END;
+conv_x123_xx21: as_u16(dst) = swab16(as_u32(src) >> 8); goto CONV_END;
+conv_x123_x123: as_u32(dst) = as_u32(src); goto CONV_END;
+conv_x123_321x: as_u32(dst) = swab32(as_u32(src)); goto CONV_END;
+conv_x123_1230: as_u32(dst) = as_u32(src) << 8; goto CONV_END;
+conv_x123_0321: as_u32(dst) = swab32(as_u32(src)) >> 8; goto CONV_END;
+conv_x123_xxx9: as_u8(dst) = (as_u32(src) >> 16) ^ 0x80; goto CONV_END;
+conv_x123_xx92: as_u16(dst) = (as_u32(src) >> 8) ^ 0x8000; goto CONV_END;
+conv_x123_xx29: as_u16(dst) = swab16(as_u32(src) >> 8) ^ 0x80; goto CONV_END;
+conv_x123_x923: as_u32(dst) = as_u32(src) ^ 0x800000; goto CONV_END;
+conv_x123_329x: as_u32(dst) = swab32(as_u32(src)) ^ 0x8000; goto CONV_END;
+conv_x123_9230: as_u32(dst) = (as_u32(src) ^ 0x800000) << 8; goto CONV_END;
+conv_x123_0329: as_u32(dst) = (swab32(as_u32(src)) >> 8) ^ 0x80; goto CONV_END;
+conv_123x_xxx3: as_u8(dst) = (as_u32(src) >> 8) & 0xff; goto CONV_END;
+conv_123x_xx32: as_u16(dst) = swab16(as_u32(src) >> 8); goto CONV_END;
+conv_123x_xx23: as_u16(dst) = (as_u32(src) >> 8) & 0xffff; goto CONV_END;
+conv_123x_x321: as_u32(dst) = swab32(as_u32(src)); goto CONV_END;
+conv_123x_123x: as_u32(dst) = as_u32(src); goto CONV_END;
+conv_123x_3210: as_u32(dst) = swab32(as_u32(src)) << 8; goto CONV_END;
+conv_123x_0123: as_u32(dst) = as_u32(src) >> 8; goto CONV_END;
+conv_123x_xxxB: as_u8(dst) = ((as_u32(src) >> 8) & 0xff) ^ 0x80; goto CONV_END;
+conv_123x_xxB2: as_u16(dst) = swab16((as_u32(src) >> 8) ^ 0x80); goto CONV_END;
+conv_123x_xx2B: as_u16(dst) = ((as_u32(src) >> 8) & 0xffff) ^ 0x80; goto CONV_END;
+conv_123x_xB21: as_u32(dst) = swab32(as_u32(src)) ^ 0x800000; goto CONV_END;
+conv_123x_12Bx: as_u32(dst) = as_u32(src) ^ 0x8000; goto CONV_END;
+conv_123x_B210: as_u32(dst) = swab32(as_u32(src) ^ 0x8000) << 8; goto CONV_END;
+conv_123x_012B: as_u32(dst) = (as_u32(src) >> 8) ^ 0x80; goto CONV_END;
+conv_1234_xxx1: as_u8(dst) = as_u32(src) >> 24; goto CONV_END;
+conv_1234_xx12: as_u16(dst) = as_u32(src) >> 16; goto CONV_END;
+conv_1234_xx21: as_u16(dst) = swab16(as_u32(src) >> 16); goto CONV_END;
+conv_1234_x123: as_u32(dst) = as_u32(src) >> 8; goto CONV_END;
+conv_1234_321x: as_u32(dst) = swab32(as_u32(src)) << 8; goto CONV_END;
+conv_1234_1234: as_u32(dst) = as_u32(src); goto CONV_END;
+conv_1234_4321: as_u32(dst) = swab32(as_u32(src)); goto CONV_END;
+conv_1234_xxx9: as_u8(dst) = (as_u32(src) >> 24) ^ 0x80; goto CONV_END;
+conv_1234_xx92: as_u16(dst) = (as_u32(src) >> 16) ^ 0x8000; goto CONV_END;
+conv_1234_xx29: as_u16(dst) = swab16(as_u32(src) >> 16) ^ 0x80; goto CONV_END;
+conv_1234_x923: as_u32(dst) = (as_u32(src) >> 8) ^ 0x800000; goto CONV_END;
+conv_1234_329x: as_u32(dst) = (swab32(as_u32(src)) ^ 0x80) << 8; goto CONV_END;
+conv_1234_9234: as_u32(dst) = as_u32(src) ^ 0x80000000; goto CONV_END;
+conv_1234_4329: as_u32(dst) = swab32(as_u32(src)) ^ 0x80; goto CONV_END;
+conv_1234_xxx4: as_u8(dst) = as_u32(src) & 0xff; goto CONV_END;
+conv_1234_xx43: as_u16(dst) = swab16(as_u32(src)); goto CONV_END;
+conv_1234_xx34: as_u16(dst) = as_u32(src) & 0xffff; goto CONV_END;
+conv_1234_x432: as_u32(dst) = swab32(as_u32(src)) >> 8; goto CONV_END;
+conv_1234_234x: as_u32(dst) = as_u32(src) << 8; goto CONV_END;
+conv_1234_xxxC: as_u8(dst) = (as_u32(src) & 0xff) ^ 0x80; goto CONV_END;
+conv_1234_xxC3: as_u16(dst) = swab16(as_u32(src) ^ 0x80); goto CONV_END;
+conv_1234_xx3C: as_u16(dst) = (as_u32(src) & 0xffff) ^ 0x80; goto CONV_END;
+conv_1234_xC32: as_u32(dst) = (swab32(as_u32(src)) >> 8) ^ 0x800000; goto CONV_END;
+conv_1234_23Cx: as_u32(dst) = (as_u32(src) ^ 0x80) << 8; goto CONV_END;
+conv_1234_C321: as_u32(dst) = swab32(as_u32(src) ^ 0x80); goto CONV_END;
+conv_1234_123C: as_u32(dst) = as_u32(src) ^ 0x80; goto CONV_END;
+}
+#endif
+
+#ifdef GET_S16_LABELS
+/* src_wid src_endswap unsigned */
+static void *get_s16_labels[4 * 2 * 2] = {
+ &&get_s16_xxx1_xx10, /* 8h -> 16h */
+ &&get_s16_xxx1_xx90, /* 8h ^> 16h */
+ &&get_s16_xxx1_xx10, /* 8s -> 16h */
+ &&get_s16_xxx1_xx90, /* 8s ^> 16h */
+ &&get_s16_xx12_xx12, /* 16h -> 16h */
+ &&get_s16_xx12_xx92, /* 16h ^> 16h */
+ &&get_s16_xx12_xx21, /* 16s -> 16h */
+ &&get_s16_xx12_xxA1, /* 16s ^> 16h */
+ &&get_s16_x123_xx12, /* 24h -> 16h */
+ &&get_s16_x123_xx92, /* 24h ^> 16h */
+ &&get_s16_123x_xx32, /* 24s -> 16h */
+ &&get_s16_123x_xxB2, /* 24s ^> 16h */
+ &&get_s16_1234_xx12, /* 32h -> 16h */
+ &&get_s16_1234_xx92, /* 32h ^> 16h */
+ &&get_s16_1234_xx43, /* 32s -> 16h */
+ &&get_s16_1234_xxC3, /* 32s ^> 16h */
+};
+#endif
+
+#ifdef GET_S16_END
+while(0) {
+get_s16_xxx1_xx10: sample = (u_int16_t)as_u8(src) << 8; goto GET_S16_END;
+get_s16_xxx1_xx90: sample = (u_int16_t)(as_u8(src) ^ 0x80) << 8; goto GET_S16_END;
+get_s16_xx12_xx12: sample = as_u16(src); goto GET_S16_END;
+get_s16_xx12_xx92: sample = as_u16(src) ^ 0x8000; goto GET_S16_END;
+get_s16_xx12_xx21: sample = swab16(as_u16(src)); goto GET_S16_END;
+get_s16_xx12_xxA1: sample = swab16(as_u16(src) ^ 0x80); goto GET_S16_END;
+get_s16_x123_xx12: sample = as_u32(src) >> 8; goto GET_S16_END;
+get_s16_x123_xx92: sample = (as_u32(src) >> 8) ^ 0x8000; goto GET_S16_END;
+get_s16_123x_xx32: sample = swab16(as_u32(src) >> 8); goto GET_S16_END;
+get_s16_123x_xxB2: sample = swab16((as_u32(src) >> 8) ^ 0x8000); goto GET_S16_END;
+get_s16_1234_xx12: sample = as_u32(src) >> 16; goto GET_S16_END;
+get_s16_1234_xx92: sample = (as_u32(src) >> 16) ^ 0x8000; goto GET_S16_END;
+get_s16_1234_xx43: sample = swab16(as_u32(src)); goto GET_S16_END;
+get_s16_1234_xxC3: sample = swab16(as_u32(src) ^ 0x80); goto GET_S16_END;
+}
+#endif
+
+#ifdef PUT_S16_LABELS
+/* dst_wid dst_endswap unsigned */
+static void *put_s16_labels[4 * 2 * 2] = {
+ &&put_s16_xx12_xxx1, /* 16h -> 8h */
+ &&put_s16_xx12_xxx9, /* 16h ^> 8h */
+ &&put_s16_xx12_xxx1, /* 16h -> 8s */
+ &&put_s16_xx12_xxx9, /* 16h ^> 8s */
+ &&put_s16_xx12_xx12, /* 16h -> 16h */
+ &&put_s16_xx12_xx92, /* 16h ^> 16h */
+ &&put_s16_xx12_xx21, /* 16h -> 16s */
+ &&put_s16_xx12_xx29, /* 16h ^> 16s */
+ &&put_s16_xx12_x120, /* 16h -> 24h */
+ &&put_s16_xx12_x920, /* 16h ^> 24h */
+ &&put_s16_xx12_021x, /* 16h -> 24s */
+ &&put_s16_xx12_029x, /* 16h ^> 24s */
+ &&put_s16_xx12_1200, /* 16h -> 32h */
+ &&put_s16_xx12_9200, /* 16h ^> 32h */
+ &&put_s16_xx12_0021, /* 16h -> 32s */
+ &&put_s16_xx12_0029, /* 16h ^> 32s */
+};
+#endif
+
+#ifdef PUT_S16_END
+while (0) {
+put_s16_xx12_xxx1: as_u8(dst) = sample >> 8; goto PUT_S16_END;
+put_s16_xx12_xxx9: as_u8(dst) = (sample >> 8) ^ 0x80; goto PUT_S16_END;
+put_s16_xx12_xx12: as_u16(dst) = sample; goto PUT_S16_END;
+put_s16_xx12_xx92: as_u16(dst) = sample ^ 0x8000; goto PUT_S16_END;
+put_s16_xx12_xx21: as_u16(dst) = swab16(sample); goto PUT_S16_END;
+put_s16_xx12_xx29: as_u16(dst) = swab16(sample) ^ 0x80; goto PUT_S16_END;
+put_s16_xx12_x120: as_u32(dst) = (u_int32_t)sample << 8; goto PUT_S16_END;
+put_s16_xx12_x920: as_u32(dst) = (u_int32_t)(sample ^ 0x8000) << 8; goto PUT_S16_END;
+put_s16_xx12_021x: as_u32(dst) = (u_int32_t)swab16(sample) << 8; goto PUT_S16_END;
+put_s16_xx12_029x: as_u32(dst) = (u_int32_t)(swab16(sample) ^ 0x80) << 8; goto PUT_S16_END;
+put_s16_xx12_1200: as_u32(dst) = (u_int32_t)sample << 16; goto PUT_S16_END;
+put_s16_xx12_9200: as_u32(dst) = (u_int32_t)(sample ^ 0x8000) << 16; goto PUT_S16_END;
+put_s16_xx12_0021: as_u32(dst) = (u_int32_t)swab16(sample); goto PUT_S16_END;
+put_s16_xx12_0029: as_u32(dst) = (u_int32_t)swab16(sample) ^ 0x80; goto PUT_S16_END;
+}
+#endif
+
+#if 0
+#ifdef GET32_LABELS
+/* src_wid src_endswap unsigned */
+static void *get32_labels[4 * 2 * 2] = {
+ &&get32_xxx1_1000, /* 8h -> 32h */
+ &&get32_xxx1_9000, /* 8h ^> 32h */
+ &&get32_xxx1_1000, /* 8s -> 32h */
+ &&get32_xxx1_9000, /* 8s ^> 32h */
+ &&get32_xx12_1200, /* 16h -> 32h */
+ &&get32_xx12_9200, /* 16h ^> 32h */
+ &&get32_xx12_2100, /* 16s -> 32h */
+ &&get32_xx12_A100, /* 16s ^> 32h */
+ &&get32_x123_1230, /* 24h -> 32h */
+ &&get32_x123_9230, /* 24h ^> 32h */
+ &&get32_123x_3210, /* 24s -> 32h */
+ &&get32_123x_B210, /* 24s ^> 32h */
+ &&get32_1234_1234, /* 32h -> 32h */
+ &&get32_1234_9234, /* 32h ^> 32h */
+ &&get32_1234_4321, /* 32s -> 32h */
+ &&get32_1234_C321, /* 32s ^> 32h */
+};
+#endif
+
+#ifdef GET32_END
+while (0) {
+get32_xxx1_1000: sample = (u_int32_t)as_u8(src) << 24; goto GET32_END;
+get32_xxx1_9000: sample = (u_int32_t)(as_u8(src) ^ 0x80) << 24; goto GET32_END;
+get32_xx12_1200: sample = (u_int32_t)as_u16(src) << 16; goto GET32_END;
+get32_xx12_9200: sample = (u_int32_t)(as_u16(src) ^ 0x8000) << 16; goto GET32_END;
+get32_xx12_2100: sample = (u_int32_t)swab16(as_u16(src)) << 16; goto GET32_END;
+get32_xx12_A100: sample = (u_int32_t)swab16(as_u16(src) ^ 0x80) << 16; goto GET32_END;
+get32_x123_1230: sample = as_u32(src) << 8; goto GET32_END;
+get32_x123_9230: sample = (as_u32(src) << 8) ^ 0x80000000; goto GET32_END;
+get32_123x_3210: sample = swab32(as_u32(src) >> 8); goto GET32_END;
+get32_123x_B210: sample = swab32((as_u32(src) >> 8) ^ 0x80); goto GET32_END;
+get32_1234_1234: sample = as_u32(src); goto GET32_END;
+get32_1234_9234: sample = as_u32(src) ^ 0x80000000; goto GET32_END;
+get32_1234_4321: sample = swab32(as_u32(src)); goto GET32_END;
+get32_1234_C321: sample = swab32(as_u32(src) ^ 0x80); goto GET32_END;
+}
+#endif
+#endif
+
+#ifdef PUT_U32_LABELS
+/* dst_wid dst_endswap unsigned */
+static void *put_u32_labels[4 * 2 * 2] = {
+ &&put_u32_1234_xxx9, /* u32h -> s8h */
+ &&put_u32_1234_xxx1, /* u32h -> u8h */
+ &&put_u32_1234_xxx9, /* u32h -> s8s */
+ &&put_u32_1234_xxx1, /* u32h -> u8s */
+ &&put_u32_1234_xx92, /* u32h -> s16h */
+ &&put_u32_1234_xx12, /* u32h -> u16h */
+ &&put_u32_1234_xx29, /* u32h -> s16s */
+ &&put_u32_1234_xx21, /* u32h -> u16s */
+ &&put_u32_1234_x923, /* u32h -> s24h */
+ &&put_u32_1234_x123, /* u32h -> u24h */
+ &&put_u32_1234_329x, /* u32h -> s24s */
+ &&put_u32_1234_321x, /* u32h -> u24s */
+ &&put_u32_1234_9234, /* u32h -> s32h */
+ &&put_u32_1234_1234, /* u32h -> u32h */
+ &&put_u32_1234_4329, /* u32h -> s32s */
+ &&put_u32_1234_4321, /* u32h -> u32s */
+};
+#endif
+
+#ifdef PUT_U32_END
+while (0) {
+put_u32_1234_xxx1: as_u8(dst) = sample >> 24; goto PUT_U32_END;
+put_u32_1234_xxx9: as_u8(dst) = (sample >> 24) ^ 0x80; goto PUT_U32_END;
+put_u32_1234_xx12: as_u16(dst) = sample >> 16; goto PUT_U32_END;
+put_u32_1234_xx92: as_u16(dst) = (sample >> 16) ^ 0x8000; goto PUT_U32_END;
+put_u32_1234_xx21: as_u16(dst) = swab16(sample >> 16); goto PUT_U32_END;
+put_u32_1234_xx29: as_u16(dst) = swab16(sample >> 16) ^ 0x80; goto PUT_U32_END;
+put_u32_1234_x123: as_u32(dst) = sample >> 8; goto PUT_U32_END;
+put_u32_1234_x923: as_u32(dst) = (sample >> 8) ^ 0x800000; goto PUT_U32_END;
+put_u32_1234_321x: as_u32(dst) = swab32(sample) << 8; goto PUT_U32_END;
+put_u32_1234_329x: as_u32(dst) = (swab32(sample) ^ 0x80) << 8; goto PUT_U32_END;
+put_u32_1234_1234: as_u32(dst) = sample; goto PUT_U32_END;
+put_u32_1234_9234: as_u32(dst) = sample ^ 0x80000000; goto PUT_U32_END;
+put_u32_1234_4321: as_u32(dst) = swab32(sample); goto PUT_U32_END;
+put_u32_1234_4329: as_u32(dst) = swab32(sample) ^ 0x80; goto PUT_U32_END;
+}
+#endif
+
+#ifdef GET_U_LABELS
+/* width endswap unsigned*/
+static void *get_u_labels[4 * 2 * 2] = {
+ &&get_u_s8, /* s8 -> u8 */
+ &&get_u_u8, /* u8 -> u8 */
+ &&get_u_s8, /* s8 -> u8 */
+ &&get_u_u8, /* u8 -> u8 */
+ &&get_u_s16h, /* s16h -> u16h */
+ &&get_u_u16h, /* u16h -> u16h */
+ &&get_u_s16s, /* s16s -> u16h */
+ &&get_u_u16s, /* u16s -> u16h */
+ &&get_u_s24h, /* s24h -> u32h */
+ &&get_u_u24h, /* u24h -> u32h */
+ &&get_u_s24s, /* s24s -> u32h */
+ &&get_u_u24s, /* u24s -> u32h */
+ &&get_u_s32h, /* s32h -> u32h */
+ &&get_u_u32h, /* u32h -> u32h */
+ &&get_u_s32s, /* s32s -> u32h */
+ &&get_u_u32s, /* u32s -> u32h */
+};
+#endif
+
+#ifdef GET_U_END
+while (0) {
+get_u_s8: sample = as_u8(src) ^ 0x80; goto GET_U_END;
+get_u_u8: sample = as_u8(src); goto GET_U_END;
+get_u_s16h: sample = as_u16(src) ^ 0x8000; goto GET_U_END;
+get_u_u16h: sample = as_u16(src); goto GET_U_END;
+get_u_s16s: sample = swab16(as_u16(src) ^ 0x80); goto GET_U_END;
+get_u_u16s: sample = swab16(as_u16(src)); goto GET_U_END;
+get_u_s24h: sample = (as_u32(src) ^ 0x800000); goto GET_U_END;
+get_u_u24h: sample = as_u32(src); goto GET_U_END;
+get_u_s24s: sample = swab32(as_u32(src) ^ 0x800000); goto GET_U_END;
+get_u_u24s: sample = swab32(as_u32(src)); goto GET_U_END;
+get_u_s32h: sample = as_u32(src) ^ 0x80000000; goto GET_U_END;
+get_u_u32h: sample = as_u32(src); goto GET_U_END;
+get_u_s32s: sample = swab32(as_u32(src) ^ 0x80); goto GET_U_END;
+get_u_u32s: sample = swab32(as_u32(src)); goto GET_U_END;
+}
+#endif
+
+#if 0
+#ifdef PUT_LABELS
+/* width endswap unsigned */
+static void *put_labels[4 * 2 * 2] = {
+ &&put_s8, /* s8 -> s8 */
+ &&put_u8, /* u8 -> s8 */
+ &&put_s8, /* s8 -> s8 */
+ &&put_u8, /* u8 -> s8 */
+ &&put_s16h, /* s16h -> s16h */
+ &&put_u16h, /* u16h -> s16h */
+ &&put_s16s, /* s16s -> s16h */
+ &&put_u16s, /* u16s -> s16h */
+ &&put_s24h, /* s24h -> s32h */
+ &&put_u24h, /* u24h -> s32h */
+ &&put_s24s, /* s24s -> s32h */
+ &&put_u24s, /* u24s -> s32h */
+ &&put_s32h, /* s32h -> s32h */
+ &&put_u32h, /* u32h -> s32h */
+ &&put_s32s, /* s32s -> s32h */
+ &&put_u32s, /* u32s -> s32h */
+};
+#endif
+
+#ifdef PUT_END
+put_s8: as_s8(dst) = sample; goto PUT_END;
+put_u8: as_u8(dst) = sample ^ 0x80; goto PUT_END;
+put_s16h: as_s16(dst) = sample; goto PUT_END;
+put_u16h: as_u16(dst) = sample ^ 0x8000; goto PUT_END;
+put_s16s: as_s16(dst) = swab16(sample); goto PUT_END;
+put_u16s: as_u16(dst) = swab16(sample ^ 0x80); goto PUT_END;
+put_s24h: as_s24(dst) = sample & 0xffffff; goto PUT_END;
+put_u24h: as_u24(dst) = sample ^ 0x80000000; goto PUT_END;
+put_s24s: as_s24(dst) = swab32(sample & 0xffffff); goto PUT_END;
+put_u24s: as_u24(dst) = swab32(sample ^ 0x80); goto PUT_END;
+put_s32h: as_s32(dst) = sample; goto PUT_END;
+put_u32h: as_u32(dst) = sample ^ 0x80000000; goto PUT_END;
+put_s32s: as_s32(dst) = swab32(sample); goto PUT_END;
+put_u32s: as_u32(dst) = swab32(sample ^ 0x80); goto PUT_END;
+#endif
+#endif
+
+#undef as_u8
+#undef as_u16
+#undef as_u32
+#undef as_s8
+#undef as_s16
+#undef as_s32
diff --git a/sound/core/oss/rate.c b/sound/core/oss/rate.c
new file mode 100644
index 00000000000..1096ec18671
--- /dev/null
+++ b/sound/core/oss/rate.c
@@ -0,0 +1,378 @@
+/*
+ * Rate conversion Plug-In
+ * Copyright (c) 1999 by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/time.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include "pcm_plugin.h"
+
+#define SHIFT 11
+#define BITS (1<<SHIFT)
+#define R_MASK (BITS-1)
+
+/*
+ * Basic rate conversion plugin
+ */
+
+typedef struct {
+ signed short last_S1;
+ signed short last_S2;
+} rate_channel_t;
+
+typedef void (*rate_f)(snd_pcm_plugin_t *plugin,
+ const snd_pcm_plugin_channel_t *src_channels,
+ snd_pcm_plugin_channel_t *dst_channels,
+ int src_frames, int dst_frames);
+
+typedef struct rate_private_data {
+ unsigned int pitch;
+ unsigned int pos;
+ rate_f func;
+ int get, put;
+ snd_pcm_sframes_t old_src_frames, old_dst_frames;
+ rate_channel_t channels[0];
+} rate_t;
+
+static void rate_init(snd_pcm_plugin_t *plugin)
+{
+ unsigned int channel;
+ rate_t *data = (rate_t *)plugin->extra_data;
+ data->pos = 0;
+ for (channel = 0; channel < plugin->src_format.channels; channel++) {
+ data->channels[channel].last_S1 = 0;
+ data->channels[channel].last_S2 = 0;
+ }
+}
+
+static void resample_expand(snd_pcm_plugin_t *plugin,
+ const snd_pcm_plugin_channel_t *src_channels,
+ snd_pcm_plugin_channel_t *dst_channels,
+ int src_frames, int dst_frames)
+{
+ unsigned int pos = 0;
+ signed int val;
+ signed short S1, S2;
+ char *src, *dst;
+ unsigned int channel;
+ int src_step, dst_step;
+ int src_frames1, dst_frames1;
+ rate_t *data = (rate_t *)plugin->extra_data;
+ rate_channel_t *rchannels = data->channels;
+
+#define GET_S16_LABELS
+#define PUT_S16_LABELS
+#include "plugin_ops.h"
+#undef GET_S16_LABELS
+#undef PUT_S16_LABELS
+ void *get = get_s16_labels[data->get];
+ void *put = put_s16_labels[data->put];
+ signed short sample = 0;
+
+ for (channel = 0; channel < plugin->src_format.channels; channel++) {
+ pos = data->pos;
+ S1 = rchannels->last_S1;
+ S2 = rchannels->last_S2;
+ if (!src_channels[channel].enabled) {
+ if (dst_channels[channel].wanted)
+ snd_pcm_area_silence(&dst_channels[channel].area, 0, dst_frames, plugin->dst_format.format);
+ dst_channels[channel].enabled = 0;
+ continue;
+ }
+ dst_channels[channel].enabled = 1;
+ src = (char *)src_channels[channel].area.addr + src_channels[channel].area.first / 8;
+ dst = (char *)dst_channels[channel].area.addr + dst_channels[channel].area.first / 8;
+ src_step = src_channels[channel].area.step / 8;
+ dst_step = dst_channels[channel].area.step / 8;
+ src_frames1 = src_frames;
+ dst_frames1 = dst_frames;
+ while (dst_frames1-- > 0) {
+ if (pos & ~R_MASK) {
+ pos &= R_MASK;
+ S1 = S2;
+ if (src_frames1-- > 0) {
+ goto *get;
+#define GET_S16_END after_get
+#include "plugin_ops.h"
+#undef GET_S16_END
+ after_get:
+ S2 = sample;
+ src += src_step;
+ }
+ }
+ val = S1 + ((S2 - S1) * (signed int)pos) / BITS;
+ if (val < -32768)
+ val = -32768;
+ else if (val > 32767)
+ val = 32767;
+ sample = val;
+ goto *put;
+#define PUT_S16_END after_put
+#include "plugin_ops.h"
+#undef PUT_S16_END
+ after_put:
+ dst += dst_step;
+ pos += data->pitch;
+ }
+ rchannels->last_S1 = S1;
+ rchannels->last_S2 = S2;
+ rchannels++;
+ }
+ data->pos = pos;
+}
+
+static void resample_shrink(snd_pcm_plugin_t *plugin,
+ const snd_pcm_plugin_channel_t *src_channels,
+ snd_pcm_plugin_channel_t *dst_channels,
+ int src_frames, int dst_frames)
+{
+ unsigned int pos = 0;
+ signed int val;
+ signed short S1, S2;
+ char *src, *dst;
+ unsigned int channel;
+ int src_step, dst_step;
+ int src_frames1, dst_frames1;
+ rate_t *data = (rate_t *)plugin->extra_data;
+ rate_channel_t *rchannels = data->channels;
+
+#define GET_S16_LABELS
+#define PUT_S16_LABELS
+#include "plugin_ops.h"
+#undef GET_S16_LABELS
+#undef PUT_S16_LABELS
+ void *get = get_s16_labels[data->get];
+ void *put = put_s16_labels[data->put];
+ signed short sample = 0;
+
+ for (channel = 0; channel < plugin->src_format.channels; ++channel) {
+ pos = data->pos;
+ S1 = rchannels->last_S1;
+ S2 = rchannels->last_S2;
+ if (!src_channels[channel].enabled) {
+ if (dst_channels[channel].wanted)
+ snd_pcm_area_silence(&dst_channels[channel].area, 0, dst_frames, plugin->dst_format.format);
+ dst_channels[channel].enabled = 0;
+ continue;
+ }
+ dst_channels[channel].enabled = 1;
+ src = (char *)src_channels[channel].area.addr + src_channels[channel].area.first / 8;
+ dst = (char *)dst_channels[channel].area.addr + dst_channels[channel].area.first / 8;
+ src_step = src_channels[channel].area.step / 8;
+ dst_step = dst_channels[channel].area.step / 8;
+ src_frames1 = src_frames;
+ dst_frames1 = dst_frames;
+ while (dst_frames1 > 0) {
+ S1 = S2;
+ if (src_frames1-- > 0) {
+ goto *get;
+#define GET_S16_END after_get
+#include "plugin_ops.h"
+#undef GET_S16_END
+ after_get:
+ S2 = sample;
+ src += src_step;
+ }
+ if (pos & ~R_MASK) {
+ pos &= R_MASK;
+ val = S1 + ((S2 - S1) * (signed int)pos) / BITS;
+ if (val < -32768)
+ val = -32768;
+ else if (val > 32767)
+ val = 32767;
+ sample = val;
+ goto *put;
+#define PUT_S16_END after_put
+#include "plugin_ops.h"
+#undef PUT_S16_END
+ after_put:
+ dst += dst_step;
+ dst_frames1--;
+ }
+ pos += data->pitch;
+ }
+ rchannels->last_S1 = S1;
+ rchannels->last_S2 = S2;
+ rchannels++;
+ }
+ data->pos = pos;
+}
+
+static snd_pcm_sframes_t rate_src_frames(snd_pcm_plugin_t *plugin, snd_pcm_uframes_t frames)
+{
+ rate_t *data;
+ snd_pcm_sframes_t res;
+
+ snd_assert(plugin != NULL, return -ENXIO);
+ if (frames == 0)
+ return 0;
+ data = (rate_t *)plugin->extra_data;
+ if (plugin->src_format.rate < plugin->dst_format.rate) {
+ res = (((frames * data->pitch) + (BITS/2)) >> SHIFT);
+ } else {
+ res = (((frames << SHIFT) + (data->pitch / 2)) / data->pitch);
+ }
+ if (data->old_src_frames > 0) {
+ snd_pcm_sframes_t frames1 = frames, res1 = data->old_dst_frames;
+ while (data->old_src_frames < frames1) {
+ frames1 >>= 1;
+ res1 <<= 1;
+ }
+ while (data->old_src_frames > frames1) {
+ frames1 <<= 1;
+ res1 >>= 1;
+ }
+ if (data->old_src_frames == frames1)
+ return res1;
+ }
+ data->old_src_frames = frames;
+ data->old_dst_frames = res;
+ return res;
+}
+
+static snd_pcm_sframes_t rate_dst_frames(snd_pcm_plugin_t *plugin, snd_pcm_uframes_t frames)
+{
+ rate_t *data;
+ snd_pcm_sframes_t res;
+
+ snd_assert(plugin != NULL, return -ENXIO);
+ if (frames == 0)
+ return 0;
+ data = (rate_t *)plugin->extra_data;
+ if (plugin->src_format.rate < plugin->dst_format.rate) {
+ res = (((frames << SHIFT) + (data->pitch / 2)) / data->pitch);
+ } else {
+ res = (((frames * data->pitch) + (BITS/2)) >> SHIFT);
+ }
+ if (data->old_dst_frames > 0) {
+ snd_pcm_sframes_t frames1 = frames, res1 = data->old_src_frames;
+ while (data->old_dst_frames < frames1) {
+ frames1 >>= 1;
+ res1 <<= 1;
+ }
+ while (data->old_dst_frames > frames1) {
+ frames1 <<= 1;
+ res1 >>= 1;
+ }
+ if (data->old_dst_frames == frames1)
+ return res1;
+ }
+ data->old_dst_frames = frames;
+ data->old_src_frames = res;
+ return res;
+}
+
+static snd_pcm_sframes_t rate_transfer(snd_pcm_plugin_t *plugin,
+ const snd_pcm_plugin_channel_t *src_channels,
+ snd_pcm_plugin_channel_t *dst_channels,
+ snd_pcm_uframes_t frames)
+{
+ snd_pcm_uframes_t dst_frames;
+ rate_t *data;
+
+ snd_assert(plugin != NULL && src_channels != NULL && dst_channels != NULL, return -ENXIO);
+ if (frames == 0)
+ return 0;
+#ifdef CONFIG_SND_DEBUG
+ {
+ unsigned int channel;
+ for (channel = 0; channel < plugin->src_format.channels; channel++) {
+ snd_assert(src_channels[channel].area.first % 8 == 0 &&
+ src_channels[channel].area.step % 8 == 0,
+ return -ENXIO);
+ snd_assert(dst_channels[channel].area.first % 8 == 0 &&
+ dst_channels[channel].area.step % 8 == 0,
+ return -ENXIO);
+ }
+ }
+#endif
+
+ dst_frames = rate_dst_frames(plugin, frames);
+ if (dst_frames > dst_channels[0].frames)
+ dst_frames = dst_channels[0].frames;
+ data = (rate_t *)plugin->extra_data;
+ data->func(plugin, src_channels, dst_channels, frames, dst_frames);
+ return dst_frames;
+}
+
+static int rate_action(snd_pcm_plugin_t *plugin,
+ snd_pcm_plugin_action_t action,
+ unsigned long udata ATTRIBUTE_UNUSED)
+{
+ snd_assert(plugin != NULL, return -ENXIO);
+ switch (action) {
+ case INIT:
+ case PREPARE:
+ rate_init(plugin);
+ break;
+ default:
+ break;
+ }
+ return 0; /* silenty ignore other actions */
+}
+
+int snd_pcm_plugin_build_rate(snd_pcm_plug_t *plug,
+ snd_pcm_plugin_format_t *src_format,
+ snd_pcm_plugin_format_t *dst_format,
+ snd_pcm_plugin_t **r_plugin)
+{
+ int err;
+ rate_t *data;
+ snd_pcm_plugin_t *plugin;
+
+ snd_assert(r_plugin != NULL, return -ENXIO);
+ *r_plugin = NULL;
+
+ snd_assert(src_format->channels == dst_format->channels, return -ENXIO);
+ snd_assert(src_format->channels > 0, return -ENXIO);
+ snd_assert(snd_pcm_format_linear(src_format->format) != 0, return -ENXIO);
+ snd_assert(snd_pcm_format_linear(dst_format->format) != 0, return -ENXIO);
+ snd_assert(src_format->rate != dst_format->rate, return -ENXIO);
+
+ err = snd_pcm_plugin_build(plug, "rate conversion",
+ src_format, dst_format,
+ sizeof(rate_t) + src_format->channels * sizeof(rate_channel_t),
+ &plugin);
+ if (err < 0)
+ return err;
+ data = (rate_t *)plugin->extra_data;
+ data->get = getput_index(src_format->format);
+ snd_assert(data->get >= 0 && data->get < 4*2*2, return -EINVAL);
+ data->put = getput_index(dst_format->format);
+ snd_assert(data->put >= 0 && data->put < 4*2*2, return -EINVAL);
+
+ if (src_format->rate < dst_format->rate) {
+ data->pitch = ((src_format->rate << SHIFT) + (dst_format->rate >> 1)) / dst_format->rate;
+ data->func = resample_expand;
+ } else {
+ data->pitch = ((dst_format->rate << SHIFT) + (src_format->rate >> 1)) / src_format->rate;
+ data->func = resample_shrink;
+ }
+ data->pos = 0;
+ rate_init(plugin);
+ data->old_src_frames = data->old_dst_frames = 0;
+ plugin->transfer = rate_transfer;
+ plugin->src_frames = rate_src_frames;
+ plugin->dst_frames = rate_dst_frames;
+ plugin->action = rate_action;
+ *r_plugin = plugin;
+ return 0;
+}
diff --git a/sound/core/oss/route.c b/sound/core/oss/route.c
new file mode 100644
index 00000000000..c955b7dfdb3
--- /dev/null
+++ b/sound/core/oss/route.c
@@ -0,0 +1,519 @@
+/*
+ * Attenuated route Plug-In
+ * Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org>
+ *
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/slab.h>
+#include <linux/time.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include "pcm_plugin.h"
+
+/* The best possible hack to support missing optimization in gcc 2.7.2.3 */
+#if ROUTE_PLUGIN_RESOLUTION & (ROUTE_PLUGIN_RESOLUTION - 1) != 0
+#define div(a) a /= ROUTE_PLUGIN_RESOLUTION
+#elif ROUTE_PLUGIN_RESOLUTION == 16
+#define div(a) a >>= 4
+#else
+#error "Add some code here"
+#endif
+
+typedef struct ttable_dst ttable_dst_t;
+typedef struct route_private_data route_t;
+
+typedef void (*route_channel_f)(snd_pcm_plugin_t *plugin,
+ const snd_pcm_plugin_channel_t *src_channels,
+ snd_pcm_plugin_channel_t *dst_channel,
+ ttable_dst_t* ttable, snd_pcm_uframes_t frames);
+
+typedef struct {
+ int channel;
+ int as_int;
+} ttable_src_t;
+
+struct ttable_dst {
+ int att; /* Attenuated */
+ unsigned int nsrcs;
+ ttable_src_t* srcs;
+ route_channel_f func;
+};
+
+struct route_private_data {
+ enum {R_UINT32=0, R_UINT64=1} sum_type;
+ int get, put;
+ int conv;
+ int src_sample_size;
+ ttable_dst_t ttable[0];
+};
+
+typedef union {
+ u_int32_t as_uint32;
+ u_int64_t as_uint64;
+} sum_t;
+
+
+static void route_to_channel_from_zero(snd_pcm_plugin_t *plugin,
+ const snd_pcm_plugin_channel_t *src_channels ATTRIBUTE_UNUSED,
+ snd_pcm_plugin_channel_t *dst_channel,
+ ttable_dst_t* ttable ATTRIBUTE_UNUSED, snd_pcm_uframes_t frames)
+{
+ if (dst_channel->wanted)
+ snd_pcm_area_silence(&dst_channel->area, 0, frames, plugin->dst_format.format);
+ dst_channel->enabled = 0;
+}
+
+static void route_to_channel_from_one(snd_pcm_plugin_t *plugin,
+ const snd_pcm_plugin_channel_t *src_channels,
+ snd_pcm_plugin_channel_t *dst_channel,
+ ttable_dst_t* ttable, snd_pcm_uframes_t frames)
+{
+#define CONV_LABELS
+#include "plugin_ops.h"
+#undef CONV_LABELS
+ route_t *data = (route_t *)plugin->extra_data;
+ void *conv;
+ const snd_pcm_plugin_channel_t *src_channel = NULL;
+ unsigned int srcidx;
+ char *src, *dst;
+ int src_step, dst_step;
+ for (srcidx = 0; srcidx < ttable->nsrcs; ++srcidx) {
+ src_channel = &src_channels[ttable->srcs[srcidx].channel];
+ if (src_channel->area.addr != NULL)
+ break;
+ }
+ if (srcidx == ttable->nsrcs) {
+ route_to_channel_from_zero(plugin, src_channels, dst_channel, ttable, frames);
+ return;
+ }
+
+ dst_channel->enabled = 1;
+ conv = conv_labels[data->conv];
+ src = src_channel->area.addr + src_channel->area.first / 8;
+ src_step = src_channel->area.step / 8;
+ dst = dst_channel->area.addr + dst_channel->area.first / 8;
+ dst_step = dst_channel->area.step / 8;
+ while (frames-- > 0) {
+ goto *conv;
+#define CONV_END after
+#include "plugin_ops.h"
+#undef CONV_END
+ after:
+ src += src_step;
+ dst += dst_step;
+ }
+}
+
+static void route_to_channel(snd_pcm_plugin_t *plugin,
+ const snd_pcm_plugin_channel_t *src_channels,
+ snd_pcm_plugin_channel_t *dst_channel,
+ ttable_dst_t* ttable, snd_pcm_uframes_t frames)
+{
+#define GET_U_LABELS
+#define PUT_U32_LABELS
+#include "plugin_ops.h"
+#undef GET_U_LABELS
+#undef PUT_U32_LABELS
+ static void *zero_labels[2] = { &&zero_int32, &&zero_int64 };
+ /* sum_type att */
+ static void *add_labels[2 * 2] = { &&add_int32_noatt, &&add_int32_att,
+ &&add_int64_noatt, &&add_int64_att,
+ };
+ /* sum_type att shift */
+ static void *norm_labels[2 * 2 * 4] = { NULL,
+ &&norm_int32_8_noatt,
+ &&norm_int32_16_noatt,
+ &&norm_int32_24_noatt,
+ NULL,
+ &&norm_int32_8_att,
+ &&norm_int32_16_att,
+ &&norm_int32_24_att,
+ &&norm_int64_0_noatt,
+ &&norm_int64_8_noatt,
+ &&norm_int64_16_noatt,
+ &&norm_int64_24_noatt,
+ &&norm_int64_0_att,
+ &&norm_int64_8_att,
+ &&norm_int64_16_att,
+ &&norm_int64_24_att,
+ };
+ route_t *data = (route_t *)plugin->extra_data;
+ void *zero, *get, *add, *norm, *put_u32;
+ int nsrcs = ttable->nsrcs;
+ char *dst;
+ int dst_step;
+ char *srcs[nsrcs];
+ int src_steps[nsrcs];
+ ttable_src_t src_tt[nsrcs];
+ u_int32_t sample = 0;
+ int srcidx, srcidx1 = 0;
+ for (srcidx = 0; srcidx < nsrcs; ++srcidx) {
+ const snd_pcm_plugin_channel_t *src_channel = &src_channels[ttable->srcs[srcidx].channel];
+ if (!src_channel->enabled)
+ continue;
+ srcs[srcidx1] = src_channel->area.addr + src_channel->area.first / 8;
+ src_steps[srcidx1] = src_channel->area.step / 8;
+ src_tt[srcidx1] = ttable->srcs[srcidx];
+ srcidx1++;
+ }
+ nsrcs = srcidx1;
+ if (nsrcs == 0) {
+ route_to_channel_from_zero(plugin, src_channels, dst_channel, ttable, frames);
+ return;
+ } else if (nsrcs == 1 && src_tt[0].as_int == ROUTE_PLUGIN_RESOLUTION) {
+ route_to_channel_from_one(plugin, src_channels, dst_channel, ttable, frames);
+ return;
+ }
+
+ dst_channel->enabled = 1;
+ zero = zero_labels[data->sum_type];
+ get = get_u_labels[data->get];
+ add = add_labels[data->sum_type * 2 + ttable->att];
+ norm = norm_labels[data->sum_type * 8 + ttable->att * 4 + 4 - data->src_sample_size];
+ put_u32 = put_u32_labels[data->put];
+ dst = dst_channel->area.addr + dst_channel->area.first / 8;
+ dst_step = dst_channel->area.step / 8;
+
+ while (frames-- > 0) {
+ ttable_src_t *ttp = src_tt;
+ sum_t sum;
+
+ /* Zero sum */
+ goto *zero;
+ zero_int32:
+ sum.as_uint32 = 0;
+ goto zero_end;
+ zero_int64:
+ sum.as_uint64 = 0;
+ goto zero_end;
+ zero_end:
+ for (srcidx = 0; srcidx < nsrcs; ++srcidx) {
+ char *src = srcs[srcidx];
+
+ /* Get sample */
+ goto *get;
+#define GET_U_END after_get
+#include "plugin_ops.h"
+#undef GET_U_END
+ after_get:
+
+ /* Sum */
+ goto *add;
+ add_int32_att:
+ sum.as_uint32 += sample * ttp->as_int;
+ goto after_sum;
+ add_int32_noatt:
+ if (ttp->as_int)
+ sum.as_uint32 += sample;
+ goto after_sum;
+ add_int64_att:
+ sum.as_uint64 += (u_int64_t) sample * ttp->as_int;
+ goto after_sum;
+ add_int64_noatt:
+ if (ttp->as_int)
+ sum.as_uint64 += sample;
+ goto after_sum;
+ after_sum:
+ srcs[srcidx] += src_steps[srcidx];
+ ttp++;
+ }
+
+ /* Normalization */
+ goto *norm;
+ norm_int32_8_att:
+ sum.as_uint64 = sum.as_uint32;
+ norm_int64_8_att:
+ sum.as_uint64 <<= 8;
+ norm_int64_0_att:
+ div(sum.as_uint64);
+ goto norm_int;
+
+ norm_int32_16_att:
+ sum.as_uint64 = sum.as_uint32;
+ norm_int64_16_att:
+ sum.as_uint64 <<= 16;
+ div(sum.as_uint64);
+ goto norm_int;
+
+ norm_int32_24_att:
+ sum.as_uint64 = sum.as_uint32;
+ norm_int64_24_att:
+ sum.as_uint64 <<= 24;
+ div(sum.as_uint64);
+ goto norm_int;
+
+ norm_int32_8_noatt:
+ sum.as_uint64 = sum.as_uint32;
+ norm_int64_8_noatt:
+ sum.as_uint64 <<= 8;
+ goto norm_int;
+
+ norm_int32_16_noatt:
+ sum.as_uint64 = sum.as_uint32;
+ norm_int64_16_noatt:
+ sum.as_uint64 <<= 16;
+ goto norm_int;
+
+ norm_int32_24_noatt:
+ sum.as_uint64 = sum.as_uint32;
+ norm_int64_24_noatt:
+ sum.as_uint64 <<= 24;
+ goto norm_int;
+
+ norm_int64_0_noatt:
+ norm_int:
+ if (sum.as_uint64 > (u_int32_t)0xffffffff)
+ sample = (u_int32_t)0xffffffff;
+ else
+ sample = sum.as_uint64;
+ goto after_norm;
+
+ after_norm:
+
+ /* Put sample */
+ goto *put_u32;
+#define PUT_U32_END after_put_u32
+#include "plugin_ops.h"
+#undef PUT_U32_END
+ after_put_u32:
+
+ dst += dst_step;
+ }
+}
+
+static int route_src_channels_mask(snd_pcm_plugin_t *plugin,
+ bitset_t *dst_vmask,
+ bitset_t **src_vmask)
+{
+ route_t *data = (route_t *)plugin->extra_data;
+ int schannels = plugin->src_format.channels;
+ int dchannels = plugin->dst_format.channels;
+ bitset_t *vmask = plugin->src_vmask;
+ int channel;
+ ttable_dst_t *dp = data->ttable;
+ bitset_zero(vmask, schannels);
+ for (channel = 0; channel < dchannels; channel++, dp++) {
+ unsigned int src;
+ ttable_src_t *sp;
+ if (!bitset_get(dst_vmask, channel))
+ continue;
+ sp = dp->srcs;
+ for (src = 0; src < dp->nsrcs; src++, sp++)
+ bitset_set(vmask, sp->channel);
+ }
+ *src_vmask = vmask;
+ return 0;
+}
+
+static int route_dst_channels_mask(snd_pcm_plugin_t *plugin,
+ bitset_t *src_vmask,
+ bitset_t **dst_vmask)
+{
+ route_t *data = (route_t *)plugin->extra_data;
+ int dchannels = plugin->dst_format.channels;
+ bitset_t *vmask = plugin->dst_vmask;
+ int channel;
+ ttable_dst_t *dp = data->ttable;
+ bitset_zero(vmask, dchannels);
+ for (channel = 0; channel < dchannels; channel++, dp++) {
+ unsigned int src;
+ ttable_src_t *sp;
+ sp = dp->srcs;
+ for (src = 0; src < dp->nsrcs; src++, sp++) {
+ if (bitset_get(src_vmask, sp->channel)) {
+ bitset_set(vmask, channel);
+ break;
+ }
+ }
+ }
+ *dst_vmask = vmask;
+ return 0;
+}
+
+static void route_free(snd_pcm_plugin_t *plugin)
+{
+ route_t *data = (route_t *)plugin->extra_data;
+ unsigned int dst_channel;
+ for (dst_channel = 0; dst_channel < plugin->dst_format.channels; ++dst_channel) {
+ kfree(data->ttable[dst_channel].srcs);
+ }
+}
+
+static int route_load_ttable(snd_pcm_plugin_t *plugin,
+ const route_ttable_entry_t* src_ttable)
+{
+ route_t *data;
+ unsigned int src_channel, dst_channel;
+ const route_ttable_entry_t *sptr;
+ ttable_dst_t *dptr;
+ if (src_ttable == NULL)
+ return 0;
+ data = (route_t *)plugin->extra_data;
+ dptr = data->ttable;
+ sptr = src_ttable;
+ plugin->private_free = route_free;
+ for (dst_channel = 0; dst_channel < plugin->dst_format.channels; ++dst_channel) {
+ route_ttable_entry_t t = 0;
+ int att = 0;
+ int nsrcs = 0;
+ ttable_src_t srcs[plugin->src_format.channels];
+ for (src_channel = 0; src_channel < plugin->src_format.channels; ++src_channel) {
+ snd_assert(*sptr >= 0 || *sptr <= FULL, return -ENXIO);
+ if (*sptr != 0) {
+ srcs[nsrcs].channel = src_channel;
+ srcs[nsrcs].as_int = *sptr;
+ if (*sptr != FULL)
+ att = 1;
+ t += *sptr;
+ nsrcs++;
+ }
+ sptr++;
+ }
+ dptr->att = att;
+ dptr->nsrcs = nsrcs;
+ if (nsrcs == 0)
+ dptr->func = route_to_channel_from_zero;
+ else if (nsrcs == 1 && !att)
+ dptr->func = route_to_channel_from_one;
+ else
+ dptr->func = route_to_channel;
+ if (nsrcs > 0) {
+ int srcidx;
+ dptr->srcs = kcalloc(nsrcs, sizeof(*srcs), GFP_KERNEL);
+ for(srcidx = 0; srcidx < nsrcs; srcidx++)
+ dptr->srcs[srcidx] = srcs[srcidx];
+ } else
+ dptr->srcs = NULL;
+ dptr++;
+ }
+ return 0;
+}
+
+static snd_pcm_sframes_t route_transfer(snd_pcm_plugin_t *plugin,
+ const snd_pcm_plugin_channel_t *src_channels,
+ snd_pcm_plugin_channel_t *dst_channels,
+ snd_pcm_uframes_t frames)
+{
+ route_t *data;
+ int src_nchannels, dst_nchannels;
+ int dst_channel;
+ ttable_dst_t *ttp;
+ snd_pcm_plugin_channel_t *dvp;
+
+ snd_assert(plugin != NULL && src_channels != NULL && dst_channels != NULL, return -ENXIO);
+ if (frames == 0)
+ return 0;
+ data = (route_t *)plugin->extra_data;
+
+ src_nchannels = plugin->src_format.channels;
+ dst_nchannels = plugin->dst_format.channels;
+
+#ifdef CONFIG_SND_DEBUG
+ {
+ int src_channel;
+ for (src_channel = 0; src_channel < src_nchannels; ++src_channel) {
+ snd_assert(src_channels[src_channel].area.first % 8 == 0 ||
+ src_channels[src_channel].area.step % 8 == 0,
+ return -ENXIO);
+ }
+ for (dst_channel = 0; dst_channel < dst_nchannels; ++dst_channel) {
+ snd_assert(dst_channels[dst_channel].area.first % 8 == 0 ||
+ dst_channels[dst_channel].area.step % 8 == 0,
+ return -ENXIO);
+ }
+ }
+#endif
+
+ ttp = data->ttable;
+ dvp = dst_channels;
+ for (dst_channel = 0; dst_channel < dst_nchannels; ++dst_channel) {
+ ttp->func(plugin, src_channels, dvp, ttp, frames);
+ dvp++;
+ ttp++;
+ }
+ return frames;
+}
+
+int getput_index(int format)
+{
+ int sign, width, endian;
+ sign = !snd_pcm_format_signed(format);
+ width = snd_pcm_format_width(format) / 8 - 1;
+ if (width < 0 || width > 3) {
+ snd_printk(KERN_ERR "snd-pcm-oss: invalid format %d\n", format);
+ width = 0;
+ }
+#ifdef SNDRV_LITTLE_ENDIAN
+ endian = snd_pcm_format_big_endian(format);
+#else
+ endian = snd_pcm_format_little_endian(format);
+#endif
+ if (endian < 0)
+ endian = 0;
+ return width * 4 + endian * 2 + sign;
+}
+
+int snd_pcm_plugin_build_route(snd_pcm_plug_t *plug,
+ snd_pcm_plugin_format_t *src_format,
+ snd_pcm_plugin_format_t *dst_format,
+ route_ttable_entry_t *ttable,
+ snd_pcm_plugin_t **r_plugin)
+{
+ route_t *data;
+ snd_pcm_plugin_t *plugin;
+ int err;
+
+ snd_assert(r_plugin != NULL, return -ENXIO);
+ *r_plugin = NULL;
+ snd_assert(src_format->rate == dst_format->rate, return -ENXIO);
+ snd_assert(snd_pcm_format_linear(src_format->format) != 0 &&
+ snd_pcm_format_linear(dst_format->format) != 0,
+ return -ENXIO);
+
+ err = snd_pcm_plugin_build(plug, "attenuated route conversion",
+ src_format, dst_format,
+ sizeof(route_t) + sizeof(data->ttable[0]) * dst_format->channels,
+ &plugin);
+ if (err < 0)
+ return err;
+
+ data = (route_t *) plugin->extra_data;
+
+ data->get = getput_index(src_format->format);
+ snd_assert(data->get >= 0 && data->get < 4*2*2, return -EINVAL);
+ data->put = getput_index(dst_format->format);
+ snd_assert(data->get >= 0 && data->get < 4*2*2, return -EINVAL);
+ data->conv = conv_index(src_format->format, dst_format->format);
+
+ if (snd_pcm_format_width(src_format->format) == 32)
+ data->sum_type = R_UINT64;
+ else
+ data->sum_type = R_UINT32;
+ data->src_sample_size = snd_pcm_format_width(src_format->format) / 8;
+
+ if ((err = route_load_ttable(plugin, ttable)) < 0) {
+ snd_pcm_plugin_free(plugin);
+ return err;
+ }
+ plugin->transfer = route_transfer;
+ plugin->src_channels_mask = route_src_channels_mask;
+ plugin->dst_channels_mask = route_dst_channels_mask;
+ *r_plugin = plugin;
+ return 0;
+}