Linux-2.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!
diff --git a/sound/core/oss/Makefile b/sound/core/oss/Makefile
new file mode 100644
index 0000000..e6d5a04
--- /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 0000000..edecbe7
--- /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 0000000..bb1c99a
--- /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 0000000..12ed27a
--- /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 0000000..98ed9a9
--- /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 0000000..44ec4c6
--- /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 0000000..1a80502
--- /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 0000000..6bb3100
--- /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 0000000..0f86ce4
--- /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 0000000..0607e95
--- /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 0000000..1096ec1
--- /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 0000000..c955b7d
--- /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;
+}