aboutsummaryrefslogtreecommitdiffstats
path: root/sound/core/oss
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 18:20:36 -0400
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 18:20:36 -0400
commit1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch)
tree0bba044c4ce775e45a88a51686b5d9f90697ea9d /sound/core/oss
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history, even though we have it. We can create a separate "historical" git archive of that later if we want to, and in the meantime it's about 3.2GB when imported into git - space that would just make the early git days unnecessarily complicated, when we don't have a lot of good infrastructure for it. Let it rip!
Diffstat (limited to 'sound/core/oss')
-rw-r--r--sound/core/oss/Makefile12
-rw-r--r--sound/core/oss/copy.c87
-rw-r--r--sound/core/oss/io.c134
-rw-r--r--sound/core/oss/linear.c158
-rw-r--r--sound/core/oss/mixer_oss.c1340
-rw-r--r--sound/core/oss/mulaw.c308
-rw-r--r--sound/core/oss/pcm_oss.c2530
-rw-r--r--sound/core/oss/pcm_plugin.c921
-rw-r--r--sound/core/oss/pcm_plugin.h250
-rw-r--r--sound/core/oss/plugin_ops.h536
-rw-r--r--sound/core/oss/rate.c378
-rw-r--r--sound/core/oss/route.c519
12 files changed, 7173 insertions, 0 deletions
diff --git a/sound/core/oss/Makefile b/sound/core/oss/Makefile
new file mode 100644
index 000000000000..e6d5a045ba27
--- /dev/null
+++ b/sound/core/oss/Makefile
@@ -0,0 +1,12 @@
1#
2# Makefile for ALSA
3# Copyright (c) 1999 by Jaroslav Kysela <perex@suse.cz>
4#
5
6snd-mixer-oss-objs := mixer_oss.o
7
8snd-pcm-oss-objs := pcm_oss.o pcm_plugin.o \
9 io.o copy.o linear.o mulaw.o route.o rate.o
10
11obj-$(CONFIG_SND_MIXER_OSS) += snd-mixer-oss.o
12obj-$(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 000000000000..edecbe7417bd
--- /dev/null
+++ b/sound/core/oss/copy.c
@@ -0,0 +1,87 @@
1/*
2 * Linear conversion Plug-In
3 * Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org>
4 *
5 *
6 * This library is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU Library General Public License as
8 * published by the Free Software Foundation; either version 2 of
9 * the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Library General Public License for more details.
15 *
16 * You should have received a copy of the GNU Library General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 *
20 */
21
22#include <sound/driver.h>
23#include <linux/time.h>
24#include <sound/core.h>
25#include <sound/pcm.h>
26#include "pcm_plugin.h"
27
28static snd_pcm_sframes_t copy_transfer(snd_pcm_plugin_t *plugin,
29 const snd_pcm_plugin_channel_t *src_channels,
30 snd_pcm_plugin_channel_t *dst_channels,
31 snd_pcm_uframes_t frames)
32{
33 unsigned int channel;
34 unsigned int nchannels;
35
36 snd_assert(plugin != NULL && src_channels != NULL && dst_channels != NULL, return -ENXIO);
37 if (frames == 0)
38 return 0;
39 nchannels = plugin->src_format.channels;
40 for (channel = 0; channel < nchannels; channel++) {
41 snd_assert(src_channels->area.first % 8 == 0 &&
42 src_channels->area.step % 8 == 0,
43 return -ENXIO);
44 snd_assert(dst_channels->area.first % 8 == 0 &&
45 dst_channels->area.step % 8 == 0,
46 return -ENXIO);
47 if (!src_channels->enabled) {
48 if (dst_channels->wanted)
49 snd_pcm_area_silence(&dst_channels->area, 0, frames, plugin->dst_format.format);
50 dst_channels->enabled = 0;
51 continue;
52 }
53 dst_channels->enabled = 1;
54 snd_pcm_area_copy(&src_channels->area, 0, &dst_channels->area, 0, frames, plugin->src_format.format);
55 src_channels++;
56 dst_channels++;
57 }
58 return frames;
59}
60
61int snd_pcm_plugin_build_copy(snd_pcm_plug_t *plug,
62 snd_pcm_plugin_format_t *src_format,
63 snd_pcm_plugin_format_t *dst_format,
64 snd_pcm_plugin_t **r_plugin)
65{
66 int err;
67 snd_pcm_plugin_t *plugin;
68 int width;
69
70 snd_assert(r_plugin != NULL, return -ENXIO);
71 *r_plugin = NULL;
72
73 snd_assert(src_format->format == dst_format->format, return -ENXIO);
74 snd_assert(src_format->rate == dst_format->rate, return -ENXIO);
75 snd_assert(src_format->channels == dst_format->channels, return -ENXIO);
76
77 width = snd_pcm_format_physical_width(src_format->format);
78 snd_assert(width > 0, return -ENXIO);
79
80 err = snd_pcm_plugin_build(plug, "copy", src_format, dst_format,
81 0, &plugin);
82 if (err < 0)
83 return err;
84 plugin->transfer = copy_transfer;
85 *r_plugin = plugin;
86 return 0;
87}
diff --git a/sound/core/oss/io.c b/sound/core/oss/io.c
new file mode 100644
index 000000000000..bb1c99a5b734
--- /dev/null
+++ b/sound/core/oss/io.c
@@ -0,0 +1,134 @@
1/*
2 * PCM I/O Plug-In Interface
3 * Copyright (c) 1999 by Jaroslav Kysela <perex@suse.cz>
4 *
5 *
6 * This library is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU Library General Public License as
8 * published by the Free Software Foundation; either version 2 of
9 * the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Library General Public License for more details.
15 *
16 * You should have received a copy of the GNU Library General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 *
20 */
21
22#include <sound/driver.h>
23#include <linux/time.h>
24#include <sound/core.h>
25#include <sound/pcm.h>
26#include <sound/pcm_params.h>
27#include "pcm_plugin.h"
28
29#define pcm_write(plug,buf,count) snd_pcm_oss_write3(plug,buf,count,1)
30#define pcm_writev(plug,vec,count) snd_pcm_oss_writev3(plug,vec,count,1)
31#define pcm_read(plug,buf,count) snd_pcm_oss_read3(plug,buf,count,1)
32#define pcm_readv(plug,vec,count) snd_pcm_oss_readv3(plug,vec,count,1)
33
34/*
35 * Basic io plugin
36 */
37
38static snd_pcm_sframes_t io_playback_transfer(snd_pcm_plugin_t *plugin,
39 const snd_pcm_plugin_channel_t *src_channels,
40 snd_pcm_plugin_channel_t *dst_channels ATTRIBUTE_UNUSED,
41 snd_pcm_uframes_t frames)
42{
43 snd_assert(plugin != NULL, return -ENXIO);
44 snd_assert(src_channels != NULL, return -ENXIO);
45 if (plugin->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED) {
46 return pcm_write(plugin->plug, src_channels->area.addr, frames);
47 } else {
48 int channel, channels = plugin->dst_format.channels;
49 void **bufs = (void**)plugin->extra_data;
50 snd_assert(bufs != NULL, return -ENXIO);
51 for (channel = 0; channel < channels; channel++) {
52 if (src_channels[channel].enabled)
53 bufs[channel] = src_channels[channel].area.addr;
54 else
55 bufs[channel] = NULL;
56 }
57 return pcm_writev(plugin->plug, bufs, frames);
58 }
59}
60
61static snd_pcm_sframes_t io_capture_transfer(snd_pcm_plugin_t *plugin,
62 const snd_pcm_plugin_channel_t *src_channels ATTRIBUTE_UNUSED,
63 snd_pcm_plugin_channel_t *dst_channels,
64 snd_pcm_uframes_t frames)
65{
66 snd_assert(plugin != NULL, return -ENXIO);
67 snd_assert(dst_channels != NULL, return -ENXIO);
68 if (plugin->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED) {
69 return pcm_read(plugin->plug, dst_channels->area.addr, frames);
70 } else {
71 int channel, channels = plugin->dst_format.channels;
72 void **bufs = (void**)plugin->extra_data;
73 snd_assert(bufs != NULL, return -ENXIO);
74 for (channel = 0; channel < channels; channel++) {
75 if (dst_channels[channel].enabled)
76 bufs[channel] = dst_channels[channel].area.addr;
77 else
78 bufs[channel] = NULL;
79 }
80 return pcm_readv(plugin->plug, bufs, frames);
81 }
82 return 0;
83}
84
85static snd_pcm_sframes_t io_src_channels(snd_pcm_plugin_t *plugin,
86 snd_pcm_uframes_t frames,
87 snd_pcm_plugin_channel_t **channels)
88{
89 int err;
90 unsigned int channel;
91 snd_pcm_plugin_channel_t *v;
92 err = snd_pcm_plugin_client_channels(plugin, frames, &v);
93 if (err < 0)
94 return err;
95 *channels = v;
96 if (plugin->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED) {
97 for (channel = 0; channel < plugin->src_format.channels; ++channel, ++v)
98 v->wanted = 1;
99 }
100 return frames;
101}
102
103int snd_pcm_plugin_build_io(snd_pcm_plug_t *plug,
104 snd_pcm_hw_params_t *params,
105 snd_pcm_plugin_t **r_plugin)
106{
107 int err;
108 snd_pcm_plugin_format_t format;
109 snd_pcm_plugin_t *plugin;
110
111 snd_assert(r_plugin != NULL, return -ENXIO);
112 *r_plugin = NULL;
113 snd_assert(plug != NULL && params != NULL, return -ENXIO);
114 format.format = params_format(params);
115 format.rate = params_rate(params);
116 format.channels = params_channels(params);
117 err = snd_pcm_plugin_build(plug, "I/O io",
118 &format, &format,
119 sizeof(void *) * format.channels,
120 &plugin);
121 if (err < 0)
122 return err;
123 plugin->access = params_access(params);
124 if (snd_pcm_plug_stream(plug) == SNDRV_PCM_STREAM_PLAYBACK) {
125 plugin->transfer = io_playback_transfer;
126 if (plugin->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED)
127 plugin->client_channels = io_src_channels;
128 } else {
129 plugin->transfer = io_capture_transfer;
130 }
131
132 *r_plugin = plugin;
133 return 0;
134}
diff --git a/sound/core/oss/linear.c b/sound/core/oss/linear.c
new file mode 100644
index 000000000000..12ed27a57b27
--- /dev/null
+++ b/sound/core/oss/linear.c
@@ -0,0 +1,158 @@
1/*
2 * Linear conversion Plug-In
3 * Copyright (c) 1999 by Jaroslav Kysela <perex@suse.cz>,
4 * Abramo Bagnara <abramo@alsa-project.org>
5 *
6 *
7 * This library is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU Library General Public License as
9 * published by the Free Software Foundation; either version 2 of
10 * the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU Library General Public License for more details.
16 *
17 * You should have received a copy of the GNU Library General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 */
22
23#include <sound/driver.h>
24#include <linux/time.h>
25#include <sound/core.h>
26#include <sound/pcm.h>
27#include "pcm_plugin.h"
28
29/*
30 * Basic linear conversion plugin
31 */
32
33typedef struct linear_private_data {
34 int conv;
35} linear_t;
36
37static void convert(snd_pcm_plugin_t *plugin,
38 const snd_pcm_plugin_channel_t *src_channels,
39 snd_pcm_plugin_channel_t *dst_channels,
40 snd_pcm_uframes_t frames)
41{
42#define CONV_LABELS
43#include "plugin_ops.h"
44#undef CONV_LABELS
45 linear_t *data = (linear_t *)plugin->extra_data;
46 void *conv = conv_labels[data->conv];
47 int channel;
48 int nchannels = plugin->src_format.channels;
49 for (channel = 0; channel < nchannels; ++channel) {
50 char *src;
51 char *dst;
52 int src_step, dst_step;
53 snd_pcm_uframes_t frames1;
54 if (!src_channels[channel].enabled) {
55 if (dst_channels[channel].wanted)
56 snd_pcm_area_silence(&dst_channels[channel].area, 0, frames, plugin->dst_format.format);
57 dst_channels[channel].enabled = 0;
58 continue;
59 }
60 dst_channels[channel].enabled = 1;
61 src = src_channels[channel].area.addr + src_channels[channel].area.first / 8;
62 dst = dst_channels[channel].area.addr + dst_channels[channel].area.first / 8;
63 src_step = src_channels[channel].area.step / 8;
64 dst_step = dst_channels[channel].area.step / 8;
65 frames1 = frames;
66 while (frames1-- > 0) {
67 goto *conv;
68#define CONV_END after
69#include "plugin_ops.h"
70#undef CONV_END
71 after:
72 src += src_step;
73 dst += dst_step;
74 }
75 }
76}
77
78static snd_pcm_sframes_t linear_transfer(snd_pcm_plugin_t *plugin,
79 const snd_pcm_plugin_channel_t *src_channels,
80 snd_pcm_plugin_channel_t *dst_channels,
81 snd_pcm_uframes_t frames)
82{
83 linear_t *data;
84
85 snd_assert(plugin != NULL && src_channels != NULL && dst_channels != NULL, return -ENXIO);
86 data = (linear_t *)plugin->extra_data;
87 if (frames == 0)
88 return 0;
89#ifdef CONFIG_SND_DEBUG
90 {
91 unsigned int channel;
92 for (channel = 0; channel < plugin->src_format.channels; channel++) {
93 snd_assert(src_channels[channel].area.first % 8 == 0 &&
94 src_channels[channel].area.step % 8 == 0,
95 return -ENXIO);
96 snd_assert(dst_channels[channel].area.first % 8 == 0 &&
97 dst_channels[channel].area.step % 8 == 0,
98 return -ENXIO);
99 }
100 }
101#endif
102 convert(plugin, src_channels, dst_channels, frames);
103 return frames;
104}
105
106int conv_index(int src_format, int dst_format)
107{
108 int src_endian, dst_endian, sign, src_width, dst_width;
109
110 sign = (snd_pcm_format_signed(src_format) !=
111 snd_pcm_format_signed(dst_format));
112#ifdef SNDRV_LITTLE_ENDIAN
113 src_endian = snd_pcm_format_big_endian(src_format);
114 dst_endian = snd_pcm_format_big_endian(dst_format);
115#else
116 src_endian = snd_pcm_format_little_endian(src_format);
117 dst_endian = snd_pcm_format_little_endian(dst_format);
118#endif
119
120 if (src_endian < 0)
121 src_endian = 0;
122 if (dst_endian < 0)
123 dst_endian = 0;
124
125 src_width = snd_pcm_format_width(src_format) / 8 - 1;
126 dst_width = snd_pcm_format_width(dst_format) / 8 - 1;
127
128 return src_width * 32 + src_endian * 16 + sign * 8 + dst_width * 2 + dst_endian;
129}
130
131int snd_pcm_plugin_build_linear(snd_pcm_plug_t *plug,
132 snd_pcm_plugin_format_t *src_format,
133 snd_pcm_plugin_format_t *dst_format,
134 snd_pcm_plugin_t **r_plugin)
135{
136 int err;
137 struct linear_private_data *data;
138 snd_pcm_plugin_t *plugin;
139
140 snd_assert(r_plugin != NULL, return -ENXIO);
141 *r_plugin = NULL;
142
143 snd_assert(src_format->rate == dst_format->rate, return -ENXIO);
144 snd_assert(src_format->channels == dst_format->channels, return -ENXIO);
145 snd_assert(snd_pcm_format_linear(src_format->format) &&
146 snd_pcm_format_linear(dst_format->format), return -ENXIO);
147
148 err = snd_pcm_plugin_build(plug, "linear format conversion",
149 src_format, dst_format,
150 sizeof(linear_t), &plugin);
151 if (err < 0)
152 return err;
153 data = (linear_t *)plugin->extra_data;
154 data->conv = conv_index(src_format->format, dst_format->format);
155 plugin->transfer = linear_transfer;
156 *r_plugin = plugin;
157 return 0;
158}
diff --git a/sound/core/oss/mixer_oss.c b/sound/core/oss/mixer_oss.c
new file mode 100644
index 000000000000..98ed9a9f0da6
--- /dev/null
+++ b/sound/core/oss/mixer_oss.c
@@ -0,0 +1,1340 @@
1/*
2 * OSS emulation layer for the mixer interface
3 * Copyright (c) by Jaroslav Kysela <perex@suse.cz>
4 *
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 *
20 */
21
22#include <sound/driver.h>
23#include <linux/init.h>
24#include <linux/smp_lock.h>
25#include <linux/slab.h>
26#include <linux/time.h>
27#include <sound/core.h>
28#include <sound/minors.h>
29#include <sound/control.h>
30#include <sound/info.h>
31#include <sound/mixer_oss.h>
32#include <linux/soundcard.h>
33
34#define OSS_ALSAEMULVER _SIOR ('M', 249, int)
35
36MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
37MODULE_DESCRIPTION("Mixer OSS emulation for ALSA.");
38MODULE_LICENSE("GPL");
39MODULE_ALIAS_SNDRV_MINOR(SNDRV_MINOR_OSS_MIXER);
40
41static int snd_mixer_oss_open(struct inode *inode, struct file *file)
42{
43 int cardnum = SNDRV_MINOR_OSS_CARD(iminor(inode));
44 snd_card_t *card;
45 snd_mixer_oss_file_t *fmixer;
46 int err;
47
48 if ((card = snd_cards[cardnum]) == NULL)
49 return -ENODEV;
50 if (card->mixer_oss == NULL)
51 return -ENODEV;
52 err = snd_card_file_add(card, file);
53 if (err < 0)
54 return err;
55 fmixer = kcalloc(1, sizeof(*fmixer), GFP_KERNEL);
56 if (fmixer == NULL) {
57 snd_card_file_remove(card, file);
58 return -ENOMEM;
59 }
60 fmixer->card = card;
61 fmixer->mixer = card->mixer_oss;
62 file->private_data = fmixer;
63 if (!try_module_get(card->module)) {
64 kfree(fmixer);
65 snd_card_file_remove(card, file);
66 return -EFAULT;
67 }
68 return 0;
69}
70
71static int snd_mixer_oss_release(struct inode *inode, struct file *file)
72{
73 snd_mixer_oss_file_t *fmixer;
74
75 if (file->private_data) {
76 fmixer = (snd_mixer_oss_file_t *) file->private_data;
77 module_put(fmixer->card->module);
78 snd_card_file_remove(fmixer->card, file);
79 kfree(fmixer);
80 }
81 return 0;
82}
83
84static int snd_mixer_oss_info(snd_mixer_oss_file_t *fmixer,
85 mixer_info __user *_info)
86{
87 snd_card_t *card = fmixer->card;
88 snd_mixer_oss_t *mixer = fmixer->mixer;
89 struct mixer_info info;
90
91 memset(&info, 0, sizeof(info));
92 strlcpy(info.id, mixer && mixer->id[0] ? mixer->id : card->driver, sizeof(info.id));
93 strlcpy(info.name, mixer && mixer->name[0] ? mixer->name : card->mixername, sizeof(info.name));
94 info.modify_counter = card->mixer_oss_change_count;
95 if (copy_to_user(_info, &info, sizeof(info)))
96 return -EFAULT;
97 return 0;
98}
99
100static int snd_mixer_oss_info_obsolete(snd_mixer_oss_file_t *fmixer,
101 _old_mixer_info __user *_info)
102{
103 snd_card_t *card = fmixer->card;
104 snd_mixer_oss_t *mixer = fmixer->mixer;
105 _old_mixer_info info;
106
107 memset(&info, 0, sizeof(info));
108 strlcpy(info.id, mixer && mixer->id[0] ? mixer->id : card->driver, sizeof(info.id));
109 strlcpy(info.name, mixer && mixer->name[0] ? mixer->name : card->mixername, sizeof(info.name));
110 if (copy_to_user(_info, &info, sizeof(info)))
111 return -EFAULT;
112 return 0;
113}
114
115static int snd_mixer_oss_caps(snd_mixer_oss_file_t *fmixer)
116{
117 snd_mixer_oss_t *mixer = fmixer->mixer;
118 int result = 0;
119
120 if (mixer == NULL)
121 return -EIO;
122 if (mixer->get_recsrc && mixer->put_recsrc)
123 result |= SOUND_CAP_EXCL_INPUT;
124 return result;
125}
126
127static int snd_mixer_oss_devmask(snd_mixer_oss_file_t *fmixer)
128{
129 snd_mixer_oss_t *mixer = fmixer->mixer;
130 snd_mixer_oss_slot_t *pslot;
131 int result = 0, chn;
132
133 if (mixer == NULL)
134 return -EIO;
135 for (chn = 0; chn < 31; chn++) {
136 pslot = &mixer->slots[chn];
137 if (pslot->put_volume || pslot->put_recsrc)
138 result |= 1 << chn;
139 }
140 return result;
141}
142
143static int snd_mixer_oss_stereodevs(snd_mixer_oss_file_t *fmixer)
144{
145 snd_mixer_oss_t *mixer = fmixer->mixer;
146 snd_mixer_oss_slot_t *pslot;
147 int result = 0, chn;
148
149 if (mixer == NULL)
150 return -EIO;
151 for (chn = 0; chn < 31; chn++) {
152 pslot = &mixer->slots[chn];
153 if (pslot->put_volume && pslot->stereo)
154 result |= 1 << chn;
155 }
156 return result;
157}
158
159static int snd_mixer_oss_recmask(snd_mixer_oss_file_t *fmixer)
160{
161 snd_mixer_oss_t *mixer = fmixer->mixer;
162 int result = 0;
163
164 if (mixer == NULL)
165 return -EIO;
166 if (mixer->put_recsrc && mixer->get_recsrc) { /* exclusive */
167 result = mixer->mask_recsrc;
168 } else {
169 snd_mixer_oss_slot_t *pslot;
170 int chn;
171 for (chn = 0; chn < 31; chn++) {
172 pslot = &mixer->slots[chn];
173 if (pslot->put_recsrc)
174 result |= 1 << chn;
175 }
176 }
177 return result;
178}
179
180static int snd_mixer_oss_get_recsrc(snd_mixer_oss_file_t *fmixer)
181{
182 snd_mixer_oss_t *mixer = fmixer->mixer;
183 int result = 0;
184
185 if (mixer == NULL)
186 return -EIO;
187 if (mixer->put_recsrc && mixer->get_recsrc) { /* exclusive */
188 int err;
189 if ((err = mixer->get_recsrc(fmixer, &result)) < 0)
190 return err;
191 result = 1 << result;
192 } else {
193 snd_mixer_oss_slot_t *pslot;
194 int chn;
195 for (chn = 0; chn < 31; chn++) {
196 pslot = &mixer->slots[chn];
197 if (pslot->get_recsrc) {
198 int active = 0;
199 pslot->get_recsrc(fmixer, pslot, &active);
200 if (active)
201 result |= 1 << chn;
202 }
203 }
204 }
205 return mixer->oss_recsrc = result;
206}
207
208static int snd_mixer_oss_set_recsrc(snd_mixer_oss_file_t *fmixer, int recsrc)
209{
210 snd_mixer_oss_t *mixer = fmixer->mixer;
211 snd_mixer_oss_slot_t *pslot;
212 int chn, active;
213 int result = 0;
214
215 if (mixer == NULL)
216 return -EIO;
217 if (mixer->get_recsrc && mixer->put_recsrc) { /* exclusive input */
218 if (recsrc & ~mixer->oss_recsrc)
219 recsrc &= ~mixer->oss_recsrc;
220 mixer->put_recsrc(fmixer, ffz(~recsrc));
221 mixer->get_recsrc(fmixer, &result);
222 result = 1 << result;
223 }
224 for (chn = 0; chn < 31; chn++) {
225 pslot = &mixer->slots[chn];
226 if (pslot->put_recsrc) {
227 active = (recsrc & (1 << chn)) ? 1 : 0;
228 pslot->put_recsrc(fmixer, pslot, active);
229 }
230 }
231 if (! result) {
232 for (chn = 0; chn < 31; chn++) {
233 pslot = &mixer->slots[chn];
234 if (pslot->get_recsrc) {
235 active = 0;
236 pslot->get_recsrc(fmixer, pslot, &active);
237 if (active)
238 result |= 1 << chn;
239 }
240 }
241 }
242 return result;
243}
244
245static int snd_mixer_oss_get_volume(snd_mixer_oss_file_t *fmixer, int slot)
246{
247 snd_mixer_oss_t *mixer = fmixer->mixer;
248 snd_mixer_oss_slot_t *pslot;
249 int result = 0, left, right;
250
251 if (mixer == NULL || slot > 30)
252 return -EIO;
253 pslot = &mixer->slots[slot];
254 left = pslot->volume[0];
255 right = pslot->volume[1];
256 if (pslot->get_volume)
257 result = pslot->get_volume(fmixer, pslot, &left, &right);
258 if (!pslot->stereo)
259 right = left;
260 snd_assert(left >= 0 && left <= 100, return -EIO);
261 snd_assert(right >= 0 && right <= 100, return -EIO);
262 if (result >= 0) {
263 pslot->volume[0] = left;
264 pslot->volume[1] = right;
265 result = (left & 0xff) | ((right & 0xff) << 8);
266 }
267 return result;
268}
269
270static int snd_mixer_oss_set_volume(snd_mixer_oss_file_t *fmixer,
271 int slot, int volume)
272{
273 snd_mixer_oss_t *mixer = fmixer->mixer;
274 snd_mixer_oss_slot_t *pslot;
275 int result = 0, left = volume & 0xff, right = (volume >> 8) & 0xff;
276
277 if (mixer == NULL || slot > 30)
278 return -EIO;
279 pslot = &mixer->slots[slot];
280 if (left > 100)
281 left = 100;
282 if (right > 100)
283 right = 100;
284 if (!pslot->stereo)
285 right = left;
286 if (pslot->put_volume)
287 result = pslot->put_volume(fmixer, pslot, left, right);
288 if (result < 0)
289 return result;
290 pslot->volume[0] = left;
291 pslot->volume[1] = right;
292 return (left & 0xff) | ((right & 0xff) << 8);
293}
294
295static int snd_mixer_oss_ioctl1(snd_mixer_oss_file_t *fmixer, unsigned int cmd, unsigned long arg)
296{
297 void __user *argp = (void __user *)arg;
298 int __user *p = argp;
299 int tmp;
300
301 snd_assert(fmixer != NULL, return -ENXIO);
302 if (((cmd >> 8) & 0xff) == 'M') {
303 switch (cmd) {
304 case SOUND_MIXER_INFO:
305 return snd_mixer_oss_info(fmixer, argp);
306 case SOUND_OLD_MIXER_INFO:
307 return snd_mixer_oss_info_obsolete(fmixer, argp);
308 case SOUND_MIXER_WRITE_RECSRC:
309 if (get_user(tmp, p))
310 return -EFAULT;
311 tmp = snd_mixer_oss_set_recsrc(fmixer, tmp);
312 if (tmp < 0)
313 return tmp;
314 return put_user(tmp, p);
315 case OSS_GETVERSION:
316 return put_user(SNDRV_OSS_VERSION, p);
317 case OSS_ALSAEMULVER:
318 return put_user(1, p);
319 case SOUND_MIXER_READ_DEVMASK:
320 tmp = snd_mixer_oss_devmask(fmixer);
321 if (tmp < 0)
322 return tmp;
323 return put_user(tmp, p);
324 case SOUND_MIXER_READ_STEREODEVS:
325 tmp = snd_mixer_oss_stereodevs(fmixer);
326 if (tmp < 0)
327 return tmp;
328 return put_user(tmp, p);
329 case SOUND_MIXER_READ_RECMASK:
330 tmp = snd_mixer_oss_recmask(fmixer);
331 if (tmp < 0)
332 return tmp;
333 return put_user(tmp, p);
334 case SOUND_MIXER_READ_CAPS:
335 tmp = snd_mixer_oss_caps(fmixer);
336 if (tmp < 0)
337 return tmp;
338 return put_user(tmp, p);
339 case SOUND_MIXER_READ_RECSRC:
340 tmp = snd_mixer_oss_get_recsrc(fmixer);
341 if (tmp < 0)
342 return tmp;
343 return put_user(tmp, p);
344 }
345 }
346 if (cmd & SIOC_IN) {
347 if (get_user(tmp, p))
348 return -EFAULT;
349 tmp = snd_mixer_oss_set_volume(fmixer, cmd & 0xff, tmp);
350 if (tmp < 0)
351 return tmp;
352 return put_user(tmp, p);
353 } else if (cmd & SIOC_OUT) {
354 tmp = snd_mixer_oss_get_volume(fmixer, cmd & 0xff);
355 if (tmp < 0)
356 return tmp;
357 return put_user(tmp, p);
358 }
359 return -ENXIO;
360}
361
362static long snd_mixer_oss_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
363{
364 return snd_mixer_oss_ioctl1((snd_mixer_oss_file_t *) file->private_data, cmd, arg);
365}
366
367int snd_mixer_oss_ioctl_card(snd_card_t *card, unsigned int cmd, unsigned long arg)
368{
369 snd_mixer_oss_file_t fmixer;
370
371 snd_assert(card != NULL, return -ENXIO);
372 if (card->mixer_oss == NULL)
373 return -ENXIO;
374 memset(&fmixer, 0, sizeof(fmixer));
375 fmixer.card = card;
376 fmixer.mixer = card->mixer_oss;
377 return snd_mixer_oss_ioctl1(&fmixer, cmd, arg);
378}
379
380#ifdef CONFIG_COMPAT
381/* all compatible */
382#define snd_mixer_oss_ioctl_compat snd_mixer_oss_ioctl
383#else
384#define snd_mixer_oss_ioctl_compat NULL
385#endif
386
387/*
388 * REGISTRATION PART
389 */
390
391static struct file_operations snd_mixer_oss_f_ops =
392{
393 .owner = THIS_MODULE,
394 .open = snd_mixer_oss_open,
395 .release = snd_mixer_oss_release,
396 .unlocked_ioctl = snd_mixer_oss_ioctl,
397 .compat_ioctl = snd_mixer_oss_ioctl_compat,
398};
399
400static snd_minor_t snd_mixer_oss_reg =
401{
402 .comment = "mixer",
403 .f_ops = &snd_mixer_oss_f_ops,
404};
405
406/*
407 * utilities
408 */
409
410static long snd_mixer_oss_conv(long val, long omin, long omax, long nmin, long nmax)
411{
412 long orange = omax - omin, nrange = nmax - nmin;
413
414 if (orange == 0)
415 return 0;
416 return ((nrange * (val - omin)) + (orange / 2)) / orange + nmin;
417}
418
419/* convert from alsa native to oss values (0-100) */
420static long snd_mixer_oss_conv1(long val, long min, long max, int *old)
421{
422 if (val == snd_mixer_oss_conv(*old, 0, 100, min, max))
423 return *old;
424 return snd_mixer_oss_conv(val, min, max, 0, 100);
425}
426
427/* convert from oss to alsa native values */
428static long snd_mixer_oss_conv2(long val, long min, long max)
429{
430 return snd_mixer_oss_conv(val, 0, 100, min, max);
431}
432
433#if 0
434static void snd_mixer_oss_recsrce_set(snd_card_t *card, int slot)
435{
436 snd_mixer_oss_t *mixer = card->mixer_oss;
437 if (mixer)
438 mixer->mask_recsrc |= 1 << slot;
439}
440
441static int snd_mixer_oss_recsrce_get(snd_card_t *card, int slot)
442{
443 snd_mixer_oss_t *mixer = card->mixer_oss;
444 if (mixer && (mixer->mask_recsrc & (1 << slot)))
445 return 1;
446 return 0;
447}
448#endif
449
450#define SNDRV_MIXER_OSS_SIGNATURE 0x65999250
451
452#define SNDRV_MIXER_OSS_ITEM_GLOBAL 0
453#define SNDRV_MIXER_OSS_ITEM_GSWITCH 1
454#define SNDRV_MIXER_OSS_ITEM_GROUTE 2
455#define SNDRV_MIXER_OSS_ITEM_GVOLUME 3
456#define SNDRV_MIXER_OSS_ITEM_PSWITCH 4
457#define SNDRV_MIXER_OSS_ITEM_PROUTE 5
458#define SNDRV_MIXER_OSS_ITEM_PVOLUME 6
459#define SNDRV_MIXER_OSS_ITEM_CSWITCH 7
460#define SNDRV_MIXER_OSS_ITEM_CROUTE 8
461#define SNDRV_MIXER_OSS_ITEM_CVOLUME 9
462#define SNDRV_MIXER_OSS_ITEM_CAPTURE 10
463
464#define SNDRV_MIXER_OSS_ITEM_COUNT 11
465
466#define SNDRV_MIXER_OSS_PRESENT_GLOBAL (1<<0)
467#define SNDRV_MIXER_OSS_PRESENT_GSWITCH (1<<1)
468#define SNDRV_MIXER_OSS_PRESENT_GROUTE (1<<2)
469#define SNDRV_MIXER_OSS_PRESENT_GVOLUME (1<<3)
470#define SNDRV_MIXER_OSS_PRESENT_PSWITCH (1<<4)
471#define SNDRV_MIXER_OSS_PRESENT_PROUTE (1<<5)
472#define SNDRV_MIXER_OSS_PRESENT_PVOLUME (1<<6)
473#define SNDRV_MIXER_OSS_PRESENT_CSWITCH (1<<7)
474#define SNDRV_MIXER_OSS_PRESENT_CROUTE (1<<8)
475#define SNDRV_MIXER_OSS_PRESENT_CVOLUME (1<<9)
476#define SNDRV_MIXER_OSS_PRESENT_CAPTURE (1<<10)
477
478struct slot {
479 unsigned int signature;
480 unsigned int present;
481 unsigned int channels;
482 unsigned int numid[SNDRV_MIXER_OSS_ITEM_COUNT];
483 unsigned int capture_item;
484 struct snd_mixer_oss_assign_table *assigned;
485 unsigned int allocated: 1;
486};
487
488#define ID_UNKNOWN ((unsigned int)-1)
489
490static snd_kcontrol_t *snd_mixer_oss_test_id(snd_mixer_oss_t *mixer, const char *name, int index)
491{
492 snd_card_t * card = mixer->card;
493 snd_ctl_elem_id_t id;
494
495 memset(&id, 0, sizeof(id));
496 id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
497 strcpy(id.name, name);
498 id.index = index;
499 return snd_ctl_find_id(card, &id);
500}
501
502static void snd_mixer_oss_get_volume1_vol(snd_mixer_oss_file_t *fmixer,
503 snd_mixer_oss_slot_t *pslot,
504 unsigned int numid,
505 int *left, int *right)
506{
507 snd_ctl_elem_info_t *uinfo;
508 snd_ctl_elem_value_t *uctl;
509 snd_kcontrol_t *kctl;
510 snd_card_t *card = fmixer->card;
511
512 if (numid == ID_UNKNOWN)
513 return;
514 down_read(&card->controls_rwsem);
515 if ((kctl = snd_ctl_find_numid(card, numid)) == NULL) {
516 up_read(&card->controls_rwsem);
517 return;
518 }
519 uinfo = kcalloc(1, sizeof(*uinfo), GFP_KERNEL);
520 uctl = kcalloc(1, sizeof(*uctl), GFP_KERNEL);
521 if (uinfo == NULL || uctl == NULL)
522 goto __unalloc;
523 snd_runtime_check(!kctl->info(kctl, uinfo), goto __unalloc);
524 snd_runtime_check(!kctl->get(kctl, uctl), goto __unalloc);
525 snd_runtime_check(uinfo->type != SNDRV_CTL_ELEM_TYPE_BOOLEAN || uinfo->value.integer.min != 0 || uinfo->value.integer.max != 1, goto __unalloc);
526 *left = snd_mixer_oss_conv1(uctl->value.integer.value[0], uinfo->value.integer.min, uinfo->value.integer.max, &pslot->volume[0]);
527 if (uinfo->count > 1)
528 *right = snd_mixer_oss_conv1(uctl->value.integer.value[1], uinfo->value.integer.min, uinfo->value.integer.max, &pslot->volume[1]);
529 __unalloc:
530 up_read(&card->controls_rwsem);
531 kfree(uctl);
532 kfree(uinfo);
533}
534
535static void snd_mixer_oss_get_volume1_sw(snd_mixer_oss_file_t *fmixer,
536 snd_mixer_oss_slot_t *pslot,
537 unsigned int numid,
538 int *left, int *right,
539 int route)
540{
541 snd_ctl_elem_info_t *uinfo;
542 snd_ctl_elem_value_t *uctl;
543 snd_kcontrol_t *kctl;
544 snd_card_t *card = fmixer->card;
545
546 if (numid == ID_UNKNOWN)
547 return;
548 down_read(&card->controls_rwsem);
549 if ((kctl = snd_ctl_find_numid(card, numid)) == NULL) {
550 up_read(&card->controls_rwsem);
551 return;
552 }
553 uinfo = kcalloc(1, sizeof(*uinfo), GFP_KERNEL);
554 uctl = kcalloc(1, sizeof(*uctl), GFP_KERNEL);
555 if (uinfo == NULL || uctl == NULL)
556 goto __unalloc;
557 snd_runtime_check(!kctl->info(kctl, uinfo), goto __unalloc);
558 snd_runtime_check(!kctl->get(kctl, uctl), goto __unalloc);
559 if (!uctl->value.integer.value[0]) {
560 *left = 0;
561 if (uinfo->count == 1)
562 *right = 0;
563 }
564 if (uinfo->count > 1 && !uctl->value.integer.value[route ? 3 : 1])
565 *right = 0;
566 __unalloc:
567 up_read(&card->controls_rwsem);
568 kfree(uctl);
569 kfree(uinfo);
570}
571
572static int snd_mixer_oss_get_volume1(snd_mixer_oss_file_t *fmixer,
573 snd_mixer_oss_slot_t *pslot,
574 int *left, int *right)
575{
576 struct slot *slot = (struct slot *)pslot->private_data;
577
578 *left = *right = 100;
579 if (slot->present & SNDRV_MIXER_OSS_PRESENT_PVOLUME) {
580 snd_mixer_oss_get_volume1_vol(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_PVOLUME], left, right);
581 } else if (slot->present & SNDRV_MIXER_OSS_PRESENT_GVOLUME) {
582 snd_mixer_oss_get_volume1_vol(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_GVOLUME], left, right);
583 } else if (slot->present & SNDRV_MIXER_OSS_PRESENT_GLOBAL) {
584 snd_mixer_oss_get_volume1_vol(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_GLOBAL], left, right);
585 }
586 if (slot->present & SNDRV_MIXER_OSS_PRESENT_PSWITCH) {
587 snd_mixer_oss_get_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_PSWITCH], left, right, 0);
588 } else if (slot->present & SNDRV_MIXER_OSS_PRESENT_GSWITCH) {
589 snd_mixer_oss_get_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_GSWITCH], left, right, 0);
590 } else if (slot->present & SNDRV_MIXER_OSS_PRESENT_PROUTE) {
591 snd_mixer_oss_get_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_PROUTE], left, right, 1);
592 } else if (slot->present & SNDRV_MIXER_OSS_PRESENT_GROUTE) {
593 snd_mixer_oss_get_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_GROUTE], left, right, 1);
594 }
595 return 0;
596}
597
598static void snd_mixer_oss_put_volume1_vol(snd_mixer_oss_file_t *fmixer,
599 snd_mixer_oss_slot_t *pslot,
600 unsigned int numid,
601 int left, int right)
602{
603 snd_ctl_elem_info_t *uinfo;
604 snd_ctl_elem_value_t *uctl;
605 snd_kcontrol_t *kctl;
606 snd_card_t *card = fmixer->card;
607 int res;
608
609 if (numid == ID_UNKNOWN)
610 return;
611 down_read(&card->controls_rwsem);
612 if ((kctl = snd_ctl_find_numid(card, numid)) == NULL)
613 return;
614 uinfo = kcalloc(1, sizeof(*uinfo), GFP_KERNEL);
615 uctl = kcalloc(1, sizeof(*uctl), GFP_KERNEL);
616 if (uinfo == NULL || uctl == NULL)
617 goto __unalloc;
618 snd_runtime_check(!kctl->info(kctl, uinfo), goto __unalloc);
619 snd_runtime_check(uinfo->type != SNDRV_CTL_ELEM_TYPE_BOOLEAN || uinfo->value.integer.min != 0 || uinfo->value.integer.max != 1, goto __unalloc);
620 uctl->value.integer.value[0] = snd_mixer_oss_conv2(left, uinfo->value.integer.min, uinfo->value.integer.max);
621 if (uinfo->count > 1)
622 uctl->value.integer.value[1] = snd_mixer_oss_conv2(right, uinfo->value.integer.min, uinfo->value.integer.max);
623 snd_runtime_check((res = kctl->put(kctl, uctl)) >= 0, goto __unalloc);
624 if (res > 0)
625 snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id);
626 __unalloc:
627 up_read(&card->controls_rwsem);
628 kfree(uctl);
629 kfree(uinfo);
630}
631
632static void snd_mixer_oss_put_volume1_sw(snd_mixer_oss_file_t *fmixer,
633 snd_mixer_oss_slot_t *pslot,
634 unsigned int numid,
635 int left, int right,
636 int route)
637{
638 snd_ctl_elem_info_t *uinfo;
639 snd_ctl_elem_value_t *uctl;
640 snd_kcontrol_t *kctl;
641 snd_card_t *card = fmixer->card;
642 int res;
643
644 if (numid == ID_UNKNOWN)
645 return;
646 down_read(&card->controls_rwsem);
647 if ((kctl = snd_ctl_find_numid(card, numid)) == NULL) {
648 up_read(&fmixer->card->controls_rwsem);
649 return;
650 }
651 uinfo = kcalloc(1, sizeof(*uinfo), GFP_KERNEL);
652 uctl = kcalloc(1, sizeof(*uctl), GFP_KERNEL);
653 if (uinfo == NULL || uctl == NULL)
654 goto __unalloc;
655 snd_runtime_check(!kctl->info(kctl, uinfo), goto __unalloc);
656 if (uinfo->count > 1) {
657 uctl->value.integer.value[0] = left > 0 ? 1 : 0;
658 uctl->value.integer.value[route ? 3 : 1] = right > 0 ? 1 : 0;
659 if (route) {
660 uctl->value.integer.value[1] =
661 uctl->value.integer.value[2] = 0;
662 }
663 } else {
664 uctl->value.integer.value[0] = (left > 0 || right > 0) ? 1 : 0;
665 }
666 snd_runtime_check((res = kctl->put(kctl, uctl)) >= 0, goto __unalloc);
667 if (res > 0)
668 snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id);
669 __unalloc:
670 up_read(&card->controls_rwsem);
671 kfree(uctl);
672 kfree(uinfo);
673}
674
675static int snd_mixer_oss_put_volume1(snd_mixer_oss_file_t *fmixer,
676 snd_mixer_oss_slot_t *pslot,
677 int left, int right)
678{
679 struct slot *slot = (struct slot *)pslot->private_data;
680
681 if (slot->present & SNDRV_MIXER_OSS_PRESENT_PVOLUME) {
682 snd_mixer_oss_put_volume1_vol(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_PVOLUME], left, right);
683 if (slot->present & SNDRV_MIXER_OSS_PRESENT_CVOLUME)
684 snd_mixer_oss_put_volume1_vol(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_CVOLUME], left, right);
685 } else if (slot->present & SNDRV_MIXER_OSS_PRESENT_GVOLUME) {
686 snd_mixer_oss_put_volume1_vol(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_GVOLUME], left, right);
687 } else if (slot->present & SNDRV_MIXER_OSS_PRESENT_GLOBAL) {
688 snd_mixer_oss_put_volume1_vol(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_GLOBAL], left, right);
689 }
690 if (left || right) {
691 if (slot->present & SNDRV_MIXER_OSS_PRESENT_PSWITCH)
692 snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_PSWITCH], left, right, 0);
693 if (slot->present & SNDRV_MIXER_OSS_PRESENT_GSWITCH)
694 snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_GSWITCH], left, right, 0);
695 if (slot->present & SNDRV_MIXER_OSS_PRESENT_PROUTE)
696 snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_PROUTE], left, right, 1);
697 if (slot->present & SNDRV_MIXER_OSS_PRESENT_GROUTE)
698 snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_GROUTE], left, right, 1);
699 } else {
700 if (slot->present & SNDRV_MIXER_OSS_PRESENT_PSWITCH) {
701 snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_PSWITCH], left, right, 0);
702 } else if (slot->present & SNDRV_MIXER_OSS_PRESENT_GSWITCH) {
703 snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_GSWITCH], left, right, 0);
704 } else if (slot->present & SNDRV_MIXER_OSS_PRESENT_PROUTE) {
705 snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_PROUTE], left, right, 1);
706 } else if (slot->present & SNDRV_MIXER_OSS_PRESENT_GROUTE) {
707 snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_GROUTE], left, right, 1);
708 }
709 }
710 return 0;
711}
712
713static int snd_mixer_oss_get_recsrc1_sw(snd_mixer_oss_file_t *fmixer,
714 snd_mixer_oss_slot_t *pslot,
715 int *active)
716{
717 struct slot *slot = (struct slot *)pslot->private_data;
718 int left, right;
719
720 left = right = 1;
721 snd_mixer_oss_get_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_CSWITCH], &left, &right, 0);
722 *active = (left || right) ? 1 : 0;
723 return 0;
724}
725
726static int snd_mixer_oss_get_recsrc1_route(snd_mixer_oss_file_t *fmixer,
727 snd_mixer_oss_slot_t *pslot,
728 int *active)
729{
730 struct slot *slot = (struct slot *)pslot->private_data;
731 int left, right;
732
733 left = right = 1;
734 snd_mixer_oss_get_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_CROUTE], &left, &right, 1);
735 *active = (left || right) ? 1 : 0;
736 return 0;
737}
738
739static int snd_mixer_oss_put_recsrc1_sw(snd_mixer_oss_file_t *fmixer,
740 snd_mixer_oss_slot_t *pslot,
741 int active)
742{
743 struct slot *slot = (struct slot *)pslot->private_data;
744
745 snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_CSWITCH], active, active, 0);
746 return 0;
747}
748
749static int snd_mixer_oss_put_recsrc1_route(snd_mixer_oss_file_t *fmixer,
750 snd_mixer_oss_slot_t *pslot,
751 int active)
752{
753 struct slot *slot = (struct slot *)pslot->private_data;
754
755 snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_CROUTE], active, active, 1);
756 return 0;
757}
758
759static int snd_mixer_oss_get_recsrc2(snd_mixer_oss_file_t *fmixer, unsigned int *active_index)
760{
761 snd_card_t *card = fmixer->card;
762 snd_mixer_oss_t *mixer = fmixer->mixer;
763 snd_kcontrol_t *kctl;
764 snd_mixer_oss_slot_t *pslot;
765 struct slot *slot;
766 snd_ctl_elem_info_t *uinfo;
767 snd_ctl_elem_value_t *uctl;
768 int err, idx;
769
770 uinfo = kcalloc(1, sizeof(*uinfo), GFP_KERNEL);
771 uctl = kcalloc(1, sizeof(*uctl), GFP_KERNEL);
772 if (uinfo == NULL || uctl == NULL) {
773 err = -ENOMEM;
774 goto __unlock;
775 }
776 down_read(&card->controls_rwsem);
777 kctl = snd_mixer_oss_test_id(mixer, "Capture Source", 0);
778 snd_runtime_check(kctl != NULL, err = -ENOENT; goto __unlock);
779 snd_runtime_check(!(err = kctl->info(kctl, uinfo)), goto __unlock);
780 snd_runtime_check(!(err = kctl->get(kctl, uctl)), goto __unlock);
781 for (idx = 0; idx < 32; idx++) {
782 if (!(mixer->mask_recsrc & (1 << idx)))
783 continue;
784 pslot = &mixer->slots[idx];
785 slot = (struct slot *)pslot->private_data;
786 if (slot->signature != SNDRV_MIXER_OSS_SIGNATURE)
787 continue;
788 if (!(slot->present & SNDRV_MIXER_OSS_PRESENT_CAPTURE))
789 continue;
790 if (slot->capture_item == uctl->value.enumerated.item[0]) {
791 *active_index = idx;
792 break;
793 }
794 }
795 err = 0;
796 __unlock:
797 up_read(&card->controls_rwsem);
798 kfree(uctl);
799 kfree(uinfo);
800 return err;
801}
802
803static int snd_mixer_oss_put_recsrc2(snd_mixer_oss_file_t *fmixer, unsigned int active_index)
804{
805 snd_card_t *card = fmixer->card;
806 snd_mixer_oss_t *mixer = fmixer->mixer;
807 snd_kcontrol_t *kctl;
808 snd_mixer_oss_slot_t *pslot;
809 struct slot *slot = NULL;
810 snd_ctl_elem_info_t *uinfo;
811 snd_ctl_elem_value_t *uctl;
812 int err;
813 unsigned int idx;
814
815 uinfo = kcalloc(1, sizeof(*uinfo), GFP_KERNEL);
816 uctl = kcalloc(1, sizeof(*uctl), GFP_KERNEL);
817 if (uinfo == NULL || uctl == NULL) {
818 err = -ENOMEM;
819 goto __unlock;
820 }
821 down_read(&card->controls_rwsem);
822 kctl = snd_mixer_oss_test_id(mixer, "Capture Source", 0);
823 snd_runtime_check(kctl != NULL, err = -ENOENT; goto __unlock);
824 snd_runtime_check(!(err = kctl->info(kctl, uinfo)), goto __unlock);
825 for (idx = 0; idx < 32; idx++) {
826 if (!(mixer->mask_recsrc & (1 << idx)))
827 continue;
828 pslot = &mixer->slots[idx];
829 slot = (struct slot *)pslot->private_data;
830 if (slot->signature != SNDRV_MIXER_OSS_SIGNATURE)
831 continue;
832 if (!(slot->present & SNDRV_MIXER_OSS_PRESENT_CAPTURE))
833 continue;
834 if (idx == active_index)
835 break;
836 slot = NULL;
837 }
838 snd_runtime_check(slot != NULL, goto __unlock);
839 for (idx = 0; idx < uinfo->count; idx++)
840 uctl->value.enumerated.item[idx] = slot->capture_item;
841 snd_runtime_check((err = kctl->put(kctl, uctl)) >= 0, );
842 if (err > 0)
843 snd_ctl_notify(fmixer->card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id);
844 err = 0;
845 __unlock:
846 up_read(&card->controls_rwsem);
847 kfree(uctl);
848 kfree(uinfo);
849 return err;
850}
851
852struct snd_mixer_oss_assign_table {
853 int oss_id;
854 const char *name;
855 int index;
856};
857
858static int snd_mixer_oss_build_test(snd_mixer_oss_t *mixer, struct slot *slot, const char *name, int index, int item)
859{
860 snd_ctl_elem_info_t *info;
861 snd_kcontrol_t *kcontrol;
862 snd_card_t *card = mixer->card;
863 int err;
864
865 down_read(&card->controls_rwsem);
866 kcontrol = snd_mixer_oss_test_id(mixer, name, index);
867 if (kcontrol == NULL) {
868 up_read(&card->controls_rwsem);
869 return 0;
870 }
871 info = kmalloc(sizeof(*info), GFP_KERNEL);
872 if (! info) {
873 up_read(&card->controls_rwsem);
874 return -ENOMEM;
875 }
876 if ((err = kcontrol->info(kcontrol, info)) < 0) {
877 up_read(&card->controls_rwsem);
878 kfree(info);
879 return err;
880 }
881 slot->numid[item] = kcontrol->id.numid;
882 up_read(&card->controls_rwsem);
883 if (info->count > slot->channels)
884 slot->channels = info->count;
885 slot->present |= 1 << item;
886 kfree(info);
887 return 0;
888}
889
890static void snd_mixer_oss_slot_free(snd_mixer_oss_slot_t *chn)
891{
892 struct slot *p = (struct slot *)chn->private_data;
893 if (p) {
894 if (p->allocated && p->assigned) {
895 kfree(p->assigned->name);
896 kfree(p->assigned);
897 }
898 kfree(p);
899 }
900}
901
902static void mixer_slot_clear(snd_mixer_oss_slot_t *rslot)
903{
904 int idx = rslot->number; /* remember this */
905 if (rslot->private_free)
906 rslot->private_free(rslot);
907 memset(rslot, 0, sizeof(*rslot));
908 rslot->number = idx;
909}
910
911/*
912 * build an OSS mixer element.
913 * ptr_allocated means the entry is dynamically allocated (change via proc file).
914 * when replace_old = 1, the old entry is replaced with the new one.
915 */
916static int snd_mixer_oss_build_input(snd_mixer_oss_t *mixer, struct snd_mixer_oss_assign_table *ptr, int ptr_allocated, int replace_old)
917{
918 struct slot slot;
919 struct slot *pslot;
920 snd_kcontrol_t *kctl;
921 snd_mixer_oss_slot_t *rslot;
922 char str[64];
923
924 /* check if already assigned */
925 if (mixer->slots[ptr->oss_id].get_volume && ! replace_old)
926 return 0;
927
928 memset(&slot, 0, sizeof(slot));
929 memset(slot.numid, 0xff, sizeof(slot.numid)); /* ID_UNKNOWN */
930 if (snd_mixer_oss_build_test(mixer, &slot, ptr->name, ptr->index,
931 SNDRV_MIXER_OSS_ITEM_GLOBAL))
932 return 0;
933 sprintf(str, "%s Switch", ptr->name);
934 if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index,
935 SNDRV_MIXER_OSS_ITEM_GSWITCH))
936 return 0;
937 sprintf(str, "%s Route", ptr->name);
938 if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index,
939 SNDRV_MIXER_OSS_ITEM_GROUTE))
940 return 0;
941 sprintf(str, "%s Volume", ptr->name);
942 if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index,
943 SNDRV_MIXER_OSS_ITEM_GVOLUME))
944 return 0;
945 sprintf(str, "%s Playback Switch", ptr->name);
946 if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index,
947 SNDRV_MIXER_OSS_ITEM_PSWITCH))
948 return 0;
949 sprintf(str, "%s Playback Route", ptr->name);
950 if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index,
951 SNDRV_MIXER_OSS_ITEM_PROUTE))
952 return 0;
953 sprintf(str, "%s Playback Volume", ptr->name);
954 if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index,
955 SNDRV_MIXER_OSS_ITEM_PVOLUME))
956 return 0;
957 sprintf(str, "%s Capture Switch", ptr->name);
958 if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index,
959 SNDRV_MIXER_OSS_ITEM_CSWITCH))
960 return 0;
961 sprintf(str, "%s Capture Route", ptr->name);
962 if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index,
963 SNDRV_MIXER_OSS_ITEM_CROUTE))
964 return 0;
965 sprintf(str, "%s Capture Volume", ptr->name);
966 if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index,
967 SNDRV_MIXER_OSS_ITEM_CVOLUME))
968 return 0;
969 down_read(&mixer->card->controls_rwsem);
970 if (ptr->index == 0 && (kctl = snd_mixer_oss_test_id(mixer, "Capture Source", 0)) != NULL) {
971 snd_ctl_elem_info_t *uinfo;
972
973 uinfo = kmalloc(sizeof(*uinfo), GFP_KERNEL);
974 if (! uinfo) {
975 up_read(&mixer->card->controls_rwsem);
976 return -ENOMEM;
977 }
978
979 memset(uinfo, 0, sizeof(*uinfo));
980 if (kctl->info(kctl, uinfo)) {
981 up_read(&mixer->card->controls_rwsem);
982 return 0;
983 }
984 strcpy(str, ptr->name);
985 if (!strcmp(str, "Master"))
986 strcpy(str, "Mix");
987 if (!strcmp(str, "Master Mono"))
988 strcpy(str, "Mix Mono");
989 slot.capture_item = 0;
990 if (!strcmp(uinfo->value.enumerated.name, str)) {
991 slot.present |= SNDRV_MIXER_OSS_PRESENT_CAPTURE;
992 } else {
993 for (slot.capture_item = 1; slot.capture_item < uinfo->value.enumerated.items; slot.capture_item++) {
994 uinfo->value.enumerated.item = slot.capture_item;
995 if (kctl->info(kctl, uinfo)) {
996 up_read(&mixer->card->controls_rwsem);
997 return 0;
998 }
999 if (!strcmp(uinfo->value.enumerated.name, str)) {
1000 slot.present |= SNDRV_MIXER_OSS_PRESENT_CAPTURE;
1001 break;
1002 }
1003 }
1004 }
1005 kfree(uinfo);
1006 }
1007 up_read(&mixer->card->controls_rwsem);
1008 if (slot.present != 0) {
1009 pslot = (struct slot *)kmalloc(sizeof(slot), GFP_KERNEL);
1010 snd_runtime_check(pslot != NULL, return -ENOMEM);
1011 *pslot = slot;
1012 pslot->signature = SNDRV_MIXER_OSS_SIGNATURE;
1013 pslot->assigned = ptr;
1014 pslot->allocated = ptr_allocated;
1015 rslot = &mixer->slots[ptr->oss_id];
1016 mixer_slot_clear(rslot);
1017 rslot->stereo = slot.channels > 1 ? 1 : 0;
1018 rslot->get_volume = snd_mixer_oss_get_volume1;
1019 rslot->put_volume = snd_mixer_oss_put_volume1;
1020 /* note: ES18xx have both Capture Source and XX Capture Volume !!! */
1021 if (slot.present & SNDRV_MIXER_OSS_PRESENT_CSWITCH) {
1022 rslot->get_recsrc = snd_mixer_oss_get_recsrc1_sw;
1023 rslot->put_recsrc = snd_mixer_oss_put_recsrc1_sw;
1024 } else if (slot.present & SNDRV_MIXER_OSS_PRESENT_CROUTE) {
1025 rslot->get_recsrc = snd_mixer_oss_get_recsrc1_route;
1026 rslot->put_recsrc = snd_mixer_oss_put_recsrc1_route;
1027 } else if (slot.present & SNDRV_MIXER_OSS_PRESENT_CAPTURE) {
1028 mixer->mask_recsrc |= 1 << ptr->oss_id;
1029 }
1030 rslot->private_data = pslot;
1031 rslot->private_free = snd_mixer_oss_slot_free;
1032 return 1;
1033 }
1034 return 0;
1035}
1036
1037/*
1038 */
1039#define MIXER_VOL(name) [SOUND_MIXER_##name] = #name
1040static char *oss_mixer_names[SNDRV_OSS_MAX_MIXERS] = {
1041 MIXER_VOL(VOLUME),
1042 MIXER_VOL(BASS),
1043 MIXER_VOL(TREBLE),
1044 MIXER_VOL(SYNTH),
1045 MIXER_VOL(PCM),
1046 MIXER_VOL(SPEAKER),
1047 MIXER_VOL(LINE),
1048 MIXER_VOL(MIC),
1049 MIXER_VOL(CD),
1050 MIXER_VOL(IMIX),
1051 MIXER_VOL(ALTPCM),
1052 MIXER_VOL(RECLEV),
1053 MIXER_VOL(IGAIN),
1054 MIXER_VOL(OGAIN),
1055 MIXER_VOL(LINE1),
1056 MIXER_VOL(LINE2),
1057 MIXER_VOL(LINE3),
1058 MIXER_VOL(DIGITAL1),
1059 MIXER_VOL(DIGITAL2),
1060 MIXER_VOL(DIGITAL3),
1061 MIXER_VOL(PHONEIN),
1062 MIXER_VOL(PHONEOUT),
1063 MIXER_VOL(VIDEO),
1064 MIXER_VOL(RADIO),
1065 MIXER_VOL(MONITOR),
1066};
1067
1068/*
1069 * /proc interface
1070 */
1071
1072static void snd_mixer_oss_proc_read(snd_info_entry_t *entry,
1073 snd_info_buffer_t * buffer)
1074{
1075 snd_mixer_oss_t *mixer = entry->private_data;
1076 int i;
1077
1078 down(&mixer->reg_mutex);
1079 for (i = 0; i < SNDRV_OSS_MAX_MIXERS; i++) {
1080 struct slot *p;
1081
1082 if (! oss_mixer_names[i])
1083 continue;
1084 p = (struct slot *)mixer->slots[i].private_data;
1085 snd_iprintf(buffer, "%s ", oss_mixer_names[i]);
1086 if (p && p->assigned)
1087 snd_iprintf(buffer, "\"%s\" %d\n",
1088 p->assigned->name,
1089 p->assigned->index);
1090 else
1091 snd_iprintf(buffer, "\"\" 0\n");
1092 }
1093 up(&mixer->reg_mutex);
1094}
1095
1096static void snd_mixer_oss_proc_write(snd_info_entry_t *entry,
1097 snd_info_buffer_t * buffer)
1098{
1099 snd_mixer_oss_t *mixer = entry->private_data;
1100 char line[128], str[32], idxstr[16], *cptr;
1101 int ch, idx;
1102 struct snd_mixer_oss_assign_table *tbl;
1103 struct slot *slot;
1104
1105 while (!snd_info_get_line(buffer, line, sizeof(line))) {
1106 cptr = snd_info_get_str(str, line, sizeof(str));
1107 for (ch = 0; ch < SNDRV_OSS_MAX_MIXERS; ch++)
1108 if (oss_mixer_names[ch] && strcmp(oss_mixer_names[ch], str) == 0)
1109 break;
1110 if (ch >= SNDRV_OSS_MAX_MIXERS) {
1111 snd_printk(KERN_ERR "mixer_oss: invalid OSS volume '%s'\n", str);
1112 continue;
1113 }
1114 cptr = snd_info_get_str(str, cptr, sizeof(str));
1115 if (! *str) {
1116 /* remove the entry */
1117 down(&mixer->reg_mutex);
1118 mixer_slot_clear(&mixer->slots[ch]);
1119 up(&mixer->reg_mutex);
1120 continue;
1121 }
1122 snd_info_get_str(idxstr, cptr, sizeof(idxstr));
1123 idx = simple_strtoul(idxstr, NULL, 10);
1124 if (idx >= 0x4000) { /* too big */
1125 snd_printk(KERN_ERR "mixer_oss: invalid index %d\n", idx);
1126 continue;
1127 }
1128 down(&mixer->reg_mutex);
1129 slot = (struct slot *)mixer->slots[ch].private_data;
1130 if (slot && slot->assigned &&
1131 slot->assigned->index == idx && ! strcmp(slot->assigned->name, str))
1132 /* not changed */
1133 goto __unlock;
1134 tbl = kmalloc(sizeof(*tbl), GFP_KERNEL);
1135 if (! tbl) {
1136 snd_printk(KERN_ERR "mixer_oss: no memory\n");
1137 goto __unlock;
1138 }
1139 tbl->oss_id = ch;
1140 tbl->name = snd_kmalloc_strdup(str, GFP_KERNEL);
1141 if (! tbl->name) {
1142 kfree(tbl);
1143 goto __unlock;
1144 }
1145 tbl->index = idx;
1146 if (snd_mixer_oss_build_input(mixer, tbl, 1, 1) <= 0) {
1147 kfree(tbl->name);
1148 kfree(tbl);
1149 }
1150 __unlock:
1151 up(&mixer->reg_mutex);
1152 }
1153}
1154
1155static void snd_mixer_oss_proc_init(snd_mixer_oss_t *mixer)
1156{
1157 snd_info_entry_t *entry;
1158
1159 entry = snd_info_create_card_entry(mixer->card, "oss_mixer",
1160 mixer->card->proc_root);
1161 if (! entry)
1162 return;
1163 entry->content = SNDRV_INFO_CONTENT_TEXT;
1164 entry->mode = S_IFREG | S_IRUGO | S_IWUSR;
1165 entry->c.text.read_size = 8192;
1166 entry->c.text.read = snd_mixer_oss_proc_read;
1167 entry->c.text.write_size = 8192;
1168 entry->c.text.write = snd_mixer_oss_proc_write;
1169 entry->private_data = mixer;
1170 if (snd_info_register(entry) < 0) {
1171 snd_info_free_entry(entry);
1172 entry = NULL;
1173 }
1174 mixer->proc_entry = entry;
1175}
1176
1177static void snd_mixer_oss_proc_done(snd_mixer_oss_t *mixer)
1178{
1179 if (mixer->proc_entry) {
1180 snd_info_unregister(mixer->proc_entry);
1181 mixer->proc_entry = NULL;
1182 }
1183}
1184
1185static void snd_mixer_oss_build(snd_mixer_oss_t *mixer)
1186{
1187 static struct snd_mixer_oss_assign_table table[] = {
1188 { SOUND_MIXER_VOLUME, "Master", 0 },
1189 { SOUND_MIXER_VOLUME, "Front", 0 }, /* fallback */
1190 { SOUND_MIXER_BASS, "Tone Control - Bass", 0 },
1191 { SOUND_MIXER_TREBLE, "Tone Control - Treble", 0 },
1192 { SOUND_MIXER_SYNTH, "Synth", 0 },
1193 { SOUND_MIXER_SYNTH, "FM", 0 }, /* fallback */
1194 { SOUND_MIXER_SYNTH, "Music", 0 }, /* fallback */
1195 { SOUND_MIXER_PCM, "PCM", 0 },
1196 { SOUND_MIXER_SPEAKER, "PC Speaker", 0 },
1197 { SOUND_MIXER_LINE, "Line", 0 },
1198 { SOUND_MIXER_MIC, "Mic", 0 },
1199 { SOUND_MIXER_CD, "CD", 0 },
1200 { SOUND_MIXER_IMIX, "Monitor Mix", 0 },
1201 { SOUND_MIXER_ALTPCM, "PCM", 1 },
1202 { SOUND_MIXER_ALTPCM, "Headphone", 0 }, /* fallback */
1203 { SOUND_MIXER_ALTPCM, "Wave", 0 }, /* fallback */
1204 { SOUND_MIXER_RECLEV, "-- nothing --", 0 },
1205 { SOUND_MIXER_IGAIN, "Capture", 0 },
1206 { SOUND_MIXER_OGAIN, "Playback", 0 },
1207 { SOUND_MIXER_LINE1, "Aux", 0 },
1208 { SOUND_MIXER_LINE2, "Aux", 1 },
1209 { SOUND_MIXER_LINE3, "Aux", 2 },
1210 { SOUND_MIXER_DIGITAL1, "Digital", 0 },
1211 { SOUND_MIXER_DIGITAL1, "IEC958", 0 }, /* fallback */
1212 { SOUND_MIXER_DIGITAL1, "IEC958 Optical", 0 }, /* fallback */
1213 { SOUND_MIXER_DIGITAL1, "IEC958 Coaxial", 0 }, /* fallback */
1214 { SOUND_MIXER_DIGITAL2, "Digital", 1 },
1215 { SOUND_MIXER_DIGITAL3, "Digital", 2 },
1216 { SOUND_MIXER_PHONEIN, "Phone", 0 },
1217 { SOUND_MIXER_PHONEOUT, "Master Mono", 0 },
1218 { SOUND_MIXER_PHONEOUT, "Phone", 0 }, /* fallback */
1219 { SOUND_MIXER_VIDEO, "Video", 0 },
1220 { SOUND_MIXER_RADIO, "Radio", 0 },
1221 { SOUND_MIXER_MONITOR, "Monitor", 0 }
1222 };
1223 unsigned int idx;
1224
1225 for (idx = 0; idx < ARRAY_SIZE(table); idx++)
1226 snd_mixer_oss_build_input(mixer, &table[idx], 0, 0);
1227 if (mixer->mask_recsrc) {
1228 mixer->get_recsrc = snd_mixer_oss_get_recsrc2;
1229 mixer->put_recsrc = snd_mixer_oss_put_recsrc2;
1230 }
1231}
1232
1233/*
1234 *
1235 */
1236
1237static int snd_mixer_oss_free1(void *private)
1238{
1239 snd_mixer_oss_t *mixer = private;
1240 snd_card_t * card;
1241 int idx;
1242
1243 snd_assert(mixer != NULL, return -ENXIO);
1244 card = mixer->card;
1245 snd_assert(mixer == card->mixer_oss, return -ENXIO);
1246 card->mixer_oss = NULL;
1247 for (idx = 0; idx < SNDRV_OSS_MAX_MIXERS; idx++) {
1248 snd_mixer_oss_slot_t *chn = &mixer->slots[idx];
1249 if (chn->private_free)
1250 chn->private_free(chn);
1251 }
1252 kfree(mixer);
1253 return 0;
1254}
1255
1256static int snd_mixer_oss_notify_handler(snd_card_t * card, int cmd)
1257{
1258 snd_mixer_oss_t *mixer;
1259
1260 if (cmd == SND_MIXER_OSS_NOTIFY_REGISTER) {
1261 char name[128];
1262 int idx, err;
1263
1264 mixer = kcalloc(2, sizeof(*mixer), GFP_KERNEL);
1265 if (mixer == NULL)
1266 return -ENOMEM;
1267 init_MUTEX(&mixer->reg_mutex);
1268 sprintf(name, "mixer%i%i", card->number, 0);
1269 if ((err = snd_register_oss_device(SNDRV_OSS_DEVICE_TYPE_MIXER,
1270 card, 0,
1271 &snd_mixer_oss_reg,
1272 name)) < 0) {
1273 snd_printk("unable to register OSS mixer device %i:%i\n", card->number, 0);
1274 kfree(mixer);
1275 return err;
1276 }
1277 mixer->oss_dev_alloc = 1;
1278 mixer->card = card;
1279 if (*card->mixername)
1280 strlcpy(mixer->name, card->mixername, sizeof(mixer->name));
1281 else
1282 strlcpy(mixer->name, name, sizeof(mixer->name));
1283#ifdef SNDRV_OSS_INFO_DEV_MIXERS
1284 snd_oss_info_register(SNDRV_OSS_INFO_DEV_MIXERS,
1285 card->number,
1286 mixer->name);
1287#endif
1288 for (idx = 0; idx < SNDRV_OSS_MAX_MIXERS; idx++)
1289 mixer->slots[idx].number = idx;
1290 card->mixer_oss = mixer;
1291 snd_mixer_oss_build(mixer);
1292 snd_mixer_oss_proc_init(mixer);
1293 } else if (cmd == SND_MIXER_OSS_NOTIFY_DISCONNECT) {
1294 mixer = card->mixer_oss;
1295 if (mixer == NULL || !mixer->oss_dev_alloc)
1296 return 0;
1297 snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_MIXER, mixer->card, 0);
1298 mixer->oss_dev_alloc = 0;
1299 } else { /* free */
1300 mixer = card->mixer_oss;
1301 if (mixer == NULL)
1302 return 0;
1303#ifdef SNDRV_OSS_INFO_DEV_MIXERS
1304 snd_oss_info_unregister(SNDRV_OSS_INFO_DEV_MIXERS, mixer->card->number);
1305#endif
1306 if (mixer->oss_dev_alloc)
1307 snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_MIXER, mixer->card, 0);
1308 snd_mixer_oss_proc_done(mixer);
1309 return snd_mixer_oss_free1(mixer);
1310 }
1311 return 0;
1312}
1313
1314static int __init alsa_mixer_oss_init(void)
1315{
1316 int idx;
1317
1318 snd_mixer_oss_notify_callback = snd_mixer_oss_notify_handler;
1319 for (idx = 0; idx < SNDRV_CARDS; idx++) {
1320 if (snd_cards[idx])
1321 snd_mixer_oss_notify_handler(snd_cards[idx], SND_MIXER_OSS_NOTIFY_REGISTER);
1322 }
1323 return 0;
1324}
1325
1326static void __exit alsa_mixer_oss_exit(void)
1327{
1328 int idx;
1329
1330 snd_mixer_oss_notify_callback = NULL;
1331 for (idx = 0; idx < SNDRV_CARDS; idx++) {
1332 if (snd_cards[idx])
1333 snd_mixer_oss_notify_handler(snd_cards[idx], SND_MIXER_OSS_NOTIFY_FREE);
1334 }
1335}
1336
1337module_init(alsa_mixer_oss_init)
1338module_exit(alsa_mixer_oss_exit)
1339
1340EXPORT_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 000000000000..44ec4c66eb19
--- /dev/null
+++ b/sound/core/oss/mulaw.c
@@ -0,0 +1,308 @@
1/*
2 * Mu-Law conversion Plug-In Interface
3 * Copyright (c) 1999 by Jaroslav Kysela <perex@suse.cz>
4 * Uros Bizjak <uros@kss-loka.si>
5 *
6 * Based on reference implementation by Sun Microsystems, Inc.
7 *
8 * This library is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU Library General Public License as
10 * published by the Free Software Foundation; either version 2 of
11 * the License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU Library General Public License for more details.
17 *
18 * You should have received a copy of the GNU Library General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 *
22 */
23
24#include <sound/driver.h>
25#include <linux/time.h>
26#include <sound/core.h>
27#include <sound/pcm.h>
28#include "pcm_plugin.h"
29
30#define SIGN_BIT (0x80) /* Sign bit for a u-law byte. */
31#define QUANT_MASK (0xf) /* Quantization field mask. */
32#define NSEGS (8) /* Number of u-law segments. */
33#define SEG_SHIFT (4) /* Left shift for segment number. */
34#define SEG_MASK (0x70) /* Segment field mask. */
35
36static inline int val_seg(int val)
37{
38 int r = 0;
39 val >>= 7;
40 if (val & 0xf0) {
41 val >>= 4;
42 r += 4;
43 }
44 if (val & 0x0c) {
45 val >>= 2;
46 r += 2;
47 }
48 if (val & 0x02)
49 r += 1;
50 return r;
51}
52
53#define BIAS (0x84) /* Bias for linear code. */
54
55/*
56 * linear2ulaw() - Convert a linear PCM value to u-law
57 *
58 * In order to simplify the encoding process, the original linear magnitude
59 * is biased by adding 33 which shifts the encoding range from (0 - 8158) to
60 * (33 - 8191). The result can be seen in the following encoding table:
61 *
62 * Biased Linear Input Code Compressed Code
63 * ------------------------ ---------------
64 * 00000001wxyza 000wxyz
65 * 0000001wxyzab 001wxyz
66 * 000001wxyzabc 010wxyz
67 * 00001wxyzabcd 011wxyz
68 * 0001wxyzabcde 100wxyz
69 * 001wxyzabcdef 101wxyz
70 * 01wxyzabcdefg 110wxyz
71 * 1wxyzabcdefgh 111wxyz
72 *
73 * Each biased linear code has a leading 1 which identifies the segment
74 * number. The value of the segment number is equal to 7 minus the number
75 * of leading 0's. The quantization interval is directly available as the
76 * four bits wxyz. * The trailing bits (a - h) are ignored.
77 *
78 * Ordinarily the complement of the resulting code word is used for
79 * transmission, and so the code word is complemented before it is returned.
80 *
81 * For further information see John C. Bellamy's Digital Telephony, 1982,
82 * John Wiley & Sons, pps 98-111 and 472-476.
83 */
84static unsigned char linear2ulaw(int pcm_val) /* 2's complement (16-bit range) */
85{
86 int mask;
87 int seg;
88 unsigned char uval;
89
90 /* Get the sign and the magnitude of the value. */
91 if (pcm_val < 0) {
92 pcm_val = BIAS - pcm_val;
93 mask = 0x7F;
94 } else {
95 pcm_val += BIAS;
96 mask = 0xFF;
97 }
98 if (pcm_val > 0x7FFF)
99 pcm_val = 0x7FFF;
100
101 /* Convert the scaled magnitude to segment number. */
102 seg = val_seg(pcm_val);
103
104 /*
105 * Combine the sign, segment, quantization bits;
106 * and complement the code word.
107 */
108 uval = (seg << 4) | ((pcm_val >> (seg + 3)) & 0xF);
109 return uval ^ mask;
110}
111
112/*
113 * ulaw2linear() - Convert a u-law value to 16-bit linear PCM
114 *
115 * First, a biased linear code is derived from the code word. An unbiased
116 * output can then be obtained by subtracting 33 from the biased code.
117 *
118 * Note that this function expects to be passed the complement of the
119 * original code word. This is in keeping with ISDN conventions.
120 */
121static int ulaw2linear(unsigned char u_val)
122{
123 int t;
124
125 /* Complement to obtain normal u-law value. */
126 u_val = ~u_val;
127
128 /*
129 * Extract and bias the quantization bits. Then
130 * shift up by the segment number and subtract out the bias.
131 */
132 t = ((u_val & QUANT_MASK) << 3) + BIAS;
133 t <<= ((unsigned)u_val & SEG_MASK) >> SEG_SHIFT;
134
135 return ((u_val & SIGN_BIT) ? (BIAS - t) : (t - BIAS));
136}
137
138/*
139 * Basic Mu-Law plugin
140 */
141
142typedef void (*mulaw_f)(snd_pcm_plugin_t *plugin,
143 const snd_pcm_plugin_channel_t *src_channels,
144 snd_pcm_plugin_channel_t *dst_channels,
145 snd_pcm_uframes_t frames);
146
147typedef struct mulaw_private_data {
148 mulaw_f func;
149 int conv;
150} mulaw_t;
151
152static void mulaw_decode(snd_pcm_plugin_t *plugin,
153 const snd_pcm_plugin_channel_t *src_channels,
154 snd_pcm_plugin_channel_t *dst_channels,
155 snd_pcm_uframes_t frames)
156{
157#define PUT_S16_LABELS
158#include "plugin_ops.h"
159#undef PUT_S16_LABELS
160 mulaw_t *data = (mulaw_t *)plugin->extra_data;
161 void *put = put_s16_labels[data->conv];
162 int channel;
163 int nchannels = plugin->src_format.channels;
164 for (channel = 0; channel < nchannels; ++channel) {
165 char *src;
166 char *dst;
167 int src_step, dst_step;
168 snd_pcm_uframes_t frames1;
169 if (!src_channels[channel].enabled) {
170 if (dst_channels[channel].wanted)
171 snd_pcm_area_silence(&dst_channels[channel].area, 0, frames, plugin->dst_format.format);
172 dst_channels[channel].enabled = 0;
173 continue;
174 }
175 dst_channels[channel].enabled = 1;
176 src = src_channels[channel].area.addr + src_channels[channel].area.first / 8;
177 dst = dst_channels[channel].area.addr + dst_channels[channel].area.first / 8;
178 src_step = src_channels[channel].area.step / 8;
179 dst_step = dst_channels[channel].area.step / 8;
180 frames1 = frames;
181 while (frames1-- > 0) {
182 signed short sample = ulaw2linear(*src);
183 goto *put;
184#define PUT_S16_END after
185#include "plugin_ops.h"
186#undef PUT_S16_END
187 after:
188 src += src_step;
189 dst += dst_step;
190 }
191 }
192}
193
194static void mulaw_encode(snd_pcm_plugin_t *plugin,
195 const snd_pcm_plugin_channel_t *src_channels,
196 snd_pcm_plugin_channel_t *dst_channels,
197 snd_pcm_uframes_t frames)
198{
199#define GET_S16_LABELS
200#include "plugin_ops.h"
201#undef GET_S16_LABELS
202 mulaw_t *data = (mulaw_t *)plugin->extra_data;
203 void *get = get_s16_labels[data->conv];
204 int channel;
205 int nchannels = plugin->src_format.channels;
206 signed short sample = 0;
207 for (channel = 0; channel < nchannels; ++channel) {
208 char *src;
209 char *dst;
210 int src_step, dst_step;
211 snd_pcm_uframes_t frames1;
212 if (!src_channels[channel].enabled) {
213 if (dst_channels[channel].wanted)
214 snd_pcm_area_silence(&dst_channels[channel].area, 0, frames, plugin->dst_format.format);
215 dst_channels[channel].enabled = 0;
216 continue;
217 }
218 dst_channels[channel].enabled = 1;
219 src = src_channels[channel].area.addr + src_channels[channel].area.first / 8;
220 dst = dst_channels[channel].area.addr + dst_channels[channel].area.first / 8;
221 src_step = src_channels[channel].area.step / 8;
222 dst_step = dst_channels[channel].area.step / 8;
223 frames1 = frames;
224 while (frames1-- > 0) {
225 goto *get;
226#define GET_S16_END after
227#include "plugin_ops.h"
228#undef GET_S16_END
229 after:
230 *dst = linear2ulaw(sample);
231 src += src_step;
232 dst += dst_step;
233 }
234 }
235}
236
237static snd_pcm_sframes_t mulaw_transfer(snd_pcm_plugin_t *plugin,
238 const snd_pcm_plugin_channel_t *src_channels,
239 snd_pcm_plugin_channel_t *dst_channels,
240 snd_pcm_uframes_t frames)
241{
242 mulaw_t *data;
243
244 snd_assert(plugin != NULL && src_channels != NULL && dst_channels != NULL, return -ENXIO);
245 if (frames == 0)
246 return 0;
247#ifdef CONFIG_SND_DEBUG
248 {
249 unsigned int channel;
250 for (channel = 0; channel < plugin->src_format.channels; channel++) {
251 snd_assert(src_channels[channel].area.first % 8 == 0 &&
252 src_channels[channel].area.step % 8 == 0,
253 return -ENXIO);
254 snd_assert(dst_channels[channel].area.first % 8 == 0 &&
255 dst_channels[channel].area.step % 8 == 0,
256 return -ENXIO);
257 }
258 }
259#endif
260 data = (mulaw_t *)plugin->extra_data;
261 data->func(plugin, src_channels, dst_channels, frames);
262 return frames;
263}
264
265int snd_pcm_plugin_build_mulaw(snd_pcm_plug_t *plug,
266 snd_pcm_plugin_format_t *src_format,
267 snd_pcm_plugin_format_t *dst_format,
268 snd_pcm_plugin_t **r_plugin)
269{
270 int err;
271 mulaw_t *data;
272 snd_pcm_plugin_t *plugin;
273 snd_pcm_plugin_format_t *format;
274 mulaw_f func;
275
276 snd_assert(r_plugin != NULL, return -ENXIO);
277 *r_plugin = NULL;
278
279 snd_assert(src_format->rate == dst_format->rate, return -ENXIO);
280 snd_assert(src_format->channels == dst_format->channels, return -ENXIO);
281
282 if (dst_format->format == SNDRV_PCM_FORMAT_MU_LAW) {
283 format = src_format;
284 func = mulaw_encode;
285 }
286 else if (src_format->format == SNDRV_PCM_FORMAT_MU_LAW) {
287 format = dst_format;
288 func = mulaw_decode;
289 }
290 else {
291 snd_BUG();
292 return -EINVAL;
293 }
294 snd_assert(snd_pcm_format_linear(format->format) != 0, return -ENXIO);
295
296 err = snd_pcm_plugin_build(plug, "Mu-Law<->linear conversion",
297 src_format, dst_format,
298 sizeof(mulaw_t), &plugin);
299 if (err < 0)
300 return err;
301 data = (mulaw_t*)plugin->extra_data;
302 data->func = func;
303 data->conv = getput_index(format->format);
304 snd_assert(data->conv >= 0 && data->conv < 4*2*2, return -EINVAL);
305 plugin->transfer = mulaw_transfer;
306 *r_plugin = plugin;
307 return 0;
308}
diff --git a/sound/core/oss/pcm_oss.c b/sound/core/oss/pcm_oss.c
new file mode 100644
index 000000000000..1a805020f57a
--- /dev/null
+++ b/sound/core/oss/pcm_oss.c
@@ -0,0 +1,2530 @@
1/*
2 * Digital Audio (PCM) abstract layer / OSS compatible
3 * Copyright (c) by Jaroslav Kysela <perex@suse.cz>
4 *
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 *
20 */
21
22#if 0
23#define PLUGIN_DEBUG
24#endif
25#if 0
26#define OSS_DEBUG
27#endif
28
29#include <sound/driver.h>
30#include <linux/init.h>
31#include <linux/smp_lock.h>
32#include <linux/slab.h>
33#include <linux/time.h>
34#include <linux/vmalloc.h>
35#include <linux/moduleparam.h>
36#include <sound/core.h>
37#include <sound/minors.h>
38#include <sound/pcm.h>
39#include <sound/pcm_params.h>
40#include "pcm_plugin.h"
41#include <sound/info.h>
42#include <linux/soundcard.h>
43#include <sound/initval.h>
44
45#define OSS_ALSAEMULVER _SIOR ('M', 249, int)
46
47static int dsp_map[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = 0};
48static int adsp_map[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = 1};
49static int nonblock_open = 1;
50
51MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>, Abramo Bagnara <abramo@alsa-project.org>");
52MODULE_DESCRIPTION("PCM OSS emulation for ALSA.");
53MODULE_LICENSE("GPL");
54module_param_array(dsp_map, int, NULL, 0444);
55MODULE_PARM_DESC(dsp_map, "PCM device number assigned to 1st OSS device.");
56module_param_array(adsp_map, int, NULL, 0444);
57MODULE_PARM_DESC(adsp_map, "PCM device number assigned to 2nd OSS device.");
58module_param(nonblock_open, bool, 0644);
59MODULE_PARM_DESC(nonblock_open, "Don't block opening busy PCM devices.");
60MODULE_ALIAS_SNDRV_MINOR(SNDRV_MINOR_OSS_PCM);
61MODULE_ALIAS_SNDRV_MINOR(SNDRV_MINOR_OSS_PCM1);
62
63extern int snd_mixer_oss_ioctl_card(snd_card_t *card, unsigned int cmd, unsigned long arg);
64static int snd_pcm_oss_get_rate(snd_pcm_oss_file_t *pcm_oss_file);
65static int snd_pcm_oss_get_channels(snd_pcm_oss_file_t *pcm_oss_file);
66static int snd_pcm_oss_get_format(snd_pcm_oss_file_t *pcm_oss_file);
67
68static inline mm_segment_t snd_enter_user(void)
69{
70 mm_segment_t fs = get_fs();
71 set_fs(get_ds());
72 return fs;
73}
74
75static inline void snd_leave_user(mm_segment_t fs)
76{
77 set_fs(fs);
78}
79
80static int snd_pcm_oss_plugin_clear(snd_pcm_substream_t *substream)
81{
82 snd_pcm_runtime_t *runtime = substream->runtime;
83 snd_pcm_plugin_t *plugin, *next;
84
85 plugin = runtime->oss.plugin_first;
86 while (plugin) {
87 next = plugin->next;
88 snd_pcm_plugin_free(plugin);
89 plugin = next;
90 }
91 runtime->oss.plugin_first = runtime->oss.plugin_last = NULL;
92 return 0;
93}
94
95static int snd_pcm_plugin_insert(snd_pcm_plugin_t *plugin)
96{
97 snd_pcm_runtime_t *runtime = plugin->plug->runtime;
98 plugin->next = runtime->oss.plugin_first;
99 plugin->prev = NULL;
100 if (runtime->oss.plugin_first) {
101 runtime->oss.plugin_first->prev = plugin;
102 runtime->oss.plugin_first = plugin;
103 } else {
104 runtime->oss.plugin_last =
105 runtime->oss.plugin_first = plugin;
106 }
107 return 0;
108}
109
110int snd_pcm_plugin_append(snd_pcm_plugin_t *plugin)
111{
112 snd_pcm_runtime_t *runtime = plugin->plug->runtime;
113 plugin->next = NULL;
114 plugin->prev = runtime->oss.plugin_last;
115 if (runtime->oss.plugin_last) {
116 runtime->oss.plugin_last->next = plugin;
117 runtime->oss.plugin_last = plugin;
118 } else {
119 runtime->oss.plugin_last =
120 runtime->oss.plugin_first = plugin;
121 }
122 return 0;
123}
124
125static long snd_pcm_oss_bytes(snd_pcm_substream_t *substream, long frames)
126{
127 snd_pcm_runtime_t *runtime = substream->runtime;
128 snd_pcm_uframes_t buffer_size = snd_pcm_lib_buffer_bytes(substream);
129 frames = frames_to_bytes(runtime, frames);
130 if (buffer_size == runtime->oss.buffer_bytes)
131 return frames;
132 return (runtime->oss.buffer_bytes * frames) / buffer_size;
133}
134
135static long snd_pcm_alsa_frames(snd_pcm_substream_t *substream, long bytes)
136{
137 snd_pcm_runtime_t *runtime = substream->runtime;
138 snd_pcm_uframes_t buffer_size = snd_pcm_lib_buffer_bytes(substream);
139 if (buffer_size == runtime->oss.buffer_bytes)
140 return bytes_to_frames(runtime, bytes);
141 return bytes_to_frames(runtime, (buffer_size * bytes) / runtime->oss.buffer_bytes);
142}
143
144static int snd_pcm_oss_format_from(int format)
145{
146 switch (format) {
147 case AFMT_MU_LAW: return SNDRV_PCM_FORMAT_MU_LAW;
148 case AFMT_A_LAW: return SNDRV_PCM_FORMAT_A_LAW;
149 case AFMT_IMA_ADPCM: return SNDRV_PCM_FORMAT_IMA_ADPCM;
150 case AFMT_U8: return SNDRV_PCM_FORMAT_U8;
151 case AFMT_S16_LE: return SNDRV_PCM_FORMAT_S16_LE;
152 case AFMT_S16_BE: return SNDRV_PCM_FORMAT_S16_BE;
153 case AFMT_S8: return SNDRV_PCM_FORMAT_S8;
154 case AFMT_U16_LE: return SNDRV_PCM_FORMAT_U16_LE;
155 case AFMT_U16_BE: return SNDRV_PCM_FORMAT_U16_BE;
156 case AFMT_MPEG: return SNDRV_PCM_FORMAT_MPEG;
157 default: return SNDRV_PCM_FORMAT_U8;
158 }
159}
160
161static int snd_pcm_oss_format_to(int format)
162{
163 switch (format) {
164 case SNDRV_PCM_FORMAT_MU_LAW: return AFMT_MU_LAW;
165 case SNDRV_PCM_FORMAT_A_LAW: return AFMT_A_LAW;
166 case SNDRV_PCM_FORMAT_IMA_ADPCM: return AFMT_IMA_ADPCM;
167 case SNDRV_PCM_FORMAT_U8: return AFMT_U8;
168 case SNDRV_PCM_FORMAT_S16_LE: return AFMT_S16_LE;
169 case SNDRV_PCM_FORMAT_S16_BE: return AFMT_S16_BE;
170 case SNDRV_PCM_FORMAT_S8: return AFMT_S8;
171 case SNDRV_PCM_FORMAT_U16_LE: return AFMT_U16_LE;
172 case SNDRV_PCM_FORMAT_U16_BE: return AFMT_U16_BE;
173 case SNDRV_PCM_FORMAT_MPEG: return AFMT_MPEG;
174 default: return -EINVAL;
175 }
176}
177
178static int snd_pcm_oss_period_size(snd_pcm_substream_t *substream,
179 snd_pcm_hw_params_t *oss_params,
180 snd_pcm_hw_params_t *slave_params)
181{
182 size_t s;
183 size_t oss_buffer_size, oss_period_size, oss_periods;
184 size_t min_period_size, max_period_size;
185 snd_pcm_runtime_t *runtime = substream->runtime;
186 size_t oss_frame_size;
187
188 oss_frame_size = snd_pcm_format_physical_width(params_format(oss_params)) *
189 params_channels(oss_params) / 8;
190
191 oss_buffer_size = snd_pcm_plug_client_size(substream,
192 snd_pcm_hw_param_value_max(slave_params, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, NULL)) * oss_frame_size;
193 oss_buffer_size = 1 << ld2(oss_buffer_size);
194 if (atomic_read(&runtime->mmap_count)) {
195 if (oss_buffer_size > runtime->oss.mmap_bytes)
196 oss_buffer_size = runtime->oss.mmap_bytes;
197 }
198
199 if (substream->oss.setup &&
200 substream->oss.setup->period_size > 16)
201 oss_period_size = substream->oss.setup->period_size;
202 else if (runtime->oss.fragshift) {
203 oss_period_size = 1 << runtime->oss.fragshift;
204 if (oss_period_size > oss_buffer_size / 2)
205 oss_period_size = oss_buffer_size / 2;
206 } else {
207 int sd;
208 size_t bytes_per_sec = params_rate(oss_params) * snd_pcm_format_physical_width(params_format(oss_params)) * params_channels(oss_params) / 8;
209
210 oss_period_size = oss_buffer_size;
211 do {
212 oss_period_size /= 2;
213 } while (oss_period_size > bytes_per_sec);
214 if (runtime->oss.subdivision == 0) {
215 sd = 4;
216 if (oss_period_size / sd > 4096)
217 sd *= 2;
218 if (oss_period_size / sd < 4096)
219 sd = 1;
220 } else
221 sd = runtime->oss.subdivision;
222 oss_period_size /= sd;
223 if (oss_period_size < 16)
224 oss_period_size = 16;
225 }
226
227 min_period_size = snd_pcm_plug_client_size(substream,
228 snd_pcm_hw_param_value_min(slave_params, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, NULL));
229 min_period_size *= oss_frame_size;
230 min_period_size = 1 << (ld2(min_period_size - 1) + 1);
231 if (oss_period_size < min_period_size)
232 oss_period_size = min_period_size;
233
234 max_period_size = snd_pcm_plug_client_size(substream,
235 snd_pcm_hw_param_value_max(slave_params, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, NULL));
236 max_period_size *= oss_frame_size;
237 max_period_size = 1 << ld2(max_period_size);
238 if (oss_period_size > max_period_size)
239 oss_period_size = max_period_size;
240
241 oss_periods = oss_buffer_size / oss_period_size;
242
243 if (substream->oss.setup) {
244 if (substream->oss.setup->periods > 1)
245 oss_periods = substream->oss.setup->periods;
246 }
247
248 s = snd_pcm_hw_param_value_max(slave_params, SNDRV_PCM_HW_PARAM_PERIODS, NULL);
249 if (runtime->oss.maxfrags && s > runtime->oss.maxfrags)
250 s = runtime->oss.maxfrags;
251 if (oss_periods > s)
252 oss_periods = s;
253
254 s = snd_pcm_hw_param_value_min(slave_params, SNDRV_PCM_HW_PARAM_PERIODS, NULL);
255 if (s < 2)
256 s = 2;
257 if (oss_periods < s)
258 oss_periods = s;
259
260 while (oss_period_size * oss_periods > oss_buffer_size)
261 oss_period_size /= 2;
262
263 snd_assert(oss_period_size >= 16, return -EINVAL);
264 runtime->oss.period_bytes = oss_period_size;
265 runtime->oss.period_frames = 1;
266 runtime->oss.periods = oss_periods;
267 return 0;
268}
269
270static int choose_rate(snd_pcm_substream_t *substream,
271 snd_pcm_hw_params_t *params, unsigned int best_rate)
272{
273 snd_interval_t *it;
274 snd_pcm_hw_params_t *save;
275 unsigned int rate, prev;
276
277 save = kmalloc(sizeof(*save), GFP_KERNEL);
278 if (save == NULL)
279 return -ENOMEM;
280 *save = *params;
281 it = hw_param_interval(save, SNDRV_PCM_HW_PARAM_RATE);
282
283 /* try multiples of the best rate */
284 rate = best_rate;
285 for (;;) {
286 if (it->max < rate || (it->max == rate && it->openmax))
287 break;
288 if (it->min < rate || (it->min == rate && !it->openmin)) {
289 int ret;
290 ret = snd_pcm_hw_param_set(substream, params,
291 SNDRV_PCM_HW_PARAM_RATE,
292 rate, 0);
293 if (ret == (int)rate) {
294 kfree(save);
295 return rate;
296 }
297 *params = *save;
298 }
299 prev = rate;
300 rate += best_rate;
301 if (rate <= prev)
302 break;
303 }
304
305 /* not found, use the nearest rate */
306 kfree(save);
307 return snd_pcm_hw_param_near(substream, params, SNDRV_PCM_HW_PARAM_RATE, best_rate, NULL);
308}
309
310static int snd_pcm_oss_change_params(snd_pcm_substream_t *substream)
311{
312 snd_pcm_runtime_t *runtime = substream->runtime;
313 snd_pcm_hw_params_t *params, *sparams;
314 snd_pcm_sw_params_t *sw_params;
315 ssize_t oss_buffer_size, oss_period_size;
316 size_t oss_frame_size;
317 int err;
318 int direct;
319 int format, sformat, n;
320 snd_mask_t sformat_mask;
321 snd_mask_t mask;
322
323 sw_params = kmalloc(sizeof(*sw_params), GFP_KERNEL);
324 params = kmalloc(sizeof(*params), GFP_KERNEL);
325 sparams = kmalloc(sizeof(*sparams), GFP_KERNEL);
326 if (!sw_params || !params || !sparams) {
327 snd_printd("No memory\n");
328 err = -ENOMEM;
329 goto failure;
330 }
331
332 if (atomic_read(&runtime->mmap_count)) {
333 direct = 1;
334 } else {
335 snd_pcm_oss_setup_t *setup = substream->oss.setup;
336 direct = (setup != NULL && setup->direct);
337 }
338
339 _snd_pcm_hw_params_any(sparams);
340 _snd_pcm_hw_param_setinteger(sparams, SNDRV_PCM_HW_PARAM_PERIODS);
341 _snd_pcm_hw_param_min(sparams, SNDRV_PCM_HW_PARAM_PERIODS, 2, 0);
342 snd_mask_none(&mask);
343 if (atomic_read(&runtime->mmap_count))
344 snd_mask_set(&mask, SNDRV_PCM_ACCESS_MMAP_INTERLEAVED);
345 else {
346 snd_mask_set(&mask, SNDRV_PCM_ACCESS_RW_INTERLEAVED);
347 if (!direct)
348 snd_mask_set(&mask, SNDRV_PCM_ACCESS_RW_NONINTERLEAVED);
349 }
350 err = snd_pcm_hw_param_mask(substream, sparams, SNDRV_PCM_HW_PARAM_ACCESS, &mask);
351 if (err < 0) {
352 snd_printd("No usable accesses\n");
353 err = -EINVAL;
354 goto failure;
355 }
356 choose_rate(substream, sparams, runtime->oss.rate);
357 snd_pcm_hw_param_near(substream, sparams, SNDRV_PCM_HW_PARAM_CHANNELS, runtime->oss.channels, NULL);
358
359 format = snd_pcm_oss_format_from(runtime->oss.format);
360
361 sformat_mask = *hw_param_mask(sparams, SNDRV_PCM_HW_PARAM_FORMAT);
362 if (direct)
363 sformat = format;
364 else
365 sformat = snd_pcm_plug_slave_format(format, &sformat_mask);
366
367 if (sformat < 0 || !snd_mask_test(&sformat_mask, sformat)) {
368 for (sformat = 0; sformat <= SNDRV_PCM_FORMAT_LAST; sformat++) {
369 if (snd_mask_test(&sformat_mask, sformat) &&
370 snd_pcm_oss_format_to(sformat) >= 0)
371 break;
372 }
373 if (sformat > SNDRV_PCM_FORMAT_LAST) {
374 snd_printd("Cannot find a format!!!\n");
375 err = -EINVAL;
376 goto failure;
377 }
378 }
379 err = _snd_pcm_hw_param_set(sparams, SNDRV_PCM_HW_PARAM_FORMAT, sformat, 0);
380 snd_assert(err >= 0, goto failure);
381
382 if (direct) {
383 memcpy(params, sparams, sizeof(*params));
384 } else {
385 _snd_pcm_hw_params_any(params);
386 _snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_ACCESS,
387 SNDRV_PCM_ACCESS_RW_INTERLEAVED, 0);
388 _snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_FORMAT,
389 snd_pcm_oss_format_from(runtime->oss.format), 0);
390 _snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_CHANNELS,
391 runtime->oss.channels, 0);
392 _snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_RATE,
393 runtime->oss.rate, 0);
394 pdprintf("client: access = %i, format = %i, channels = %i, rate = %i\n",
395 params_access(params), params_format(params),
396 params_channels(params), params_rate(params));
397 }
398 pdprintf("slave: access = %i, format = %i, channels = %i, rate = %i\n",
399 params_access(sparams), params_format(sparams),
400 params_channels(sparams), params_rate(sparams));
401
402 oss_frame_size = snd_pcm_format_physical_width(params_format(params)) *
403 params_channels(params) / 8;
404
405 snd_pcm_oss_plugin_clear(substream);
406 if (!direct) {
407 /* add necessary plugins */
408 snd_pcm_oss_plugin_clear(substream);
409 if ((err = snd_pcm_plug_format_plugins(substream,
410 params,
411 sparams)) < 0) {
412 snd_printd("snd_pcm_plug_format_plugins failed: %i\n", err);
413 snd_pcm_oss_plugin_clear(substream);
414 goto failure;
415 }
416 if (runtime->oss.plugin_first) {
417 snd_pcm_plugin_t *plugin;
418 if ((err = snd_pcm_plugin_build_io(substream, sparams, &plugin)) < 0) {
419 snd_printd("snd_pcm_plugin_build_io failed: %i\n", err);
420 snd_pcm_oss_plugin_clear(substream);
421 goto failure;
422 }
423 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
424 err = snd_pcm_plugin_append(plugin);
425 } else {
426 err = snd_pcm_plugin_insert(plugin);
427 }
428 if (err < 0) {
429 snd_pcm_oss_plugin_clear(substream);
430 goto failure;
431 }
432 }
433 }
434
435 err = snd_pcm_oss_period_size(substream, params, sparams);
436 if (err < 0)
437 goto failure;
438
439 n = snd_pcm_plug_slave_size(substream, runtime->oss.period_bytes / oss_frame_size);
440 err = snd_pcm_hw_param_near(substream, sparams, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, n, NULL);
441 snd_assert(err >= 0, goto failure);
442
443 err = snd_pcm_hw_param_near(substream, sparams, SNDRV_PCM_HW_PARAM_PERIODS,
444 runtime->oss.periods, NULL);
445 snd_assert(err >= 0, goto failure);
446
447 snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DROP, NULL);
448
449 if ((err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_HW_PARAMS, sparams)) < 0) {
450 snd_printd("HW_PARAMS failed: %i\n", err);
451 goto failure;
452 }
453
454 memset(sw_params, 0, sizeof(*sw_params));
455 if (runtime->oss.trigger) {
456 sw_params->start_threshold = 1;
457 } else {
458 sw_params->start_threshold = runtime->boundary;
459 }
460 if (atomic_read(&runtime->mmap_count) || substream->stream == SNDRV_PCM_STREAM_CAPTURE)
461 sw_params->stop_threshold = runtime->boundary;
462 else
463 sw_params->stop_threshold = runtime->buffer_size;
464 sw_params->tstamp_mode = SNDRV_PCM_TSTAMP_NONE;
465 sw_params->period_step = 1;
466 sw_params->sleep_min = 0;
467 sw_params->avail_min = 1;
468 sw_params->xfer_align = 1;
469 if (atomic_read(&runtime->mmap_count) ||
470 (substream->oss.setup && substream->oss.setup->nosilence)) {
471 sw_params->silence_threshold = 0;
472 sw_params->silence_size = 0;
473 } else {
474 snd_pcm_uframes_t frames;
475 frames = runtime->period_size + 16;
476 if (frames > runtime->buffer_size)
477 frames = runtime->buffer_size;
478 sw_params->silence_threshold = frames;
479 sw_params->silence_size = frames;
480 }
481
482 if ((err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_SW_PARAMS, sw_params)) < 0) {
483 snd_printd("SW_PARAMS failed: %i\n", err);
484 goto failure;
485 }
486
487 runtime->oss.periods = params_periods(sparams);
488 oss_period_size = snd_pcm_plug_client_size(substream, params_period_size(sparams));
489 snd_assert(oss_period_size >= 0, err = -EINVAL; goto failure);
490 if (runtime->oss.plugin_first) {
491 err = snd_pcm_plug_alloc(substream, oss_period_size);
492 if (err < 0)
493 goto failure;
494 }
495 oss_period_size *= oss_frame_size;
496
497 oss_buffer_size = oss_period_size * runtime->oss.periods;
498 snd_assert(oss_buffer_size >= 0, err = -EINVAL; goto failure);
499
500 runtime->oss.period_bytes = oss_period_size;
501 runtime->oss.buffer_bytes = oss_buffer_size;
502
503 pdprintf("oss: period bytes = %i, buffer bytes = %i\n",
504 runtime->oss.period_bytes,
505 runtime->oss.buffer_bytes);
506 pdprintf("slave: period_size = %i, buffer_size = %i\n",
507 params_period_size(sparams),
508 params_buffer_size(sparams));
509
510 runtime->oss.format = snd_pcm_oss_format_to(params_format(params));
511 runtime->oss.channels = params_channels(params);
512 runtime->oss.rate = params_rate(params);
513
514 runtime->oss.params = 0;
515 runtime->oss.prepare = 1;
516 vfree(runtime->oss.buffer);
517 runtime->oss.buffer = vmalloc(runtime->oss.period_bytes);
518 runtime->oss.buffer_used = 0;
519 if (runtime->dma_area)
520 snd_pcm_format_set_silence(runtime->format, runtime->dma_area, bytes_to_samples(runtime, runtime->dma_bytes));
521
522 runtime->oss.period_frames = snd_pcm_alsa_frames(substream, oss_period_size);
523
524 err = 0;
525failure:
526 kfree(sw_params);
527 kfree(params);
528 kfree(sparams);
529 return err;
530}
531
532static int snd_pcm_oss_get_active_substream(snd_pcm_oss_file_t *pcm_oss_file, snd_pcm_substream_t **r_substream)
533{
534 int idx, err;
535 snd_pcm_substream_t *asubstream = NULL, *substream;
536
537 for (idx = 0; idx < 2; idx++) {
538 substream = pcm_oss_file->streams[idx];
539 if (substream == NULL)
540 continue;
541 if (asubstream == NULL)
542 asubstream = substream;
543 if (substream->runtime->oss.params) {
544 err = snd_pcm_oss_change_params(substream);
545 if (err < 0)
546 return err;
547 }
548 }
549 snd_assert(asubstream != NULL, return -EIO);
550 if (r_substream)
551 *r_substream = asubstream;
552 return 0;
553}
554
555static int snd_pcm_oss_prepare(snd_pcm_substream_t *substream)
556{
557 int err;
558 snd_pcm_runtime_t *runtime = substream->runtime;
559
560 err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_PREPARE, NULL);
561 if (err < 0) {
562 snd_printd("snd_pcm_oss_prepare: SNDRV_PCM_IOCTL_PREPARE failed\n");
563 return err;
564 }
565 runtime->oss.prepare = 0;
566 runtime->oss.prev_hw_ptr_interrupt = 0;
567 runtime->oss.period_ptr = 0;
568 runtime->oss.buffer_used = 0;
569
570 return 0;
571}
572
573static int snd_pcm_oss_make_ready(snd_pcm_substream_t *substream)
574{
575 snd_pcm_runtime_t *runtime;
576 int err;
577
578 if (substream == NULL)
579 return 0;
580 runtime = substream->runtime;
581 if (runtime->oss.params) {
582 err = snd_pcm_oss_change_params(substream);
583 if (err < 0)
584 return err;
585 }
586 if (runtime->oss.prepare) {
587 err = snd_pcm_oss_prepare(substream);
588 if (err < 0)
589 return err;
590 }
591 return 0;
592}
593
594static int snd_pcm_oss_capture_position_fixup(snd_pcm_substream_t *substream, snd_pcm_sframes_t *delay)
595{
596 snd_pcm_runtime_t *runtime;
597 snd_pcm_uframes_t frames;
598 int err = 0;
599
600 while (1) {
601 err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DELAY, delay);
602 if (err < 0)
603 break;
604 runtime = substream->runtime;
605 if (*delay <= (snd_pcm_sframes_t)runtime->buffer_size)
606 break;
607 /* in case of overrun, skip whole periods like OSS/Linux driver does */
608 /* until avail(delay) <= buffer_size */
609 frames = (*delay - runtime->buffer_size) + runtime->period_size - 1;
610 frames /= runtime->period_size;
611 frames *= runtime->period_size;
612 err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_FORWARD, &frames);
613 if (err < 0)
614 break;
615 }
616 return err;
617}
618
619snd_pcm_sframes_t snd_pcm_oss_write3(snd_pcm_substream_t *substream, const char *ptr, snd_pcm_uframes_t frames, int in_kernel)
620{
621 snd_pcm_runtime_t *runtime = substream->runtime;
622 int ret;
623 while (1) {
624 if (runtime->status->state == SNDRV_PCM_STATE_XRUN ||
625 runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) {
626#ifdef OSS_DEBUG
627 if (runtime->status->state == SNDRV_PCM_STATE_XRUN)
628 printk("pcm_oss: write: recovering from XRUN\n");
629 else
630 printk("pcm_oss: write: recovering from SUSPEND\n");
631#endif
632 ret = snd_pcm_oss_prepare(substream);
633 if (ret < 0)
634 break;
635 }
636 if (in_kernel) {
637 mm_segment_t fs;
638 fs = snd_enter_user();
639 ret = snd_pcm_lib_write(substream, (void __user *)ptr, frames);
640 snd_leave_user(fs);
641 } else {
642 ret = snd_pcm_lib_write(substream, (void __user *)ptr, frames);
643 }
644 if (ret != -EPIPE && ret != -ESTRPIPE)
645 break;
646 /* test, if we can't store new data, because the stream */
647 /* has not been started */
648 if (runtime->status->state == SNDRV_PCM_STATE_PREPARED)
649 return -EAGAIN;
650 }
651 return ret;
652}
653
654snd_pcm_sframes_t snd_pcm_oss_read3(snd_pcm_substream_t *substream, char *ptr, snd_pcm_uframes_t frames, int in_kernel)
655{
656 snd_pcm_runtime_t *runtime = substream->runtime;
657 snd_pcm_sframes_t delay;
658 int ret;
659 while (1) {
660 if (runtime->status->state == SNDRV_PCM_STATE_XRUN ||
661 runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) {
662#ifdef OSS_DEBUG
663 if (runtime->status->state == SNDRV_PCM_STATE_XRUN)
664 printk("pcm_oss: read: recovering from XRUN\n");
665 else
666 printk("pcm_oss: read: recovering from SUSPEND\n");
667#endif
668 ret = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DRAIN, NULL);
669 if (ret < 0)
670 break;
671 } else if (runtime->status->state == SNDRV_PCM_STATE_SETUP) {
672 ret = snd_pcm_oss_prepare(substream);
673 if (ret < 0)
674 break;
675 }
676 ret = snd_pcm_oss_capture_position_fixup(substream, &delay);
677 if (ret < 0)
678 break;
679 if (in_kernel) {
680 mm_segment_t fs;
681 fs = snd_enter_user();
682 ret = snd_pcm_lib_read(substream, (void __user *)ptr, frames);
683 snd_leave_user(fs);
684 } else {
685 ret = snd_pcm_lib_read(substream, (void __user *)ptr, frames);
686 }
687 if (ret == -EPIPE) {
688 if (runtime->status->state == SNDRV_PCM_STATE_DRAINING) {
689 ret = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DROP, NULL);
690 if (ret < 0)
691 break;
692 }
693 continue;
694 }
695 if (ret != -ESTRPIPE)
696 break;
697 }
698 return ret;
699}
700
701snd_pcm_sframes_t snd_pcm_oss_writev3(snd_pcm_substream_t *substream, void **bufs, snd_pcm_uframes_t frames, int in_kernel)
702{
703 snd_pcm_runtime_t *runtime = substream->runtime;
704 int ret;
705 while (1) {
706 if (runtime->status->state == SNDRV_PCM_STATE_XRUN ||
707 runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) {
708#ifdef OSS_DEBUG
709 if (runtime->status->state == SNDRV_PCM_STATE_XRUN)
710 printk("pcm_oss: writev: recovering from XRUN\n");
711 else
712 printk("pcm_oss: writev: recovering from SUSPEND\n");
713#endif
714 ret = snd_pcm_oss_prepare(substream);
715 if (ret < 0)
716 break;
717 }
718 if (in_kernel) {
719 mm_segment_t fs;
720 fs = snd_enter_user();
721 ret = snd_pcm_lib_writev(substream, (void __user **)bufs, frames);
722 snd_leave_user(fs);
723 } else {
724 ret = snd_pcm_lib_writev(substream, (void __user **)bufs, frames);
725 }
726 if (ret != -EPIPE && ret != -ESTRPIPE)
727 break;
728
729 /* test, if we can't store new data, because the stream */
730 /* has not been started */
731 if (runtime->status->state == SNDRV_PCM_STATE_PREPARED)
732 return -EAGAIN;
733 }
734 return ret;
735}
736
737snd_pcm_sframes_t snd_pcm_oss_readv3(snd_pcm_substream_t *substream, void **bufs, snd_pcm_uframes_t frames, int in_kernel)
738{
739 snd_pcm_runtime_t *runtime = substream->runtime;
740 int ret;
741 while (1) {
742 if (runtime->status->state == SNDRV_PCM_STATE_XRUN ||
743 runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) {
744#ifdef OSS_DEBUG
745 if (runtime->status->state == SNDRV_PCM_STATE_XRUN)
746 printk("pcm_oss: readv: recovering from XRUN\n");
747 else
748 printk("pcm_oss: readv: recovering from SUSPEND\n");
749#endif
750 ret = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DRAIN, NULL);
751 if (ret < 0)
752 break;
753 } else if (runtime->status->state == SNDRV_PCM_STATE_SETUP) {
754 ret = snd_pcm_oss_prepare(substream);
755 if (ret < 0)
756 break;
757 }
758 if (in_kernel) {
759 mm_segment_t fs;
760 fs = snd_enter_user();
761 ret = snd_pcm_lib_readv(substream, (void __user **)bufs, frames);
762 snd_leave_user(fs);
763 } else {
764 ret = snd_pcm_lib_readv(substream, (void __user **)bufs, frames);
765 }
766 if (ret != -EPIPE && ret != -ESTRPIPE)
767 break;
768 }
769 return ret;
770}
771
772static ssize_t snd_pcm_oss_write2(snd_pcm_substream_t *substream, const char *buf, size_t bytes, int in_kernel)
773{
774 snd_pcm_runtime_t *runtime = substream->runtime;
775 snd_pcm_sframes_t frames, frames1;
776 if (runtime->oss.plugin_first) {
777 snd_pcm_plugin_channel_t *channels;
778 size_t oss_frame_bytes = (runtime->oss.plugin_first->src_width * runtime->oss.plugin_first->src_format.channels) / 8;
779 if (!in_kernel) {
780 if (copy_from_user(runtime->oss.buffer, (const char __user *)buf, bytes))
781 return -EFAULT;
782 buf = runtime->oss.buffer;
783 }
784 frames = bytes / oss_frame_bytes;
785 frames1 = snd_pcm_plug_client_channels_buf(substream, (char *)buf, frames, &channels);
786 if (frames1 < 0)
787 return frames1;
788 frames1 = snd_pcm_plug_write_transfer(substream, channels, frames1);
789 if (frames1 <= 0)
790 return frames1;
791 bytes = frames1 * oss_frame_bytes;
792 } else {
793 frames = bytes_to_frames(runtime, bytes);
794 frames1 = snd_pcm_oss_write3(substream, buf, frames, in_kernel);
795 if (frames1 <= 0)
796 return frames1;
797 bytes = frames_to_bytes(runtime, frames1);
798 }
799 return bytes;
800}
801
802static ssize_t snd_pcm_oss_write1(snd_pcm_substream_t *substream, const char __user *buf, size_t bytes)
803{
804 size_t xfer = 0;
805 ssize_t tmp;
806 snd_pcm_runtime_t *runtime = substream->runtime;
807
808 if (atomic_read(&runtime->mmap_count))
809 return -ENXIO;
810
811 if ((tmp = snd_pcm_oss_make_ready(substream)) < 0)
812 return tmp;
813 while (bytes > 0) {
814 if (bytes < runtime->oss.period_bytes || runtime->oss.buffer_used > 0) {
815 tmp = bytes;
816 if (tmp + runtime->oss.buffer_used > runtime->oss.period_bytes)
817 tmp = runtime->oss.period_bytes - runtime->oss.buffer_used;
818 if (tmp > 0) {
819 if (copy_from_user(runtime->oss.buffer + runtime->oss.buffer_used, buf, tmp))
820 return xfer > 0 ? (snd_pcm_sframes_t)xfer : -EFAULT;
821 }
822 runtime->oss.buffer_used += tmp;
823 buf += tmp;
824 bytes -= tmp;
825 xfer += tmp;
826 if ((substream->oss.setup != NULL && substream->oss.setup->partialfrag) ||
827 runtime->oss.buffer_used == runtime->oss.period_bytes) {
828 tmp = snd_pcm_oss_write2(substream, runtime->oss.buffer + runtime->oss.period_ptr,
829 runtime->oss.buffer_used - runtime->oss.period_ptr, 1);
830 if (tmp <= 0)
831 return xfer > 0 ? (snd_pcm_sframes_t)xfer : tmp;
832 runtime->oss.bytes += tmp;
833 runtime->oss.period_ptr += tmp;
834 runtime->oss.period_ptr %= runtime->oss.period_bytes;
835 if (runtime->oss.period_ptr == 0 ||
836 runtime->oss.period_ptr == runtime->oss.buffer_used)
837 runtime->oss.buffer_used = 0;
838 else if ((substream->ffile->f_flags & O_NONBLOCK) != 0)
839 return xfer > 0 ? xfer : -EAGAIN;
840 }
841 } else {
842 tmp = snd_pcm_oss_write2(substream, (const char *)buf, runtime->oss.period_bytes, 0);
843 if (tmp <= 0)
844 return xfer > 0 ? (snd_pcm_sframes_t)xfer : tmp;
845 runtime->oss.bytes += tmp;
846 buf += tmp;
847 bytes -= tmp;
848 xfer += tmp;
849 if ((substream->ffile->f_flags & O_NONBLOCK) != 0 &&
850 tmp != runtime->oss.period_bytes)
851 break;
852 }
853 }
854 return xfer;
855}
856
857static ssize_t snd_pcm_oss_read2(snd_pcm_substream_t *substream, char *buf, size_t bytes, int in_kernel)
858{
859 snd_pcm_runtime_t *runtime = substream->runtime;
860 snd_pcm_sframes_t frames, frames1;
861 char __user *final_dst = (char __user *)buf;
862 if (runtime->oss.plugin_first) {
863 snd_pcm_plugin_channel_t *channels;
864 size_t oss_frame_bytes = (runtime->oss.plugin_last->dst_width * runtime->oss.plugin_last->dst_format.channels) / 8;
865 if (!in_kernel)
866 buf = runtime->oss.buffer;
867 frames = bytes / oss_frame_bytes;
868 frames1 = snd_pcm_plug_client_channels_buf(substream, buf, frames, &channels);
869 if (frames1 < 0)
870 return frames1;
871 frames1 = snd_pcm_plug_read_transfer(substream, channels, frames1);
872 if (frames1 <= 0)
873 return frames1;
874 bytes = frames1 * oss_frame_bytes;
875 if (!in_kernel && copy_to_user(final_dst, buf, bytes))
876 return -EFAULT;
877 } else {
878 frames = bytes_to_frames(runtime, bytes);
879 frames1 = snd_pcm_oss_read3(substream, buf, frames, in_kernel);
880 if (frames1 <= 0)
881 return frames1;
882 bytes = frames_to_bytes(runtime, frames1);
883 }
884 return bytes;
885}
886
887static ssize_t snd_pcm_oss_read1(snd_pcm_substream_t *substream, char __user *buf, size_t bytes)
888{
889 size_t xfer = 0;
890 ssize_t tmp;
891 snd_pcm_runtime_t *runtime = substream->runtime;
892
893 if (atomic_read(&runtime->mmap_count))
894 return -ENXIO;
895
896 if ((tmp = snd_pcm_oss_make_ready(substream)) < 0)
897 return tmp;
898 while (bytes > 0) {
899 if (bytes < runtime->oss.period_bytes || runtime->oss.buffer_used > 0) {
900 if (runtime->oss.buffer_used == 0) {
901 tmp = snd_pcm_oss_read2(substream, runtime->oss.buffer, runtime->oss.period_bytes, 1);
902 if (tmp <= 0)
903 return xfer > 0 ? (snd_pcm_sframes_t)xfer : tmp;
904 runtime->oss.bytes += tmp;
905 runtime->oss.period_ptr = tmp;
906 runtime->oss.buffer_used = tmp;
907 }
908 tmp = bytes;
909 if ((size_t) tmp > runtime->oss.buffer_used)
910 tmp = runtime->oss.buffer_used;
911 if (copy_to_user(buf, runtime->oss.buffer + (runtime->oss.period_ptr - runtime->oss.buffer_used), tmp))
912 return xfer > 0 ? (snd_pcm_sframes_t)xfer : -EFAULT;
913 buf += tmp;
914 bytes -= tmp;
915 xfer += tmp;
916 runtime->oss.buffer_used -= tmp;
917 } else {
918 tmp = snd_pcm_oss_read2(substream, (char *)buf, runtime->oss.period_bytes, 0);
919 if (tmp <= 0)
920 return xfer > 0 ? (snd_pcm_sframes_t)xfer : tmp;
921 runtime->oss.bytes += tmp;
922 buf += tmp;
923 bytes -= tmp;
924 xfer += tmp;
925 }
926 }
927 return xfer;
928}
929
930static int snd_pcm_oss_reset(snd_pcm_oss_file_t *pcm_oss_file)
931{
932 snd_pcm_substream_t *substream;
933
934 substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK];
935 if (substream != NULL) {
936 snd_pcm_kernel_playback_ioctl(substream, SNDRV_PCM_IOCTL_DROP, NULL);
937 substream->runtime->oss.prepare = 1;
938 }
939 substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE];
940 if (substream != NULL) {
941 snd_pcm_kernel_capture_ioctl(substream, SNDRV_PCM_IOCTL_DROP, NULL);
942 substream->runtime->oss.prepare = 1;
943 }
944 return 0;
945}
946
947static int snd_pcm_oss_post(snd_pcm_oss_file_t *pcm_oss_file)
948{
949 snd_pcm_substream_t *substream;
950 int err;
951
952 substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK];
953 if (substream != NULL) {
954 if ((err = snd_pcm_oss_make_ready(substream)) < 0)
955 return err;
956 snd_pcm_kernel_playback_ioctl(substream, SNDRV_PCM_IOCTL_START, NULL);
957 }
958 /* note: all errors from the start action are ignored */
959 /* OSS apps do not know, how to handle them */
960 return 0;
961}
962
963static int snd_pcm_oss_sync1(snd_pcm_substream_t *substream, size_t size)
964{
965 snd_pcm_runtime_t *runtime;
966 ssize_t result = 0;
967 long res;
968 wait_queue_t wait;
969
970 runtime = substream->runtime;
971 init_waitqueue_entry(&wait, current);
972 add_wait_queue(&runtime->sleep, &wait);
973#ifdef OSS_DEBUG
974 printk("sync1: size = %li\n", size);
975#endif
976 while (1) {
977 result = snd_pcm_oss_write2(substream, runtime->oss.buffer, size, 1);
978 if (result > 0) {
979 runtime->oss.buffer_used = 0;
980 result = 0;
981 break;
982 }
983 if (result != 0 && result != -EAGAIN)
984 break;
985 result = 0;
986 set_current_state(TASK_INTERRUPTIBLE);
987 snd_pcm_stream_lock_irq(substream);
988 res = runtime->status->state;
989 snd_pcm_stream_unlock_irq(substream);
990 if (res != SNDRV_PCM_STATE_RUNNING) {
991 set_current_state(TASK_RUNNING);
992 break;
993 }
994 res = schedule_timeout(10 * HZ);
995 if (signal_pending(current)) {
996 result = -ERESTARTSYS;
997 break;
998 }
999 if (res == 0) {
1000 snd_printk(KERN_ERR "OSS sync error - DMA timeout\n");
1001 result = -EIO;
1002 break;
1003 }
1004 }
1005 remove_wait_queue(&runtime->sleep, &wait);
1006 return result;
1007}
1008
1009static int snd_pcm_oss_sync(snd_pcm_oss_file_t *pcm_oss_file)
1010{
1011 int err = 0;
1012 unsigned int saved_f_flags;
1013 snd_pcm_substream_t *substream;
1014 snd_pcm_runtime_t *runtime;
1015 snd_pcm_format_t format;
1016 unsigned long width;
1017 size_t size;
1018
1019 substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK];
1020 if (substream != NULL) {
1021 runtime = substream->runtime;
1022 if (atomic_read(&runtime->mmap_count))
1023 goto __direct;
1024 if ((err = snd_pcm_oss_make_ready(substream)) < 0)
1025 return err;
1026 format = snd_pcm_oss_format_from(runtime->oss.format);
1027 width = snd_pcm_format_physical_width(format);
1028 if (runtime->oss.buffer_used > 0) {
1029#ifdef OSS_DEBUG
1030 printk("sync: buffer_used\n");
1031#endif
1032 size = (8 * (runtime->oss.period_bytes - runtime->oss.buffer_used) + 7) / width;
1033 snd_pcm_format_set_silence(format,
1034 runtime->oss.buffer + runtime->oss.buffer_used,
1035 size);
1036 err = snd_pcm_oss_sync1(substream, runtime->oss.period_bytes);
1037 if (err < 0)
1038 return err;
1039 } else if (runtime->oss.period_ptr > 0) {
1040#ifdef OSS_DEBUG
1041 printk("sync: period_ptr\n");
1042#endif
1043 size = runtime->oss.period_bytes - runtime->oss.period_ptr;
1044 snd_pcm_format_set_silence(format,
1045 runtime->oss.buffer,
1046 size * 8 / width);
1047 err = snd_pcm_oss_sync1(substream, size);
1048 if (err < 0)
1049 return err;
1050 }
1051 /*
1052 * The ALSA's period might be a bit large than OSS one.
1053 * Fill the remain portion of ALSA period with zeros.
1054 */
1055 size = runtime->control->appl_ptr % runtime->period_size;
1056 if (size > 0) {
1057 size = runtime->period_size - size;
1058 if (runtime->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED) {
1059 size = (runtime->frame_bits * size) / 8;
1060 while (size > 0) {
1061 mm_segment_t fs;
1062 size_t size1 = size < runtime->oss.period_bytes ? size : runtime->oss.period_bytes;
1063 size -= size1;
1064 size1 *= 8;
1065 size1 /= runtime->sample_bits;
1066 snd_pcm_format_set_silence(runtime->format,
1067 runtime->oss.buffer,
1068 size1);
1069 fs = snd_enter_user();
1070 snd_pcm_lib_write(substream, (void __user *)runtime->oss.buffer, size1);
1071 snd_leave_user(fs);
1072 }
1073 } else if (runtime->access == SNDRV_PCM_ACCESS_RW_NONINTERLEAVED) {
1074 void __user *buffers[runtime->channels];
1075 memset(buffers, 0, runtime->channels * sizeof(void *));
1076 snd_pcm_lib_writev(substream, buffers, size);
1077 }
1078 }
1079 /*
1080 * finish sync: drain the buffer
1081 */
1082 __direct:
1083 saved_f_flags = substream->ffile->f_flags;
1084 substream->ffile->f_flags &= ~O_NONBLOCK;
1085 err = snd_pcm_kernel_playback_ioctl(substream, SNDRV_PCM_IOCTL_DRAIN, NULL);
1086 substream->ffile->f_flags = saved_f_flags;
1087 if (err < 0)
1088 return err;
1089 runtime->oss.prepare = 1;
1090 }
1091
1092 substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE];
1093 if (substream != NULL) {
1094 if ((err = snd_pcm_oss_make_ready(substream)) < 0)
1095 return err;
1096 runtime = substream->runtime;
1097 err = snd_pcm_kernel_capture_ioctl(substream, SNDRV_PCM_IOCTL_DROP, NULL);
1098 if (err < 0)
1099 return err;
1100 runtime->oss.buffer_used = 0;
1101 runtime->oss.prepare = 1;
1102 }
1103 return 0;
1104}
1105
1106static int snd_pcm_oss_set_rate(snd_pcm_oss_file_t *pcm_oss_file, int rate)
1107{
1108 int idx;
1109
1110 for (idx = 1; idx >= 0; --idx) {
1111 snd_pcm_substream_t *substream = pcm_oss_file->streams[idx];
1112 snd_pcm_runtime_t *runtime;
1113 if (substream == NULL)
1114 continue;
1115 runtime = substream->runtime;
1116 if (rate < 1000)
1117 rate = 1000;
1118 else if (rate > 192000)
1119 rate = 192000;
1120 if (runtime->oss.rate != rate) {
1121 runtime->oss.params = 1;
1122 runtime->oss.rate = rate;
1123 }
1124 }
1125 return snd_pcm_oss_get_rate(pcm_oss_file);
1126}
1127
1128static int snd_pcm_oss_get_rate(snd_pcm_oss_file_t *pcm_oss_file)
1129{
1130 snd_pcm_substream_t *substream;
1131 int err;
1132
1133 if ((err = snd_pcm_oss_get_active_substream(pcm_oss_file, &substream)) < 0)
1134 return err;
1135 return substream->runtime->oss.rate;
1136}
1137
1138static int snd_pcm_oss_set_channels(snd_pcm_oss_file_t *pcm_oss_file, unsigned int channels)
1139{
1140 int idx;
1141 if (channels < 1)
1142 channels = 1;
1143 if (channels > 128)
1144 return -EINVAL;
1145 for (idx = 1; idx >= 0; --idx) {
1146 snd_pcm_substream_t *substream = pcm_oss_file->streams[idx];
1147 snd_pcm_runtime_t *runtime;
1148 if (substream == NULL)
1149 continue;
1150 runtime = substream->runtime;
1151 if (runtime->oss.channels != channels) {
1152 runtime->oss.params = 1;
1153 runtime->oss.channels = channels;
1154 }
1155 }
1156 return snd_pcm_oss_get_channels(pcm_oss_file);
1157}
1158
1159static int snd_pcm_oss_get_channels(snd_pcm_oss_file_t *pcm_oss_file)
1160{
1161 snd_pcm_substream_t *substream;
1162 int err;
1163
1164 if ((err = snd_pcm_oss_get_active_substream(pcm_oss_file, &substream)) < 0)
1165 return err;
1166 return substream->runtime->oss.channels;
1167}
1168
1169static int snd_pcm_oss_get_block_size(snd_pcm_oss_file_t *pcm_oss_file)
1170{
1171 snd_pcm_substream_t *substream;
1172 int err;
1173
1174 if ((err = snd_pcm_oss_get_active_substream(pcm_oss_file, &substream)) < 0)
1175 return err;
1176 return substream->runtime->oss.period_bytes;
1177}
1178
1179static int snd_pcm_oss_get_formats(snd_pcm_oss_file_t *pcm_oss_file)
1180{
1181 snd_pcm_substream_t *substream;
1182 int err;
1183 int direct;
1184 snd_pcm_hw_params_t *params;
1185 unsigned int formats = 0;
1186 snd_mask_t format_mask;
1187 int fmt;
1188
1189 if ((err = snd_pcm_oss_get_active_substream(pcm_oss_file, &substream)) < 0)
1190 return err;
1191 if (atomic_read(&substream->runtime->mmap_count)) {
1192 direct = 1;
1193 } else {
1194 snd_pcm_oss_setup_t *setup = substream->oss.setup;
1195 direct = (setup != NULL && setup->direct);
1196 }
1197 if (!direct)
1198 return AFMT_MU_LAW | AFMT_U8 |
1199 AFMT_S16_LE | AFMT_S16_BE |
1200 AFMT_S8 | AFMT_U16_LE |
1201 AFMT_U16_BE;
1202 params = kmalloc(sizeof(*params), GFP_KERNEL);
1203 if (!params)
1204 return -ENOMEM;
1205 _snd_pcm_hw_params_any(params);
1206 err = snd_pcm_hw_refine(substream, params);
1207 format_mask = *hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
1208 kfree(params);
1209 snd_assert(err >= 0, return err);
1210 for (fmt = 0; fmt < 32; ++fmt) {
1211 if (snd_mask_test(&format_mask, fmt)) {
1212 int f = snd_pcm_oss_format_to(fmt);
1213 if (f >= 0)
1214 formats |= f;
1215 }
1216 }
1217 return formats;
1218}
1219
1220static int snd_pcm_oss_set_format(snd_pcm_oss_file_t *pcm_oss_file, int format)
1221{
1222 int formats, idx;
1223
1224 if (format != AFMT_QUERY) {
1225 formats = snd_pcm_oss_get_formats(pcm_oss_file);
1226 if (!(formats & format))
1227 format = AFMT_U8;
1228 for (idx = 1; idx >= 0; --idx) {
1229 snd_pcm_substream_t *substream = pcm_oss_file->streams[idx];
1230 snd_pcm_runtime_t *runtime;
1231 if (substream == NULL)
1232 continue;
1233 runtime = substream->runtime;
1234 if (runtime->oss.format != format) {
1235 runtime->oss.params = 1;
1236 runtime->oss.format = format;
1237 }
1238 }
1239 }
1240 return snd_pcm_oss_get_format(pcm_oss_file);
1241}
1242
1243static int snd_pcm_oss_get_format(snd_pcm_oss_file_t *pcm_oss_file)
1244{
1245 snd_pcm_substream_t *substream;
1246 int err;
1247
1248 if ((err = snd_pcm_oss_get_active_substream(pcm_oss_file, &substream)) < 0)
1249 return err;
1250 return substream->runtime->oss.format;
1251}
1252
1253static int snd_pcm_oss_set_subdivide1(snd_pcm_substream_t *substream, int subdivide)
1254{
1255 snd_pcm_runtime_t *runtime;
1256
1257 if (substream == NULL)
1258 return 0;
1259 runtime = substream->runtime;
1260 if (subdivide == 0) {
1261 subdivide = runtime->oss.subdivision;
1262 if (subdivide == 0)
1263 subdivide = 1;
1264 return subdivide;
1265 }
1266 if (runtime->oss.subdivision || runtime->oss.fragshift)
1267 return -EINVAL;
1268 if (subdivide != 1 && subdivide != 2 && subdivide != 4 &&
1269 subdivide != 8 && subdivide != 16)
1270 return -EINVAL;
1271 runtime->oss.subdivision = subdivide;
1272 runtime->oss.params = 1;
1273 return subdivide;
1274}
1275
1276static int snd_pcm_oss_set_subdivide(snd_pcm_oss_file_t *pcm_oss_file, int subdivide)
1277{
1278 int err = -EINVAL, idx;
1279
1280 for (idx = 1; idx >= 0; --idx) {
1281 snd_pcm_substream_t *substream = pcm_oss_file->streams[idx];
1282 if (substream == NULL)
1283 continue;
1284 if ((err = snd_pcm_oss_set_subdivide1(substream, subdivide)) < 0)
1285 return err;
1286 }
1287 return err;
1288}
1289
1290static int snd_pcm_oss_set_fragment1(snd_pcm_substream_t *substream, unsigned int val)
1291{
1292 snd_pcm_runtime_t *runtime;
1293
1294 if (substream == NULL)
1295 return 0;
1296 runtime = substream->runtime;
1297 if (runtime->oss.subdivision || runtime->oss.fragshift)
1298 return -EINVAL;
1299 runtime->oss.fragshift = val & 0xffff;
1300 runtime->oss.maxfrags = (val >> 16) & 0xffff;
1301 if (runtime->oss.fragshift < 4) /* < 16 */
1302 runtime->oss.fragshift = 4;
1303 if (runtime->oss.maxfrags < 2)
1304 runtime->oss.maxfrags = 2;
1305 runtime->oss.params = 1;
1306 return 0;
1307}
1308
1309static int snd_pcm_oss_set_fragment(snd_pcm_oss_file_t *pcm_oss_file, unsigned int val)
1310{
1311 int err = -EINVAL, idx;
1312
1313 for (idx = 1; idx >= 0; --idx) {
1314 snd_pcm_substream_t *substream = pcm_oss_file->streams[idx];
1315 if (substream == NULL)
1316 continue;
1317 if ((err = snd_pcm_oss_set_fragment1(substream, val)) < 0)
1318 return err;
1319 }
1320 return err;
1321}
1322
1323static int snd_pcm_oss_nonblock(struct file * file)
1324{
1325 file->f_flags |= O_NONBLOCK;
1326 return 0;
1327}
1328
1329static int snd_pcm_oss_get_caps1(snd_pcm_substream_t *substream, int res)
1330{
1331
1332 if (substream == NULL) {
1333 res &= ~DSP_CAP_DUPLEX;
1334 return res;
1335 }
1336#ifdef DSP_CAP_MULTI
1337 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
1338 if (substream->pstr->substream_count > 1)
1339 res |= DSP_CAP_MULTI;
1340#endif
1341 /* DSP_CAP_REALTIME is set all times: */
1342 /* all ALSA drivers can return actual pointer in ring buffer */
1343#if defined(DSP_CAP_REALTIME) && 0
1344 {
1345 snd_pcm_runtime_t *runtime = substream->runtime;
1346 if (runtime->info & (SNDRV_PCM_INFO_BLOCK_TRANSFER|SNDRV_PCM_INFO_BATCH))
1347 res &= ~DSP_CAP_REALTIME;
1348 }
1349#endif
1350 return res;
1351}
1352
1353static int snd_pcm_oss_get_caps(snd_pcm_oss_file_t *pcm_oss_file)
1354{
1355 int result, idx;
1356
1357 result = DSP_CAP_TRIGGER | DSP_CAP_MMAP | DSP_CAP_DUPLEX | DSP_CAP_REALTIME;
1358 for (idx = 0; idx < 2; idx++) {
1359 snd_pcm_substream_t *substream = pcm_oss_file->streams[idx];
1360 result = snd_pcm_oss_get_caps1(substream, result);
1361 }
1362 result |= 0x0001; /* revision - same as SB AWE 64 */
1363 return result;
1364}
1365
1366static void snd_pcm_oss_simulate_fill(snd_pcm_substream_t *substream, snd_pcm_uframes_t hw_ptr)
1367{
1368 snd_pcm_runtime_t *runtime = substream->runtime;
1369 snd_pcm_uframes_t appl_ptr;
1370 appl_ptr = hw_ptr + runtime->buffer_size;
1371 appl_ptr %= runtime->boundary;
1372 runtime->control->appl_ptr = appl_ptr;
1373}
1374
1375static int snd_pcm_oss_set_trigger(snd_pcm_oss_file_t *pcm_oss_file, int trigger)
1376{
1377 snd_pcm_runtime_t *runtime;
1378 snd_pcm_substream_t *psubstream = NULL, *csubstream = NULL;
1379 int err, cmd;
1380
1381#ifdef OSS_DEBUG
1382 printk("pcm_oss: trigger = 0x%x\n", trigger);
1383#endif
1384
1385 psubstream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK];
1386 csubstream = pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE];
1387
1388 if (psubstream) {
1389 if ((err = snd_pcm_oss_make_ready(psubstream)) < 0)
1390 return err;
1391 }
1392 if (csubstream) {
1393 if ((err = snd_pcm_oss_make_ready(csubstream)) < 0)
1394 return err;
1395 }
1396 if (psubstream) {
1397 runtime = psubstream->runtime;
1398 if (trigger & PCM_ENABLE_OUTPUT) {
1399 if (runtime->oss.trigger)
1400 goto _skip1;
1401 if (atomic_read(&psubstream->runtime->mmap_count))
1402 snd_pcm_oss_simulate_fill(psubstream, runtime->hw_ptr_interrupt);
1403 runtime->oss.trigger = 1;
1404 runtime->start_threshold = 1;
1405 cmd = SNDRV_PCM_IOCTL_START;
1406 } else {
1407 if (!runtime->oss.trigger)
1408 goto _skip1;
1409 runtime->oss.trigger = 0;
1410 runtime->start_threshold = runtime->boundary;
1411 cmd = SNDRV_PCM_IOCTL_DROP;
1412 runtime->oss.prepare = 1;
1413 }
1414 err = snd_pcm_kernel_playback_ioctl(psubstream, cmd, NULL);
1415 if (err < 0)
1416 return err;
1417 }
1418 _skip1:
1419 if (csubstream) {
1420 runtime = csubstream->runtime;
1421 if (trigger & PCM_ENABLE_INPUT) {
1422 if (runtime->oss.trigger)
1423 goto _skip2;
1424 runtime->oss.trigger = 1;
1425 runtime->start_threshold = 1;
1426 cmd = SNDRV_PCM_IOCTL_START;
1427 } else {
1428 if (!runtime->oss.trigger)
1429 goto _skip2;
1430 runtime->oss.trigger = 0;
1431 runtime->start_threshold = runtime->boundary;
1432 cmd = SNDRV_PCM_IOCTL_DROP;
1433 runtime->oss.prepare = 1;
1434 }
1435 err = snd_pcm_kernel_capture_ioctl(csubstream, cmd, NULL);
1436 if (err < 0)
1437 return err;
1438 }
1439 _skip2:
1440 return 0;
1441}
1442
1443static int snd_pcm_oss_get_trigger(snd_pcm_oss_file_t *pcm_oss_file)
1444{
1445 snd_pcm_substream_t *psubstream = NULL, *csubstream = NULL;
1446 int result = 0;
1447
1448 psubstream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK];
1449 csubstream = pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE];
1450 if (psubstream && psubstream->runtime && psubstream->runtime->oss.trigger)
1451 result |= PCM_ENABLE_OUTPUT;
1452 if (csubstream && csubstream->runtime && csubstream->runtime->oss.trigger)
1453 result |= PCM_ENABLE_INPUT;
1454 return result;
1455}
1456
1457static int snd_pcm_oss_get_odelay(snd_pcm_oss_file_t *pcm_oss_file)
1458{
1459 snd_pcm_substream_t *substream;
1460 snd_pcm_runtime_t *runtime;
1461 snd_pcm_sframes_t delay;
1462 int err;
1463
1464 substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK];
1465 if (substream == NULL)
1466 return -EINVAL;
1467 if ((err = snd_pcm_oss_make_ready(substream)) < 0)
1468 return err;
1469 runtime = substream->runtime;
1470 if (runtime->oss.params || runtime->oss.prepare)
1471 return 0;
1472 err = snd_pcm_kernel_playback_ioctl(substream, SNDRV_PCM_IOCTL_DELAY, &delay);
1473 if (err == -EPIPE)
1474 delay = 0; /* hack for broken OSS applications */
1475 else if (err < 0)
1476 return err;
1477 return snd_pcm_oss_bytes(substream, delay);
1478}
1479
1480static int snd_pcm_oss_get_ptr(snd_pcm_oss_file_t *pcm_oss_file, int stream, struct count_info __user * _info)
1481{
1482 snd_pcm_substream_t *substream;
1483 snd_pcm_runtime_t *runtime;
1484 snd_pcm_sframes_t delay;
1485 int fixup;
1486 struct count_info info;
1487 int err;
1488
1489 if (_info == NULL)
1490 return -EFAULT;
1491 substream = pcm_oss_file->streams[stream];
1492 if (substream == NULL)
1493 return -EINVAL;
1494 if ((err = snd_pcm_oss_make_ready(substream)) < 0)
1495 return err;
1496 runtime = substream->runtime;
1497 if (runtime->oss.params || runtime->oss.prepare) {
1498 memset(&info, 0, sizeof(info));
1499 if (copy_to_user(_info, &info, sizeof(info)))
1500 return -EFAULT;
1501 return 0;
1502 }
1503 if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
1504 err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DELAY, &delay);
1505 if (err == -EPIPE || err == -ESTRPIPE || (! err && delay < 0)) {
1506 err = 0;
1507 delay = 0;
1508 fixup = 0;
1509 } else {
1510 fixup = runtime->oss.buffer_used;
1511 }
1512 } else {
1513 err = snd_pcm_oss_capture_position_fixup(substream, &delay);
1514 fixup = -runtime->oss.buffer_used;
1515 }
1516 if (err < 0)
1517 return err;
1518 info.ptr = snd_pcm_oss_bytes(substream, runtime->status->hw_ptr % runtime->buffer_size);
1519 if (atomic_read(&runtime->mmap_count)) {
1520 snd_pcm_sframes_t n;
1521 n = (delay = runtime->hw_ptr_interrupt) - runtime->oss.prev_hw_ptr_interrupt;
1522 if (n < 0)
1523 n += runtime->boundary;
1524 info.blocks = n / runtime->period_size;
1525 runtime->oss.prev_hw_ptr_interrupt = delay;
1526 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
1527 snd_pcm_oss_simulate_fill(substream, delay);
1528 info.bytes = snd_pcm_oss_bytes(substream, runtime->status->hw_ptr) & INT_MAX;
1529 } else {
1530 delay = snd_pcm_oss_bytes(substream, delay) + fixup;
1531 info.blocks = delay / runtime->oss.period_bytes;
1532 if (stream == SNDRV_PCM_STREAM_PLAYBACK)
1533 info.bytes = (runtime->oss.bytes - delay) & INT_MAX;
1534 else
1535 info.bytes = (runtime->oss.bytes + delay) & INT_MAX;
1536 }
1537 if (copy_to_user(_info, &info, sizeof(info)))
1538 return -EFAULT;
1539 return 0;
1540}
1541
1542static int snd_pcm_oss_get_space(snd_pcm_oss_file_t *pcm_oss_file, int stream, struct audio_buf_info __user *_info)
1543{
1544 snd_pcm_substream_t *substream;
1545 snd_pcm_runtime_t *runtime;
1546 snd_pcm_sframes_t avail;
1547 int fixup;
1548 struct audio_buf_info info;
1549 int err;
1550
1551 if (_info == NULL)
1552 return -EFAULT;
1553 substream = pcm_oss_file->streams[stream];
1554 if (substream == NULL)
1555 return -EINVAL;
1556 runtime = substream->runtime;
1557
1558 if (runtime->oss.params &&
1559 (err = snd_pcm_oss_change_params(substream)) < 0)
1560 return err;
1561
1562 info.fragsize = runtime->oss.period_bytes;
1563 info.fragstotal = runtime->periods;
1564 if (runtime->oss.prepare) {
1565 if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
1566 info.bytes = runtime->oss.period_bytes * runtime->oss.periods;
1567 info.fragments = runtime->oss.periods;
1568 } else {
1569 info.bytes = 0;
1570 info.fragments = 0;
1571 }
1572 } else {
1573 if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
1574 err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DELAY, &avail);
1575 if (err == -EPIPE || err == -ESTRPIPE || (! err && avail < 0)) {
1576 avail = runtime->buffer_size;
1577 err = 0;
1578 fixup = 0;
1579 } else {
1580 avail = runtime->buffer_size - avail;
1581 fixup = -runtime->oss.buffer_used;
1582 }
1583 } else {
1584 err = snd_pcm_oss_capture_position_fixup(substream, &avail);
1585 fixup = runtime->oss.buffer_used;
1586 }
1587 if (err < 0)
1588 return err;
1589 info.bytes = snd_pcm_oss_bytes(substream, avail) + fixup;
1590 info.fragments = info.bytes / runtime->oss.period_bytes;
1591 }
1592
1593#ifdef OSS_DEBUG
1594 printk("pcm_oss: space: bytes = %i, fragments = %i, fragstotal = %i, fragsize = %i\n", info.bytes, info.fragments, info.fragstotal, info.fragsize);
1595#endif
1596 if (copy_to_user(_info, &info, sizeof(info)))
1597 return -EFAULT;
1598 return 0;
1599}
1600
1601static int snd_pcm_oss_get_mapbuf(snd_pcm_oss_file_t *pcm_oss_file, int stream, struct buffmem_desc __user * _info)
1602{
1603 // it won't be probably implemented
1604 // snd_printd("TODO: snd_pcm_oss_get_mapbuf\n");
1605 return -EINVAL;
1606}
1607
1608static snd_pcm_oss_setup_t *snd_pcm_oss_look_for_setup(snd_pcm_t *pcm, int stream, const char *task_name)
1609{
1610 const char *ptr, *ptrl;
1611 snd_pcm_oss_setup_t *setup;
1612
1613 down(&pcm->streams[stream].oss.setup_mutex);
1614 for (setup = pcm->streams[stream].oss.setup_list; setup; setup = setup->next) {
1615 if (!strcmp(setup->task_name, task_name)) {
1616 up(&pcm->streams[stream].oss.setup_mutex);
1617 return setup;
1618 }
1619 }
1620 ptr = ptrl = task_name;
1621 while (*ptr) {
1622 if (*ptr == '/')
1623 ptrl = ptr + 1;
1624 ptr++;
1625 }
1626 if (ptrl == task_name) {
1627 goto __not_found;
1628 return NULL;
1629 }
1630 for (setup = pcm->streams[stream].oss.setup_list; setup; setup = setup->next) {
1631 if (!strcmp(setup->task_name, ptrl)) {
1632 up(&pcm->streams[stream].oss.setup_mutex);
1633 return setup;
1634 }
1635 }
1636 __not_found:
1637 up(&pcm->streams[stream].oss.setup_mutex);
1638 return NULL;
1639}
1640
1641static void snd_pcm_oss_init_substream(snd_pcm_substream_t *substream,
1642 snd_pcm_oss_setup_t *setup,
1643 int minor)
1644{
1645 snd_pcm_runtime_t *runtime;
1646
1647 substream->oss.oss = 1;
1648 substream->oss.setup = setup;
1649 runtime = substream->runtime;
1650 runtime->oss.params = 1;
1651 runtime->oss.trigger = 1;
1652 runtime->oss.rate = 8000;
1653 switch (SNDRV_MINOR_OSS_DEVICE(minor)) {
1654 case SNDRV_MINOR_OSS_PCM_8:
1655 runtime->oss.format = AFMT_U8;
1656 break;
1657 case SNDRV_MINOR_OSS_PCM_16:
1658 runtime->oss.format = AFMT_S16_LE;
1659 break;
1660 default:
1661 runtime->oss.format = AFMT_MU_LAW;
1662 }
1663 runtime->oss.channels = 1;
1664 runtime->oss.fragshift = 0;
1665 runtime->oss.maxfrags = 0;
1666 runtime->oss.subdivision = 0;
1667}
1668
1669static void snd_pcm_oss_release_substream(snd_pcm_substream_t *substream)
1670{
1671 snd_pcm_runtime_t *runtime;
1672 runtime = substream->runtime;
1673 vfree(runtime->oss.buffer);
1674 snd_pcm_oss_plugin_clear(substream);
1675 substream->oss.file = NULL;
1676 substream->oss.oss = 0;
1677}
1678
1679static int snd_pcm_oss_release_file(snd_pcm_oss_file_t *pcm_oss_file)
1680{
1681 int cidx;
1682 snd_assert(pcm_oss_file != NULL, return -ENXIO);
1683 for (cidx = 0; cidx < 2; ++cidx) {
1684 snd_pcm_substream_t *substream = pcm_oss_file->streams[cidx];
1685 snd_pcm_runtime_t *runtime;
1686 if (substream == NULL)
1687 continue;
1688 runtime = substream->runtime;
1689
1690 snd_pcm_stream_lock_irq(substream);
1691 if (snd_pcm_running(substream))
1692 snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP);
1693 snd_pcm_stream_unlock_irq(substream);
1694 if (substream->open_flag) {
1695 if (substream->ops->hw_free != NULL)
1696 substream->ops->hw_free(substream);
1697 substream->ops->close(substream);
1698 substream->open_flag = 0;
1699 }
1700 substream->ffile = NULL;
1701 snd_pcm_oss_release_substream(substream);
1702 snd_pcm_release_substream(substream);
1703 }
1704 kfree(pcm_oss_file);
1705 return 0;
1706}
1707
1708static int snd_pcm_oss_open_file(struct file *file,
1709 snd_pcm_t *pcm,
1710 snd_pcm_oss_file_t **rpcm_oss_file,
1711 int minor,
1712 snd_pcm_oss_setup_t *psetup,
1713 snd_pcm_oss_setup_t *csetup)
1714{
1715 int err = 0;
1716 snd_pcm_oss_file_t *pcm_oss_file;
1717 snd_pcm_substream_t *psubstream = NULL, *csubstream = NULL;
1718 unsigned int f_mode = file->f_mode;
1719
1720 snd_assert(rpcm_oss_file != NULL, return -EINVAL);
1721 *rpcm_oss_file = NULL;
1722
1723 pcm_oss_file = kcalloc(1, sizeof(*pcm_oss_file), GFP_KERNEL);
1724 if (pcm_oss_file == NULL)
1725 return -ENOMEM;
1726
1727 if ((f_mode & (FMODE_WRITE|FMODE_READ)) == (FMODE_WRITE|FMODE_READ) &&
1728 (pcm->info_flags & SNDRV_PCM_INFO_HALF_DUPLEX))
1729 f_mode = FMODE_WRITE;
1730 if ((f_mode & FMODE_WRITE) && !(psetup && psetup->disable)) {
1731 if ((err = snd_pcm_open_substream(pcm, SNDRV_PCM_STREAM_PLAYBACK,
1732 &psubstream)) < 0) {
1733 snd_pcm_oss_release_file(pcm_oss_file);
1734 return err;
1735 }
1736 pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK] = psubstream;
1737 }
1738 if ((f_mode & FMODE_READ) && !(csetup && csetup->disable)) {
1739 if ((err = snd_pcm_open_substream(pcm, SNDRV_PCM_STREAM_CAPTURE,
1740 &csubstream)) < 0) {
1741 if (!(f_mode & FMODE_WRITE) || err != -ENODEV) {
1742 snd_pcm_oss_release_file(pcm_oss_file);
1743 return err;
1744 } else {
1745 csubstream = NULL;
1746 }
1747 }
1748 pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE] = csubstream;
1749 }
1750
1751 if (psubstream == NULL && csubstream == NULL) {
1752 snd_pcm_oss_release_file(pcm_oss_file);
1753 return -EINVAL;
1754 }
1755 if (psubstream != NULL) {
1756 psubstream->oss.file = pcm_oss_file;
1757 err = snd_pcm_hw_constraints_init(psubstream);
1758 if (err < 0) {
1759 snd_printd("snd_pcm_hw_constraint_init failed\n");
1760 snd_pcm_oss_release_file(pcm_oss_file);
1761 return err;
1762 }
1763 if ((err = psubstream->ops->open(psubstream)) < 0) {
1764 snd_pcm_oss_release_file(pcm_oss_file);
1765 return err;
1766 }
1767 psubstream->open_flag = 1;
1768 err = snd_pcm_hw_constraints_complete(psubstream);
1769 if (err < 0) {
1770 snd_printd("snd_pcm_hw_constraint_complete failed\n");
1771 snd_pcm_oss_release_file(pcm_oss_file);
1772 return err;
1773 }
1774 psubstream->ffile = file;
1775 snd_pcm_oss_init_substream(psubstream, psetup, minor);
1776 }
1777 if (csubstream != NULL) {
1778 csubstream->oss.file = pcm_oss_file;
1779 err = snd_pcm_hw_constraints_init(csubstream);
1780 if (err < 0) {
1781 snd_printd("snd_pcm_hw_constraint_init failed\n");
1782 snd_pcm_oss_release_file(pcm_oss_file);
1783 return err;
1784 }
1785 if ((err = csubstream->ops->open(csubstream)) < 0) {
1786 snd_pcm_oss_release_file(pcm_oss_file);
1787 return err;
1788 }
1789 csubstream->open_flag = 1;
1790 err = snd_pcm_hw_constraints_complete(csubstream);
1791 if (err < 0) {
1792 snd_printd("snd_pcm_hw_constraint_complete failed\n");
1793 snd_pcm_oss_release_file(pcm_oss_file);
1794 return err;
1795 }
1796 csubstream->ffile = file;
1797 snd_pcm_oss_init_substream(csubstream, csetup, minor);
1798 }
1799
1800 file->private_data = pcm_oss_file;
1801 *rpcm_oss_file = pcm_oss_file;
1802 return 0;
1803}
1804
1805
1806static int snd_pcm_oss_open(struct inode *inode, struct file *file)
1807{
1808 int minor = iminor(inode);
1809 int cardnum = SNDRV_MINOR_OSS_CARD(minor);
1810 int device;
1811 int err;
1812 char task_name[32];
1813 snd_pcm_t *pcm;
1814 snd_pcm_oss_file_t *pcm_oss_file;
1815 snd_pcm_oss_setup_t *psetup = NULL, *csetup = NULL;
1816 int nonblock;
1817 wait_queue_t wait;
1818
1819 snd_assert(cardnum >= 0 && cardnum < SNDRV_CARDS, return -ENXIO);
1820 device = SNDRV_MINOR_OSS_DEVICE(minor) == SNDRV_MINOR_OSS_PCM1 ?
1821 adsp_map[cardnum] : dsp_map[cardnum];
1822
1823 pcm = snd_pcm_devices[(cardnum * SNDRV_PCM_DEVICES) + device];
1824 if (pcm == NULL) {
1825 err = -ENODEV;
1826 goto __error1;
1827 }
1828 err = snd_card_file_add(pcm->card, file);
1829 if (err < 0)
1830 goto __error1;
1831 if (!try_module_get(pcm->card->module)) {
1832 err = -EFAULT;
1833 goto __error2;
1834 }
1835 if (snd_task_name(current, task_name, sizeof(task_name)) < 0) {
1836 err = -EFAULT;
1837 goto __error;
1838 }
1839 if (file->f_mode & FMODE_WRITE)
1840 psetup = snd_pcm_oss_look_for_setup(pcm, SNDRV_PCM_STREAM_PLAYBACK, task_name);
1841 if (file->f_mode & FMODE_READ)
1842 csetup = snd_pcm_oss_look_for_setup(pcm, SNDRV_PCM_STREAM_CAPTURE, task_name);
1843
1844 nonblock = !!(file->f_flags & O_NONBLOCK);
1845 if (psetup && !psetup->disable) {
1846 if (psetup->nonblock)
1847 nonblock = 1;
1848 else if (psetup->block)
1849 nonblock = 0;
1850 } else if (csetup && !csetup->disable) {
1851 if (csetup->nonblock)
1852 nonblock = 1;
1853 else if (csetup->block)
1854 nonblock = 0;
1855 }
1856 if (!nonblock)
1857 nonblock = nonblock_open;
1858
1859 init_waitqueue_entry(&wait, current);
1860 add_wait_queue(&pcm->open_wait, &wait);
1861 down(&pcm->open_mutex);
1862 while (1) {
1863 err = snd_pcm_oss_open_file(file, pcm, &pcm_oss_file,
1864 minor, psetup, csetup);
1865 if (err >= 0)
1866 break;
1867 if (err == -EAGAIN) {
1868 if (nonblock) {
1869 err = -EBUSY;
1870 break;
1871 }
1872 } else
1873 break;
1874 set_current_state(TASK_INTERRUPTIBLE);
1875 up(&pcm->open_mutex);
1876 schedule();
1877 down(&pcm->open_mutex);
1878 if (signal_pending(current)) {
1879 err = -ERESTARTSYS;
1880 break;
1881 }
1882 }
1883 remove_wait_queue(&pcm->open_wait, &wait);
1884 up(&pcm->open_mutex);
1885 if (err < 0)
1886 goto __error;
1887 return err;
1888
1889 __error:
1890 module_put(pcm->card->module);
1891 __error2:
1892 snd_card_file_remove(pcm->card, file);
1893 __error1:
1894 return err;
1895}
1896
1897static int snd_pcm_oss_release(struct inode *inode, struct file *file)
1898{
1899 snd_pcm_t *pcm;
1900 snd_pcm_substream_t *substream;
1901 snd_pcm_oss_file_t *pcm_oss_file;
1902
1903 pcm_oss_file = file->private_data;
1904 substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK];
1905 if (substream == NULL)
1906 substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE];
1907 snd_assert(substream != NULL, return -ENXIO);
1908 pcm = substream->pcm;
1909 snd_pcm_oss_sync(pcm_oss_file);
1910 down(&pcm->open_mutex);
1911 snd_pcm_oss_release_file(pcm_oss_file);
1912 up(&pcm->open_mutex);
1913 wake_up(&pcm->open_wait);
1914 module_put(pcm->card->module);
1915 snd_card_file_remove(pcm->card, file);
1916 return 0;
1917}
1918
1919static long snd_pcm_oss_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
1920{
1921 snd_pcm_oss_file_t *pcm_oss_file;
1922 int __user *p = (int __user *)arg;
1923 int res;
1924
1925 pcm_oss_file = file->private_data;
1926 if (cmd == OSS_GETVERSION)
1927 return put_user(SNDRV_OSS_VERSION, p);
1928 if (cmd == OSS_ALSAEMULVER)
1929 return put_user(1, p);
1930#if defined(CONFIG_SND_MIXER_OSS) || (defined(MODULE) && defined(CONFIG_SND_MIXER_OSS_MODULE))
1931 if (((cmd >> 8) & 0xff) == 'M') { /* mixer ioctl - for OSS compatibility */
1932 snd_pcm_substream_t *substream;
1933 int idx;
1934 for (idx = 0; idx < 2; ++idx) {
1935 substream = pcm_oss_file->streams[idx];
1936 if (substream != NULL)
1937 break;
1938 }
1939 snd_assert(substream != NULL, return -ENXIO);
1940 return snd_mixer_oss_ioctl_card(substream->pcm->card, cmd, arg);
1941 }
1942#endif
1943 if (((cmd >> 8) & 0xff) != 'P')
1944 return -EINVAL;
1945#ifdef OSS_DEBUG
1946 printk("pcm_oss: ioctl = 0x%x\n", cmd);
1947#endif
1948 switch (cmd) {
1949 case SNDCTL_DSP_RESET:
1950 return snd_pcm_oss_reset(pcm_oss_file);
1951 case SNDCTL_DSP_SYNC:
1952 return snd_pcm_oss_sync(pcm_oss_file);
1953 case SNDCTL_DSP_SPEED:
1954 if (get_user(res, p))
1955 return -EFAULT;
1956 if ((res = snd_pcm_oss_set_rate(pcm_oss_file, res))<0)
1957 return res;
1958 return put_user(res, p);
1959 case SOUND_PCM_READ_RATE:
1960 res = snd_pcm_oss_get_rate(pcm_oss_file);
1961 if (res < 0)
1962 return res;
1963 return put_user(res, p);
1964 case SNDCTL_DSP_STEREO:
1965 if (get_user(res, p))
1966 return -EFAULT;
1967 res = res > 0 ? 2 : 1;
1968 if ((res = snd_pcm_oss_set_channels(pcm_oss_file, res)) < 0)
1969 return res;
1970 return put_user(--res, p);
1971 case SNDCTL_DSP_GETBLKSIZE:
1972 res = snd_pcm_oss_get_block_size(pcm_oss_file);
1973 if (res < 0)
1974 return res;
1975 return put_user(res, p);
1976 case SNDCTL_DSP_SETFMT:
1977 if (get_user(res, p))
1978 return -EFAULT;
1979 res = snd_pcm_oss_set_format(pcm_oss_file, res);
1980 if (res < 0)
1981 return res;
1982 return put_user(res, p);
1983 case SOUND_PCM_READ_BITS:
1984 res = snd_pcm_oss_get_format(pcm_oss_file);
1985 if (res < 0)
1986 return res;
1987 return put_user(res, p);
1988 case SNDCTL_DSP_CHANNELS:
1989 if (get_user(res, p))
1990 return -EFAULT;
1991 res = snd_pcm_oss_set_channels(pcm_oss_file, res);
1992 if (res < 0)
1993 return res;
1994 return put_user(res, p);
1995 case SOUND_PCM_READ_CHANNELS:
1996 res = snd_pcm_oss_get_channels(pcm_oss_file);
1997 if (res < 0)
1998 return res;
1999 return put_user(res, p);
2000 case SOUND_PCM_WRITE_FILTER:
2001 case SOUND_PCM_READ_FILTER:
2002 return -EIO;
2003 case SNDCTL_DSP_POST:
2004 return snd_pcm_oss_post(pcm_oss_file);
2005 case SNDCTL_DSP_SUBDIVIDE:
2006 if (get_user(res, p))
2007 return -EFAULT;
2008 res = snd_pcm_oss_set_subdivide(pcm_oss_file, res);
2009 if (res < 0)
2010 return res;
2011 return put_user(res, p);
2012 case SNDCTL_DSP_SETFRAGMENT:
2013 if (get_user(res, p))
2014 return -EFAULT;
2015 return snd_pcm_oss_set_fragment(pcm_oss_file, res);
2016 case SNDCTL_DSP_GETFMTS:
2017 res = snd_pcm_oss_get_formats(pcm_oss_file);
2018 if (res < 0)
2019 return res;
2020 return put_user(res, p);
2021 case SNDCTL_DSP_GETOSPACE:
2022 case SNDCTL_DSP_GETISPACE:
2023 return snd_pcm_oss_get_space(pcm_oss_file,
2024 cmd == SNDCTL_DSP_GETISPACE ?
2025 SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK,
2026 (struct audio_buf_info __user *) arg);
2027 case SNDCTL_DSP_NONBLOCK:
2028 return snd_pcm_oss_nonblock(file);
2029 case SNDCTL_DSP_GETCAPS:
2030 res = snd_pcm_oss_get_caps(pcm_oss_file);
2031 if (res < 0)
2032 return res;
2033 return put_user(res, p);
2034 case SNDCTL_DSP_GETTRIGGER:
2035 res = snd_pcm_oss_get_trigger(pcm_oss_file);
2036 if (res < 0)
2037 return res;
2038 return put_user(res, p);
2039 case SNDCTL_DSP_SETTRIGGER:
2040 if (get_user(res, p))
2041 return -EFAULT;
2042 return snd_pcm_oss_set_trigger(pcm_oss_file, res);
2043 case SNDCTL_DSP_GETIPTR:
2044 case SNDCTL_DSP_GETOPTR:
2045 return snd_pcm_oss_get_ptr(pcm_oss_file,
2046 cmd == SNDCTL_DSP_GETIPTR ?
2047 SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK,
2048 (struct count_info __user *) arg);
2049 case SNDCTL_DSP_MAPINBUF:
2050 case SNDCTL_DSP_MAPOUTBUF:
2051 return snd_pcm_oss_get_mapbuf(pcm_oss_file,
2052 cmd == SNDCTL_DSP_MAPINBUF ?
2053 SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK,
2054 (struct buffmem_desc __user *) arg);
2055 case SNDCTL_DSP_SETSYNCRO:
2056 /* stop DMA now.. */
2057 return 0;
2058 case SNDCTL_DSP_SETDUPLEX:
2059 if (snd_pcm_oss_get_caps(pcm_oss_file) & DSP_CAP_DUPLEX)
2060 return 0;
2061 return -EIO;
2062 case SNDCTL_DSP_GETODELAY:
2063 res = snd_pcm_oss_get_odelay(pcm_oss_file);
2064 if (res < 0) {
2065 /* it's for sure, some broken apps don't check for error codes */
2066 put_user(0, p);
2067 return res;
2068 }
2069 return put_user(res, p);
2070 case SNDCTL_DSP_PROFILE:
2071 return 0; /* silently ignore */
2072 default:
2073 snd_printd("pcm_oss: unknown command = 0x%x\n", cmd);
2074 }
2075 return -EINVAL;
2076}
2077
2078#ifdef CONFIG_COMPAT
2079/* all compatible */
2080#define snd_pcm_oss_ioctl_compat snd_pcm_oss_ioctl
2081#else
2082#define snd_pcm_oss_ioctl_compat NULL
2083#endif
2084
2085static ssize_t snd_pcm_oss_read(struct file *file, char __user *buf, size_t count, loff_t *offset)
2086{
2087 snd_pcm_oss_file_t *pcm_oss_file;
2088 snd_pcm_substream_t *substream;
2089
2090 pcm_oss_file = file->private_data;
2091 substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE];
2092 if (substream == NULL)
2093 return -ENXIO;
2094#ifndef OSS_DEBUG
2095 return snd_pcm_oss_read1(substream, buf, count);
2096#else
2097 {
2098 ssize_t res = snd_pcm_oss_read1(substream, buf, count);
2099 printk("pcm_oss: read %li bytes (returned %li bytes)\n", (long)count, (long)res);
2100 return res;
2101 }
2102#endif
2103}
2104
2105static ssize_t snd_pcm_oss_write(struct file *file, const char __user *buf, size_t count, loff_t *offset)
2106{
2107 snd_pcm_oss_file_t *pcm_oss_file;
2108 snd_pcm_substream_t *substream;
2109 long result;
2110
2111 pcm_oss_file = file->private_data;
2112 substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK];
2113 if (substream == NULL)
2114 return -ENXIO;
2115 up(&file->f_dentry->d_inode->i_sem);
2116 result = snd_pcm_oss_write1(substream, buf, count);
2117 down(&file->f_dentry->d_inode->i_sem);
2118#ifdef OSS_DEBUG
2119 printk("pcm_oss: write %li bytes (wrote %li bytes)\n", (long)count, (long)result);
2120#endif
2121 return result;
2122}
2123
2124static int snd_pcm_oss_playback_ready(snd_pcm_substream_t *substream)
2125{
2126 snd_pcm_runtime_t *runtime = substream->runtime;
2127 if (atomic_read(&runtime->mmap_count))
2128 return runtime->oss.prev_hw_ptr_interrupt != runtime->hw_ptr_interrupt;
2129 else
2130 return snd_pcm_playback_avail(runtime) >= runtime->oss.period_frames;
2131}
2132
2133static int snd_pcm_oss_capture_ready(snd_pcm_substream_t *substream)
2134{
2135 snd_pcm_runtime_t *runtime = substream->runtime;
2136 if (atomic_read(&runtime->mmap_count))
2137 return runtime->oss.prev_hw_ptr_interrupt != runtime->hw_ptr_interrupt;
2138 else
2139 return snd_pcm_capture_avail(runtime) >= runtime->oss.period_frames;
2140}
2141
2142static unsigned int snd_pcm_oss_poll(struct file *file, poll_table * wait)
2143{
2144 snd_pcm_oss_file_t *pcm_oss_file;
2145 unsigned int mask;
2146 snd_pcm_substream_t *psubstream = NULL, *csubstream = NULL;
2147
2148 pcm_oss_file = file->private_data;
2149
2150 psubstream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK];
2151 csubstream = pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE];
2152
2153 mask = 0;
2154 if (psubstream != NULL) {
2155 snd_pcm_runtime_t *runtime = psubstream->runtime;
2156 poll_wait(file, &runtime->sleep, wait);
2157 snd_pcm_stream_lock_irq(psubstream);
2158 if (runtime->status->state != SNDRV_PCM_STATE_DRAINING &&
2159 (runtime->status->state != SNDRV_PCM_STATE_RUNNING ||
2160 snd_pcm_oss_playback_ready(psubstream)))
2161 mask |= POLLOUT | POLLWRNORM;
2162 snd_pcm_stream_unlock_irq(psubstream);
2163 }
2164 if (csubstream != NULL) {
2165 snd_pcm_runtime_t *runtime = csubstream->runtime;
2166 enum sndrv_pcm_state ostate;
2167 poll_wait(file, &runtime->sleep, wait);
2168 snd_pcm_stream_lock_irq(csubstream);
2169 if ((ostate = runtime->status->state) != SNDRV_PCM_STATE_RUNNING ||
2170 snd_pcm_oss_capture_ready(csubstream))
2171 mask |= POLLIN | POLLRDNORM;
2172 snd_pcm_stream_unlock_irq(csubstream);
2173 if (ostate != SNDRV_PCM_STATE_RUNNING && runtime->oss.trigger) {
2174 snd_pcm_oss_file_t ofile;
2175 memset(&ofile, 0, sizeof(ofile));
2176 ofile.streams[SNDRV_PCM_STREAM_CAPTURE] = pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE];
2177 runtime->oss.trigger = 0;
2178 snd_pcm_oss_set_trigger(&ofile, PCM_ENABLE_INPUT);
2179 }
2180 }
2181
2182 return mask;
2183}
2184
2185static int snd_pcm_oss_mmap(struct file *file, struct vm_area_struct *area)
2186{
2187 snd_pcm_oss_file_t *pcm_oss_file;
2188 snd_pcm_substream_t *substream = NULL;
2189 snd_pcm_runtime_t *runtime;
2190 int err;
2191
2192#ifdef OSS_DEBUG
2193 printk("pcm_oss: mmap begin\n");
2194#endif
2195 pcm_oss_file = file->private_data;
2196 switch ((area->vm_flags & (VM_READ | VM_WRITE))) {
2197 case VM_READ | VM_WRITE:
2198 substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK];
2199 if (substream)
2200 break;
2201 /* Fall through */
2202 case VM_READ:
2203 substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE];
2204 break;
2205 case VM_WRITE:
2206 substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK];
2207 break;
2208 default:
2209 return -EINVAL;
2210 }
2211 /* set VM_READ access as well to fix memset() routines that do
2212 reads before writes (to improve performance) */
2213 area->vm_flags |= VM_READ;
2214 if (substream == NULL)
2215 return -ENXIO;
2216 runtime = substream->runtime;
2217 if (!(runtime->info & SNDRV_PCM_INFO_MMAP_VALID))
2218 return -EIO;
2219 if (runtime->info & SNDRV_PCM_INFO_INTERLEAVED)
2220 runtime->access = SNDRV_PCM_ACCESS_MMAP_INTERLEAVED;
2221 else
2222 return -EIO;
2223
2224 if (runtime->oss.params) {
2225 if ((err = snd_pcm_oss_change_params(substream)) < 0)
2226 return err;
2227 }
2228 if (runtime->oss.plugin_first != NULL)
2229 return -EIO;
2230
2231 if (area->vm_pgoff != 0)
2232 return -EINVAL;
2233
2234 err = snd_pcm_mmap_data(substream, file, area);
2235 if (err < 0)
2236 return err;
2237 runtime->oss.mmap_bytes = area->vm_end - area->vm_start;
2238 runtime->silence_threshold = 0;
2239 runtime->silence_size = 0;
2240#ifdef OSS_DEBUG
2241 printk("pcm_oss: mmap ok, bytes = 0x%x\n", runtime->oss.mmap_bytes);
2242#endif
2243 /* In mmap mode we never stop */
2244 runtime->stop_threshold = runtime->boundary;
2245
2246 return 0;
2247}
2248
2249/*
2250 * /proc interface
2251 */
2252
2253static void snd_pcm_oss_proc_read(snd_info_entry_t *entry,
2254 snd_info_buffer_t * buffer)
2255{
2256 snd_pcm_str_t *pstr = (snd_pcm_str_t *)entry->private_data;
2257 snd_pcm_oss_setup_t *setup = pstr->oss.setup_list;
2258 down(&pstr->oss.setup_mutex);
2259 while (setup) {
2260 snd_iprintf(buffer, "%s %u %u%s%s%s%s%s%s\n",
2261 setup->task_name,
2262 setup->periods,
2263 setup->period_size,
2264 setup->disable ? " disable" : "",
2265 setup->direct ? " direct" : "",
2266 setup->block ? " block" : "",
2267 setup->nonblock ? " non-block" : "",
2268 setup->partialfrag ? " partial-frag" : "",
2269 setup->nosilence ? " no-silence" : "");
2270 setup = setup->next;
2271 }
2272 up(&pstr->oss.setup_mutex);
2273}
2274
2275static void snd_pcm_oss_proc_free_setup_list(snd_pcm_str_t * pstr)
2276{
2277 unsigned int idx;
2278 snd_pcm_substream_t *substream;
2279 snd_pcm_oss_setup_t *setup, *setupn;
2280
2281 for (idx = 0, substream = pstr->substream;
2282 idx < pstr->substream_count; idx++, substream = substream->next)
2283 substream->oss.setup = NULL;
2284 for (setup = pstr->oss.setup_list, pstr->oss.setup_list = NULL;
2285 setup; setup = setupn) {
2286 setupn = setup->next;
2287 kfree(setup->task_name);
2288 kfree(setup);
2289 }
2290 pstr->oss.setup_list = NULL;
2291}
2292
2293static void snd_pcm_oss_proc_write(snd_info_entry_t *entry,
2294 snd_info_buffer_t * buffer)
2295{
2296 snd_pcm_str_t *pstr = (snd_pcm_str_t *)entry->private_data;
2297 char line[128], str[32], task_name[32], *ptr;
2298 int idx1;
2299 snd_pcm_oss_setup_t *setup, *setup1, template;
2300
2301 while (!snd_info_get_line(buffer, line, sizeof(line))) {
2302 down(&pstr->oss.setup_mutex);
2303 memset(&template, 0, sizeof(template));
2304 ptr = snd_info_get_str(task_name, line, sizeof(task_name));
2305 if (!strcmp(task_name, "clear") || !strcmp(task_name, "erase")) {
2306 snd_pcm_oss_proc_free_setup_list(pstr);
2307 up(&pstr->oss.setup_mutex);
2308 continue;
2309 }
2310 for (setup = pstr->oss.setup_list; setup; setup = setup->next) {
2311 if (!strcmp(setup->task_name, task_name)) {
2312 template = *setup;
2313 break;
2314 }
2315 }
2316 ptr = snd_info_get_str(str, ptr, sizeof(str));
2317 template.periods = simple_strtoul(str, NULL, 10);
2318 ptr = snd_info_get_str(str, ptr, sizeof(str));
2319 template.period_size = simple_strtoul(str, NULL, 10);
2320 for (idx1 = 31; idx1 >= 0; idx1--)
2321 if (template.period_size & (1 << idx1))
2322 break;
2323 for (idx1--; idx1 >= 0; idx1--)
2324 template.period_size &= ~(1 << idx1);
2325 do {
2326 ptr = snd_info_get_str(str, ptr, sizeof(str));
2327 if (!strcmp(str, "disable")) {
2328 template.disable = 1;
2329 } else if (!strcmp(str, "direct")) {
2330 template.direct = 1;
2331 } else if (!strcmp(str, "block")) {
2332 template.block = 1;
2333 } else if (!strcmp(str, "non-block")) {
2334 template.nonblock = 1;
2335 } else if (!strcmp(str, "partial-frag")) {
2336 template.partialfrag = 1;
2337 } else if (!strcmp(str, "no-silence")) {
2338 template.nosilence = 1;
2339 }
2340 } while (*str);
2341 if (setup == NULL) {
2342 setup = (snd_pcm_oss_setup_t *) kmalloc(sizeof(snd_pcm_oss_setup_t), GFP_KERNEL);
2343 if (setup) {
2344 if (pstr->oss.setup_list == NULL) {
2345 pstr->oss.setup_list = setup;
2346 } else {
2347 for (setup1 = pstr->oss.setup_list; setup1->next; setup1 = setup1->next);
2348 setup1->next = setup;
2349 }
2350 template.task_name = snd_kmalloc_strdup(task_name, GFP_KERNEL);
2351 } else {
2352 buffer->error = -ENOMEM;
2353 }
2354 }
2355 if (setup)
2356 *setup = template;
2357 up(&pstr->oss.setup_mutex);
2358 }
2359}
2360
2361static void snd_pcm_oss_proc_init(snd_pcm_t *pcm)
2362{
2363 int stream;
2364 for (stream = 0; stream < 2; ++stream) {
2365 snd_info_entry_t *entry;
2366 snd_pcm_str_t *pstr = &pcm->streams[stream];
2367 if (pstr->substream_count == 0)
2368 continue;
2369 if ((entry = snd_info_create_card_entry(pcm->card, "oss", pstr->proc_root)) != NULL) {
2370 entry->content = SNDRV_INFO_CONTENT_TEXT;
2371 entry->mode = S_IFREG | S_IRUGO | S_IWUSR;
2372 entry->c.text.read_size = 8192;
2373 entry->c.text.read = snd_pcm_oss_proc_read;
2374 entry->c.text.write_size = 8192;
2375 entry->c.text.write = snd_pcm_oss_proc_write;
2376 entry->private_data = pstr;
2377 if (snd_info_register(entry) < 0) {
2378 snd_info_free_entry(entry);
2379 entry = NULL;
2380 }
2381 }
2382 pstr->oss.proc_entry = entry;
2383 }
2384}
2385
2386static void snd_pcm_oss_proc_done(snd_pcm_t *pcm)
2387{
2388 int stream;
2389 for (stream = 0; stream < 2; ++stream) {
2390 snd_pcm_str_t *pstr = &pcm->streams[stream];
2391 if (pstr->oss.proc_entry) {
2392 snd_info_unregister(pstr->oss.proc_entry);
2393 pstr->oss.proc_entry = NULL;
2394 snd_pcm_oss_proc_free_setup_list(pstr);
2395 }
2396 }
2397}
2398
2399/*
2400 * ENTRY functions
2401 */
2402
2403static struct file_operations snd_pcm_oss_f_reg =
2404{
2405 .owner = THIS_MODULE,
2406 .read = snd_pcm_oss_read,
2407 .write = snd_pcm_oss_write,
2408 .open = snd_pcm_oss_open,
2409 .release = snd_pcm_oss_release,
2410 .poll = snd_pcm_oss_poll,
2411 .unlocked_ioctl = snd_pcm_oss_ioctl,
2412 .compat_ioctl = snd_pcm_oss_ioctl_compat,
2413 .mmap = snd_pcm_oss_mmap,
2414};
2415
2416static snd_minor_t snd_pcm_oss_reg =
2417{
2418 .comment = "digital audio",
2419 .f_ops = &snd_pcm_oss_f_reg,
2420};
2421
2422static void register_oss_dsp(snd_pcm_t *pcm, int index)
2423{
2424 char name[128];
2425 sprintf(name, "dsp%i%i", pcm->card->number, pcm->device);
2426 if (snd_register_oss_device(SNDRV_OSS_DEVICE_TYPE_PCM,
2427 pcm->card, index, &snd_pcm_oss_reg,
2428 name) < 0) {
2429 snd_printk("unable to register OSS PCM device %i:%i\n", pcm->card->number, pcm->device);
2430 }
2431}
2432
2433static int snd_pcm_oss_register_minor(snd_pcm_t * pcm)
2434{
2435 pcm->oss.reg = 0;
2436 if (dsp_map[pcm->card->number] == (int)pcm->device) {
2437 char name[128];
2438 int duplex;
2439 register_oss_dsp(pcm, 0);
2440 duplex = (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream_count > 0 &&
2441 pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream_count &&
2442 !(pcm->info_flags & SNDRV_PCM_INFO_HALF_DUPLEX));
2443 sprintf(name, "%s%s", pcm->name, duplex ? " (DUPLEX)" : "");
2444#ifdef SNDRV_OSS_INFO_DEV_AUDIO
2445 snd_oss_info_register(SNDRV_OSS_INFO_DEV_AUDIO,
2446 pcm->card->number,
2447 name);
2448#endif
2449 pcm->oss.reg++;
2450 pcm->oss.reg_mask |= 1;
2451 }
2452 if (adsp_map[pcm->card->number] == (int)pcm->device) {
2453 register_oss_dsp(pcm, 1);
2454 pcm->oss.reg++;
2455 pcm->oss.reg_mask |= 2;
2456 }
2457
2458 if (pcm->oss.reg)
2459 snd_pcm_oss_proc_init(pcm);
2460
2461 return 0;
2462}
2463
2464static int snd_pcm_oss_disconnect_minor(snd_pcm_t * pcm)
2465{
2466 if (pcm->oss.reg) {
2467 if (pcm->oss.reg_mask & 1) {
2468 pcm->oss.reg_mask &= ~1;
2469 snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_PCM,
2470 pcm->card, 0);
2471 }
2472 if (pcm->oss.reg_mask & 2) {
2473 pcm->oss.reg_mask &= ~2;
2474 snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_PCM,
2475 pcm->card, 1);
2476 }
2477 }
2478 return 0;
2479}
2480
2481static int snd_pcm_oss_unregister_minor(snd_pcm_t * pcm)
2482{
2483 snd_pcm_oss_disconnect_minor(pcm);
2484 if (pcm->oss.reg) {
2485 if (dsp_map[pcm->card->number] == (int)pcm->device) {
2486#ifdef SNDRV_OSS_INFO_DEV_AUDIO
2487 snd_oss_info_unregister(SNDRV_OSS_INFO_DEV_AUDIO, pcm->card->number);
2488#endif
2489 }
2490 pcm->oss.reg = 0;
2491 snd_pcm_oss_proc_done(pcm);
2492 }
2493 return 0;
2494}
2495
2496static snd_pcm_notify_t snd_pcm_oss_notify =
2497{
2498 .n_register = snd_pcm_oss_register_minor,
2499 .n_disconnect = snd_pcm_oss_disconnect_minor,
2500 .n_unregister = snd_pcm_oss_unregister_minor,
2501};
2502
2503static int __init alsa_pcm_oss_init(void)
2504{
2505 int i;
2506 int err;
2507
2508 /* check device map table */
2509 for (i = 0; i < SNDRV_CARDS; i++) {
2510 if (dsp_map[i] < 0 || dsp_map[i] >= SNDRV_PCM_DEVICES) {
2511 snd_printk("invalid dsp_map[%d] = %d\n", i, dsp_map[i]);
2512 dsp_map[i] = 0;
2513 }
2514 if (adsp_map[i] < 0 || adsp_map[i] >= SNDRV_PCM_DEVICES) {
2515 snd_printk("invalid adsp_map[%d] = %d\n", i, adsp_map[i]);
2516 adsp_map[i] = 1;
2517 }
2518 }
2519 if ((err = snd_pcm_notify(&snd_pcm_oss_notify, 0)) < 0)
2520 return err;
2521 return 0;
2522}
2523
2524static void __exit alsa_pcm_oss_exit(void)
2525{
2526 snd_pcm_notify(&snd_pcm_oss_notify, 1);
2527}
2528
2529module_init(alsa_pcm_oss_init)
2530module_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 000000000000..6bb31009f0b4
--- /dev/null
+++ b/sound/core/oss/pcm_plugin.c
@@ -0,0 +1,921 @@
1/*
2 * PCM Plug-In shared (kernel/library) code
3 * Copyright (c) 1999 by Jaroslav Kysela <perex@suse.cz>
4 * Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org>
5 *
6 *
7 * This library is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU Library General Public License as
9 * published by the Free Software Foundation; either version 2 of
10 * the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU Library General Public License for more details.
16 *
17 * You should have received a copy of the GNU Library General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 */
22
23#if 0
24#define PLUGIN_DEBUG
25#endif
26
27#include <sound/driver.h>
28#include <linux/slab.h>
29#include <linux/time.h>
30#include <linux/vmalloc.h>
31#include <sound/core.h>
32#include <sound/pcm.h>
33#include <sound/pcm_params.h>
34#include "pcm_plugin.h"
35
36#define snd_pcm_plug_first(plug) ((plug)->runtime->oss.plugin_first)
37#define snd_pcm_plug_last(plug) ((plug)->runtime->oss.plugin_last)
38
39static int snd_pcm_plugin_src_channels_mask(snd_pcm_plugin_t *plugin,
40 bitset_t *dst_vmask,
41 bitset_t **src_vmask)
42{
43 bitset_t *vmask = plugin->src_vmask;
44 bitset_copy(vmask, dst_vmask, plugin->src_format.channels);
45 *src_vmask = vmask;
46 return 0;
47}
48
49static int snd_pcm_plugin_dst_channels_mask(snd_pcm_plugin_t *plugin,
50 bitset_t *src_vmask,
51 bitset_t **dst_vmask)
52{
53 bitset_t *vmask = plugin->dst_vmask;
54 bitset_copy(vmask, src_vmask, plugin->dst_format.channels);
55 *dst_vmask = vmask;
56 return 0;
57}
58
59/*
60 * because some cards might have rates "very close", we ignore
61 * all "resampling" requests within +-5%
62 */
63static int rate_match(unsigned int src_rate, unsigned int dst_rate)
64{
65 unsigned int low = (src_rate * 95) / 100;
66 unsigned int high = (src_rate * 105) / 100;
67 return dst_rate >= low && dst_rate <= high;
68}
69
70static int snd_pcm_plugin_alloc(snd_pcm_plugin_t *plugin, snd_pcm_uframes_t frames)
71{
72 snd_pcm_plugin_format_t *format;
73 ssize_t width;
74 size_t size;
75 unsigned int channel;
76 snd_pcm_plugin_channel_t *c;
77
78 if (plugin->stream == SNDRV_PCM_STREAM_PLAYBACK) {
79 format = &plugin->src_format;
80 } else {
81 format = &plugin->dst_format;
82 }
83 if ((width = snd_pcm_format_physical_width(format->format)) < 0)
84 return width;
85 size = frames * format->channels * width;
86 snd_assert((size % 8) == 0, return -ENXIO);
87 size /= 8;
88 if (plugin->buf_frames < frames) {
89 vfree(plugin->buf);
90 plugin->buf = vmalloc(size);
91 plugin->buf_frames = frames;
92 }
93 if (!plugin->buf) {
94 plugin->buf_frames = 0;
95 return -ENOMEM;
96 }
97 c = plugin->buf_channels;
98 if (plugin->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED) {
99 for (channel = 0; channel < format->channels; channel++, c++) {
100 c->frames = frames;
101 c->enabled = 1;
102 c->wanted = 0;
103 c->area.addr = plugin->buf;
104 c->area.first = channel * width;
105 c->area.step = format->channels * width;
106 }
107 } else if (plugin->access == SNDRV_PCM_ACCESS_RW_NONINTERLEAVED) {
108 snd_assert((size % format->channels) == 0,);
109 size /= format->channels;
110 for (channel = 0; channel < format->channels; channel++, c++) {
111 c->frames = frames;
112 c->enabled = 1;
113 c->wanted = 0;
114 c->area.addr = plugin->buf + (channel * size);
115 c->area.first = 0;
116 c->area.step = width;
117 }
118 } else
119 return -EINVAL;
120 return 0;
121}
122
123int snd_pcm_plug_alloc(snd_pcm_plug_t *plug, snd_pcm_uframes_t frames)
124{
125 int err;
126 snd_assert(snd_pcm_plug_first(plug) != NULL, return -ENXIO);
127 if (snd_pcm_plug_stream(plug) == SNDRV_PCM_STREAM_PLAYBACK) {
128 snd_pcm_plugin_t *plugin = snd_pcm_plug_first(plug);
129 while (plugin->next) {
130 if (plugin->dst_frames)
131 frames = plugin->dst_frames(plugin, frames);
132 snd_assert(frames > 0, return -ENXIO);
133 plugin = plugin->next;
134 err = snd_pcm_plugin_alloc(plugin, frames);
135 if (err < 0)
136 return err;
137 }
138 } else {
139 snd_pcm_plugin_t *plugin = snd_pcm_plug_last(plug);
140 while (plugin->prev) {
141 if (plugin->src_frames)
142 frames = plugin->src_frames(plugin, frames);
143 snd_assert(frames > 0, return -ENXIO);
144 plugin = plugin->prev;
145 err = snd_pcm_plugin_alloc(plugin, frames);
146 if (err < 0)
147 return err;
148 }
149 }
150 return 0;
151}
152
153
154snd_pcm_sframes_t snd_pcm_plugin_client_channels(snd_pcm_plugin_t *plugin,
155 snd_pcm_uframes_t frames,
156 snd_pcm_plugin_channel_t **channels)
157{
158 *channels = plugin->buf_channels;
159 return frames;
160}
161
162int snd_pcm_plugin_build(snd_pcm_plug_t *plug,
163 const char *name,
164 snd_pcm_plugin_format_t *src_format,
165 snd_pcm_plugin_format_t *dst_format,
166 size_t extra,
167 snd_pcm_plugin_t **ret)
168{
169 snd_pcm_plugin_t *plugin;
170 unsigned int channels;
171
172 snd_assert(plug != NULL, return -ENXIO);
173 snd_assert(src_format != NULL && dst_format != NULL, return -ENXIO);
174 plugin = kcalloc(1, sizeof(*plugin) + extra, GFP_KERNEL);
175 if (plugin == NULL)
176 return -ENOMEM;
177 plugin->name = name;
178 plugin->plug = plug;
179 plugin->stream = snd_pcm_plug_stream(plug);
180 plugin->access = SNDRV_PCM_ACCESS_RW_INTERLEAVED;
181 plugin->src_format = *src_format;
182 plugin->src_width = snd_pcm_format_physical_width(src_format->format);
183 snd_assert(plugin->src_width > 0, );
184 plugin->dst_format = *dst_format;
185 plugin->dst_width = snd_pcm_format_physical_width(dst_format->format);
186 snd_assert(plugin->dst_width > 0, );
187 if (plugin->stream == SNDRV_PCM_STREAM_PLAYBACK)
188 channels = src_format->channels;
189 else
190 channels = dst_format->channels;
191 plugin->buf_channels = kcalloc(channels, sizeof(*plugin->buf_channels), GFP_KERNEL);
192 if (plugin->buf_channels == NULL) {
193 snd_pcm_plugin_free(plugin);
194 return -ENOMEM;
195 }
196 plugin->src_vmask = bitset_alloc(src_format->channels);
197 if (plugin->src_vmask == NULL) {
198 snd_pcm_plugin_free(plugin);
199 return -ENOMEM;
200 }
201 plugin->dst_vmask = bitset_alloc(dst_format->channels);
202 if (plugin->dst_vmask == NULL) {
203 snd_pcm_plugin_free(plugin);
204 return -ENOMEM;
205 }
206 plugin->client_channels = snd_pcm_plugin_client_channels;
207 plugin->src_channels_mask = snd_pcm_plugin_src_channels_mask;
208 plugin->dst_channels_mask = snd_pcm_plugin_dst_channels_mask;
209 *ret = plugin;
210 return 0;
211}
212
213int snd_pcm_plugin_free(snd_pcm_plugin_t *plugin)
214{
215 if (! plugin)
216 return 0;
217 if (plugin->private_free)
218 plugin->private_free(plugin);
219 kfree(plugin->buf_channels);
220 vfree(plugin->buf);
221 kfree(plugin->src_vmask);
222 kfree(plugin->dst_vmask);
223 kfree(plugin);
224 return 0;
225}
226
227snd_pcm_sframes_t snd_pcm_plug_client_size(snd_pcm_plug_t *plug, snd_pcm_uframes_t drv_frames)
228{
229 snd_pcm_plugin_t *plugin, *plugin_prev, *plugin_next;
230 int stream = snd_pcm_plug_stream(plug);
231
232 snd_assert(plug != NULL, return -ENXIO);
233 if (drv_frames == 0)
234 return 0;
235 if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
236 plugin = snd_pcm_plug_last(plug);
237 while (plugin && drv_frames > 0) {
238 plugin_prev = plugin->prev;
239 if (plugin->src_frames)
240 drv_frames = plugin->src_frames(plugin, drv_frames);
241 plugin = plugin_prev;
242 }
243 } else if (stream == SNDRV_PCM_STREAM_CAPTURE) {
244 plugin = snd_pcm_plug_first(plug);
245 while (plugin && drv_frames > 0) {
246 plugin_next = plugin->next;
247 if (plugin->dst_frames)
248 drv_frames = plugin->dst_frames(plugin, drv_frames);
249 plugin = plugin_next;
250 }
251 } else
252 snd_BUG();
253 return drv_frames;
254}
255
256snd_pcm_sframes_t snd_pcm_plug_slave_size(snd_pcm_plug_t *plug, snd_pcm_uframes_t clt_frames)
257{
258 snd_pcm_plugin_t *plugin, *plugin_prev, *plugin_next;
259 snd_pcm_sframes_t frames;
260 int stream = snd_pcm_plug_stream(plug);
261
262 snd_assert(plug != NULL, return -ENXIO);
263 if (clt_frames == 0)
264 return 0;
265 frames = clt_frames;
266 if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
267 plugin = snd_pcm_plug_first(plug);
268 while (plugin && frames > 0) {
269 plugin_next = plugin->next;
270 if (plugin->dst_frames) {
271 frames = plugin->dst_frames(plugin, frames);
272 if (frames < 0)
273 return frames;
274 }
275 plugin = plugin_next;
276 }
277 } else if (stream == SNDRV_PCM_STREAM_CAPTURE) {
278 plugin = snd_pcm_plug_last(plug);
279 while (plugin) {
280 plugin_prev = plugin->prev;
281 if (plugin->src_frames) {
282 frames = plugin->src_frames(plugin, frames);
283 if (frames < 0)
284 return frames;
285 }
286 plugin = plugin_prev;
287 }
288 } else
289 snd_BUG();
290 return frames;
291}
292
293static int snd_pcm_plug_formats(snd_mask_t *mask, int format)
294{
295 snd_mask_t formats = *mask;
296 u64 linfmts = (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 |
297 SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_S16_LE |
298 SNDRV_PCM_FMTBIT_U16_BE | SNDRV_PCM_FMTBIT_S16_BE |
299 SNDRV_PCM_FMTBIT_U24_LE | SNDRV_PCM_FMTBIT_S24_LE |
300 SNDRV_PCM_FMTBIT_U24_BE | SNDRV_PCM_FMTBIT_S24_BE |
301 SNDRV_PCM_FMTBIT_U32_LE | SNDRV_PCM_FMTBIT_S32_LE |
302 SNDRV_PCM_FMTBIT_U32_BE | SNDRV_PCM_FMTBIT_S32_BE);
303 snd_mask_set(&formats, SNDRV_PCM_FORMAT_MU_LAW);
304
305 if (formats.bits[0] & (u32)linfmts)
306 formats.bits[0] |= (u32)linfmts;
307 if (formats.bits[1] & (u32)(linfmts >> 32))
308 formats.bits[1] |= (u32)(linfmts >> 32);
309 return snd_mask_test(&formats, format);
310}
311
312static int preferred_formats[] = {
313 SNDRV_PCM_FORMAT_S16_LE,
314 SNDRV_PCM_FORMAT_S16_BE,
315 SNDRV_PCM_FORMAT_U16_LE,
316 SNDRV_PCM_FORMAT_U16_BE,
317 SNDRV_PCM_FORMAT_S24_LE,
318 SNDRV_PCM_FORMAT_S24_BE,
319 SNDRV_PCM_FORMAT_U24_LE,
320 SNDRV_PCM_FORMAT_U24_BE,
321 SNDRV_PCM_FORMAT_S32_LE,
322 SNDRV_PCM_FORMAT_S32_BE,
323 SNDRV_PCM_FORMAT_U32_LE,
324 SNDRV_PCM_FORMAT_U32_BE,
325 SNDRV_PCM_FORMAT_S8,
326 SNDRV_PCM_FORMAT_U8
327};
328
329int snd_pcm_plug_slave_format(int format, snd_mask_t *format_mask)
330{
331 if (snd_mask_test(format_mask, format))
332 return format;
333 if (! snd_pcm_plug_formats(format_mask, format))
334 return -EINVAL;
335 if (snd_pcm_format_linear(format)) {
336 int width = snd_pcm_format_width(format);
337 int unsignd = snd_pcm_format_unsigned(format);
338 int big = snd_pcm_format_big_endian(format);
339 int format1;
340 int wid, width1=width;
341 int dwidth1 = 8;
342 for (wid = 0; wid < 4; ++wid) {
343 int end, big1 = big;
344 for (end = 0; end < 2; ++end) {
345 int sgn, unsignd1 = unsignd;
346 for (sgn = 0; sgn < 2; ++sgn) {
347 format1 = snd_pcm_build_linear_format(width1, unsignd1, big1);
348 if (format1 >= 0 &&
349 snd_mask_test(format_mask, format1))
350 goto _found;
351 unsignd1 = !unsignd1;
352 }
353 big1 = !big1;
354 }
355 if (width1 == 32) {
356 dwidth1 = -dwidth1;
357 width1 = width;
358 }
359 width1 += dwidth1;
360 }
361 return -EINVAL;
362 _found:
363 return format1;
364 } else {
365 unsigned int i;
366 switch (format) {
367 case SNDRV_PCM_FORMAT_MU_LAW:
368 for (i = 0; i < ARRAY_SIZE(preferred_formats); ++i) {
369 int format1 = preferred_formats[i];
370 if (snd_mask_test(format_mask, format1))
371 return format1;
372 }
373 default:
374 return -EINVAL;
375 }
376 }
377}
378
379int snd_pcm_plug_format_plugins(snd_pcm_plug_t *plug,
380 snd_pcm_hw_params_t *params,
381 snd_pcm_hw_params_t *slave_params)
382{
383 snd_pcm_plugin_format_t tmpformat;
384 snd_pcm_plugin_format_t dstformat;
385 snd_pcm_plugin_format_t srcformat;
386 int src_access, dst_access;
387 snd_pcm_plugin_t *plugin = NULL;
388 int err;
389 int stream = snd_pcm_plug_stream(plug);
390 int slave_interleaved = (params_channels(slave_params) == 1 ||
391 params_access(slave_params) == SNDRV_PCM_ACCESS_RW_INTERLEAVED);
392
393 switch (stream) {
394 case SNDRV_PCM_STREAM_PLAYBACK:
395 dstformat.format = params_format(slave_params);
396 dstformat.rate = params_rate(slave_params);
397 dstformat.channels = params_channels(slave_params);
398 srcformat.format = params_format(params);
399 srcformat.rate = params_rate(params);
400 srcformat.channels = params_channels(params);
401 src_access = SNDRV_PCM_ACCESS_RW_INTERLEAVED;
402 dst_access = (slave_interleaved ? SNDRV_PCM_ACCESS_RW_INTERLEAVED :
403 SNDRV_PCM_ACCESS_RW_NONINTERLEAVED);
404 break;
405 case SNDRV_PCM_STREAM_CAPTURE:
406 dstformat.format = params_format(params);
407 dstformat.rate = params_rate(params);
408 dstformat.channels = params_channels(params);
409 srcformat.format = params_format(slave_params);
410 srcformat.rate = params_rate(slave_params);
411 srcformat.channels = params_channels(slave_params);
412 src_access = (slave_interleaved ? SNDRV_PCM_ACCESS_RW_INTERLEAVED :
413 SNDRV_PCM_ACCESS_RW_NONINTERLEAVED);
414 dst_access = SNDRV_PCM_ACCESS_RW_INTERLEAVED;
415 break;
416 default:
417 snd_BUG();
418 return -EINVAL;
419 }
420 tmpformat = srcformat;
421
422 pdprintf("srcformat: format=%i, rate=%i, channels=%i\n",
423 srcformat.format,
424 srcformat.rate,
425 srcformat.channels);
426 pdprintf("dstformat: format=%i, rate=%i, channels=%i\n",
427 dstformat.format,
428 dstformat.rate,
429 dstformat.channels);
430
431 /* Format change (linearization) */
432 if ((srcformat.format != dstformat.format ||
433 !rate_match(srcformat.rate, dstformat.rate) ||
434 srcformat.channels != dstformat.channels) &&
435 !snd_pcm_format_linear(srcformat.format)) {
436 if (snd_pcm_format_linear(dstformat.format))
437 tmpformat.format = dstformat.format;
438 else
439 tmpformat.format = SNDRV_PCM_FORMAT_S16;
440 switch (srcformat.format) {
441 case SNDRV_PCM_FORMAT_MU_LAW:
442 err = snd_pcm_plugin_build_mulaw(plug,
443 &srcformat, &tmpformat,
444 &plugin);
445 break;
446 default:
447 return -EINVAL;
448 }
449 pdprintf("format change: src=%i, dst=%i returns %i\n", srcformat.format, tmpformat.format, err);
450 if (err < 0)
451 return err;
452 err = snd_pcm_plugin_append(plugin);
453 if (err < 0) {
454 snd_pcm_plugin_free(plugin);
455 return err;
456 }
457 srcformat = tmpformat;
458 src_access = dst_access;
459 }
460
461 /* channels reduction */
462 if (srcformat.channels > dstformat.channels) {
463 int sv = srcformat.channels;
464 int dv = dstformat.channels;
465 route_ttable_entry_t *ttable = kcalloc(dv * sv, sizeof(*ttable), GFP_KERNEL);
466 if (ttable == NULL)
467 return -ENOMEM;
468#if 1
469 if (sv == 2 && dv == 1) {
470 ttable[0] = HALF;
471 ttable[1] = HALF;
472 } else
473#endif
474 {
475 int v;
476 for (v = 0; v < dv; ++v)
477 ttable[v * sv + v] = FULL;
478 }
479 tmpformat.channels = dstformat.channels;
480 if (rate_match(srcformat.rate, dstformat.rate) &&
481 snd_pcm_format_linear(dstformat.format))
482 tmpformat.format = dstformat.format;
483 err = snd_pcm_plugin_build_route(plug,
484 &srcformat, &tmpformat,
485 ttable, &plugin);
486 kfree(ttable);
487 pdprintf("channels reduction: src=%i, dst=%i returns %i\n", srcformat.channels, tmpformat.channels, err);
488 if (err < 0) {
489 snd_pcm_plugin_free(plugin);
490 return err;
491 }
492 err = snd_pcm_plugin_append(plugin);
493 if (err < 0) {
494 snd_pcm_plugin_free(plugin);
495 return err;
496 }
497 srcformat = tmpformat;
498 src_access = dst_access;
499 }
500
501 /* rate resampling */
502 if (!rate_match(srcformat.rate, dstformat.rate)) {
503 tmpformat.rate = dstformat.rate;
504 if (srcformat.channels == dstformat.channels &&
505 snd_pcm_format_linear(dstformat.format))
506 tmpformat.format = dstformat.format;
507 err = snd_pcm_plugin_build_rate(plug,
508 &srcformat, &tmpformat,
509 &plugin);
510 pdprintf("rate down resampling: src=%i, dst=%i returns %i\n", srcformat.rate, tmpformat.rate, err);
511 if (err < 0) {
512 snd_pcm_plugin_free(plugin);
513 return err;
514 }
515 err = snd_pcm_plugin_append(plugin);
516 if (err < 0) {
517 snd_pcm_plugin_free(plugin);
518 return err;
519 }
520 srcformat = tmpformat;
521 src_access = dst_access;
522 }
523
524 /* channels extension */
525 if (srcformat.channels < dstformat.channels) {
526 int sv = srcformat.channels;
527 int dv = dstformat.channels;
528 route_ttable_entry_t *ttable = kcalloc(dv * sv, sizeof(*ttable), GFP_KERNEL);
529 if (ttable == NULL)
530 return -ENOMEM;
531#if 0
532 {
533 int v;
534 for (v = 0; v < sv; ++v)
535 ttable[v * sv + v] = FULL;
536 }
537#else
538 {
539 /* Playback is spreaded on all channels */
540 int vd, vs;
541 for (vd = 0, vs = 0; vd < dv; ++vd) {
542 ttable[vd * sv + vs] = FULL;
543 vs++;
544 if (vs == sv)
545 vs = 0;
546 }
547 }
548#endif
549 tmpformat.channels = dstformat.channels;
550 if (snd_pcm_format_linear(dstformat.format))
551 tmpformat.format = dstformat.format;
552 err = snd_pcm_plugin_build_route(plug,
553 &srcformat, &tmpformat,
554 ttable, &plugin);
555 kfree(ttable);
556 pdprintf("channels extension: src=%i, dst=%i returns %i\n", srcformat.channels, tmpformat.channels, err);
557 if (err < 0) {
558 snd_pcm_plugin_free(plugin);
559 return err;
560 }
561 err = snd_pcm_plugin_append(plugin);
562 if (err < 0) {
563 snd_pcm_plugin_free(plugin);
564 return err;
565 }
566 srcformat = tmpformat;
567 src_access = dst_access;
568 }
569
570 /* format change */
571 if (srcformat.format != dstformat.format) {
572 tmpformat.format = dstformat.format;
573 if (tmpformat.format == SNDRV_PCM_FORMAT_MU_LAW) {
574 err = snd_pcm_plugin_build_mulaw(plug,
575 &srcformat, &tmpformat,
576 &plugin);
577 }
578 else if (snd_pcm_format_linear(srcformat.format) &&
579 snd_pcm_format_linear(tmpformat.format)) {
580 err = snd_pcm_plugin_build_linear(plug,
581 &srcformat, &tmpformat,
582 &plugin);
583 }
584 else
585 return -EINVAL;
586 pdprintf("format change: src=%i, dst=%i returns %i\n", srcformat.format, tmpformat.format, err);
587 if (err < 0)
588 return err;
589 err = snd_pcm_plugin_append(plugin);
590 if (err < 0) {
591 snd_pcm_plugin_free(plugin);
592 return err;
593 }
594 srcformat = tmpformat;
595 src_access = dst_access;
596 }
597
598 /* de-interleave */
599 if (src_access != dst_access) {
600 err = snd_pcm_plugin_build_copy(plug,
601 &srcformat,
602 &tmpformat,
603 &plugin);
604 pdprintf("interleave change (copy: returns %i)\n", err);
605 if (err < 0)
606 return err;
607 err = snd_pcm_plugin_append(plugin);
608 if (err < 0) {
609 snd_pcm_plugin_free(plugin);
610 return err;
611 }
612 }
613
614 return 0;
615}
616
617snd_pcm_sframes_t snd_pcm_plug_client_channels_buf(snd_pcm_plug_t *plug,
618 char *buf,
619 snd_pcm_uframes_t count,
620 snd_pcm_plugin_channel_t **channels)
621{
622 snd_pcm_plugin_t *plugin;
623 snd_pcm_plugin_channel_t *v;
624 snd_pcm_plugin_format_t *format;
625 int width, nchannels, channel;
626 int stream = snd_pcm_plug_stream(plug);
627
628 snd_assert(buf != NULL, return -ENXIO);
629 if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
630 plugin = snd_pcm_plug_first(plug);
631 format = &plugin->src_format;
632 } else {
633 plugin = snd_pcm_plug_last(plug);
634 format = &plugin->dst_format;
635 }
636 v = plugin->buf_channels;
637 *channels = v;
638 if ((width = snd_pcm_format_physical_width(format->format)) < 0)
639 return width;
640 nchannels = format->channels;
641 snd_assert(plugin->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED || format->channels <= 1, return -ENXIO);
642 for (channel = 0; channel < nchannels; channel++, v++) {
643 v->frames = count;
644 v->enabled = 1;
645 v->wanted = (stream == SNDRV_PCM_STREAM_CAPTURE);
646 v->area.addr = buf;
647 v->area.first = channel * width;
648 v->area.step = nchannels * width;
649 }
650 return count;
651}
652
653static int snd_pcm_plug_playback_channels_mask(snd_pcm_plug_t *plug,
654 bitset_t *client_vmask)
655{
656 snd_pcm_plugin_t *plugin = snd_pcm_plug_last(plug);
657 if (plugin == NULL) {
658 return 0;
659 } else {
660 int schannels = plugin->dst_format.channels;
661 bitset_t bs[bitset_size(schannels)];
662 bitset_t *srcmask;
663 bitset_t *dstmask = bs;
664 int err;
665 bitset_one(dstmask, schannels);
666 if (plugin == NULL) {
667 bitset_and(client_vmask, dstmask, schannels);
668 return 0;
669 }
670 while (1) {
671 err = plugin->src_channels_mask(plugin, dstmask, &srcmask);
672 if (err < 0)
673 return err;
674 dstmask = srcmask;
675 if (plugin->prev == NULL)
676 break;
677 plugin = plugin->prev;
678 }
679 bitset_and(client_vmask, dstmask, plugin->src_format.channels);
680 return 0;
681 }
682}
683
684static int snd_pcm_plug_playback_disable_useless_channels(snd_pcm_plug_t *plug,
685 snd_pcm_plugin_channel_t *src_channels)
686{
687 snd_pcm_plugin_t *plugin = snd_pcm_plug_first(plug);
688 unsigned int nchannels = plugin->src_format.channels;
689 bitset_t bs[bitset_size(nchannels)];
690 bitset_t *srcmask = bs;
691 int err;
692 unsigned int channel;
693 for (channel = 0; channel < nchannels; channel++) {
694 if (src_channels[channel].enabled)
695 bitset_set(srcmask, channel);
696 else
697 bitset_reset(srcmask, channel);
698 }
699 err = snd_pcm_plug_playback_channels_mask(plug, srcmask);
700 if (err < 0)
701 return err;
702 for (channel = 0; channel < nchannels; channel++) {
703 if (!bitset_get(srcmask, channel))
704 src_channels[channel].enabled = 0;
705 }
706 return 0;
707}
708
709static int snd_pcm_plug_capture_disable_useless_channels(snd_pcm_plug_t *plug,
710 snd_pcm_plugin_channel_t *src_channels,
711 snd_pcm_plugin_channel_t *client_channels)
712{
713 snd_pcm_plugin_t *plugin = snd_pcm_plug_last(plug);
714 unsigned int nchannels = plugin->dst_format.channels;
715 bitset_t bs[bitset_size(nchannels)];
716 bitset_t *dstmask = bs;
717 bitset_t *srcmask;
718 int err;
719 unsigned int channel;
720 for (channel = 0; channel < nchannels; channel++) {
721 if (client_channels[channel].enabled)
722 bitset_set(dstmask, channel);
723 else
724 bitset_reset(dstmask, channel);
725 }
726 while (plugin) {
727 err = plugin->src_channels_mask(plugin, dstmask, &srcmask);
728 if (err < 0)
729 return err;
730 dstmask = srcmask;
731 plugin = plugin->prev;
732 }
733 plugin = snd_pcm_plug_first(plug);
734 nchannels = plugin->src_format.channels;
735 for (channel = 0; channel < nchannels; channel++) {
736 if (!bitset_get(dstmask, channel))
737 src_channels[channel].enabled = 0;
738 }
739 return 0;
740}
741
742snd_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)
743{
744 snd_pcm_plugin_t *plugin, *next;
745 snd_pcm_plugin_channel_t *dst_channels;
746 int err;
747 snd_pcm_sframes_t frames = size;
748
749 if ((err = snd_pcm_plug_playback_disable_useless_channels(plug, src_channels)) < 0)
750 return err;
751
752 plugin = snd_pcm_plug_first(plug);
753 while (plugin && frames > 0) {
754 if ((next = plugin->next) != NULL) {
755 snd_pcm_sframes_t frames1 = frames;
756 if (plugin->dst_frames)
757 frames1 = plugin->dst_frames(plugin, frames);
758 if ((err = next->client_channels(next, frames1, &dst_channels)) < 0) {
759 return err;
760 }
761 if (err != frames1) {
762 frames = err;
763 if (plugin->src_frames)
764 frames = plugin->src_frames(plugin, frames1);
765 }
766 } else
767 dst_channels = NULL;
768 pdprintf("write plugin: %s, %li\n", plugin->name, frames);
769 if ((frames = plugin->transfer(plugin, src_channels, dst_channels, frames)) < 0)
770 return frames;
771 src_channels = dst_channels;
772 plugin = next;
773 }
774 return snd_pcm_plug_client_size(plug, frames);
775}
776
777snd_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)
778{
779 snd_pcm_plugin_t *plugin, *next;
780 snd_pcm_plugin_channel_t *src_channels, *dst_channels;
781 snd_pcm_sframes_t frames = size;
782 int err;
783
784 frames = snd_pcm_plug_slave_size(plug, frames);
785 if (frames < 0)
786 return frames;
787
788 src_channels = NULL;
789 plugin = snd_pcm_plug_first(plug);
790 while (plugin && frames > 0) {
791 if ((next = plugin->next) != NULL) {
792 if ((err = plugin->client_channels(plugin, frames, &dst_channels)) < 0) {
793 return err;
794 }
795 frames = err;
796 if (!plugin->prev) {
797 if ((err = snd_pcm_plug_capture_disable_useless_channels(plug, dst_channels, dst_channels_final)) < 0)
798 return err;
799 }
800 } else {
801 dst_channels = dst_channels_final;
802 }
803 pdprintf("read plugin: %s, %li\n", plugin->name, frames);
804 if ((frames = plugin->transfer(plugin, src_channels, dst_channels, frames)) < 0)
805 return frames;
806 plugin = next;
807 src_channels = dst_channels;
808 }
809 return frames;
810}
811
812int snd_pcm_area_silence(const snd_pcm_channel_area_t *dst_area, size_t dst_offset,
813 size_t samples, int format)
814{
815 /* FIXME: sub byte resolution and odd dst_offset */
816 unsigned char *dst;
817 unsigned int dst_step;
818 int width;
819 const unsigned char *silence;
820 if (!dst_area->addr)
821 return 0;
822 dst = dst_area->addr + (dst_area->first + dst_area->step * dst_offset) / 8;
823 width = snd_pcm_format_physical_width(format);
824 if (width <= 0)
825 return -EINVAL;
826 if (dst_area->step == (unsigned int) width && width >= 8)
827 return snd_pcm_format_set_silence(format, dst, samples);
828 silence = snd_pcm_format_silence_64(format);
829 if (! silence)
830 return -EINVAL;
831 dst_step = dst_area->step / 8;
832 if (width == 4) {
833 /* Ima ADPCM */
834 int dstbit = dst_area->first % 8;
835 int dstbit_step = dst_area->step % 8;
836 while (samples-- > 0) {
837 if (dstbit)
838 *dst &= 0xf0;
839 else
840 *dst &= 0x0f;
841 dst += dst_step;
842 dstbit += dstbit_step;
843 if (dstbit == 8) {
844 dst++;
845 dstbit = 0;
846 }
847 }
848 } else {
849 width /= 8;
850 while (samples-- > 0) {
851 memcpy(dst, silence, width);
852 dst += dst_step;
853 }
854 }
855 return 0;
856}
857
858int snd_pcm_area_copy(const snd_pcm_channel_area_t *src_area, size_t src_offset,
859 const snd_pcm_channel_area_t *dst_area, size_t dst_offset,
860 size_t samples, int format)
861{
862 /* FIXME: sub byte resolution and odd dst_offset */
863 char *src, *dst;
864 int width;
865 int src_step, dst_step;
866 src = src_area->addr + (src_area->first + src_area->step * src_offset) / 8;
867 if (!src_area->addr)
868 return snd_pcm_area_silence(dst_area, dst_offset, samples, format);
869 dst = dst_area->addr + (dst_area->first + dst_area->step * dst_offset) / 8;
870 if (!dst_area->addr)
871 return 0;
872 width = snd_pcm_format_physical_width(format);
873 if (width <= 0)
874 return -EINVAL;
875 if (src_area->step == (unsigned int) width &&
876 dst_area->step == (unsigned int) width && width >= 8) {
877 size_t bytes = samples * width / 8;
878 memcpy(dst, src, bytes);
879 return 0;
880 }
881 src_step = src_area->step / 8;
882 dst_step = dst_area->step / 8;
883 if (width == 4) {
884 /* Ima ADPCM */
885 int srcbit = src_area->first % 8;
886 int srcbit_step = src_area->step % 8;
887 int dstbit = dst_area->first % 8;
888 int dstbit_step = dst_area->step % 8;
889 while (samples-- > 0) {
890 unsigned char srcval;
891 if (srcbit)
892 srcval = *src & 0x0f;
893 else
894 srcval = (*src & 0xf0) >> 4;
895 if (dstbit)
896 *dst = (*dst & 0xf0) | srcval;
897 else
898 *dst = (*dst & 0x0f) | (srcval << 4);
899 src += src_step;
900 srcbit += srcbit_step;
901 if (srcbit == 8) {
902 src++;
903 srcbit = 0;
904 }
905 dst += dst_step;
906 dstbit += dstbit_step;
907 if (dstbit == 8) {
908 dst++;
909 dstbit = 0;
910 }
911 }
912 } else {
913 width /= 8;
914 while (samples-- > 0) {
915 memcpy(dst, src, width);
916 src += src_step;
917 dst += dst_step;
918 }
919 }
920 return 0;
921}
diff --git a/sound/core/oss/pcm_plugin.h b/sound/core/oss/pcm_plugin.h
new file mode 100644
index 000000000000..0f86ce477490
--- /dev/null
+++ b/sound/core/oss/pcm_plugin.h
@@ -0,0 +1,250 @@
1#ifndef __PCM_PLUGIN_H
2#define __PCM_PLUGIN_H
3
4/*
5 * Digital Audio (Plugin interface) abstract layer
6 * Copyright (c) by Jaroslav Kysela <perex@suse.cz>
7 *
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 *
23 */
24
25#ifndef ATTRIBUTE_UNUSED
26#define ATTRIBUTE_UNUSED __attribute__ ((__unused__))
27#endif
28
29typedef unsigned int bitset_t;
30
31static inline size_t bitset_size(int nbits)
32{
33 return (nbits + sizeof(bitset_t) * 8 - 1) / (sizeof(bitset_t) * 8);
34}
35
36static inline bitset_t *bitset_alloc(int nbits)
37{
38 return kcalloc(bitset_size(nbits), sizeof(bitset_t), GFP_KERNEL);
39}
40
41static inline void bitset_set(bitset_t *bitmap, unsigned int pos)
42{
43 size_t bits = sizeof(*bitmap) * 8;
44 bitmap[pos / bits] |= 1 << (pos % bits);
45}
46
47static inline void bitset_reset(bitset_t *bitmap, unsigned int pos)
48{
49 size_t bits = sizeof(*bitmap) * 8;
50 bitmap[pos / bits] &= ~(1 << (pos % bits));
51}
52
53static inline int bitset_get(bitset_t *bitmap, unsigned int pos)
54{
55 size_t bits = sizeof(*bitmap) * 8;
56 return !!(bitmap[pos / bits] & (1 << (pos % bits)));
57}
58
59static inline void bitset_copy(bitset_t *dst, bitset_t *src, unsigned int nbits)
60{
61 memcpy(dst, src, bitset_size(nbits) * sizeof(bitset_t));
62}
63
64static inline void bitset_and(bitset_t *dst, bitset_t *bs, unsigned int nbits)
65{
66 bitset_t *end = dst + bitset_size(nbits);
67 while (dst < end)
68 *dst++ &= *bs++;
69}
70
71static inline void bitset_or(bitset_t *dst, bitset_t *bs, unsigned int nbits)
72{
73 bitset_t *end = dst + bitset_size(nbits);
74 while (dst < end)
75 *dst++ |= *bs++;
76}
77
78static inline void bitset_zero(bitset_t *dst, unsigned int nbits)
79{
80 bitset_t *end = dst + bitset_size(nbits);
81 while (dst < end)
82 *dst++ = 0;
83}
84
85static inline void bitset_one(bitset_t *dst, unsigned int nbits)
86{
87 bitset_t *end = dst + bitset_size(nbits);
88 while (dst < end)
89 *dst++ = ~(bitset_t)0;
90}
91
92#define snd_pcm_plug_t snd_pcm_substream_t
93#define snd_pcm_plug_stream(plug) ((plug)->stream)
94
95typedef enum {
96 INIT = 0,
97 PREPARE = 1,
98} snd_pcm_plugin_action_t;
99
100typedef struct _snd_pcm_channel_area {
101 void *addr; /* base address of channel samples */
102 unsigned int first; /* offset to first sample in bits */
103 unsigned int step; /* samples distance in bits */
104} snd_pcm_channel_area_t;
105
106typedef struct _snd_pcm_plugin_channel {
107 void *aptr; /* pointer to the allocated area */
108 snd_pcm_channel_area_t area;
109 snd_pcm_uframes_t frames; /* allocated frames */
110 unsigned int enabled:1; /* channel need to be processed */
111 unsigned int wanted:1; /* channel is wanted */
112} snd_pcm_plugin_channel_t;
113
114typedef struct _snd_pcm_plugin_format {
115 int format;
116 unsigned int rate;
117 unsigned int channels;
118} snd_pcm_plugin_format_t;
119
120struct _snd_pcm_plugin {
121 const char *name; /* plug-in name */
122 int stream;
123 snd_pcm_plugin_format_t src_format; /* source format */
124 snd_pcm_plugin_format_t dst_format; /* destination format */
125 int src_width; /* sample width in bits */
126 int dst_width; /* sample width in bits */
127 int access;
128 snd_pcm_sframes_t (*src_frames)(snd_pcm_plugin_t *plugin, snd_pcm_uframes_t dst_frames);
129 snd_pcm_sframes_t (*dst_frames)(snd_pcm_plugin_t *plugin, snd_pcm_uframes_t src_frames);
130 snd_pcm_sframes_t (*client_channels)(snd_pcm_plugin_t *plugin,
131 snd_pcm_uframes_t frames,
132 snd_pcm_plugin_channel_t **channels);
133 int (*src_channels_mask)(snd_pcm_plugin_t *plugin,
134 bitset_t *dst_vmask,
135 bitset_t **src_vmask);
136 int (*dst_channels_mask)(snd_pcm_plugin_t *plugin,
137 bitset_t *src_vmask,
138 bitset_t **dst_vmask);
139 snd_pcm_sframes_t (*transfer)(snd_pcm_plugin_t *plugin,
140 const snd_pcm_plugin_channel_t *src_channels,
141 snd_pcm_plugin_channel_t *dst_channels,
142 snd_pcm_uframes_t frames);
143 int (*action)(snd_pcm_plugin_t *plugin,
144 snd_pcm_plugin_action_t action,
145 unsigned long data);
146 snd_pcm_plugin_t *prev;
147 snd_pcm_plugin_t *next;
148 snd_pcm_plug_t *plug;
149 void *private_data;
150 void (*private_free)(snd_pcm_plugin_t *plugin);
151 char *buf;
152 snd_pcm_uframes_t buf_frames;
153 snd_pcm_plugin_channel_t *buf_channels;
154 bitset_t *src_vmask;
155 bitset_t *dst_vmask;
156 char extra_data[0];
157};
158
159int snd_pcm_plugin_build(snd_pcm_plug_t *handle,
160 const char *name,
161 snd_pcm_plugin_format_t *src_format,
162 snd_pcm_plugin_format_t *dst_format,
163 size_t extra,
164 snd_pcm_plugin_t **ret);
165int snd_pcm_plugin_free(snd_pcm_plugin_t *plugin);
166int snd_pcm_plugin_clear(snd_pcm_plugin_t **first);
167int snd_pcm_plug_alloc(snd_pcm_plug_t *plug, snd_pcm_uframes_t frames);
168snd_pcm_sframes_t snd_pcm_plug_client_size(snd_pcm_plug_t *handle, snd_pcm_uframes_t drv_size);
169snd_pcm_sframes_t snd_pcm_plug_slave_size(snd_pcm_plug_t *handle, snd_pcm_uframes_t clt_size);
170
171#define FULL ROUTE_PLUGIN_RESOLUTION
172#define HALF ROUTE_PLUGIN_RESOLUTION / 2
173typedef int route_ttable_entry_t;
174
175int snd_pcm_plugin_build_io(snd_pcm_plug_t *handle,
176 snd_pcm_hw_params_t *params,
177 snd_pcm_plugin_t **r_plugin);
178int snd_pcm_plugin_build_linear(snd_pcm_plug_t *handle,
179 snd_pcm_plugin_format_t *src_format,
180 snd_pcm_plugin_format_t *dst_format,
181 snd_pcm_plugin_t **r_plugin);
182int snd_pcm_plugin_build_mulaw(snd_pcm_plug_t *handle,
183 snd_pcm_plugin_format_t *src_format,
184 snd_pcm_plugin_format_t *dst_format,
185 snd_pcm_plugin_t **r_plugin);
186int snd_pcm_plugin_build_rate(snd_pcm_plug_t *handle,
187 snd_pcm_plugin_format_t *src_format,
188 snd_pcm_plugin_format_t *dst_format,
189 snd_pcm_plugin_t **r_plugin);
190int snd_pcm_plugin_build_route(snd_pcm_plug_t *handle,
191 snd_pcm_plugin_format_t *src_format,
192 snd_pcm_plugin_format_t *dst_format,
193 route_ttable_entry_t *ttable,
194 snd_pcm_plugin_t **r_plugin);
195int snd_pcm_plugin_build_copy(snd_pcm_plug_t *handle,
196 snd_pcm_plugin_format_t *src_format,
197 snd_pcm_plugin_format_t *dst_format,
198 snd_pcm_plugin_t **r_plugin);
199
200int snd_pcm_plug_format_plugins(snd_pcm_plug_t *substream,
201 snd_pcm_hw_params_t *params,
202 snd_pcm_hw_params_t *slave_params);
203
204int snd_pcm_plug_slave_format(int format, snd_mask_t *format_mask);
205
206int snd_pcm_plugin_append(snd_pcm_plugin_t *plugin);
207
208snd_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);
209snd_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);
210
211snd_pcm_sframes_t snd_pcm_plug_client_channels_buf(snd_pcm_plug_t *handle,
212 char *buf, snd_pcm_uframes_t count,
213 snd_pcm_plugin_channel_t **channels);
214
215snd_pcm_sframes_t snd_pcm_plugin_client_channels(snd_pcm_plugin_t *plugin,
216 snd_pcm_uframes_t frames,
217 snd_pcm_plugin_channel_t **channels);
218
219int snd_pcm_area_silence(const snd_pcm_channel_area_t *dst_channel, size_t dst_offset,
220 size_t samples, int format);
221int snd_pcm_area_copy(const snd_pcm_channel_area_t *src_channel, size_t src_offset,
222 const snd_pcm_channel_area_t *dst_channel, size_t dst_offset,
223 size_t samples, int format);
224
225void *snd_pcm_plug_buf_alloc(snd_pcm_plug_t *plug, snd_pcm_uframes_t size);
226void snd_pcm_plug_buf_unlock(snd_pcm_plug_t *plug, void *ptr);
227snd_pcm_sframes_t snd_pcm_oss_write3(snd_pcm_substream_t *substream, const char *ptr, snd_pcm_uframes_t size, int in_kernel);
228snd_pcm_sframes_t snd_pcm_oss_read3(snd_pcm_substream_t *substream, char *ptr, snd_pcm_uframes_t size, int in_kernel);
229snd_pcm_sframes_t snd_pcm_oss_writev3(snd_pcm_substream_t *substream, void **bufs, snd_pcm_uframes_t frames, int in_kernel);
230snd_pcm_sframes_t snd_pcm_oss_readv3(snd_pcm_substream_t *substream, void **bufs, snd_pcm_uframes_t frames, int in_kernel);
231
232
233
234#define ROUTE_PLUGIN_RESOLUTION 16
235
236int getput_index(int format);
237int copy_index(int format);
238int conv_index(int src_format, int dst_format);
239
240void zero_channel(snd_pcm_plugin_t *plugin,
241 const snd_pcm_plugin_channel_t *dst_channel,
242 size_t samples);
243
244#ifdef PLUGIN_DEBUG
245#define pdprintf( fmt, args... ) printk( "plugin: " fmt, ##args)
246#else
247#define pdprintf( fmt, args... )
248#endif
249
250#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 000000000000..0607e9566084
--- /dev/null
+++ b/sound/core/oss/plugin_ops.h
@@ -0,0 +1,536 @@
1/*
2 * Plugin sample operators with fast switch
3 * Copyright (c) 2000 by Jaroslav Kysela <perex@suse.cz>
4 *
5 *
6 * This library is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU Library General Public License as
8 * published by the Free Software Foundation; either version 2 of
9 * the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Library General Public License for more details.
15 *
16 * You should have received a copy of the GNU Library General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 *
20 */
21
22
23#define as_u8(ptr) (*(u_int8_t*)(ptr))
24#define as_u16(ptr) (*(u_int16_t*)(ptr))
25#define as_u32(ptr) (*(u_int32_t*)(ptr))
26#define as_u64(ptr) (*(u_int64_t*)(ptr))
27#define as_s8(ptr) (*(int8_t*)(ptr))
28#define as_s16(ptr) (*(int16_t*)(ptr))
29#define as_s32(ptr) (*(int32_t*)(ptr))
30#define as_s64(ptr) (*(int64_t*)(ptr))
31
32#ifdef COPY_LABELS
33static void *copy_labels[4] = {
34 &&copy_8,
35 &&copy_16,
36 &&copy_32,
37 &&copy_64
38};
39#endif
40
41#ifdef COPY_END
42while(0) {
43copy_8: as_s8(dst) = as_s8(src); goto COPY_END;
44copy_16: as_s16(dst) = as_s16(src); goto COPY_END;
45copy_32: as_s32(dst) = as_s32(src); goto COPY_END;
46copy_64: as_s64(dst) = as_s64(src); goto COPY_END;
47}
48#endif
49
50#ifdef CONV_LABELS
51/* src_wid src_endswap sign_toggle dst_wid dst_endswap */
52static void *conv_labels[4 * 2 * 2 * 4 * 2] = {
53 &&conv_xxx1_xxx1, /* 8h -> 8h */
54 &&conv_xxx1_xxx1, /* 8h -> 8s */
55 &&conv_xxx1_xx10, /* 8h -> 16h */
56 &&conv_xxx1_xx01, /* 8h -> 16s */
57 &&conv_xxx1_x100, /* 8h -> 24h */
58 &&conv_xxx1_001x, /* 8h -> 24s */
59 &&conv_xxx1_1000, /* 8h -> 32h */
60 &&conv_xxx1_0001, /* 8h -> 32s */
61 &&conv_xxx1_xxx9, /* 8h ^> 8h */
62 &&conv_xxx1_xxx9, /* 8h ^> 8s */
63 &&conv_xxx1_xx90, /* 8h ^> 16h */
64 &&conv_xxx1_xx09, /* 8h ^> 16s */
65 &&conv_xxx1_x900, /* 8h ^> 24h */
66 &&conv_xxx1_009x, /* 8h ^> 24s */
67 &&conv_xxx1_9000, /* 8h ^> 32h */
68 &&conv_xxx1_0009, /* 8h ^> 32s */
69 &&conv_xxx1_xxx1, /* 8s -> 8h */
70 &&conv_xxx1_xxx1, /* 8s -> 8s */
71 &&conv_xxx1_xx10, /* 8s -> 16h */
72 &&conv_xxx1_xx01, /* 8s -> 16s */
73 &&conv_xxx1_x100, /* 8s -> 24h */
74 &&conv_xxx1_001x, /* 8s -> 24s */
75 &&conv_xxx1_1000, /* 8s -> 32h */
76 &&conv_xxx1_0001, /* 8s -> 32s */
77 &&conv_xxx1_xxx9, /* 8s ^> 8h */
78 &&conv_xxx1_xxx9, /* 8s ^> 8s */
79 &&conv_xxx1_xx90, /* 8s ^> 16h */
80 &&conv_xxx1_xx09, /* 8s ^> 16s */
81 &&conv_xxx1_x900, /* 8s ^> 24h */
82 &&conv_xxx1_009x, /* 8s ^> 24s */
83 &&conv_xxx1_9000, /* 8s ^> 32h */
84 &&conv_xxx1_0009, /* 8s ^> 32s */
85 &&conv_xx12_xxx1, /* 16h -> 8h */
86 &&conv_xx12_xxx1, /* 16h -> 8s */
87 &&conv_xx12_xx12, /* 16h -> 16h */
88 &&conv_xx12_xx21, /* 16h -> 16s */
89 &&conv_xx12_x120, /* 16h -> 24h */
90 &&conv_xx12_021x, /* 16h -> 24s */
91 &&conv_xx12_1200, /* 16h -> 32h */
92 &&conv_xx12_0021, /* 16h -> 32s */
93 &&conv_xx12_xxx9, /* 16h ^> 8h */
94 &&conv_xx12_xxx9, /* 16h ^> 8s */
95 &&conv_xx12_xx92, /* 16h ^> 16h */
96 &&conv_xx12_xx29, /* 16h ^> 16s */
97 &&conv_xx12_x920, /* 16h ^> 24h */
98 &&conv_xx12_029x, /* 16h ^> 24s */
99 &&conv_xx12_9200, /* 16h ^> 32h */
100 &&conv_xx12_0029, /* 16h ^> 32s */
101 &&conv_xx12_xxx2, /* 16s -> 8h */
102 &&conv_xx12_xxx2, /* 16s -> 8s */
103 &&conv_xx12_xx21, /* 16s -> 16h */
104 &&conv_xx12_xx12, /* 16s -> 16s */
105 &&conv_xx12_x210, /* 16s -> 24h */
106 &&conv_xx12_012x, /* 16s -> 24s */
107 &&conv_xx12_2100, /* 16s -> 32h */
108 &&conv_xx12_0012, /* 16s -> 32s */
109 &&conv_xx12_xxxA, /* 16s ^> 8h */
110 &&conv_xx12_xxxA, /* 16s ^> 8s */
111 &&conv_xx12_xxA1, /* 16s ^> 16h */
112 &&conv_xx12_xx1A, /* 16s ^> 16s */
113 &&conv_xx12_xA10, /* 16s ^> 24h */
114 &&conv_xx12_01Ax, /* 16s ^> 24s */
115 &&conv_xx12_A100, /* 16s ^> 32h */
116 &&conv_xx12_001A, /* 16s ^> 32s */
117 &&conv_x123_xxx1, /* 24h -> 8h */
118 &&conv_x123_xxx1, /* 24h -> 8s */
119 &&conv_x123_xx12, /* 24h -> 16h */
120 &&conv_x123_xx21, /* 24h -> 16s */
121 &&conv_x123_x123, /* 24h -> 24h */
122 &&conv_x123_321x, /* 24h -> 24s */
123 &&conv_x123_1230, /* 24h -> 32h */
124 &&conv_x123_0321, /* 24h -> 32s */
125 &&conv_x123_xxx9, /* 24h ^> 8h */
126 &&conv_x123_xxx9, /* 24h ^> 8s */
127 &&conv_x123_xx92, /* 24h ^> 16h */
128 &&conv_x123_xx29, /* 24h ^> 16s */
129 &&conv_x123_x923, /* 24h ^> 24h */
130 &&conv_x123_329x, /* 24h ^> 24s */
131 &&conv_x123_9230, /* 24h ^> 32h */
132 &&conv_x123_0329, /* 24h ^> 32s */
133 &&conv_123x_xxx3, /* 24s -> 8h */
134 &&conv_123x_xxx3, /* 24s -> 8s */
135 &&conv_123x_xx32, /* 24s -> 16h */
136 &&conv_123x_xx23, /* 24s -> 16s */
137 &&conv_123x_x321, /* 24s -> 24h */
138 &&conv_123x_123x, /* 24s -> 24s */
139 &&conv_123x_3210, /* 24s -> 32h */
140 &&conv_123x_0123, /* 24s -> 32s */
141 &&conv_123x_xxxB, /* 24s ^> 8h */
142 &&conv_123x_xxxB, /* 24s ^> 8s */
143 &&conv_123x_xxB2, /* 24s ^> 16h */
144 &&conv_123x_xx2B, /* 24s ^> 16s */
145 &&conv_123x_xB21, /* 24s ^> 24h */
146 &&conv_123x_12Bx, /* 24s ^> 24s */
147 &&conv_123x_B210, /* 24s ^> 32h */
148 &&conv_123x_012B, /* 24s ^> 32s */
149 &&conv_1234_xxx1, /* 32h -> 8h */
150 &&conv_1234_xxx1, /* 32h -> 8s */
151 &&conv_1234_xx12, /* 32h -> 16h */
152 &&conv_1234_xx21, /* 32h -> 16s */
153 &&conv_1234_x123, /* 32h -> 24h */
154 &&conv_1234_321x, /* 32h -> 24s */
155 &&conv_1234_1234, /* 32h -> 32h */
156 &&conv_1234_4321, /* 32h -> 32s */
157 &&conv_1234_xxx9, /* 32h ^> 8h */
158 &&conv_1234_xxx9, /* 32h ^> 8s */
159 &&conv_1234_xx92, /* 32h ^> 16h */
160 &&conv_1234_xx29, /* 32h ^> 16s */
161 &&conv_1234_x923, /* 32h ^> 24h */
162 &&conv_1234_329x, /* 32h ^> 24s */
163 &&conv_1234_9234, /* 32h ^> 32h */
164 &&conv_1234_4329, /* 32h ^> 32s */
165 &&conv_1234_xxx4, /* 32s -> 8h */
166 &&conv_1234_xxx4, /* 32s -> 8s */
167 &&conv_1234_xx43, /* 32s -> 16h */
168 &&conv_1234_xx34, /* 32s -> 16s */
169 &&conv_1234_x432, /* 32s -> 24h */
170 &&conv_1234_234x, /* 32s -> 24s */
171 &&conv_1234_4321, /* 32s -> 32h */
172 &&conv_1234_1234, /* 32s -> 32s */
173 &&conv_1234_xxxC, /* 32s ^> 8h */
174 &&conv_1234_xxxC, /* 32s ^> 8s */
175 &&conv_1234_xxC3, /* 32s ^> 16h */
176 &&conv_1234_xx3C, /* 32s ^> 16s */
177 &&conv_1234_xC32, /* 32s ^> 24h */
178 &&conv_1234_23Cx, /* 32s ^> 24s */
179 &&conv_1234_C321, /* 32s ^> 32h */
180 &&conv_1234_123C, /* 32s ^> 32s */
181};
182#endif
183
184#ifdef CONV_END
185while(0) {
186conv_xxx1_xxx1: as_u8(dst) = as_u8(src); goto CONV_END;
187conv_xxx1_xx10: as_u16(dst) = (u_int16_t)as_u8(src) << 8; goto CONV_END;
188conv_xxx1_xx01: as_u16(dst) = (u_int16_t)as_u8(src); goto CONV_END;
189conv_xxx1_x100: as_u32(dst) = (u_int32_t)as_u8(src) << 16; goto CONV_END;
190conv_xxx1_001x: as_u32(dst) = (u_int32_t)as_u8(src) << 8; goto CONV_END;
191conv_xxx1_1000: as_u32(dst) = (u_int32_t)as_u8(src) << 24; goto CONV_END;
192conv_xxx1_0001: as_u32(dst) = (u_int32_t)as_u8(src); goto CONV_END;
193conv_xxx1_xxx9: as_u8(dst) = as_u8(src) ^ 0x80; goto CONV_END;
194conv_xxx1_xx90: as_u16(dst) = (u_int16_t)(as_u8(src) ^ 0x80) << 8; goto CONV_END;
195conv_xxx1_xx09: as_u16(dst) = (u_int16_t)(as_u8(src) ^ 0x80); goto CONV_END;
196conv_xxx1_x900: as_u32(dst) = (u_int32_t)(as_u8(src) ^ 0x80) << 16; goto CONV_END;
197conv_xxx1_009x: as_u32(dst) = (u_int32_t)(as_u8(src) ^ 0x80) << 8; goto CONV_END;
198conv_xxx1_9000: as_u32(dst) = (u_int32_t)(as_u8(src) ^ 0x80) << 24; goto CONV_END;
199conv_xxx1_0009: as_u32(dst) = (u_int32_t)(as_u8(src) ^ 0x80); goto CONV_END;
200conv_xx12_xxx1: as_u8(dst) = as_u16(src) >> 8; goto CONV_END;
201conv_xx12_xx12: as_u16(dst) = as_u16(src); goto CONV_END;
202conv_xx12_xx21: as_u16(dst) = swab16(as_u16(src)); goto CONV_END;
203conv_xx12_x120: as_u32(dst) = (u_int32_t)as_u16(src) << 8; goto CONV_END;
204conv_xx12_021x: as_u32(dst) = (u_int32_t)swab16(as_u16(src)) << 8; goto CONV_END;
205conv_xx12_1200: as_u32(dst) = (u_int32_t)as_u16(src) << 16; goto CONV_END;
206conv_xx12_0021: as_u32(dst) = (u_int32_t)swab16(as_u16(src)); goto CONV_END;
207conv_xx12_xxx9: as_u8(dst) = (as_u16(src) >> 8) ^ 0x80; goto CONV_END;
208conv_xx12_xx92: as_u16(dst) = as_u16(src) ^ 0x8000; goto CONV_END;
209conv_xx12_xx29: as_u16(dst) = swab16(as_u16(src)) ^ 0x80; goto CONV_END;
210conv_xx12_x920: as_u32(dst) = (u_int32_t)(as_u16(src) ^ 0x8000) << 8; goto CONV_END;
211conv_xx12_029x: as_u32(dst) = (u_int32_t)(swab16(as_u16(src)) ^ 0x80) << 8; goto CONV_END;
212conv_xx12_9200: as_u32(dst) = (u_int32_t)(as_u16(src) ^ 0x8000) << 16; goto CONV_END;
213conv_xx12_0029: as_u32(dst) = (u_int32_t)(swab16(as_u16(src)) ^ 0x80); goto CONV_END;
214conv_xx12_xxx2: as_u8(dst) = as_u16(src) & 0xff; goto CONV_END;
215conv_xx12_x210: as_u32(dst) = (u_int32_t)swab16(as_u16(src)) << 8; goto CONV_END;
216conv_xx12_012x: as_u32(dst) = (u_int32_t)as_u16(src) << 8; goto CONV_END;
217conv_xx12_2100: as_u32(dst) = (u_int32_t)swab16(as_u16(src)) << 16; goto CONV_END;
218conv_xx12_0012: as_u32(dst) = (u_int32_t)as_u16(src); goto CONV_END;
219conv_xx12_xxxA: as_u8(dst) = (as_u16(src) ^ 0x80) & 0xff; goto CONV_END;
220conv_xx12_xxA1: as_u16(dst) = swab16(as_u16(src) ^ 0x80); goto CONV_END;
221conv_xx12_xx1A: as_u16(dst) = as_u16(src) ^ 0x80; goto CONV_END;
222conv_xx12_xA10: as_u32(dst) = (u_int32_t)swab16(as_u16(src) ^ 0x80) << 8; goto CONV_END;
223conv_xx12_01Ax: as_u32(dst) = (u_int32_t)(as_u16(src) ^ 0x80) << 8; goto CONV_END;
224conv_xx12_A100: as_u32(dst) = (u_int32_t)swab16(as_u16(src) ^ 0x80) << 16; goto CONV_END;
225conv_xx12_001A: as_u32(dst) = (u_int32_t)(as_u16(src) ^ 0x80); goto CONV_END;
226conv_x123_xxx1: as_u8(dst) = as_u32(src) >> 16; goto CONV_END;
227conv_x123_xx12: as_u16(dst) = as_u32(src) >> 8; goto CONV_END;
228conv_x123_xx21: as_u16(dst) = swab16(as_u32(src) >> 8); goto CONV_END;
229conv_x123_x123: as_u32(dst) = as_u32(src); goto CONV_END;
230conv_x123_321x: as_u32(dst) = swab32(as_u32(src)); goto CONV_END;
231conv_x123_1230: as_u32(dst) = as_u32(src) << 8; goto CONV_END;
232conv_x123_0321: as_u32(dst) = swab32(as_u32(src)) >> 8; goto CONV_END;
233conv_x123_xxx9: as_u8(dst) = (as_u32(src) >> 16) ^ 0x80; goto CONV_END;
234conv_x123_xx92: as_u16(dst) = (as_u32(src) >> 8) ^ 0x8000; goto CONV_END;
235conv_x123_xx29: as_u16(dst) = swab16(as_u32(src) >> 8) ^ 0x80; goto CONV_END;
236conv_x123_x923: as_u32(dst) = as_u32(src) ^ 0x800000; goto CONV_END;
237conv_x123_329x: as_u32(dst) = swab32(as_u32(src)) ^ 0x8000; goto CONV_END;
238conv_x123_9230: as_u32(dst) = (as_u32(src) ^ 0x800000) << 8; goto CONV_END;
239conv_x123_0329: as_u32(dst) = (swab32(as_u32(src)) >> 8) ^ 0x80; goto CONV_END;
240conv_123x_xxx3: as_u8(dst) = (as_u32(src) >> 8) & 0xff; goto CONV_END;
241conv_123x_xx32: as_u16(dst) = swab16(as_u32(src) >> 8); goto CONV_END;
242conv_123x_xx23: as_u16(dst) = (as_u32(src) >> 8) & 0xffff; goto CONV_END;
243conv_123x_x321: as_u32(dst) = swab32(as_u32(src)); goto CONV_END;
244conv_123x_123x: as_u32(dst) = as_u32(src); goto CONV_END;
245conv_123x_3210: as_u32(dst) = swab32(as_u32(src)) << 8; goto CONV_END;
246conv_123x_0123: as_u32(dst) = as_u32(src) >> 8; goto CONV_END;
247conv_123x_xxxB: as_u8(dst) = ((as_u32(src) >> 8) & 0xff) ^ 0x80; goto CONV_END;
248conv_123x_xxB2: as_u16(dst) = swab16((as_u32(src) >> 8) ^ 0x80); goto CONV_END;
249conv_123x_xx2B: as_u16(dst) = ((as_u32(src) >> 8) & 0xffff) ^ 0x80; goto CONV_END;
250conv_123x_xB21: as_u32(dst) = swab32(as_u32(src)) ^ 0x800000; goto CONV_END;
251conv_123x_12Bx: as_u32(dst) = as_u32(src) ^ 0x8000; goto CONV_END;
252conv_123x_B210: as_u32(dst) = swab32(as_u32(src) ^ 0x8000) << 8; goto CONV_END;
253conv_123x_012B: as_u32(dst) = (as_u32(src) >> 8) ^ 0x80; goto CONV_END;
254conv_1234_xxx1: as_u8(dst) = as_u32(src) >> 24; goto CONV_END;
255conv_1234_xx12: as_u16(dst) = as_u32(src) >> 16; goto CONV_END;
256conv_1234_xx21: as_u16(dst) = swab16(as_u32(src) >> 16); goto CONV_END;
257conv_1234_x123: as_u32(dst) = as_u32(src) >> 8; goto CONV_END;
258conv_1234_321x: as_u32(dst) = swab32(as_u32(src)) << 8; goto CONV_END;
259conv_1234_1234: as_u32(dst) = as_u32(src); goto CONV_END;
260conv_1234_4321: as_u32(dst) = swab32(as_u32(src)); goto CONV_END;
261conv_1234_xxx9: as_u8(dst) = (as_u32(src) >> 24) ^ 0x80; goto CONV_END;
262conv_1234_xx92: as_u16(dst) = (as_u32(src) >> 16) ^ 0x8000; goto CONV_END;
263conv_1234_xx29: as_u16(dst) = swab16(as_u32(src) >> 16) ^ 0x80; goto CONV_END;
264conv_1234_x923: as_u32(dst) = (as_u32(src) >> 8) ^ 0x800000; goto CONV_END;
265conv_1234_329x: as_u32(dst) = (swab32(as_u32(src)) ^ 0x80) << 8; goto CONV_END;
266conv_1234_9234: as_u32(dst) = as_u32(src) ^ 0x80000000; goto CONV_END;
267conv_1234_4329: as_u32(dst) = swab32(as_u32(src)) ^ 0x80; goto CONV_END;
268conv_1234_xxx4: as_u8(dst) = as_u32(src) & 0xff; goto CONV_END;
269conv_1234_xx43: as_u16(dst) = swab16(as_u32(src)); goto CONV_END;
270conv_1234_xx34: as_u16(dst) = as_u32(src) & 0xffff; goto CONV_END;
271conv_1234_x432: as_u32(dst) = swab32(as_u32(src)) >> 8; goto CONV_END;
272conv_1234_234x: as_u32(dst) = as_u32(src) << 8; goto CONV_END;
273conv_1234_xxxC: as_u8(dst) = (as_u32(src) & 0xff) ^ 0x80; goto CONV_END;
274conv_1234_xxC3: as_u16(dst) = swab16(as_u32(src) ^ 0x80); goto CONV_END;
275conv_1234_xx3C: as_u16(dst) = (as_u32(src) & 0xffff) ^ 0x80; goto CONV_END;
276conv_1234_xC32: as_u32(dst) = (swab32(as_u32(src)) >> 8) ^ 0x800000; goto CONV_END;
277conv_1234_23Cx: as_u32(dst) = (as_u32(src) ^ 0x80) << 8; goto CONV_END;
278conv_1234_C321: as_u32(dst) = swab32(as_u32(src) ^ 0x80); goto CONV_END;
279conv_1234_123C: as_u32(dst) = as_u32(src) ^ 0x80; goto CONV_END;
280}
281#endif
282
283#ifdef GET_S16_LABELS
284/* src_wid src_endswap unsigned */
285static void *get_s16_labels[4 * 2 * 2] = {
286 &&get_s16_xxx1_xx10, /* 8h -> 16h */
287 &&get_s16_xxx1_xx90, /* 8h ^> 16h */
288 &&get_s16_xxx1_xx10, /* 8s -> 16h */
289 &&get_s16_xxx1_xx90, /* 8s ^> 16h */
290 &&get_s16_xx12_xx12, /* 16h -> 16h */
291 &&get_s16_xx12_xx92, /* 16h ^> 16h */
292 &&get_s16_xx12_xx21, /* 16s -> 16h */
293 &&get_s16_xx12_xxA1, /* 16s ^> 16h */
294 &&get_s16_x123_xx12, /* 24h -> 16h */
295 &&get_s16_x123_xx92, /* 24h ^> 16h */
296 &&get_s16_123x_xx32, /* 24s -> 16h */
297 &&get_s16_123x_xxB2, /* 24s ^> 16h */
298 &&get_s16_1234_xx12, /* 32h -> 16h */
299 &&get_s16_1234_xx92, /* 32h ^> 16h */
300 &&get_s16_1234_xx43, /* 32s -> 16h */
301 &&get_s16_1234_xxC3, /* 32s ^> 16h */
302};
303#endif
304
305#ifdef GET_S16_END
306while(0) {
307get_s16_xxx1_xx10: sample = (u_int16_t)as_u8(src) << 8; goto GET_S16_END;
308get_s16_xxx1_xx90: sample = (u_int16_t)(as_u8(src) ^ 0x80) << 8; goto GET_S16_END;
309get_s16_xx12_xx12: sample = as_u16(src); goto GET_S16_END;
310get_s16_xx12_xx92: sample = as_u16(src) ^ 0x8000; goto GET_S16_END;
311get_s16_xx12_xx21: sample = swab16(as_u16(src)); goto GET_S16_END;
312get_s16_xx12_xxA1: sample = swab16(as_u16(src) ^ 0x80); goto GET_S16_END;
313get_s16_x123_xx12: sample = as_u32(src) >> 8; goto GET_S16_END;
314get_s16_x123_xx92: sample = (as_u32(src) >> 8) ^ 0x8000; goto GET_S16_END;
315get_s16_123x_xx32: sample = swab16(as_u32(src) >> 8); goto GET_S16_END;
316get_s16_123x_xxB2: sample = swab16((as_u32(src) >> 8) ^ 0x8000); goto GET_S16_END;
317get_s16_1234_xx12: sample = as_u32(src) >> 16; goto GET_S16_END;
318get_s16_1234_xx92: sample = (as_u32(src) >> 16) ^ 0x8000; goto GET_S16_END;
319get_s16_1234_xx43: sample = swab16(as_u32(src)); goto GET_S16_END;
320get_s16_1234_xxC3: sample = swab16(as_u32(src) ^ 0x80); goto GET_S16_END;
321}
322#endif
323
324#ifdef PUT_S16_LABELS
325/* dst_wid dst_endswap unsigned */
326static void *put_s16_labels[4 * 2 * 2] = {
327 &&put_s16_xx12_xxx1, /* 16h -> 8h */
328 &&put_s16_xx12_xxx9, /* 16h ^> 8h */
329 &&put_s16_xx12_xxx1, /* 16h -> 8s */
330 &&put_s16_xx12_xxx9, /* 16h ^> 8s */
331 &&put_s16_xx12_xx12, /* 16h -> 16h */
332 &&put_s16_xx12_xx92, /* 16h ^> 16h */
333 &&put_s16_xx12_xx21, /* 16h -> 16s */
334 &&put_s16_xx12_xx29, /* 16h ^> 16s */
335 &&put_s16_xx12_x120, /* 16h -> 24h */
336 &&put_s16_xx12_x920, /* 16h ^> 24h */
337 &&put_s16_xx12_021x, /* 16h -> 24s */
338 &&put_s16_xx12_029x, /* 16h ^> 24s */
339 &&put_s16_xx12_1200, /* 16h -> 32h */
340 &&put_s16_xx12_9200, /* 16h ^> 32h */
341 &&put_s16_xx12_0021, /* 16h -> 32s */
342 &&put_s16_xx12_0029, /* 16h ^> 32s */
343};
344#endif
345
346#ifdef PUT_S16_END
347while (0) {
348put_s16_xx12_xxx1: as_u8(dst) = sample >> 8; goto PUT_S16_END;
349put_s16_xx12_xxx9: as_u8(dst) = (sample >> 8) ^ 0x80; goto PUT_S16_END;
350put_s16_xx12_xx12: as_u16(dst) = sample; goto PUT_S16_END;
351put_s16_xx12_xx92: as_u16(dst) = sample ^ 0x8000; goto PUT_S16_END;
352put_s16_xx12_xx21: as_u16(dst) = swab16(sample); goto PUT_S16_END;
353put_s16_xx12_xx29: as_u16(dst) = swab16(sample) ^ 0x80; goto PUT_S16_END;
354put_s16_xx12_x120: as_u32(dst) = (u_int32_t)sample << 8; goto PUT_S16_END;
355put_s16_xx12_x920: as_u32(dst) = (u_int32_t)(sample ^ 0x8000) << 8; goto PUT_S16_END;
356put_s16_xx12_021x: as_u32(dst) = (u_int32_t)swab16(sample) << 8; goto PUT_S16_END;
357put_s16_xx12_029x: as_u32(dst) = (u_int32_t)(swab16(sample) ^ 0x80) << 8; goto PUT_S16_END;
358put_s16_xx12_1200: as_u32(dst) = (u_int32_t)sample << 16; goto PUT_S16_END;
359put_s16_xx12_9200: as_u32(dst) = (u_int32_t)(sample ^ 0x8000) << 16; goto PUT_S16_END;
360put_s16_xx12_0021: as_u32(dst) = (u_int32_t)swab16(sample); goto PUT_S16_END;
361put_s16_xx12_0029: as_u32(dst) = (u_int32_t)swab16(sample) ^ 0x80; goto PUT_S16_END;
362}
363#endif
364
365#if 0
366#ifdef GET32_LABELS
367/* src_wid src_endswap unsigned */
368static void *get32_labels[4 * 2 * 2] = {
369 &&get32_xxx1_1000, /* 8h -> 32h */
370 &&get32_xxx1_9000, /* 8h ^> 32h */
371 &&get32_xxx1_1000, /* 8s -> 32h */
372 &&get32_xxx1_9000, /* 8s ^> 32h */
373 &&get32_xx12_1200, /* 16h -> 32h */
374 &&get32_xx12_9200, /* 16h ^> 32h */
375 &&get32_xx12_2100, /* 16s -> 32h */
376 &&get32_xx12_A100, /* 16s ^> 32h */
377 &&get32_x123_1230, /* 24h -> 32h */
378 &&get32_x123_9230, /* 24h ^> 32h */
379 &&get32_123x_3210, /* 24s -> 32h */
380 &&get32_123x_B210, /* 24s ^> 32h */
381 &&get32_1234_1234, /* 32h -> 32h */
382 &&get32_1234_9234, /* 32h ^> 32h */
383 &&get32_1234_4321, /* 32s -> 32h */
384 &&get32_1234_C321, /* 32s ^> 32h */
385};
386#endif
387
388#ifdef GET32_END
389while (0) {
390get32_xxx1_1000: sample = (u_int32_t)as_u8(src) << 24; goto GET32_END;
391get32_xxx1_9000: sample = (u_int32_t)(as_u8(src) ^ 0x80) << 24; goto GET32_END;
392get32_xx12_1200: sample = (u_int32_t)as_u16(src) << 16; goto GET32_END;
393get32_xx12_9200: sample = (u_int32_t)(as_u16(src) ^ 0x8000) << 16; goto GET32_END;
394get32_xx12_2100: sample = (u_int32_t)swab16(as_u16(src)) << 16; goto GET32_END;
395get32_xx12_A100: sample = (u_int32_t)swab16(as_u16(src) ^ 0x80) << 16; goto GET32_END;
396get32_x123_1230: sample = as_u32(src) << 8; goto GET32_END;
397get32_x123_9230: sample = (as_u32(src) << 8) ^ 0x80000000; goto GET32_END;
398get32_123x_3210: sample = swab32(as_u32(src) >> 8); goto GET32_END;
399get32_123x_B210: sample = swab32((as_u32(src) >> 8) ^ 0x80); goto GET32_END;
400get32_1234_1234: sample = as_u32(src); goto GET32_END;
401get32_1234_9234: sample = as_u32(src) ^ 0x80000000; goto GET32_END;
402get32_1234_4321: sample = swab32(as_u32(src)); goto GET32_END;
403get32_1234_C321: sample = swab32(as_u32(src) ^ 0x80); goto GET32_END;
404}
405#endif
406#endif
407
408#ifdef PUT_U32_LABELS
409/* dst_wid dst_endswap unsigned */
410static void *put_u32_labels[4 * 2 * 2] = {
411 &&put_u32_1234_xxx9, /* u32h -> s8h */
412 &&put_u32_1234_xxx1, /* u32h -> u8h */
413 &&put_u32_1234_xxx9, /* u32h -> s8s */
414 &&put_u32_1234_xxx1, /* u32h -> u8s */
415 &&put_u32_1234_xx92, /* u32h -> s16h */
416 &&put_u32_1234_xx12, /* u32h -> u16h */
417 &&put_u32_1234_xx29, /* u32h -> s16s */
418 &&put_u32_1234_xx21, /* u32h -> u16s */
419 &&put_u32_1234_x923, /* u32h -> s24h */
420 &&put_u32_1234_x123, /* u32h -> u24h */
421 &&put_u32_1234_329x, /* u32h -> s24s */
422 &&put_u32_1234_321x, /* u32h -> u24s */
423 &&put_u32_1234_9234, /* u32h -> s32h */
424 &&put_u32_1234_1234, /* u32h -> u32h */
425 &&put_u32_1234_4329, /* u32h -> s32s */
426 &&put_u32_1234_4321, /* u32h -> u32s */
427};
428#endif
429
430#ifdef PUT_U32_END
431while (0) {
432put_u32_1234_xxx1: as_u8(dst) = sample >> 24; goto PUT_U32_END;
433put_u32_1234_xxx9: as_u8(dst) = (sample >> 24) ^ 0x80; goto PUT_U32_END;
434put_u32_1234_xx12: as_u16(dst) = sample >> 16; goto PUT_U32_END;
435put_u32_1234_xx92: as_u16(dst) = (sample >> 16) ^ 0x8000; goto PUT_U32_END;
436put_u32_1234_xx21: as_u16(dst) = swab16(sample >> 16); goto PUT_U32_END;
437put_u32_1234_xx29: as_u16(dst) = swab16(sample >> 16) ^ 0x80; goto PUT_U32_END;
438put_u32_1234_x123: as_u32(dst) = sample >> 8; goto PUT_U32_END;
439put_u32_1234_x923: as_u32(dst) = (sample >> 8) ^ 0x800000; goto PUT_U32_END;
440put_u32_1234_321x: as_u32(dst) = swab32(sample) << 8; goto PUT_U32_END;
441put_u32_1234_329x: as_u32(dst) = (swab32(sample) ^ 0x80) << 8; goto PUT_U32_END;
442put_u32_1234_1234: as_u32(dst) = sample; goto PUT_U32_END;
443put_u32_1234_9234: as_u32(dst) = sample ^ 0x80000000; goto PUT_U32_END;
444put_u32_1234_4321: as_u32(dst) = swab32(sample); goto PUT_U32_END;
445put_u32_1234_4329: as_u32(dst) = swab32(sample) ^ 0x80; goto PUT_U32_END;
446}
447#endif
448
449#ifdef GET_U_LABELS
450/* width endswap unsigned*/
451static void *get_u_labels[4 * 2 * 2] = {
452 &&get_u_s8, /* s8 -> u8 */
453 &&get_u_u8, /* u8 -> u8 */
454 &&get_u_s8, /* s8 -> u8 */
455 &&get_u_u8, /* u8 -> u8 */
456 &&get_u_s16h, /* s16h -> u16h */
457 &&get_u_u16h, /* u16h -> u16h */
458 &&get_u_s16s, /* s16s -> u16h */
459 &&get_u_u16s, /* u16s -> u16h */
460 &&get_u_s24h, /* s24h -> u32h */
461 &&get_u_u24h, /* u24h -> u32h */
462 &&get_u_s24s, /* s24s -> u32h */
463 &&get_u_u24s, /* u24s -> u32h */
464 &&get_u_s32h, /* s32h -> u32h */
465 &&get_u_u32h, /* u32h -> u32h */
466 &&get_u_s32s, /* s32s -> u32h */
467 &&get_u_u32s, /* u32s -> u32h */
468};
469#endif
470
471#ifdef GET_U_END
472while (0) {
473get_u_s8: sample = as_u8(src) ^ 0x80; goto GET_U_END;
474get_u_u8: sample = as_u8(src); goto GET_U_END;
475get_u_s16h: sample = as_u16(src) ^ 0x8000; goto GET_U_END;
476get_u_u16h: sample = as_u16(src); goto GET_U_END;
477get_u_s16s: sample = swab16(as_u16(src) ^ 0x80); goto GET_U_END;
478get_u_u16s: sample = swab16(as_u16(src)); goto GET_U_END;
479get_u_s24h: sample = (as_u32(src) ^ 0x800000); goto GET_U_END;
480get_u_u24h: sample = as_u32(src); goto GET_U_END;
481get_u_s24s: sample = swab32(as_u32(src) ^ 0x800000); goto GET_U_END;
482get_u_u24s: sample = swab32(as_u32(src)); goto GET_U_END;
483get_u_s32h: sample = as_u32(src) ^ 0x80000000; goto GET_U_END;
484get_u_u32h: sample = as_u32(src); goto GET_U_END;
485get_u_s32s: sample = swab32(as_u32(src) ^ 0x80); goto GET_U_END;
486get_u_u32s: sample = swab32(as_u32(src)); goto GET_U_END;
487}
488#endif
489
490#if 0
491#ifdef PUT_LABELS
492/* width endswap unsigned */
493static void *put_labels[4 * 2 * 2] = {
494 &&put_s8, /* s8 -> s8 */
495 &&put_u8, /* u8 -> s8 */
496 &&put_s8, /* s8 -> s8 */
497 &&put_u8, /* u8 -> s8 */
498 &&put_s16h, /* s16h -> s16h */
499 &&put_u16h, /* u16h -> s16h */
500 &&put_s16s, /* s16s -> s16h */
501 &&put_u16s, /* u16s -> s16h */
502 &&put_s24h, /* s24h -> s32h */
503 &&put_u24h, /* u24h -> s32h */
504 &&put_s24s, /* s24s -> s32h */
505 &&put_u24s, /* u24s -> s32h */
506 &&put_s32h, /* s32h -> s32h */
507 &&put_u32h, /* u32h -> s32h */
508 &&put_s32s, /* s32s -> s32h */
509 &&put_u32s, /* u32s -> s32h */
510};
511#endif
512
513#ifdef PUT_END
514put_s8: as_s8(dst) = sample; goto PUT_END;
515put_u8: as_u8(dst) = sample ^ 0x80; goto PUT_END;
516put_s16h: as_s16(dst) = sample; goto PUT_END;
517put_u16h: as_u16(dst) = sample ^ 0x8000; goto PUT_END;
518put_s16s: as_s16(dst) = swab16(sample); goto PUT_END;
519put_u16s: as_u16(dst) = swab16(sample ^ 0x80); goto PUT_END;
520put_s24h: as_s24(dst) = sample & 0xffffff; goto PUT_END;
521put_u24h: as_u24(dst) = sample ^ 0x80000000; goto PUT_END;
522put_s24s: as_s24(dst) = swab32(sample & 0xffffff); goto PUT_END;
523put_u24s: as_u24(dst) = swab32(sample ^ 0x80); goto PUT_END;
524put_s32h: as_s32(dst) = sample; goto PUT_END;
525put_u32h: as_u32(dst) = sample ^ 0x80000000; goto PUT_END;
526put_s32s: as_s32(dst) = swab32(sample); goto PUT_END;
527put_u32s: as_u32(dst) = swab32(sample ^ 0x80); goto PUT_END;
528#endif
529#endif
530
531#undef as_u8
532#undef as_u16
533#undef as_u32
534#undef as_s8
535#undef as_s16
536#undef as_s32
diff --git a/sound/core/oss/rate.c b/sound/core/oss/rate.c
new file mode 100644
index 000000000000..1096ec186714
--- /dev/null
+++ b/sound/core/oss/rate.c
@@ -0,0 +1,378 @@
1/*
2 * Rate conversion Plug-In
3 * Copyright (c) 1999 by Jaroslav Kysela <perex@suse.cz>
4 *
5 *
6 * This library is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU Library General Public License as
8 * published by the Free Software Foundation; either version 2 of
9 * the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Library General Public License for more details.
15 *
16 * You should have received a copy of the GNU Library General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 *
20 */
21
22#include <sound/driver.h>
23#include <linux/time.h>
24#include <sound/core.h>
25#include <sound/pcm.h>
26#include "pcm_plugin.h"
27
28#define SHIFT 11
29#define BITS (1<<SHIFT)
30#define R_MASK (BITS-1)
31
32/*
33 * Basic rate conversion plugin
34 */
35
36typedef struct {
37 signed short last_S1;
38 signed short last_S2;
39} rate_channel_t;
40
41typedef void (*rate_f)(snd_pcm_plugin_t *plugin,
42 const snd_pcm_plugin_channel_t *src_channels,
43 snd_pcm_plugin_channel_t *dst_channels,
44 int src_frames, int dst_frames);
45
46typedef struct rate_private_data {
47 unsigned int pitch;
48 unsigned int pos;
49 rate_f func;
50 int get, put;
51 snd_pcm_sframes_t old_src_frames, old_dst_frames;
52 rate_channel_t channels[0];
53} rate_t;
54
55static void rate_init(snd_pcm_plugin_t *plugin)
56{
57 unsigned int channel;
58 rate_t *data = (rate_t *)plugin->extra_data;
59 data->pos = 0;
60 for (channel = 0; channel < plugin->src_format.channels; channel++) {
61 data->channels[channel].last_S1 = 0;
62 data->channels[channel].last_S2 = 0;
63 }
64}
65
66static void resample_expand(snd_pcm_plugin_t *plugin,
67 const snd_pcm_plugin_channel_t *src_channels,
68 snd_pcm_plugin_channel_t *dst_channels,
69 int src_frames, int dst_frames)
70{
71 unsigned int pos = 0;
72 signed int val;
73 signed short S1, S2;
74 char *src, *dst;
75 unsigned int channel;
76 int src_step, dst_step;
77 int src_frames1, dst_frames1;
78 rate_t *data = (rate_t *)plugin->extra_data;
79 rate_channel_t *rchannels = data->channels;
80
81#define GET_S16_LABELS
82#define PUT_S16_LABELS
83#include "plugin_ops.h"
84#undef GET_S16_LABELS
85#undef PUT_S16_LABELS
86 void *get = get_s16_labels[data->get];
87 void *put = put_s16_labels[data->put];
88 signed short sample = 0;
89
90 for (channel = 0; channel < plugin->src_format.channels; channel++) {
91 pos = data->pos;
92 S1 = rchannels->last_S1;
93 S2 = rchannels->last_S2;
94 if (!src_channels[channel].enabled) {
95 if (dst_channels[channel].wanted)
96 snd_pcm_area_silence(&dst_channels[channel].area, 0, dst_frames, plugin->dst_format.format);
97 dst_channels[channel].enabled = 0;
98 continue;
99 }
100 dst_channels[channel].enabled = 1;
101 src = (char *)src_channels[channel].area.addr + src_channels[channel].area.first / 8;
102 dst = (char *)dst_channels[channel].area.addr + dst_channels[channel].area.first / 8;
103 src_step = src_channels[channel].area.step / 8;
104 dst_step = dst_channels[channel].area.step / 8;
105 src_frames1 = src_frames;
106 dst_frames1 = dst_frames;
107 while (dst_frames1-- > 0) {
108 if (pos & ~R_MASK) {
109 pos &= R_MASK;
110 S1 = S2;
111 if (src_frames1-- > 0) {
112 goto *get;
113#define GET_S16_END after_get
114#include "plugin_ops.h"
115#undef GET_S16_END
116 after_get:
117 S2 = sample;
118 src += src_step;
119 }
120 }
121 val = S1 + ((S2 - S1) * (signed int)pos) / BITS;
122 if (val < -32768)
123 val = -32768;
124 else if (val > 32767)
125 val = 32767;
126 sample = val;
127 goto *put;
128#define PUT_S16_END after_put
129#include "plugin_ops.h"
130#undef PUT_S16_END
131 after_put:
132 dst += dst_step;
133 pos += data->pitch;
134 }
135 rchannels->last_S1 = S1;
136 rchannels->last_S2 = S2;
137 rchannels++;
138 }
139 data->pos = pos;
140}
141
142static void resample_shrink(snd_pcm_plugin_t *plugin,
143 const snd_pcm_plugin_channel_t *src_channels,
144 snd_pcm_plugin_channel_t *dst_channels,
145 int src_frames, int dst_frames)
146{
147 unsigned int pos = 0;
148 signed int val;
149 signed short S1, S2;
150 char *src, *dst;
151 unsigned int channel;
152 int src_step, dst_step;
153 int src_frames1, dst_frames1;
154 rate_t *data = (rate_t *)plugin->extra_data;
155 rate_channel_t *rchannels = data->channels;
156
157#define GET_S16_LABELS
158#define PUT_S16_LABELS
159#include "plugin_ops.h"
160#undef GET_S16_LABELS
161#undef PUT_S16_LABELS
162 void *get = get_s16_labels[data->get];
163 void *put = put_s16_labels[data->put];
164 signed short sample = 0;
165
166 for (channel = 0; channel < plugin->src_format.channels; ++channel) {
167 pos = data->pos;
168 S1 = rchannels->last_S1;
169 S2 = rchannels->last_S2;
170 if (!src_channels[channel].enabled) {
171 if (dst_channels[channel].wanted)
172 snd_pcm_area_silence(&dst_channels[channel].area, 0, dst_frames, plugin->dst_format.format);
173 dst_channels[channel].enabled = 0;
174 continue;
175 }
176 dst_channels[channel].enabled = 1;
177 src = (char *)src_channels[channel].area.addr + src_channels[channel].area.first / 8;
178 dst = (char *)dst_channels[channel].area.addr + dst_channels[channel].area.first / 8;
179 src_step = src_channels[channel].area.step / 8;
180 dst_step = dst_channels[channel].area.step / 8;
181 src_frames1 = src_frames;
182 dst_frames1 = dst_frames;
183 while (dst_frames1 > 0) {
184 S1 = S2;
185 if (src_frames1-- > 0) {
186 goto *get;
187#define GET_S16_END after_get
188#include "plugin_ops.h"
189#undef GET_S16_END
190 after_get:
191 S2 = sample;
192 src += src_step;
193 }
194 if (pos & ~R_MASK) {
195 pos &= R_MASK;
196 val = S1 + ((S2 - S1) * (signed int)pos) / BITS;
197 if (val < -32768)
198 val = -32768;
199 else if (val > 32767)
200 val = 32767;
201 sample = val;
202 goto *put;
203#define PUT_S16_END after_put
204#include "plugin_ops.h"
205#undef PUT_S16_END
206 after_put:
207 dst += dst_step;
208 dst_frames1--;
209 }
210 pos += data->pitch;
211 }
212 rchannels->last_S1 = S1;
213 rchannels->last_S2 = S2;
214 rchannels++;
215 }
216 data->pos = pos;
217}
218
219static snd_pcm_sframes_t rate_src_frames(snd_pcm_plugin_t *plugin, snd_pcm_uframes_t frames)
220{
221 rate_t *data;
222 snd_pcm_sframes_t res;
223
224 snd_assert(plugin != NULL, return -ENXIO);
225 if (frames == 0)
226 return 0;
227 data = (rate_t *)plugin->extra_data;
228 if (plugin->src_format.rate < plugin->dst_format.rate) {
229 res = (((frames * data->pitch) + (BITS/2)) >> SHIFT);
230 } else {
231 res = (((frames << SHIFT) + (data->pitch / 2)) / data->pitch);
232 }
233 if (data->old_src_frames > 0) {
234 snd_pcm_sframes_t frames1 = frames, res1 = data->old_dst_frames;
235 while (data->old_src_frames < frames1) {
236 frames1 >>= 1;
237 res1 <<= 1;
238 }
239 while (data->old_src_frames > frames1) {
240 frames1 <<= 1;
241 res1 >>= 1;
242 }
243 if (data->old_src_frames == frames1)
244 return res1;
245 }
246 data->old_src_frames = frames;
247 data->old_dst_frames = res;
248 return res;
249}
250
251static snd_pcm_sframes_t rate_dst_frames(snd_pcm_plugin_t *plugin, snd_pcm_uframes_t frames)
252{
253 rate_t *data;
254 snd_pcm_sframes_t res;
255
256 snd_assert(plugin != NULL, return -ENXIO);
257 if (frames == 0)
258 return 0;
259 data = (rate_t *)plugin->extra_data;
260 if (plugin->src_format.rate < plugin->dst_format.rate) {
261 res = (((frames << SHIFT) + (data->pitch / 2)) / data->pitch);
262 } else {
263 res = (((frames * data->pitch) + (BITS/2)) >> SHIFT);
264 }
265 if (data->old_dst_frames > 0) {
266 snd_pcm_sframes_t frames1 = frames, res1 = data->old_src_frames;
267 while (data->old_dst_frames < frames1) {
268 frames1 >>= 1;
269 res1 <<= 1;
270 }
271 while (data->old_dst_frames > frames1) {
272 frames1 <<= 1;
273 res1 >>= 1;
274 }
275 if (data->old_dst_frames == frames1)
276 return res1;
277 }
278 data->old_dst_frames = frames;
279 data->old_src_frames = res;
280 return res;
281}
282
283static snd_pcm_sframes_t rate_transfer(snd_pcm_plugin_t *plugin,
284 const snd_pcm_plugin_channel_t *src_channels,
285 snd_pcm_plugin_channel_t *dst_channels,
286 snd_pcm_uframes_t frames)
287{
288 snd_pcm_uframes_t dst_frames;
289 rate_t *data;
290
291 snd_assert(plugin != NULL && src_channels != NULL && dst_channels != NULL, return -ENXIO);
292 if (frames == 0)
293 return 0;
294#ifdef CONFIG_SND_DEBUG
295 {
296 unsigned int channel;
297 for (channel = 0; channel < plugin->src_format.channels; channel++) {
298 snd_assert(src_channels[channel].area.first % 8 == 0 &&
299 src_channels[channel].area.step % 8 == 0,
300 return -ENXIO);
301 snd_assert(dst_channels[channel].area.first % 8 == 0 &&
302 dst_channels[channel].area.step % 8 == 0,
303 return -ENXIO);
304 }
305 }
306#endif
307
308 dst_frames = rate_dst_frames(plugin, frames);
309 if (dst_frames > dst_channels[0].frames)
310 dst_frames = dst_channels[0].frames;
311 data = (rate_t *)plugin->extra_data;
312 data->func(plugin, src_channels, dst_channels, frames, dst_frames);
313 return dst_frames;
314}
315
316static int rate_action(snd_pcm_plugin_t *plugin,
317 snd_pcm_plugin_action_t action,
318 unsigned long udata ATTRIBUTE_UNUSED)
319{
320 snd_assert(plugin != NULL, return -ENXIO);
321 switch (action) {
322 case INIT:
323 case PREPARE:
324 rate_init(plugin);
325 break;
326 default:
327 break;
328 }
329 return 0; /* silenty ignore other actions */
330}
331
332int snd_pcm_plugin_build_rate(snd_pcm_plug_t *plug,
333 snd_pcm_plugin_format_t *src_format,
334 snd_pcm_plugin_format_t *dst_format,
335 snd_pcm_plugin_t **r_plugin)
336{
337 int err;
338 rate_t *data;
339 snd_pcm_plugin_t *plugin;
340
341 snd_assert(r_plugin != NULL, return -ENXIO);
342 *r_plugin = NULL;
343
344 snd_assert(src_format->channels == dst_format->channels, return -ENXIO);
345 snd_assert(src_format->channels > 0, return -ENXIO);
346 snd_assert(snd_pcm_format_linear(src_format->format) != 0, return -ENXIO);
347 snd_assert(snd_pcm_format_linear(dst_format->format) != 0, return -ENXIO);
348 snd_assert(src_format->rate != dst_format->rate, return -ENXIO);
349
350 err = snd_pcm_plugin_build(plug, "rate conversion",
351 src_format, dst_format,
352 sizeof(rate_t) + src_format->channels * sizeof(rate_channel_t),
353 &plugin);
354 if (err < 0)
355 return err;
356 data = (rate_t *)plugin->extra_data;
357 data->get = getput_index(src_format->format);
358 snd_assert(data->get >= 0 && data->get < 4*2*2, return -EINVAL);
359 data->put = getput_index(dst_format->format);
360 snd_assert(data->put >= 0 && data->put < 4*2*2, return -EINVAL);
361
362 if (src_format->rate < dst_format->rate) {
363 data->pitch = ((src_format->rate << SHIFT) + (dst_format->rate >> 1)) / dst_format->rate;
364 data->func = resample_expand;
365 } else {
366 data->pitch = ((dst_format->rate << SHIFT) + (src_format->rate >> 1)) / src_format->rate;
367 data->func = resample_shrink;
368 }
369 data->pos = 0;
370 rate_init(plugin);
371 data->old_src_frames = data->old_dst_frames = 0;
372 plugin->transfer = rate_transfer;
373 plugin->src_frames = rate_src_frames;
374 plugin->dst_frames = rate_dst_frames;
375 plugin->action = rate_action;
376 *r_plugin = plugin;
377 return 0;
378}
diff --git a/sound/core/oss/route.c b/sound/core/oss/route.c
new file mode 100644
index 000000000000..c955b7dfdb30
--- /dev/null
+++ b/sound/core/oss/route.c
@@ -0,0 +1,519 @@
1/*
2 * Attenuated route Plug-In
3 * Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org>
4 *
5 *
6 * This library is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU Library General Public License as
8 * published by the Free Software Foundation; either version 2 of
9 * the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Library General Public License for more details.
15 *
16 * You should have received a copy of the GNU Library General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 *
20 */
21
22#include <sound/driver.h>
23#include <linux/slab.h>
24#include <linux/time.h>
25#include <sound/core.h>
26#include <sound/pcm.h>
27#include "pcm_plugin.h"
28
29/* The best possible hack to support missing optimization in gcc 2.7.2.3 */
30#if ROUTE_PLUGIN_RESOLUTION & (ROUTE_PLUGIN_RESOLUTION - 1) != 0
31#define div(a) a /= ROUTE_PLUGIN_RESOLUTION
32#elif ROUTE_PLUGIN_RESOLUTION == 16
33#define div(a) a >>= 4
34#else
35#error "Add some code here"
36#endif
37
38typedef struct ttable_dst ttable_dst_t;
39typedef struct route_private_data route_t;
40
41typedef void (*route_channel_f)(snd_pcm_plugin_t *plugin,
42 const snd_pcm_plugin_channel_t *src_channels,
43 snd_pcm_plugin_channel_t *dst_channel,
44 ttable_dst_t* ttable, snd_pcm_uframes_t frames);
45
46typedef struct {
47 int channel;
48 int as_int;
49} ttable_src_t;
50
51struct ttable_dst {
52 int att; /* Attenuated */
53 unsigned int nsrcs;
54 ttable_src_t* srcs;
55 route_channel_f func;
56};
57
58struct route_private_data {
59 enum {R_UINT32=0, R_UINT64=1} sum_type;
60 int get, put;
61 int conv;
62 int src_sample_size;
63 ttable_dst_t ttable[0];
64};
65
66typedef union {
67 u_int32_t as_uint32;
68 u_int64_t as_uint64;
69} sum_t;
70
71
72static void route_to_channel_from_zero(snd_pcm_plugin_t *plugin,
73 const snd_pcm_plugin_channel_t *src_channels ATTRIBUTE_UNUSED,
74 snd_pcm_plugin_channel_t *dst_channel,
75 ttable_dst_t* ttable ATTRIBUTE_UNUSED, snd_pcm_uframes_t frames)
76{
77 if (dst_channel->wanted)
78 snd_pcm_area_silence(&dst_channel->area, 0, frames, plugin->dst_format.format);
79 dst_channel->enabled = 0;
80}
81
82static void route_to_channel_from_one(snd_pcm_plugin_t *plugin,
83 const snd_pcm_plugin_channel_t *src_channels,
84 snd_pcm_plugin_channel_t *dst_channel,
85 ttable_dst_t* ttable, snd_pcm_uframes_t frames)
86{
87#define CONV_LABELS
88#include "plugin_ops.h"
89#undef CONV_LABELS
90 route_t *data = (route_t *)plugin->extra_data;
91 void *conv;
92 const snd_pcm_plugin_channel_t *src_channel = NULL;
93 unsigned int srcidx;
94 char *src, *dst;
95 int src_step, dst_step;
96 for (srcidx = 0; srcidx < ttable->nsrcs; ++srcidx) {
97 src_channel = &src_channels[ttable->srcs[srcidx].channel];
98 if (src_channel->area.addr != NULL)
99 break;
100 }
101 if (srcidx == ttable->nsrcs) {
102 route_to_channel_from_zero(plugin, src_channels, dst_channel, ttable, frames);
103 return;
104 }
105
106 dst_channel->enabled = 1;
107 conv = conv_labels[data->conv];
108 src = src_channel->area.addr + src_channel->area.first / 8;
109 src_step = src_channel->area.step / 8;
110 dst = dst_channel->area.addr + dst_channel->area.first / 8;
111 dst_step = dst_channel->area.step / 8;
112 while (frames-- > 0) {
113 goto *conv;
114#define CONV_END after
115#include "plugin_ops.h"
116#undef CONV_END
117 after:
118 src += src_step;
119 dst += dst_step;
120 }
121}
122
123static void route_to_channel(snd_pcm_plugin_t *plugin,
124 const snd_pcm_plugin_channel_t *src_channels,
125 snd_pcm_plugin_channel_t *dst_channel,
126 ttable_dst_t* ttable, snd_pcm_uframes_t frames)
127{
128#define GET_U_LABELS
129#define PUT_U32_LABELS
130#include "plugin_ops.h"
131#undef GET_U_LABELS
132#undef PUT_U32_LABELS
133 static void *zero_labels[2] = { &&zero_int32, &&zero_int64 };
134 /* sum_type att */
135 static void *add_labels[2 * 2] = { &&add_int32_noatt, &&add_int32_att,
136 &&add_int64_noatt, &&add_int64_att,
137 };
138 /* sum_type att shift */
139 static void *norm_labels[2 * 2 * 4] = { NULL,
140 &&norm_int32_8_noatt,
141 &&norm_int32_16_noatt,
142 &&norm_int32_24_noatt,
143 NULL,
144 &&norm_int32_8_att,
145 &&norm_int32_16_att,
146 &&norm_int32_24_att,
147 &&norm_int64_0_noatt,
148 &&norm_int64_8_noatt,
149 &&norm_int64_16_noatt,
150 &&norm_int64_24_noatt,
151 &&norm_int64_0_att,
152 &&norm_int64_8_att,
153 &&norm_int64_16_att,
154 &&norm_int64_24_att,
155 };
156 route_t *data = (route_t *)plugin->extra_data;
157 void *zero, *get, *add, *norm, *put_u32;
158 int nsrcs = ttable->nsrcs;
159 char *dst;
160 int dst_step;
161 char *srcs[nsrcs];
162 int src_steps[nsrcs];
163 ttable_src_t src_tt[nsrcs];
164 u_int32_t sample = 0;
165 int srcidx, srcidx1 = 0;
166 for (srcidx = 0; srcidx < nsrcs; ++srcidx) {
167 const snd_pcm_plugin_channel_t *src_channel = &src_channels[ttable->srcs[srcidx].channel];
168 if (!src_channel->enabled)
169 continue;
170 srcs[srcidx1] = src_channel->area.addr + src_channel->area.first / 8;
171 src_steps[srcidx1] = src_channel->area.step / 8;
172 src_tt[srcidx1] = ttable->srcs[srcidx];
173 srcidx1++;
174 }
175 nsrcs = srcidx1;
176 if (nsrcs == 0) {
177 route_to_channel_from_zero(plugin, src_channels, dst_channel, ttable, frames);
178 return;
179 } else if (nsrcs == 1 && src_tt[0].as_int == ROUTE_PLUGIN_RESOLUTION) {
180 route_to_channel_from_one(plugin, src_channels, dst_channel, ttable, frames);
181 return;
182 }
183
184 dst_channel->enabled = 1;
185 zero = zero_labels[data->sum_type];
186 get = get_u_labels[data->get];
187 add = add_labels[data->sum_type * 2 + ttable->att];
188 norm = norm_labels[data->sum_type * 8 + ttable->att * 4 + 4 - data->src_sample_size];
189 put_u32 = put_u32_labels[data->put];
190 dst = dst_channel->area.addr + dst_channel->area.first / 8;
191 dst_step = dst_channel->area.step / 8;
192
193 while (frames-- > 0) {
194 ttable_src_t *ttp = src_tt;
195 sum_t sum;
196
197 /* Zero sum */
198 goto *zero;
199 zero_int32:
200 sum.as_uint32 = 0;
201 goto zero_end;
202 zero_int64:
203 sum.as_uint64 = 0;
204 goto zero_end;
205 zero_end:
206 for (srcidx = 0; srcidx < nsrcs; ++srcidx) {
207 char *src = srcs[srcidx];
208
209 /* Get sample */
210 goto *get;
211#define GET_U_END after_get
212#include "plugin_ops.h"
213#undef GET_U_END
214 after_get:
215
216 /* Sum */
217 goto *add;
218 add_int32_att:
219 sum.as_uint32 += sample * ttp->as_int;
220 goto after_sum;
221 add_int32_noatt:
222 if (ttp->as_int)
223 sum.as_uint32 += sample;
224 goto after_sum;
225 add_int64_att:
226 sum.as_uint64 += (u_int64_t) sample * ttp->as_int;
227 goto after_sum;
228 add_int64_noatt:
229 if (ttp->as_int)
230 sum.as_uint64 += sample;
231 goto after_sum;
232 after_sum:
233 srcs[srcidx] += src_steps[srcidx];
234 ttp++;
235 }
236
237 /* Normalization */
238 goto *norm;
239 norm_int32_8_att:
240 sum.as_uint64 = sum.as_uint32;
241 norm_int64_8_att:
242 sum.as_uint64 <<= 8;
243 norm_int64_0_att:
244 div(sum.as_uint64);
245 goto norm_int;
246
247 norm_int32_16_att:
248 sum.as_uint64 = sum.as_uint32;
249 norm_int64_16_att:
250 sum.as_uint64 <<= 16;
251 div(sum.as_uint64);
252 goto norm_int;
253
254 norm_int32_24_att:
255 sum.as_uint64 = sum.as_uint32;
256 norm_int64_24_att:
257 sum.as_uint64 <<= 24;
258 div(sum.as_uint64);
259 goto norm_int;
260
261 norm_int32_8_noatt:
262 sum.as_uint64 = sum.as_uint32;
263 norm_int64_8_noatt:
264 sum.as_uint64 <<= 8;
265 goto norm_int;
266
267 norm_int32_16_noatt:
268 sum.as_uint64 = sum.as_uint32;
269 norm_int64_16_noatt:
270 sum.as_uint64 <<= 16;
271 goto norm_int;
272
273 norm_int32_24_noatt:
274 sum.as_uint64 = sum.as_uint32;
275 norm_int64_24_noatt:
276 sum.as_uint64 <<= 24;
277 goto norm_int;
278
279 norm_int64_0_noatt:
280 norm_int:
281 if (sum.as_uint64 > (u_int32_t)0xffffffff)
282 sample = (u_int32_t)0xffffffff;
283 else
284 sample = sum.as_uint64;
285 goto after_norm;
286
287 after_norm:
288
289 /* Put sample */
290 goto *put_u32;
291#define PUT_U32_END after_put_u32
292#include "plugin_ops.h"
293#undef PUT_U32_END
294 after_put_u32:
295
296 dst += dst_step;
297 }
298}
299
300static int route_src_channels_mask(snd_pcm_plugin_t *plugin,
301 bitset_t *dst_vmask,
302 bitset_t **src_vmask)
303{
304 route_t *data = (route_t *)plugin->extra_data;
305 int schannels = plugin->src_format.channels;
306 int dchannels = plugin->dst_format.channels;
307 bitset_t *vmask = plugin->src_vmask;
308 int channel;
309 ttable_dst_t *dp = data->ttable;
310 bitset_zero(vmask, schannels);
311 for (channel = 0; channel < dchannels; channel++, dp++) {
312 unsigned int src;
313 ttable_src_t *sp;
314 if (!bitset_get(dst_vmask, channel))
315 continue;
316 sp = dp->srcs;
317 for (src = 0; src < dp->nsrcs; src++, sp++)
318 bitset_set(vmask, sp->channel);
319 }
320 *src_vmask = vmask;
321 return 0;
322}
323
324static int route_dst_channels_mask(snd_pcm_plugin_t *plugin,
325 bitset_t *src_vmask,
326 bitset_t **dst_vmask)
327{
328 route_t *data = (route_t *)plugin->extra_data;
329 int dchannels = plugin->dst_format.channels;
330 bitset_t *vmask = plugin->dst_vmask;
331 int channel;
332 ttable_dst_t *dp = data->ttable;
333 bitset_zero(vmask, dchannels);
334 for (channel = 0; channel < dchannels; channel++, dp++) {
335 unsigned int src;
336 ttable_src_t *sp;
337 sp = dp->srcs;
338 for (src = 0; src < dp->nsrcs; src++, sp++) {
339 if (bitset_get(src_vmask, sp->channel)) {
340 bitset_set(vmask, channel);
341 break;
342 }
343 }
344 }
345 *dst_vmask = vmask;
346 return 0;
347}
348
349static void route_free(snd_pcm_plugin_t *plugin)
350{
351 route_t *data = (route_t *)plugin->extra_data;
352 unsigned int dst_channel;
353 for (dst_channel = 0; dst_channel < plugin->dst_format.channels; ++dst_channel) {
354 kfree(data->ttable[dst_channel].srcs);
355 }
356}
357
358static int route_load_ttable(snd_pcm_plugin_t *plugin,
359 const route_ttable_entry_t* src_ttable)
360{
361 route_t *data;
362 unsigned int src_channel, dst_channel;
363 const route_ttable_entry_t *sptr;
364 ttable_dst_t *dptr;
365 if (src_ttable == NULL)
366 return 0;
367 data = (route_t *)plugin->extra_data;
368 dptr = data->ttable;
369 sptr = src_ttable;
370 plugin->private_free = route_free;
371 for (dst_channel = 0; dst_channel < plugin->dst_format.channels; ++dst_channel) {
372 route_ttable_entry_t t = 0;
373 int att = 0;
374 int nsrcs = 0;
375 ttable_src_t srcs[plugin->src_format.channels];
376 for (src_channel = 0; src_channel < plugin->src_format.channels; ++src_channel) {
377 snd_assert(*sptr >= 0 || *sptr <= FULL, return -ENXIO);
378 if (*sptr != 0) {
379 srcs[nsrcs].channel = src_channel;
380 srcs[nsrcs].as_int = *sptr;
381 if (*sptr != FULL)
382 att = 1;
383 t += *sptr;
384 nsrcs++;
385 }
386 sptr++;
387 }
388 dptr->att = att;
389 dptr->nsrcs = nsrcs;
390 if (nsrcs == 0)
391 dptr->func = route_to_channel_from_zero;
392 else if (nsrcs == 1 && !att)
393 dptr->func = route_to_channel_from_one;
394 else
395 dptr->func = route_to_channel;
396 if (nsrcs > 0) {
397 int srcidx;
398 dptr->srcs = kcalloc(nsrcs, sizeof(*srcs), GFP_KERNEL);
399 for(srcidx = 0; srcidx < nsrcs; srcidx++)
400 dptr->srcs[srcidx] = srcs[srcidx];
401 } else
402 dptr->srcs = NULL;
403 dptr++;
404 }
405 return 0;
406}
407
408static snd_pcm_sframes_t route_transfer(snd_pcm_plugin_t *plugin,
409 const snd_pcm_plugin_channel_t *src_channels,
410 snd_pcm_plugin_channel_t *dst_channels,
411 snd_pcm_uframes_t frames)
412{
413 route_t *data;
414 int src_nchannels, dst_nchannels;
415 int dst_channel;
416 ttable_dst_t *ttp;
417 snd_pcm_plugin_channel_t *dvp;
418
419 snd_assert(plugin != NULL && src_channels != NULL && dst_channels != NULL, return -ENXIO);
420 if (frames == 0)
421 return 0;
422 data = (route_t *)plugin->extra_data;
423
424 src_nchannels = plugin->src_format.channels;
425 dst_nchannels = plugin->dst_format.channels;
426
427#ifdef CONFIG_SND_DEBUG
428 {
429 int src_channel;
430 for (src_channel = 0; src_channel < src_nchannels; ++src_channel) {
431 snd_assert(src_channels[src_channel].area.first % 8 == 0 ||
432 src_channels[src_channel].area.step % 8 == 0,
433 return -ENXIO);
434 }
435 for (dst_channel = 0; dst_channel < dst_nchannels; ++dst_channel) {
436 snd_assert(dst_channels[dst_channel].area.first % 8 == 0 ||
437 dst_channels[dst_channel].area.step % 8 == 0,
438 return -ENXIO);
439 }
440 }
441#endif
442
443 ttp = data->ttable;
444 dvp = dst_channels;
445 for (dst_channel = 0; dst_channel < dst_nchannels; ++dst_channel) {
446 ttp->func(plugin, src_channels, dvp, ttp, frames);
447 dvp++;
448 ttp++;
449 }
450 return frames;
451}
452
453int getput_index(int format)
454{
455 int sign, width, endian;
456 sign = !snd_pcm_format_signed(format);
457 width = snd_pcm_format_width(format) / 8 - 1;
458 if (width < 0 || width > 3) {
459 snd_printk(KERN_ERR "snd-pcm-oss: invalid format %d\n", format);
460 width = 0;
461 }
462#ifdef SNDRV_LITTLE_ENDIAN
463 endian = snd_pcm_format_big_endian(format);
464#else
465 endian = snd_pcm_format_little_endian(format);
466#endif
467 if (endian < 0)
468 endian = 0;
469 return width * 4 + endian * 2 + sign;
470}
471
472int snd_pcm_plugin_build_route(snd_pcm_plug_t *plug,
473 snd_pcm_plugin_format_t *src_format,
474 snd_pcm_plugin_format_t *dst_format,
475 route_ttable_entry_t *ttable,
476 snd_pcm_plugin_t **r_plugin)
477{
478 route_t *data;
479 snd_pcm_plugin_t *plugin;
480 int err;
481
482 snd_assert(r_plugin != NULL, return -ENXIO);
483 *r_plugin = NULL;
484 snd_assert(src_format->rate == dst_format->rate, return -ENXIO);
485 snd_assert(snd_pcm_format_linear(src_format->format) != 0 &&
486 snd_pcm_format_linear(dst_format->format) != 0,
487 return -ENXIO);
488
489 err = snd_pcm_plugin_build(plug, "attenuated route conversion",
490 src_format, dst_format,
491 sizeof(route_t) + sizeof(data->ttable[0]) * dst_format->channels,
492 &plugin);
493 if (err < 0)
494 return err;
495
496 data = (route_t *) plugin->extra_data;
497
498 data->get = getput_index(src_format->format);
499 snd_assert(data->get >= 0 && data->get < 4*2*2, return -EINVAL);
500 data->put = getput_index(dst_format->format);
501 snd_assert(data->get >= 0 && data->get < 4*2*2, return -EINVAL);
502 data->conv = conv_index(src_format->format, dst_format->format);
503
504 if (snd_pcm_format_width(src_format->format) == 32)
505 data->sum_type = R_UINT64;
506 else
507 data->sum_type = R_UINT32;
508 data->src_sample_size = snd_pcm_format_width(src_format->format) / 8;
509
510 if ((err = route_load_ttable(plugin, ttable)) < 0) {
511 snd_pcm_plugin_free(plugin);
512 return err;
513 }
514 plugin->transfer = route_transfer;
515 plugin->src_channels_mask = route_src_channels_mask;
516 plugin->dst_channels_mask = route_dst_channels_mask;
517 *r_plugin = plugin;
518 return 0;
519}