aboutsummaryrefslogtreecommitdiffstats
path: root/sound/core/seq
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/seq
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/seq')
-rw-r--r--sound/core/seq/Makefile44
-rw-r--r--sound/core/seq/instr/Makefile23
-rw-r--r--sound/core/seq/instr/ainstr_fm.c156
-rw-r--r--sound/core/seq/instr/ainstr_gf1.c358
-rw-r--r--sound/core/seq/instr/ainstr_iw.c622
-rw-r--r--sound/core/seq/instr/ainstr_simple.c215
-rw-r--r--sound/core/seq/oss/Makefile10
-rw-r--r--sound/core/seq/oss/seq_oss.c317
-rw-r--r--sound/core/seq/oss/seq_oss_device.h198
-rw-r--r--sound/core/seq/oss/seq_oss_event.c447
-rw-r--r--sound/core/seq/oss/seq_oss_event.h112
-rw-r--r--sound/core/seq/oss/seq_oss_init.c555
-rw-r--r--sound/core/seq/oss/seq_oss_ioctl.c209
-rw-r--r--sound/core/seq/oss/seq_oss_midi.c710
-rw-r--r--sound/core/seq/oss/seq_oss_midi.h49
-rw-r--r--sound/core/seq/oss/seq_oss_readq.c234
-rw-r--r--sound/core/seq/oss/seq_oss_readq.h56
-rw-r--r--sound/core/seq/oss/seq_oss_rw.c216
-rw-r--r--sound/core/seq/oss/seq_oss_synth.c659
-rw-r--r--sound/core/seq/oss/seq_oss_synth.h49
-rw-r--r--sound/core/seq/oss/seq_oss_timer.c283
-rw-r--r--sound/core/seq/oss/seq_oss_timer.h70
-rw-r--r--sound/core/seq/oss/seq_oss_writeq.c170
-rw-r--r--sound/core/seq/oss/seq_oss_writeq.h50
-rw-r--r--sound/core/seq/seq.c147
-rw-r--r--sound/core/seq/seq_clientmgr.c2503
-rw-r--r--sound/core/seq/seq_clientmgr.h104
-rw-r--r--sound/core/seq/seq_compat.c137
-rw-r--r--sound/core/seq/seq_device.c575
-rw-r--r--sound/core/seq/seq_dummy.c273
-rw-r--r--sound/core/seq/seq_fifo.c264
-rw-r--r--sound/core/seq/seq_fifo.h72
-rw-r--r--sound/core/seq/seq_info.c75
-rw-r--r--sound/core/seq/seq_info.h36
-rw-r--r--sound/core/seq/seq_instr.c653
-rw-r--r--sound/core/seq/seq_lock.c48
-rw-r--r--sound/core/seq/seq_lock.h33
-rw-r--r--sound/core/seq/seq_memory.c510
-rw-r--r--sound/core/seq/seq_memory.h104
-rw-r--r--sound/core/seq/seq_midi.c489
-rw-r--r--sound/core/seq/seq_midi_emul.c735
-rw-r--r--sound/core/seq/seq_midi_event.c539
-rw-r--r--sound/core/seq/seq_ports.c674
-rw-r--r--sound/core/seq/seq_ports.h128
-rw-r--r--sound/core/seq/seq_prioq.c449
-rw-r--r--sound/core/seq/seq_prioq.h62
-rw-r--r--sound/core/seq/seq_queue.c783
-rw-r--r--sound/core/seq/seq_queue.h140
-rw-r--r--sound/core/seq/seq_system.c190
-rw-r--r--sound/core/seq/seq_system.h46
-rw-r--r--sound/core/seq/seq_timer.c435
-rw-r--r--sound/core/seq/seq_timer.h141
-rw-r--r--sound/core/seq/seq_virmidi.c551
53 files changed, 16708 insertions, 0 deletions
diff --git a/sound/core/seq/Makefile b/sound/core/seq/Makefile
new file mode 100644
index 000000000000..64cb50d7b589
--- /dev/null
+++ b/sound/core/seq/Makefile
@@ -0,0 +1,44 @@
1#
2# Makefile for ALSA
3# Copyright (c) 1999 by Jaroslav Kysela <perex@suse.cz>
4#
5
6obj-$(CONFIG_SND) += instr/
7ifeq ($(CONFIG_SND_SEQUENCER_OSS),y)
8 obj-$(CONFIG_SND_SEQUENCER) += oss/
9endif
10
11snd-seq-device-objs := seq_device.o
12snd-seq-objs := seq.o seq_lock.o seq_clientmgr.o seq_memory.o seq_queue.o \
13 seq_fifo.o seq_prioq.o seq_timer.o \
14 seq_system.o seq_ports.o seq_info.o
15snd-seq-midi-objs := seq_midi.o
16snd-seq-midi-emul-objs := seq_midi_emul.o
17snd-seq-midi-event-objs := seq_midi_event.o
18snd-seq-instr-objs := seq_instr.o
19snd-seq-dummy-objs := seq_dummy.o
20snd-seq-virmidi-objs := seq_virmidi.o
21
22#
23# this function returns:
24# "m" - CONFIG_SND_SEQUENCER is m
25# <empty string> - CONFIG_SND_SEQUENCER is undefined
26# otherwise parameter #1 value
27#
28sequencer = $(if $(subst y,,$(CONFIG_SND_SEQUENCER)),$(if $(1),m),$(if $(CONFIG_SND_SEQUENCER),$(1)))
29
30obj-$(CONFIG_SND_SEQUENCER) += snd-seq.o snd-seq-device.o
31ifeq ($(CONFIG_SND_SEQUENCER_OSS),y)
32obj-$(CONFIG_SND_SEQUENCER) += snd-seq-midi-event.o
33endif
34obj-$(CONFIG_SND_SEQ_DUMMY) += snd-seq-dummy.o
35
36# Toplevel Module Dependency
37obj-$(CONFIG_SND_VIRMIDI) += snd-seq-virmidi.o snd-seq-midi-event.o
38obj-$(call sequencer,$(CONFIG_SND_RAWMIDI)) += snd-seq-midi.o snd-seq-midi-event.o
39obj-$(call sequencer,$(CONFIG_SND_OPL3_LIB)) += snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o
40obj-$(call sequencer,$(CONFIG_SND_OPL4_LIB)) += snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o
41obj-$(call sequencer,$(CONFIG_SND_GUS_SYNTH)) += snd-seq-instr.o
42obj-$(call sequencer,$(CONFIG_SND_SBAWE)) += snd-seq-midi-emul.o snd-seq-virmidi.o
43obj-$(call sequencer,$(CONFIG_SND_EMU10K1)) += snd-seq-midi-emul.o snd-seq-virmidi.o
44obj-$(call sequencer,$(CONFIG_SND_TRIDENT)) += snd-seq-midi-emul.o snd-seq-instr.o
diff --git a/sound/core/seq/instr/Makefile b/sound/core/seq/instr/Makefile
new file mode 100644
index 000000000000..69138f30a293
--- /dev/null
+++ b/sound/core/seq/instr/Makefile
@@ -0,0 +1,23 @@
1#
2# Makefile for ALSA
3# Copyright (c) 1999 by Jaroslav Kysela <perex@suse.cz>
4#
5
6snd-ainstr-fm-objs := ainstr_fm.o
7snd-ainstr-simple-objs := ainstr_simple.o
8snd-ainstr-gf1-objs := ainstr_gf1.o
9snd-ainstr-iw-objs := ainstr_iw.o
10
11#
12# this function returns:
13# "m" - CONFIG_SND_SEQUENCER is m
14# <empty string> - CONFIG_SND_SEQUENCER is undefined
15# otherwise parameter #1 value
16#
17sequencer = $(if $(subst y,,$(CONFIG_SND_SEQUENCER)),$(if $(1),m),$(if $(CONFIG_SND_SEQUENCER),$(1)))
18
19# Toplevel Module Dependency
20obj-$(call sequencer,$(CONFIG_SND_OPL3_LIB)) += snd-ainstr-fm.o
21obj-$(call sequencer,$(CONFIG_SND_OPL4_LIB)) += snd-ainstr-fm.o
22obj-$(call sequencer,$(CONFIG_SND_GUS_SYNTH)) += snd-ainstr-gf1.o snd-ainstr-simple.o snd-ainstr-iw.o
23obj-$(call sequencer,$(CONFIG_SND_TRIDENT)) += snd-ainstr-simple.o
diff --git a/sound/core/seq/instr/ainstr_fm.c b/sound/core/seq/instr/ainstr_fm.c
new file mode 100644
index 000000000000..5c671e69884f
--- /dev/null
+++ b/sound/core/seq/instr/ainstr_fm.c
@@ -0,0 +1,156 @@
1/*
2 * FM (OPL2/3) Instrument routines
3 * Copyright (c) 2000 Uros Bizjak <uros@kss-loka.si>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 *
19 */
20
21#include <sound/driver.h>
22#include <linux/init.h>
23#include <linux/sched.h>
24#include <sound/core.h>
25#include <sound/ainstr_fm.h>
26#include <sound/initval.h>
27#include <asm/uaccess.h>
28
29MODULE_AUTHOR("Uros Bizjak <uros@kss-loka.si>");
30MODULE_DESCRIPTION("Advanced Linux Sound Architecture FM Instrument support.");
31MODULE_LICENSE("GPL");
32
33static int snd_seq_fm_put(void *private_data, snd_seq_kinstr_t *instr,
34 char __user *instr_data, long len, int atomic, int cmd)
35{
36 fm_instrument_t *ip;
37 fm_xinstrument_t ix;
38 int idx;
39
40 if (cmd != SNDRV_SEQ_INSTR_PUT_CMD_CREATE)
41 return -EINVAL;
42 /* copy instrument data */
43 if (len < (long)sizeof(ix))
44 return -EINVAL;
45 if (copy_from_user(&ix, instr_data, sizeof(ix)))
46 return -EFAULT;
47 if (ix.stype != FM_STRU_INSTR)
48 return -EINVAL;
49 ip = (fm_instrument_t *)KINSTR_DATA(instr);
50 ip->share_id[0] = le32_to_cpu(ix.share_id[0]);
51 ip->share_id[1] = le32_to_cpu(ix.share_id[1]);
52 ip->share_id[2] = le32_to_cpu(ix.share_id[2]);
53 ip->share_id[3] = le32_to_cpu(ix.share_id[3]);
54 ip->type = ix.type;
55 for (idx = 0; idx < 4; idx++) {
56 ip->op[idx].am_vib = ix.op[idx].am_vib;
57 ip->op[idx].ksl_level = ix.op[idx].ksl_level;
58 ip->op[idx].attack_decay = ix.op[idx].attack_decay;
59 ip->op[idx].sustain_release = ix.op[idx].sustain_release;
60 ip->op[idx].wave_select = ix.op[idx].wave_select;
61 }
62 for (idx = 0; idx < 2; idx++) {
63 ip->feedback_connection[idx] = ix.feedback_connection[idx];
64 }
65 ip->echo_delay = ix.echo_delay;
66 ip->echo_atten = ix.echo_atten;
67 ip->chorus_spread = ix.chorus_spread;
68 ip->trnsps = ix.trnsps;
69 ip->fix_dur = ix.fix_dur;
70 ip->modes = ix.modes;
71 ip->fix_key = ix.fix_key;
72 return 0;
73}
74
75static int snd_seq_fm_get(void *private_data, snd_seq_kinstr_t *instr,
76 char __user *instr_data, long len, int atomic,
77 int cmd)
78{
79 fm_instrument_t *ip;
80 fm_xinstrument_t ix;
81 int idx;
82
83 if (cmd != SNDRV_SEQ_INSTR_GET_CMD_FULL)
84 return -EINVAL;
85 if (len < (long)sizeof(ix))
86 return -ENOMEM;
87 memset(&ix, 0, sizeof(ix));
88 ip = (fm_instrument_t *)KINSTR_DATA(instr);
89 ix.stype = FM_STRU_INSTR;
90 ix.share_id[0] = cpu_to_le32(ip->share_id[0]);
91 ix.share_id[1] = cpu_to_le32(ip->share_id[1]);
92 ix.share_id[2] = cpu_to_le32(ip->share_id[2]);
93 ix.share_id[3] = cpu_to_le32(ip->share_id[3]);
94 ix.type = ip->type;
95 for (idx = 0; idx < 4; idx++) {
96 ix.op[idx].am_vib = ip->op[idx].am_vib;
97 ix.op[idx].ksl_level = ip->op[idx].ksl_level;
98 ix.op[idx].attack_decay = ip->op[idx].attack_decay;
99 ix.op[idx].sustain_release = ip->op[idx].sustain_release;
100 ix.op[idx].wave_select = ip->op[idx].wave_select;
101 }
102 for (idx = 0; idx < 2; idx++) {
103 ix.feedback_connection[idx] = ip->feedback_connection[idx];
104 }
105 if (copy_to_user(instr_data, &ix, sizeof(ix)))
106 return -EFAULT;
107 ix.echo_delay = ip->echo_delay;
108 ix.echo_atten = ip->echo_atten;
109 ix.chorus_spread = ip->chorus_spread;
110 ix.trnsps = ip->trnsps;
111 ix.fix_dur = ip->fix_dur;
112 ix.modes = ip->modes;
113 ix.fix_key = ip->fix_key;
114 return 0;
115}
116
117static int snd_seq_fm_get_size(void *private_data, snd_seq_kinstr_t *instr,
118 long *size)
119{
120 *size = sizeof(fm_xinstrument_t);
121 return 0;
122}
123
124int snd_seq_fm_init(snd_seq_kinstr_ops_t *ops,
125 snd_seq_kinstr_ops_t *next)
126{
127 memset(ops, 0, sizeof(*ops));
128 // ops->private_data = private_data;
129 ops->add_len = sizeof(fm_instrument_t);
130 ops->instr_type = SNDRV_SEQ_INSTR_ID_OPL2_3;
131 ops->put = snd_seq_fm_put;
132 ops->get = snd_seq_fm_get;
133 ops->get_size = snd_seq_fm_get_size;
134 // ops->remove = snd_seq_fm_remove;
135 // ops->notify = snd_seq_fm_notify;
136 ops->next = next;
137 return 0;
138}
139
140/*
141 * Init part
142 */
143
144static int __init alsa_ainstr_fm_init(void)
145{
146 return 0;
147}
148
149static void __exit alsa_ainstr_fm_exit(void)
150{
151}
152
153module_init(alsa_ainstr_fm_init)
154module_exit(alsa_ainstr_fm_exit)
155
156EXPORT_SYMBOL(snd_seq_fm_init);
diff --git a/sound/core/seq/instr/ainstr_gf1.c b/sound/core/seq/instr/ainstr_gf1.c
new file mode 100644
index 000000000000..0779c41ca037
--- /dev/null
+++ b/sound/core/seq/instr/ainstr_gf1.c
@@ -0,0 +1,358 @@
1/*
2 * GF1 (GUS) Patch - Instrument routines
3 * Copyright (c) 1999 by Jaroslav Kysela <perex@suse.cz>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 *
19 */
20
21#include <sound/driver.h>
22#include <linux/init.h>
23#include <linux/sched.h>
24#include <linux/slab.h>
25#include <sound/core.h>
26#include <sound/ainstr_gf1.h>
27#include <sound/initval.h>
28#include <asm/uaccess.h>
29
30MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
31MODULE_DESCRIPTION("Advanced Linux Sound Architecture GF1 (GUS) Patch support.");
32MODULE_LICENSE("GPL");
33
34static unsigned int snd_seq_gf1_size(unsigned int size, unsigned int format)
35{
36 unsigned int result = size;
37
38 if (format & GF1_WAVE_16BIT)
39 result <<= 1;
40 if (format & GF1_WAVE_STEREO)
41 result <<= 1;
42 return format;
43}
44
45static int snd_seq_gf1_copy_wave_from_stream(snd_gf1_ops_t *ops,
46 gf1_instrument_t *ip,
47 char __user **data,
48 long *len,
49 int atomic)
50{
51 gf1_wave_t *wp, *prev;
52 gf1_xwave_t xp;
53 int err, gfp_mask;
54 unsigned int real_size;
55
56 gfp_mask = atomic ? GFP_ATOMIC : GFP_KERNEL;
57 if (*len < (long)sizeof(xp))
58 return -EINVAL;
59 if (copy_from_user(&xp, *data, sizeof(xp)))
60 return -EFAULT;
61 *data += sizeof(xp);
62 *len -= sizeof(xp);
63 wp = kcalloc(1, sizeof(*wp), gfp_mask);
64 if (wp == NULL)
65 return -ENOMEM;
66 wp->share_id[0] = le32_to_cpu(xp.share_id[0]);
67 wp->share_id[1] = le32_to_cpu(xp.share_id[1]);
68 wp->share_id[2] = le32_to_cpu(xp.share_id[2]);
69 wp->share_id[3] = le32_to_cpu(xp.share_id[3]);
70 wp->format = le32_to_cpu(xp.format);
71 wp->size = le32_to_cpu(xp.size);
72 wp->start = le32_to_cpu(xp.start);
73 wp->loop_start = le32_to_cpu(xp.loop_start);
74 wp->loop_end = le32_to_cpu(xp.loop_end);
75 wp->loop_repeat = le16_to_cpu(xp.loop_repeat);
76 wp->flags = xp.flags;
77 wp->sample_rate = le32_to_cpu(xp.sample_rate);
78 wp->low_frequency = le32_to_cpu(xp.low_frequency);
79 wp->high_frequency = le32_to_cpu(xp.high_frequency);
80 wp->root_frequency = le32_to_cpu(xp.root_frequency);
81 wp->tune = le16_to_cpu(xp.tune);
82 wp->balance = xp.balance;
83 memcpy(wp->envelope_rate, xp.envelope_rate, 6);
84 memcpy(wp->envelope_offset, xp.envelope_offset, 6);
85 wp->tremolo_sweep = xp.tremolo_sweep;
86 wp->tremolo_rate = xp.tremolo_rate;
87 wp->tremolo_depth = xp.tremolo_depth;
88 wp->vibrato_sweep = xp.vibrato_sweep;
89 wp->vibrato_rate = xp.vibrato_rate;
90 wp->vibrato_depth = xp.vibrato_depth;
91 wp->scale_frequency = le16_to_cpu(xp.scale_frequency);
92 wp->scale_factor = le16_to_cpu(xp.scale_factor);
93 real_size = snd_seq_gf1_size(wp->size, wp->format);
94 if ((long)real_size > *len) {
95 kfree(wp);
96 return -ENOMEM;
97 }
98 if (ops->put_sample) {
99 err = ops->put_sample(ops->private_data, wp,
100 *data, real_size, atomic);
101 if (err < 0) {
102 kfree(wp);
103 return err;
104 }
105 }
106 *data += real_size;
107 *len -= real_size;
108 prev = ip->wave;
109 if (prev) {
110 while (prev->next) prev = prev->next;
111 prev->next = wp;
112 } else {
113 ip->wave = wp;
114 }
115 return 0;
116}
117
118static void snd_seq_gf1_wave_free(snd_gf1_ops_t *ops,
119 gf1_wave_t *wave,
120 int atomic)
121{
122 if (ops->remove_sample)
123 ops->remove_sample(ops->private_data, wave, atomic);
124 kfree(wave);
125}
126
127static void snd_seq_gf1_instr_free(snd_gf1_ops_t *ops,
128 gf1_instrument_t *ip,
129 int atomic)
130{
131 gf1_wave_t *wave;
132
133 while ((wave = ip->wave) != NULL) {
134 ip->wave = wave->next;
135 snd_seq_gf1_wave_free(ops, wave, atomic);
136 }
137}
138
139static int snd_seq_gf1_put(void *private_data, snd_seq_kinstr_t *instr,
140 char __user *instr_data, long len, int atomic,
141 int cmd)
142{
143 snd_gf1_ops_t *ops = (snd_gf1_ops_t *)private_data;
144 gf1_instrument_t *ip;
145 gf1_xinstrument_t ix;
146 int err, gfp_mask;
147
148 if (cmd != SNDRV_SEQ_INSTR_PUT_CMD_CREATE)
149 return -EINVAL;
150 gfp_mask = atomic ? GFP_ATOMIC : GFP_KERNEL;
151 /* copy instrument data */
152 if (len < (long)sizeof(ix))
153 return -EINVAL;
154 if (copy_from_user(&ix, instr_data, sizeof(ix)))
155 return -EFAULT;
156 if (ix.stype != GF1_STRU_INSTR)
157 return -EINVAL;
158 instr_data += sizeof(ix);
159 len -= sizeof(ix);
160 ip = (gf1_instrument_t *)KINSTR_DATA(instr);
161 ip->exclusion = le16_to_cpu(ix.exclusion);
162 ip->exclusion_group = le16_to_cpu(ix.exclusion_group);
163 ip->effect1 = ix.effect1;
164 ip->effect1_depth = ix.effect1_depth;
165 ip->effect2 = ix.effect2;
166 ip->effect2_depth = ix.effect2_depth;
167 /* copy layers */
168 while (len > (long)sizeof(__u32)) {
169 __u32 stype;
170
171 if (copy_from_user(&stype, instr_data, sizeof(stype)))
172 return -EFAULT;
173 if (stype != GF1_STRU_WAVE) {
174 snd_seq_gf1_instr_free(ops, ip, atomic);
175 return -EINVAL;
176 }
177 err = snd_seq_gf1_copy_wave_from_stream(ops,
178 ip,
179 &instr_data,
180 &len,
181 atomic);
182 if (err < 0) {
183 snd_seq_gf1_instr_free(ops, ip, atomic);
184 return err;
185 }
186 }
187 return 0;
188}
189
190static int snd_seq_gf1_copy_wave_to_stream(snd_gf1_ops_t *ops,
191 gf1_instrument_t *ip,
192 char __user **data,
193 long *len,
194 int atomic)
195{
196 gf1_wave_t *wp;
197 gf1_xwave_t xp;
198 int err;
199 unsigned int real_size;
200
201 for (wp = ip->wave; wp; wp = wp->next) {
202 if (*len < (long)sizeof(xp))
203 return -ENOMEM;
204 memset(&xp, 0, sizeof(xp));
205 xp.stype = GF1_STRU_WAVE;
206 xp.share_id[0] = cpu_to_le32(wp->share_id[0]);
207 xp.share_id[1] = cpu_to_le32(wp->share_id[1]);
208 xp.share_id[2] = cpu_to_le32(wp->share_id[2]);
209 xp.share_id[3] = cpu_to_le32(wp->share_id[3]);
210 xp.format = cpu_to_le32(wp->format);
211 xp.size = cpu_to_le32(wp->size);
212 xp.start = cpu_to_le32(wp->start);
213 xp.loop_start = cpu_to_le32(wp->loop_start);
214 xp.loop_end = cpu_to_le32(wp->loop_end);
215 xp.loop_repeat = cpu_to_le32(wp->loop_repeat);
216 xp.flags = wp->flags;
217 xp.sample_rate = cpu_to_le32(wp->sample_rate);
218 xp.low_frequency = cpu_to_le32(wp->low_frequency);
219 xp.high_frequency = cpu_to_le32(wp->high_frequency);
220 xp.root_frequency = cpu_to_le32(wp->root_frequency);
221 xp.tune = cpu_to_le16(wp->tune);
222 xp.balance = wp->balance;
223 memcpy(xp.envelope_rate, wp->envelope_rate, 6);
224 memcpy(xp.envelope_offset, wp->envelope_offset, 6);
225 xp.tremolo_sweep = wp->tremolo_sweep;
226 xp.tremolo_rate = wp->tremolo_rate;
227 xp.tremolo_depth = wp->tremolo_depth;
228 xp.vibrato_sweep = wp->vibrato_sweep;
229 xp.vibrato_rate = wp->vibrato_rate;
230 xp.vibrato_depth = wp->vibrato_depth;
231 xp.scale_frequency = cpu_to_le16(wp->scale_frequency);
232 xp.scale_factor = cpu_to_le16(wp->scale_factor);
233 if (copy_to_user(*data, &xp, sizeof(xp)))
234 return -EFAULT;
235 *data += sizeof(xp);
236 *len -= sizeof(xp);
237 real_size = snd_seq_gf1_size(wp->size, wp->format);
238 if (*len < (long)real_size)
239 return -ENOMEM;
240 if (ops->get_sample) {
241 err = ops->get_sample(ops->private_data, wp,
242 *data, real_size, atomic);
243 if (err < 0)
244 return err;
245 }
246 *data += wp->size;
247 *len -= wp->size;
248 }
249 return 0;
250}
251
252static int snd_seq_gf1_get(void *private_data, snd_seq_kinstr_t *instr,
253 char __user *instr_data, long len, int atomic,
254 int cmd)
255{
256 snd_gf1_ops_t *ops = (snd_gf1_ops_t *)private_data;
257 gf1_instrument_t *ip;
258 gf1_xinstrument_t ix;
259
260 if (cmd != SNDRV_SEQ_INSTR_GET_CMD_FULL)
261 return -EINVAL;
262 if (len < (long)sizeof(ix))
263 return -ENOMEM;
264 memset(&ix, 0, sizeof(ix));
265 ip = (gf1_instrument_t *)KINSTR_DATA(instr);
266 ix.stype = GF1_STRU_INSTR;
267 ix.exclusion = cpu_to_le16(ip->exclusion);
268 ix.exclusion_group = cpu_to_le16(ip->exclusion_group);
269 ix.effect1 = cpu_to_le16(ip->effect1);
270 ix.effect1_depth = cpu_to_le16(ip->effect1_depth);
271 ix.effect2 = ip->effect2;
272 ix.effect2_depth = ip->effect2_depth;
273 if (copy_to_user(instr_data, &ix, sizeof(ix)))
274 return -EFAULT;
275 instr_data += sizeof(ix);
276 len -= sizeof(ix);
277 return snd_seq_gf1_copy_wave_to_stream(ops,
278 ip,
279 &instr_data,
280 &len,
281 atomic);
282}
283
284static int snd_seq_gf1_get_size(void *private_data, snd_seq_kinstr_t *instr,
285 long *size)
286{
287 long result;
288 gf1_instrument_t *ip;
289 gf1_wave_t *wp;
290
291 *size = 0;
292 ip = (gf1_instrument_t *)KINSTR_DATA(instr);
293 result = sizeof(gf1_xinstrument_t);
294 for (wp = ip->wave; wp; wp = wp->next) {
295 result += sizeof(gf1_xwave_t);
296 result += wp->size;
297 }
298 *size = result;
299 return 0;
300}
301
302static int snd_seq_gf1_remove(void *private_data,
303 snd_seq_kinstr_t *instr,
304 int atomic)
305{
306 snd_gf1_ops_t *ops = (snd_gf1_ops_t *)private_data;
307 gf1_instrument_t *ip;
308
309 ip = (gf1_instrument_t *)KINSTR_DATA(instr);
310 snd_seq_gf1_instr_free(ops, ip, atomic);
311 return 0;
312}
313
314static void snd_seq_gf1_notify(void *private_data,
315 snd_seq_kinstr_t *instr,
316 int what)
317{
318 snd_gf1_ops_t *ops = (snd_gf1_ops_t *)private_data;
319
320 if (ops->notify)
321 ops->notify(ops->private_data, instr, what);
322}
323
324int snd_seq_gf1_init(snd_gf1_ops_t *ops,
325 void *private_data,
326 snd_seq_kinstr_ops_t *next)
327{
328 memset(ops, 0, sizeof(*ops));
329 ops->private_data = private_data;
330 ops->kops.private_data = ops;
331 ops->kops.add_len = sizeof(gf1_instrument_t);
332 ops->kops.instr_type = SNDRV_SEQ_INSTR_ID_GUS_PATCH;
333 ops->kops.put = snd_seq_gf1_put;
334 ops->kops.get = snd_seq_gf1_get;
335 ops->kops.get_size = snd_seq_gf1_get_size;
336 ops->kops.remove = snd_seq_gf1_remove;
337 ops->kops.notify = snd_seq_gf1_notify;
338 ops->kops.next = next;
339 return 0;
340}
341
342/*
343 * Init part
344 */
345
346static int __init alsa_ainstr_gf1_init(void)
347{
348 return 0;
349}
350
351static void __exit alsa_ainstr_gf1_exit(void)
352{
353}
354
355module_init(alsa_ainstr_gf1_init)
356module_exit(alsa_ainstr_gf1_exit)
357
358EXPORT_SYMBOL(snd_seq_gf1_init);
diff --git a/sound/core/seq/instr/ainstr_iw.c b/sound/core/seq/instr/ainstr_iw.c
new file mode 100644
index 000000000000..39ff72b2aab3
--- /dev/null
+++ b/sound/core/seq/instr/ainstr_iw.c
@@ -0,0 +1,622 @@
1/*
2 * IWFFFF - AMD InterWave (tm) - Instrument routines
3 * Copyright (c) 1999 by Jaroslav Kysela <perex@suse.cz>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 *
19 */
20
21#include <sound/driver.h>
22#include <linux/init.h>
23#include <linux/sched.h>
24#include <linux/slab.h>
25#include <sound/core.h>
26#include <sound/ainstr_iw.h>
27#include <sound/initval.h>
28#include <asm/uaccess.h>
29
30MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
31MODULE_DESCRIPTION("Advanced Linux Sound Architecture IWFFFF support.");
32MODULE_LICENSE("GPL");
33
34static unsigned int snd_seq_iwffff_size(unsigned int size, unsigned int format)
35{
36 unsigned int result = size;
37
38 if (format & IWFFFF_WAVE_16BIT)
39 result <<= 1;
40 if (format & IWFFFF_WAVE_STEREO)
41 result <<= 1;
42 return result;
43}
44
45static void snd_seq_iwffff_copy_lfo_from_stream(iwffff_lfo_t *fp,
46 iwffff_xlfo_t *fx)
47{
48 fp->freq = le16_to_cpu(fx->freq);
49 fp->depth = le16_to_cpu(fx->depth);
50 fp->sweep = le16_to_cpu(fx->sweep);
51 fp->shape = fx->shape;
52 fp->delay = fx->delay;
53}
54
55static int snd_seq_iwffff_copy_env_from_stream(__u32 req_stype,
56 iwffff_layer_t *lp,
57 iwffff_env_t *ep,
58 iwffff_xenv_t *ex,
59 char __user **data,
60 long *len,
61 int gfp_mask)
62{
63 __u32 stype;
64 iwffff_env_record_t *rp, *rp_last;
65 iwffff_xenv_record_t rx;
66 iwffff_env_point_t *pp;
67 iwffff_xenv_point_t px;
68 int points_size, idx;
69
70 ep->flags = ex->flags;
71 ep->mode = ex->mode;
72 ep->index = ex->index;
73 rp_last = NULL;
74 while (1) {
75 if (*len < (long)sizeof(__u32))
76 return -EINVAL;
77 if (copy_from_user(&stype, *data, sizeof(stype)))
78 return -EFAULT;
79 if (stype == IWFFFF_STRU_WAVE)
80 return 0;
81 if (req_stype != stype) {
82 if (stype == IWFFFF_STRU_ENV_RECP ||
83 stype == IWFFFF_STRU_ENV_RECV)
84 return 0;
85 }
86 if (*len < (long)sizeof(rx))
87 return -EINVAL;
88 if (copy_from_user(&rx, *data, sizeof(rx)))
89 return -EFAULT;
90 *data += sizeof(rx);
91 *len -= sizeof(rx);
92 points_size = (le16_to_cpu(rx.nattack) + le16_to_cpu(rx.nrelease)) * 2 * sizeof(__u16);
93 if (points_size > *len)
94 return -EINVAL;
95 rp = kcalloc(1, sizeof(*rp) + points_size, gfp_mask);
96 if (rp == NULL)
97 return -ENOMEM;
98 rp->nattack = le16_to_cpu(rx.nattack);
99 rp->nrelease = le16_to_cpu(rx.nrelease);
100 rp->sustain_offset = le16_to_cpu(rx.sustain_offset);
101 rp->sustain_rate = le16_to_cpu(rx.sustain_rate);
102 rp->release_rate = le16_to_cpu(rx.release_rate);
103 rp->hirange = rx.hirange;
104 pp = (iwffff_env_point_t *)(rp + 1);
105 for (idx = 0; idx < rp->nattack + rp->nrelease; idx++) {
106 if (copy_from_user(&px, *data, sizeof(px)))
107 return -EFAULT;
108 *data += sizeof(px);
109 *len -= sizeof(px);
110 pp->offset = le16_to_cpu(px.offset);
111 pp->rate = le16_to_cpu(px.rate);
112 }
113 if (ep->record == NULL) {
114 ep->record = rp;
115 } else {
116 rp_last = rp;
117 }
118 rp_last = rp;
119 }
120 return 0;
121}
122
123static int snd_seq_iwffff_copy_wave_from_stream(snd_iwffff_ops_t *ops,
124 iwffff_layer_t *lp,
125 char __user **data,
126 long *len,
127 int atomic)
128{
129 iwffff_wave_t *wp, *prev;
130 iwffff_xwave_t xp;
131 int err, gfp_mask;
132 unsigned int real_size;
133
134 gfp_mask = atomic ? GFP_ATOMIC : GFP_KERNEL;
135 if (*len < (long)sizeof(xp))
136 return -EINVAL;
137 if (copy_from_user(&xp, *data, sizeof(xp)))
138 return -EFAULT;
139 *data += sizeof(xp);
140 *len -= sizeof(xp);
141 wp = kcalloc(1, sizeof(*wp), gfp_mask);
142 if (wp == NULL)
143 return -ENOMEM;
144 wp->share_id[0] = le32_to_cpu(xp.share_id[0]);
145 wp->share_id[1] = le32_to_cpu(xp.share_id[1]);
146 wp->share_id[2] = le32_to_cpu(xp.share_id[2]);
147 wp->share_id[3] = le32_to_cpu(xp.share_id[3]);
148 wp->format = le32_to_cpu(xp.format);
149 wp->address.memory = le32_to_cpu(xp.offset);
150 wp->size = le32_to_cpu(xp.size);
151 wp->start = le32_to_cpu(xp.start);
152 wp->loop_start = le32_to_cpu(xp.loop_start);
153 wp->loop_end = le32_to_cpu(xp.loop_end);
154 wp->loop_repeat = le16_to_cpu(xp.loop_repeat);
155 wp->sample_ratio = le32_to_cpu(xp.sample_ratio);
156 wp->attenuation = xp.attenuation;
157 wp->low_note = xp.low_note;
158 wp->high_note = xp.high_note;
159 real_size = snd_seq_iwffff_size(wp->size, wp->format);
160 if (!(wp->format & IWFFFF_WAVE_ROM)) {
161 if ((long)real_size > *len) {
162 kfree(wp);
163 return -ENOMEM;
164 }
165 }
166 if (ops->put_sample) {
167 err = ops->put_sample(ops->private_data, wp,
168 *data, real_size, atomic);
169 if (err < 0) {
170 kfree(wp);
171 return err;
172 }
173 }
174 if (!(wp->format & IWFFFF_WAVE_ROM)) {
175 *data += real_size;
176 *len -= real_size;
177 }
178 prev = lp->wave;
179 if (prev) {
180 while (prev->next) prev = prev->next;
181 prev->next = wp;
182 } else {
183 lp->wave = wp;
184 }
185 return 0;
186}
187
188static void snd_seq_iwffff_env_free(snd_iwffff_ops_t *ops,
189 iwffff_env_t *env,
190 int atomic)
191{
192 iwffff_env_record_t *rec;
193
194 while ((rec = env->record) != NULL) {
195 env->record = rec->next;
196 kfree(rec);
197 }
198}
199
200static void snd_seq_iwffff_wave_free(snd_iwffff_ops_t *ops,
201 iwffff_wave_t *wave,
202 int atomic)
203{
204 if (ops->remove_sample)
205 ops->remove_sample(ops->private_data, wave, atomic);
206 kfree(wave);
207}
208
209static void snd_seq_iwffff_instr_free(snd_iwffff_ops_t *ops,
210 iwffff_instrument_t *ip,
211 int atomic)
212{
213 iwffff_layer_t *layer;
214 iwffff_wave_t *wave;
215
216 while ((layer = ip->layer) != NULL) {
217 ip->layer = layer->next;
218 snd_seq_iwffff_env_free(ops, &layer->penv, atomic);
219 snd_seq_iwffff_env_free(ops, &layer->venv, atomic);
220 while ((wave = layer->wave) != NULL) {
221 layer->wave = wave->next;
222 snd_seq_iwffff_wave_free(ops, wave, atomic);
223 }
224 kfree(layer);
225 }
226}
227
228static int snd_seq_iwffff_put(void *private_data, snd_seq_kinstr_t *instr,
229 char __user *instr_data, long len, int atomic,
230 int cmd)
231{
232 snd_iwffff_ops_t *ops = (snd_iwffff_ops_t *)private_data;
233 iwffff_instrument_t *ip;
234 iwffff_xinstrument_t ix;
235 iwffff_layer_t *lp, *prev_lp;
236 iwffff_xlayer_t lx;
237 int err, gfp_mask;
238
239 if (cmd != SNDRV_SEQ_INSTR_PUT_CMD_CREATE)
240 return -EINVAL;
241 gfp_mask = atomic ? GFP_ATOMIC : GFP_KERNEL;
242 /* copy instrument data */
243 if (len < (long)sizeof(ix))
244 return -EINVAL;
245 if (copy_from_user(&ix, instr_data, sizeof(ix)))
246 return -EFAULT;
247 if (ix.stype != IWFFFF_STRU_INSTR)
248 return -EINVAL;
249 instr_data += sizeof(ix);
250 len -= sizeof(ix);
251 ip = (iwffff_instrument_t *)KINSTR_DATA(instr);
252 ip->exclusion = le16_to_cpu(ix.exclusion);
253 ip->layer_type = le16_to_cpu(ix.layer_type);
254 ip->exclusion_group = le16_to_cpu(ix.exclusion_group);
255 ip->effect1 = ix.effect1;
256 ip->effect1_depth = ix.effect1_depth;
257 ip->effect2 = ix.effect2;
258 ip->effect2_depth = ix.effect2_depth;
259 /* copy layers */
260 prev_lp = NULL;
261 while (len > 0) {
262 if (len < (long)sizeof(iwffff_xlayer_t)) {
263 snd_seq_iwffff_instr_free(ops, ip, atomic);
264 return -EINVAL;
265 }
266 if (copy_from_user(&lx, instr_data, sizeof(lx)))
267 return -EFAULT;
268 instr_data += sizeof(lx);
269 len -= sizeof(lx);
270 if (lx.stype != IWFFFF_STRU_LAYER) {
271 snd_seq_iwffff_instr_free(ops, ip, atomic);
272 return -EINVAL;
273 }
274 lp = kcalloc(1, sizeof(*lp), gfp_mask);
275 if (lp == NULL) {
276 snd_seq_iwffff_instr_free(ops, ip, atomic);
277 return -ENOMEM;
278 }
279 if (prev_lp) {
280 prev_lp->next = lp;
281 } else {
282 ip->layer = lp;
283 }
284 prev_lp = lp;
285 lp->flags = lx.flags;
286 lp->velocity_mode = lx.velocity_mode;
287 lp->layer_event = lx.layer_event;
288 lp->low_range = lx.low_range;
289 lp->high_range = lx.high_range;
290 lp->pan = lx.pan;
291 lp->pan_freq_scale = lx.pan_freq_scale;
292 lp->attenuation = lx.attenuation;
293 snd_seq_iwffff_copy_lfo_from_stream(&lp->tremolo, &lx.tremolo);
294 snd_seq_iwffff_copy_lfo_from_stream(&lp->vibrato, &lx.vibrato);
295 lp->freq_scale = le16_to_cpu(lx.freq_scale);
296 lp->freq_center = lx.freq_center;
297 err = snd_seq_iwffff_copy_env_from_stream(IWFFFF_STRU_ENV_RECP,
298 lp,
299 &lp->penv, &lx.penv,
300 &instr_data, &len,
301 gfp_mask);
302 if (err < 0) {
303 snd_seq_iwffff_instr_free(ops, ip, atomic);
304 return err;
305 }
306 err = snd_seq_iwffff_copy_env_from_stream(IWFFFF_STRU_ENV_RECV,
307 lp,
308 &lp->venv, &lx.venv,
309 &instr_data, &len,
310 gfp_mask);
311 if (err < 0) {
312 snd_seq_iwffff_instr_free(ops, ip, atomic);
313 return err;
314 }
315 while (len > (long)sizeof(__u32)) {
316 __u32 stype;
317
318 if (copy_from_user(&stype, instr_data, sizeof(stype)))
319 return -EFAULT;
320 if (stype != IWFFFF_STRU_WAVE)
321 break;
322 err = snd_seq_iwffff_copy_wave_from_stream(ops,
323 lp,
324 &instr_data,
325 &len,
326 atomic);
327 if (err < 0) {
328 snd_seq_iwffff_instr_free(ops, ip, atomic);
329 return err;
330 }
331 }
332 }
333 return 0;
334}
335
336static void snd_seq_iwffff_copy_lfo_to_stream(iwffff_xlfo_t *fx,
337 iwffff_lfo_t *fp)
338{
339 fx->freq = cpu_to_le16(fp->freq);
340 fx->depth = cpu_to_le16(fp->depth);
341 fx->sweep = cpu_to_le16(fp->sweep);
342 fp->shape = fx->shape;
343 fp->delay = fx->delay;
344}
345
346static int snd_seq_iwffff_copy_env_to_stream(__u32 req_stype,
347 iwffff_layer_t *lp,
348 iwffff_xenv_t *ex,
349 iwffff_env_t *ep,
350 char __user **data,
351 long *len)
352{
353 iwffff_env_record_t *rp;
354 iwffff_xenv_record_t rx;
355 iwffff_env_point_t *pp;
356 iwffff_xenv_point_t px;
357 int points_size, idx;
358
359 ex->flags = ep->flags;
360 ex->mode = ep->mode;
361 ex->index = ep->index;
362 for (rp = ep->record; rp; rp = rp->next) {
363 if (*len < (long)sizeof(rx))
364 return -ENOMEM;
365 memset(&rx, 0, sizeof(rx));
366 rx.stype = req_stype;
367 rx.nattack = cpu_to_le16(rp->nattack);
368 rx.nrelease = cpu_to_le16(rp->nrelease);
369 rx.sustain_offset = cpu_to_le16(rp->sustain_offset);
370 rx.sustain_rate = cpu_to_le16(rp->sustain_rate);
371 rx.release_rate = cpu_to_le16(rp->release_rate);
372 rx.hirange = cpu_to_le16(rp->hirange);
373 if (copy_to_user(*data, &rx, sizeof(rx)))
374 return -EFAULT;
375 *data += sizeof(rx);
376 *len -= sizeof(rx);
377 points_size = (rp->nattack + rp->nrelease) * 2 * sizeof(__u16);
378 if (*len < points_size)
379 return -ENOMEM;
380 pp = (iwffff_env_point_t *)(rp + 1);
381 for (idx = 0; idx < rp->nattack + rp->nrelease; idx++) {
382 px.offset = cpu_to_le16(pp->offset);
383 px.rate = cpu_to_le16(pp->rate);
384 if (copy_to_user(*data, &px, sizeof(px)))
385 return -EFAULT;
386 *data += sizeof(px);
387 *len -= sizeof(px);
388 }
389 }
390 return 0;
391}
392
393static int snd_seq_iwffff_copy_wave_to_stream(snd_iwffff_ops_t *ops,
394 iwffff_layer_t *lp,
395 char __user **data,
396 long *len,
397 int atomic)
398{
399 iwffff_wave_t *wp;
400 iwffff_xwave_t xp;
401 int err;
402 unsigned int real_size;
403
404 for (wp = lp->wave; wp; wp = wp->next) {
405 if (*len < (long)sizeof(xp))
406 return -ENOMEM;
407 memset(&xp, 0, sizeof(xp));
408 xp.stype = IWFFFF_STRU_WAVE;
409 xp.share_id[0] = cpu_to_le32(wp->share_id[0]);
410 xp.share_id[1] = cpu_to_le32(wp->share_id[1]);
411 xp.share_id[2] = cpu_to_le32(wp->share_id[2]);
412 xp.share_id[3] = cpu_to_le32(wp->share_id[3]);
413 xp.format = cpu_to_le32(wp->format);
414 if (wp->format & IWFFFF_WAVE_ROM)
415 xp.offset = cpu_to_le32(wp->address.memory);
416 xp.size = cpu_to_le32(wp->size);
417 xp.start = cpu_to_le32(wp->start);
418 xp.loop_start = cpu_to_le32(wp->loop_start);
419 xp.loop_end = cpu_to_le32(wp->loop_end);
420 xp.loop_repeat = cpu_to_le32(wp->loop_repeat);
421 xp.sample_ratio = cpu_to_le32(wp->sample_ratio);
422 xp.attenuation = wp->attenuation;
423 xp.low_note = wp->low_note;
424 xp.high_note = wp->high_note;
425 if (copy_to_user(*data, &xp, sizeof(xp)))
426 return -EFAULT;
427 *data += sizeof(xp);
428 *len -= sizeof(xp);
429 real_size = snd_seq_iwffff_size(wp->size, wp->format);
430 if (!(wp->format & IWFFFF_WAVE_ROM)) {
431 if (*len < (long)real_size)
432 return -ENOMEM;
433 }
434 if (ops->get_sample) {
435 err = ops->get_sample(ops->private_data, wp,
436 *data, real_size, atomic);
437 if (err < 0)
438 return err;
439 }
440 if (!(wp->format & IWFFFF_WAVE_ROM)) {
441 *data += real_size;
442 *len -= real_size;
443 }
444 }
445 return 0;
446}
447
448static int snd_seq_iwffff_get(void *private_data, snd_seq_kinstr_t *instr,
449 char __user *instr_data, long len, int atomic, int cmd)
450{
451 snd_iwffff_ops_t *ops = (snd_iwffff_ops_t *)private_data;
452 iwffff_instrument_t *ip;
453 iwffff_xinstrument_t ix;
454 iwffff_layer_t *lp;
455 iwffff_xlayer_t lx;
456 char __user *layer_instr_data;
457 int err;
458
459 if (cmd != SNDRV_SEQ_INSTR_GET_CMD_FULL)
460 return -EINVAL;
461 if (len < (long)sizeof(ix))
462 return -ENOMEM;
463 memset(&ix, 0, sizeof(ix));
464 ip = (iwffff_instrument_t *)KINSTR_DATA(instr);
465 ix.stype = IWFFFF_STRU_INSTR;
466 ix.exclusion = cpu_to_le16(ip->exclusion);
467 ix.layer_type = cpu_to_le16(ip->layer_type);
468 ix.exclusion_group = cpu_to_le16(ip->exclusion_group);
469 ix.effect1 = cpu_to_le16(ip->effect1);
470 ix.effect1_depth = cpu_to_le16(ip->effect1_depth);
471 ix.effect2 = ip->effect2;
472 ix.effect2_depth = ip->effect2_depth;
473 if (copy_to_user(instr_data, &ix, sizeof(ix)))
474 return -EFAULT;
475 instr_data += sizeof(ix);
476 len -= sizeof(ix);
477 for (lp = ip->layer; lp; lp = lp->next) {
478 if (len < (long)sizeof(lx))
479 return -ENOMEM;
480 memset(&lx, 0, sizeof(lx));
481 lx.stype = IWFFFF_STRU_LAYER;
482 lx.flags = lp->flags;
483 lx.velocity_mode = lp->velocity_mode;
484 lx.layer_event = lp->layer_event;
485 lx.low_range = lp->low_range;
486 lx.high_range = lp->high_range;
487 lx.pan = lp->pan;
488 lx.pan_freq_scale = lp->pan_freq_scale;
489 lx.attenuation = lp->attenuation;
490 snd_seq_iwffff_copy_lfo_to_stream(&lx.tremolo, &lp->tremolo);
491 snd_seq_iwffff_copy_lfo_to_stream(&lx.vibrato, &lp->vibrato);
492 layer_instr_data = instr_data;
493 instr_data += sizeof(lx);
494 len -= sizeof(lx);
495 err = snd_seq_iwffff_copy_env_to_stream(IWFFFF_STRU_ENV_RECP,
496 lp,
497 &lx.penv, &lp->penv,
498 &instr_data, &len);
499 if (err < 0)
500 return err;
501 err = snd_seq_iwffff_copy_env_to_stream(IWFFFF_STRU_ENV_RECV,
502 lp,
503 &lx.venv, &lp->venv,
504 &instr_data, &len);
505 if (err < 0)
506 return err;
507 /* layer structure updating is now finished */
508 if (copy_to_user(layer_instr_data, &lx, sizeof(lx)))
509 return -EFAULT;
510 err = snd_seq_iwffff_copy_wave_to_stream(ops,
511 lp,
512 &instr_data,
513 &len,
514 atomic);
515 if (err < 0)
516 return err;
517 }
518 return 0;
519}
520
521static long snd_seq_iwffff_env_size_in_stream(iwffff_env_t *ep)
522{
523 long result = 0;
524 iwffff_env_record_t *rp;
525
526 for (rp = ep->record; rp; rp = rp->next) {
527 result += sizeof(iwffff_xenv_record_t);
528 result += (rp->nattack + rp->nrelease) * 2 * sizeof(__u16);
529 }
530 return 0;
531}
532
533static long snd_seq_iwffff_wave_size_in_stream(iwffff_layer_t *lp)
534{
535 long result = 0;
536 iwffff_wave_t *wp;
537
538 for (wp = lp->wave; wp; wp = wp->next) {
539 result += sizeof(iwffff_xwave_t);
540 if (!(wp->format & IWFFFF_WAVE_ROM))
541 result += wp->size;
542 }
543 return result;
544}
545
546static int snd_seq_iwffff_get_size(void *private_data, snd_seq_kinstr_t *instr,
547 long *size)
548{
549 long result;
550 iwffff_instrument_t *ip;
551 iwffff_layer_t *lp;
552
553 *size = 0;
554 ip = (iwffff_instrument_t *)KINSTR_DATA(instr);
555 result = sizeof(iwffff_xinstrument_t);
556 for (lp = ip->layer; lp; lp = lp->next) {
557 result += sizeof(iwffff_xlayer_t);
558 result += snd_seq_iwffff_env_size_in_stream(&lp->penv);
559 result += snd_seq_iwffff_env_size_in_stream(&lp->venv);
560 result += snd_seq_iwffff_wave_size_in_stream(lp);
561 }
562 *size = result;
563 return 0;
564}
565
566static int snd_seq_iwffff_remove(void *private_data,
567 snd_seq_kinstr_t *instr,
568 int atomic)
569{
570 snd_iwffff_ops_t *ops = (snd_iwffff_ops_t *)private_data;
571 iwffff_instrument_t *ip;
572
573 ip = (iwffff_instrument_t *)KINSTR_DATA(instr);
574 snd_seq_iwffff_instr_free(ops, ip, atomic);
575 return 0;
576}
577
578static void snd_seq_iwffff_notify(void *private_data,
579 snd_seq_kinstr_t *instr,
580 int what)
581{
582 snd_iwffff_ops_t *ops = (snd_iwffff_ops_t *)private_data;
583
584 if (ops->notify)
585 ops->notify(ops->private_data, instr, what);
586}
587
588int snd_seq_iwffff_init(snd_iwffff_ops_t *ops,
589 void *private_data,
590 snd_seq_kinstr_ops_t *next)
591{
592 memset(ops, 0, sizeof(*ops));
593 ops->private_data = private_data;
594 ops->kops.private_data = ops;
595 ops->kops.add_len = sizeof(iwffff_instrument_t);
596 ops->kops.instr_type = SNDRV_SEQ_INSTR_ID_INTERWAVE;
597 ops->kops.put = snd_seq_iwffff_put;
598 ops->kops.get = snd_seq_iwffff_get;
599 ops->kops.get_size = snd_seq_iwffff_get_size;
600 ops->kops.remove = snd_seq_iwffff_remove;
601 ops->kops.notify = snd_seq_iwffff_notify;
602 ops->kops.next = next;
603 return 0;
604}
605
606/*
607 * Init part
608 */
609
610static int __init alsa_ainstr_iw_init(void)
611{
612 return 0;
613}
614
615static void __exit alsa_ainstr_iw_exit(void)
616{
617}
618
619module_init(alsa_ainstr_iw_init)
620module_exit(alsa_ainstr_iw_exit)
621
622EXPORT_SYMBOL(snd_seq_iwffff_init);
diff --git a/sound/core/seq/instr/ainstr_simple.c b/sound/core/seq/instr/ainstr_simple.c
new file mode 100644
index 000000000000..6183d2151034
--- /dev/null
+++ b/sound/core/seq/instr/ainstr_simple.c
@@ -0,0 +1,215 @@
1/*
2 * Simple (MOD player) - Instrument routines
3 * Copyright (c) 1999 by Jaroslav Kysela <perex@suse.cz>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 *
19 */
20
21#include <sound/driver.h>
22#include <linux/init.h>
23#include <linux/sched.h>
24#include <linux/slab.h>
25#include <sound/core.h>
26#include <sound/ainstr_simple.h>
27#include <sound/initval.h>
28#include <asm/uaccess.h>
29
30MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
31MODULE_DESCRIPTION("Advanced Linux Sound Architecture Simple Instrument support.");
32MODULE_LICENSE("GPL");
33
34static unsigned int snd_seq_simple_size(unsigned int size, unsigned int format)
35{
36 unsigned int result = size;
37
38 if (format & SIMPLE_WAVE_16BIT)
39 result <<= 1;
40 if (format & SIMPLE_WAVE_STEREO)
41 result <<= 1;
42 return result;
43}
44
45static void snd_seq_simple_instr_free(snd_simple_ops_t *ops,
46 simple_instrument_t *ip,
47 int atomic)
48{
49 if (ops->remove_sample)
50 ops->remove_sample(ops->private_data, ip, atomic);
51}
52
53static int snd_seq_simple_put(void *private_data, snd_seq_kinstr_t *instr,
54 char __user *instr_data, long len,
55 int atomic, int cmd)
56{
57 snd_simple_ops_t *ops = (snd_simple_ops_t *)private_data;
58 simple_instrument_t *ip;
59 simple_xinstrument_t ix;
60 int err, gfp_mask;
61 unsigned int real_size;
62
63 if (cmd != SNDRV_SEQ_INSTR_PUT_CMD_CREATE)
64 return -EINVAL;
65 gfp_mask = atomic ? GFP_ATOMIC : GFP_KERNEL;
66 /* copy instrument data */
67 if (len < (long)sizeof(ix))
68 return -EINVAL;
69 if (copy_from_user(&ix, instr_data, sizeof(ix)))
70 return -EFAULT;
71 if (ix.stype != SIMPLE_STRU_INSTR)
72 return -EINVAL;
73 instr_data += sizeof(ix);
74 len -= sizeof(ix);
75 ip = (simple_instrument_t *)KINSTR_DATA(instr);
76 ip->share_id[0] = le32_to_cpu(ix.share_id[0]);
77 ip->share_id[1] = le32_to_cpu(ix.share_id[1]);
78 ip->share_id[2] = le32_to_cpu(ix.share_id[2]);
79 ip->share_id[3] = le32_to_cpu(ix.share_id[3]);
80 ip->format = le32_to_cpu(ix.format);
81 ip->size = le32_to_cpu(ix.size);
82 ip->start = le32_to_cpu(ix.start);
83 ip->loop_start = le32_to_cpu(ix.loop_start);
84 ip->loop_end = le32_to_cpu(ix.loop_end);
85 ip->loop_repeat = le16_to_cpu(ix.loop_repeat);
86 ip->effect1 = ix.effect1;
87 ip->effect1_depth = ix.effect1_depth;
88 ip->effect2 = ix.effect2;
89 ip->effect2_depth = ix.effect2_depth;
90 real_size = snd_seq_simple_size(ip->size, ip->format);
91 if (len < (long)real_size)
92 return -EINVAL;
93 if (ops->put_sample) {
94 err = ops->put_sample(ops->private_data, ip,
95 instr_data, real_size, atomic);
96 if (err < 0)
97 return err;
98 }
99 return 0;
100}
101
102static int snd_seq_simple_get(void *private_data, snd_seq_kinstr_t *instr,
103 char __user *instr_data, long len,
104 int atomic, int cmd)
105{
106 snd_simple_ops_t *ops = (snd_simple_ops_t *)private_data;
107 simple_instrument_t *ip;
108 simple_xinstrument_t ix;
109 int err;
110 unsigned int real_size;
111
112 if (cmd != SNDRV_SEQ_INSTR_GET_CMD_FULL)
113 return -EINVAL;
114 if (len < (long)sizeof(ix))
115 return -ENOMEM;
116 memset(&ix, 0, sizeof(ix));
117 ip = (simple_instrument_t *)KINSTR_DATA(instr);
118 ix.stype = SIMPLE_STRU_INSTR;
119 ix.share_id[0] = cpu_to_le32(ip->share_id[0]);
120 ix.share_id[1] = cpu_to_le32(ip->share_id[1]);
121 ix.share_id[2] = cpu_to_le32(ip->share_id[2]);
122 ix.share_id[3] = cpu_to_le32(ip->share_id[3]);
123 ix.format = cpu_to_le32(ip->format);
124 ix.size = cpu_to_le32(ip->size);
125 ix.start = cpu_to_le32(ip->start);
126 ix.loop_start = cpu_to_le32(ip->loop_start);
127 ix.loop_end = cpu_to_le32(ip->loop_end);
128 ix.loop_repeat = cpu_to_le32(ip->loop_repeat);
129 ix.effect1 = cpu_to_le16(ip->effect1);
130 ix.effect1_depth = cpu_to_le16(ip->effect1_depth);
131 ix.effect2 = ip->effect2;
132 ix.effect2_depth = ip->effect2_depth;
133 if (copy_to_user(instr_data, &ix, sizeof(ix)))
134 return -EFAULT;
135 instr_data += sizeof(ix);
136 len -= sizeof(ix);
137 real_size = snd_seq_simple_size(ip->size, ip->format);
138 if (len < (long)real_size)
139 return -ENOMEM;
140 if (ops->get_sample) {
141 err = ops->get_sample(ops->private_data, ip,
142 instr_data, real_size, atomic);
143 if (err < 0)
144 return err;
145 }
146 return 0;
147}
148
149static int snd_seq_simple_get_size(void *private_data, snd_seq_kinstr_t *instr,
150 long *size)
151{
152 simple_instrument_t *ip;
153
154 ip = (simple_instrument_t *)KINSTR_DATA(instr);
155 *size = sizeof(simple_xinstrument_t) + snd_seq_simple_size(ip->size, ip->format);
156 return 0;
157}
158
159static int snd_seq_simple_remove(void *private_data,
160 snd_seq_kinstr_t *instr,
161 int atomic)
162{
163 snd_simple_ops_t *ops = (snd_simple_ops_t *)private_data;
164 simple_instrument_t *ip;
165
166 ip = (simple_instrument_t *)KINSTR_DATA(instr);
167 snd_seq_simple_instr_free(ops, ip, atomic);
168 return 0;
169}
170
171static void snd_seq_simple_notify(void *private_data,
172 snd_seq_kinstr_t *instr,
173 int what)
174{
175 snd_simple_ops_t *ops = (snd_simple_ops_t *)private_data;
176
177 if (ops->notify)
178 ops->notify(ops->private_data, instr, what);
179}
180
181int snd_seq_simple_init(snd_simple_ops_t *ops,
182 void *private_data,
183 snd_seq_kinstr_ops_t *next)
184{
185 memset(ops, 0, sizeof(*ops));
186 ops->private_data = private_data;
187 ops->kops.private_data = ops;
188 ops->kops.add_len = sizeof(simple_instrument_t);
189 ops->kops.instr_type = SNDRV_SEQ_INSTR_ID_SIMPLE;
190 ops->kops.put = snd_seq_simple_put;
191 ops->kops.get = snd_seq_simple_get;
192 ops->kops.get_size = snd_seq_simple_get_size;
193 ops->kops.remove = snd_seq_simple_remove;
194 ops->kops.notify = snd_seq_simple_notify;
195 ops->kops.next = next;
196 return 0;
197}
198
199/*
200 * Init part
201 */
202
203static int __init alsa_ainstr_simple_init(void)
204{
205 return 0;
206}
207
208static void __exit alsa_ainstr_simple_exit(void)
209{
210}
211
212module_init(alsa_ainstr_simple_init)
213module_exit(alsa_ainstr_simple_exit)
214
215EXPORT_SYMBOL(snd_seq_simple_init);
diff --git a/sound/core/seq/oss/Makefile b/sound/core/seq/oss/Makefile
new file mode 100644
index 000000000000..a37ddedf7107
--- /dev/null
+++ b/sound/core/seq/oss/Makefile
@@ -0,0 +1,10 @@
1#
2# Makefile for ALSA
3# Copyright (c) 1999 by Jaroslav Kysela <perex@suse.cz>
4#
5
6snd-seq-oss-objs := seq_oss.o seq_oss_init.o seq_oss_timer.o seq_oss_ioctl.o \
7 seq_oss_event.o seq_oss_rw.o seq_oss_synth.o \
8 seq_oss_midi.o seq_oss_readq.o seq_oss_writeq.o
9
10obj-$(CONFIG_SND_SEQUENCER) += snd-seq-oss.o
diff --git a/sound/core/seq/oss/seq_oss.c b/sound/core/seq/oss/seq_oss.c
new file mode 100644
index 000000000000..4c0558c0a8b4
--- /dev/null
+++ b/sound/core/seq/oss/seq_oss.c
@@ -0,0 +1,317 @@
1/*
2 * OSS compatible sequencer driver
3 *
4 * registration of device and proc
5 *
6 * Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (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 General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 */
22
23#include <sound/driver.h>
24#include <linux/init.h>
25#include <linux/smp_lock.h>
26#include <linux/moduleparam.h>
27#include <sound/core.h>
28#include <sound/minors.h>
29#include <sound/initval.h>
30#include "seq_oss_device.h"
31#include "seq_oss_synth.h"
32
33/*
34 * module option
35 */
36MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");
37MODULE_DESCRIPTION("OSS-compatible sequencer module");
38MODULE_LICENSE("GPL");
39/* Takashi says this is really only for sound-service-0-, but this is OK. */
40MODULE_ALIAS_SNDRV_MINOR(SNDRV_MINOR_OSS_SEQUENCER);
41MODULE_ALIAS_SNDRV_MINOR(SNDRV_MINOR_OSS_MUSIC);
42
43#ifdef SNDRV_SEQ_OSS_DEBUG
44module_param(seq_oss_debug, int, 0644);
45MODULE_PARM_DESC(seq_oss_debug, "debug option");
46int seq_oss_debug = 0;
47#endif
48
49
50/*
51 * prototypes
52 */
53static int register_device(void);
54static void unregister_device(void);
55static int register_proc(void);
56static void unregister_proc(void);
57
58static int odev_open(struct inode *inode, struct file *file);
59static int odev_release(struct inode *inode, struct file *file);
60static ssize_t odev_read(struct file *file, char __user *buf, size_t count, loff_t *offset);
61static ssize_t odev_write(struct file *file, const char __user *buf, size_t count, loff_t *offset);
62static long odev_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
63static unsigned int odev_poll(struct file *file, poll_table * wait);
64#ifdef CONFIG_PROC_FS
65static void info_read(snd_info_entry_t *entry, snd_info_buffer_t *buf);
66#endif
67
68
69/*
70 * module interface
71 */
72
73static int __init alsa_seq_oss_init(void)
74{
75 int rc;
76 static snd_seq_dev_ops_t ops = {
77 snd_seq_oss_synth_register,
78 snd_seq_oss_synth_unregister,
79 };
80
81 snd_seq_autoload_lock();
82 if ((rc = register_device()) < 0)
83 goto error;
84 if ((rc = register_proc()) < 0) {
85 unregister_device();
86 goto error;
87 }
88 if ((rc = snd_seq_oss_create_client()) < 0) {
89 unregister_proc();
90 unregister_device();
91 goto error;
92 }
93
94 if ((rc = snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_OSS, &ops,
95 sizeof(snd_seq_oss_reg_t))) < 0) {
96 snd_seq_oss_delete_client();
97 unregister_proc();
98 unregister_device();
99 goto error;
100 }
101
102 /* success */
103 snd_seq_oss_synth_init();
104
105 error:
106 snd_seq_autoload_unlock();
107 return rc;
108}
109
110static void __exit alsa_seq_oss_exit(void)
111{
112 snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_OSS);
113 snd_seq_oss_delete_client();
114 unregister_proc();
115 unregister_device();
116}
117
118module_init(alsa_seq_oss_init)
119module_exit(alsa_seq_oss_exit)
120
121/*
122 * ALSA minor device interface
123 */
124
125static DECLARE_MUTEX(register_mutex);
126
127static int
128odev_open(struct inode *inode, struct file *file)
129{
130 int level, rc;
131
132 if (iminor(inode) == SNDRV_MINOR_OSS_MUSIC)
133 level = SNDRV_SEQ_OSS_MODE_MUSIC;
134 else
135 level = SNDRV_SEQ_OSS_MODE_SYNTH;
136
137 down(&register_mutex);
138 rc = snd_seq_oss_open(file, level);
139 up(&register_mutex);
140
141 return rc;
142}
143
144static int
145odev_release(struct inode *inode, struct file *file)
146{
147 seq_oss_devinfo_t *dp;
148
149 if ((dp = file->private_data) == NULL)
150 return 0;
151
152 snd_seq_oss_drain_write(dp);
153
154 down(&register_mutex);
155 snd_seq_oss_release(dp);
156 up(&register_mutex);
157
158 return 0;
159}
160
161static ssize_t
162odev_read(struct file *file, char __user *buf, size_t count, loff_t *offset)
163{
164 seq_oss_devinfo_t *dp;
165 dp = file->private_data;
166 snd_assert(dp != NULL, return -EIO);
167 return snd_seq_oss_read(dp, buf, count);
168}
169
170
171static ssize_t
172odev_write(struct file *file, const char __user *buf, size_t count, loff_t *offset)
173{
174 seq_oss_devinfo_t *dp;
175 dp = file->private_data;
176 snd_assert(dp != NULL, return -EIO);
177 return snd_seq_oss_write(dp, buf, count, file);
178}
179
180static long
181odev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
182{
183 seq_oss_devinfo_t *dp;
184 dp = file->private_data;
185 snd_assert(dp != NULL, return -EIO);
186 return snd_seq_oss_ioctl(dp, cmd, arg);
187}
188
189#ifdef CONFIG_COMPAT
190#define odev_ioctl_compat odev_ioctl
191#else
192#define odev_ioctl_compat NULL
193#endif
194
195static unsigned int
196odev_poll(struct file *file, poll_table * wait)
197{
198 seq_oss_devinfo_t *dp;
199 dp = file->private_data;
200 snd_assert(dp != NULL, return 0);
201 return snd_seq_oss_poll(dp, file, wait);
202}
203
204/*
205 * registration of sequencer minor device
206 */
207
208static struct file_operations seq_oss_f_ops =
209{
210 .owner = THIS_MODULE,
211 .read = odev_read,
212 .write = odev_write,
213 .open = odev_open,
214 .release = odev_release,
215 .poll = odev_poll,
216 .unlocked_ioctl = odev_ioctl,
217 .compat_ioctl = odev_ioctl_compat,
218};
219
220static snd_minor_t seq_oss_reg = {
221 .comment = "sequencer",
222 .f_ops = &seq_oss_f_ops,
223};
224
225static int __init
226register_device(void)
227{
228 int rc;
229
230 down(&register_mutex);
231 if ((rc = snd_register_oss_device(SNDRV_OSS_DEVICE_TYPE_SEQUENCER,
232 NULL, 0,
233 &seq_oss_reg,
234 SNDRV_SEQ_OSS_DEVNAME)) < 0) {
235 snd_printk(KERN_ERR "can't register device seq\n");
236 up(&register_mutex);
237 return rc;
238 }
239 if ((rc = snd_register_oss_device(SNDRV_OSS_DEVICE_TYPE_MUSIC,
240 NULL, 0,
241 &seq_oss_reg,
242 SNDRV_SEQ_OSS_DEVNAME)) < 0) {
243 snd_printk(KERN_ERR "can't register device music\n");
244 snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_SEQUENCER, NULL, 0);
245 up(&register_mutex);
246 return rc;
247 }
248 debug_printk(("device registered\n"));
249 up(&register_mutex);
250 return 0;
251}
252
253static void
254unregister_device(void)
255{
256 down(&register_mutex);
257 debug_printk(("device unregistered\n"));
258 if (snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_MUSIC, NULL, 0) < 0)
259 snd_printk(KERN_ERR "error unregister device music\n");
260 if (snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_SEQUENCER, NULL, 0) < 0)
261 snd_printk(KERN_ERR "error unregister device seq\n");
262 up(&register_mutex);
263}
264
265/*
266 * /proc interface
267 */
268
269#ifdef CONFIG_PROC_FS
270
271static snd_info_entry_t *info_entry;
272
273static void
274info_read(snd_info_entry_t *entry, snd_info_buffer_t *buf)
275{
276 down(&register_mutex);
277 snd_iprintf(buf, "OSS sequencer emulation version %s\n", SNDRV_SEQ_OSS_VERSION_STR);
278 snd_seq_oss_system_info_read(buf);
279 snd_seq_oss_synth_info_read(buf);
280 snd_seq_oss_midi_info_read(buf);
281 up(&register_mutex);
282}
283
284#endif /* CONFIG_PROC_FS */
285
286static int __init
287register_proc(void)
288{
289#ifdef CONFIG_PROC_FS
290 snd_info_entry_t *entry;
291
292 entry = snd_info_create_module_entry(THIS_MODULE, SNDRV_SEQ_OSS_PROCNAME, snd_seq_root);
293 if (entry == NULL)
294 return -ENOMEM;
295
296 entry->content = SNDRV_INFO_CONTENT_TEXT;
297 entry->private_data = NULL;
298 entry->c.text.read_size = 1024;
299 entry->c.text.read = info_read;
300 if (snd_info_register(entry) < 0) {
301 snd_info_free_entry(entry);
302 return -ENOMEM;
303 }
304 info_entry = entry;
305#endif
306 return 0;
307}
308
309static void
310unregister_proc(void)
311{
312#ifdef CONFIG_PROC_FS
313 if (info_entry)
314 snd_info_unregister(info_entry);
315 info_entry = NULL;
316#endif
317}
diff --git a/sound/core/seq/oss/seq_oss_device.h b/sound/core/seq/oss/seq_oss_device.h
new file mode 100644
index 000000000000..da23c4db8dd5
--- /dev/null
+++ b/sound/core/seq/oss/seq_oss_device.h
@@ -0,0 +1,198 @@
1/*
2 * OSS compatible sequencer driver
3 *
4 * Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>
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#ifndef __SEQ_OSS_DEVICE_H
22#define __SEQ_OSS_DEVICE_H
23
24#include <sound/driver.h>
25#include <linux/time.h>
26#include <linux/wait.h>
27#include <linux/slab.h>
28#include <linux/sched.h>
29#include <sound/core.h>
30#include <sound/seq_oss.h>
31#include <sound/rawmidi.h>
32#include <sound/seq_kernel.h>
33#include <sound/info.h>
34
35/* enable debug print */
36#define SNDRV_SEQ_OSS_DEBUG
37
38/* max. applications */
39#define SNDRV_SEQ_OSS_MAX_CLIENTS 16
40#define SNDRV_SEQ_OSS_MAX_SYNTH_DEVS 16
41#define SNDRV_SEQ_OSS_MAX_MIDI_DEVS 32
42
43/* version */
44#define SNDRV_SEQ_OSS_MAJOR_VERSION 0
45#define SNDRV_SEQ_OSS_MINOR_VERSION 1
46#define SNDRV_SEQ_OSS_TINY_VERSION 8
47#define SNDRV_SEQ_OSS_VERSION_STR "0.1.8"
48
49/* device and proc interface name */
50#define SNDRV_SEQ_OSS_DEVNAME "seq_oss"
51#define SNDRV_SEQ_OSS_PROCNAME "oss"
52
53
54/*
55 * type definitions
56 */
57
58typedef struct seq_oss_devinfo_t seq_oss_devinfo_t;
59typedef struct seq_oss_writeq_t seq_oss_writeq_t;
60typedef struct seq_oss_readq_t seq_oss_readq_t;
61typedef struct seq_oss_timer_t seq_oss_timer_t;
62typedef struct seq_oss_synthinfo_t seq_oss_synthinfo_t;
63typedef struct seq_oss_synth_sysex_t seq_oss_synth_sysex_t;
64typedef struct seq_oss_chinfo_t seq_oss_chinfo_t;
65typedef unsigned int reltime_t;
66typedef unsigned int abstime_t;
67typedef union evrec_t evrec_t;
68
69
70/*
71 * synthesizer channel information
72 */
73struct seq_oss_chinfo_t {
74 int note, vel;
75};
76
77/*
78 * synthesizer information
79 */
80struct seq_oss_synthinfo_t {
81 snd_seq_oss_arg_t arg;
82 seq_oss_chinfo_t *ch;
83 seq_oss_synth_sysex_t *sysex;
84 int nr_voices;
85 int opened;
86 int is_midi;
87 int midi_mapped;
88};
89
90
91/*
92 * sequencer client information
93 */
94
95struct seq_oss_devinfo_t {
96
97 int index; /* application index */
98 int cseq; /* sequencer client number */
99 int port; /* sequencer port number */
100 int queue; /* sequencer queue number */
101
102 snd_seq_addr_t addr; /* address of this device */
103
104 int seq_mode; /* sequencer mode */
105 int file_mode; /* file access */
106
107 /* midi device table */
108 int max_mididev;
109
110 /* synth device table */
111 int max_synthdev;
112 seq_oss_synthinfo_t synths[SNDRV_SEQ_OSS_MAX_SYNTH_DEVS];
113 int synth_opened;
114
115 /* output queue */
116 seq_oss_writeq_t *writeq;
117
118 /* midi input queue */
119 seq_oss_readq_t *readq;
120
121 /* timer */
122 seq_oss_timer_t *timer;
123};
124
125
126/*
127 * function prototypes
128 */
129
130/* create/delete OSS sequencer client */
131int snd_seq_oss_create_client(void);
132int snd_seq_oss_delete_client(void);
133
134/* device file interface */
135int snd_seq_oss_open(struct file *file, int level);
136void snd_seq_oss_release(seq_oss_devinfo_t *dp);
137int snd_seq_oss_ioctl(seq_oss_devinfo_t *dp, unsigned int cmd, unsigned long arg);
138int snd_seq_oss_read(seq_oss_devinfo_t *dev, char __user *buf, int count);
139int snd_seq_oss_write(seq_oss_devinfo_t *dp, const char __user *buf, int count, struct file *opt);
140unsigned int snd_seq_oss_poll(seq_oss_devinfo_t *dp, struct file *file, poll_table * wait);
141
142void snd_seq_oss_reset(seq_oss_devinfo_t *dp);
143void snd_seq_oss_drain_write(seq_oss_devinfo_t *dp);
144
145/* */
146void snd_seq_oss_process_queue(seq_oss_devinfo_t *dp, abstime_t time);
147
148
149/* proc interface */
150void snd_seq_oss_system_info_read(snd_info_buffer_t *buf);
151void snd_seq_oss_midi_info_read(snd_info_buffer_t *buf);
152void snd_seq_oss_synth_info_read(snd_info_buffer_t *buf);
153void snd_seq_oss_readq_info_read(seq_oss_readq_t *q, snd_info_buffer_t *buf);
154
155/* file mode macros */
156#define is_read_mode(mode) ((mode) & SNDRV_SEQ_OSS_FILE_READ)
157#define is_write_mode(mode) ((mode) & SNDRV_SEQ_OSS_FILE_WRITE)
158#define is_nonblock_mode(mode) ((mode) & SNDRV_SEQ_OSS_FILE_NONBLOCK)
159
160/* dispatch event */
161inline static int
162snd_seq_oss_dispatch(seq_oss_devinfo_t *dp, snd_seq_event_t *ev, int atomic, int hop)
163{
164 return snd_seq_kernel_client_dispatch(dp->cseq, ev, atomic, hop);
165}
166
167/* ioctl */
168inline static int
169snd_seq_oss_control(seq_oss_devinfo_t *dp, unsigned int type, void *arg)
170{
171 return snd_seq_kernel_client_ctl(dp->cseq, type, arg);
172}
173
174/* fill the addresses in header */
175inline static void
176snd_seq_oss_fill_addr(seq_oss_devinfo_t *dp, snd_seq_event_t *ev,
177 int dest_client, int dest_port)
178{
179 ev->queue = dp->queue;
180 ev->source = dp->addr;
181 ev->dest.client = dest_client;
182 ev->dest.port = dest_port;
183}
184
185
186/* misc. functions for proc interface */
187char *enabled_str(int bool);
188
189
190/* for debug */
191#ifdef SNDRV_SEQ_OSS_DEBUG
192extern int seq_oss_debug;
193#define debug_printk(x) do { if (seq_oss_debug > 0) snd_printk x; } while (0)
194#else
195#define debug_printk(x) /**/
196#endif
197
198#endif /* __SEQ_OSS_DEVICE_H */
diff --git a/sound/core/seq/oss/seq_oss_event.c b/sound/core/seq/oss/seq_oss_event.c
new file mode 100644
index 000000000000..58e52ddd2927
--- /dev/null
+++ b/sound/core/seq/oss/seq_oss_event.c
@@ -0,0 +1,447 @@
1/*
2 * OSS compatible sequencer driver
3 *
4 * Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>
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#include "seq_oss_device.h"
22#include "seq_oss_synth.h"
23#include "seq_oss_midi.h"
24#include "seq_oss_event.h"
25#include "seq_oss_timer.h"
26#include <sound/seq_oss_legacy.h>
27#include "seq_oss_readq.h"
28#include "seq_oss_writeq.h"
29
30
31/*
32 * prototypes
33 */
34static int extended_event(seq_oss_devinfo_t *dp, evrec_t *q, snd_seq_event_t *ev);
35static int chn_voice_event(seq_oss_devinfo_t *dp, evrec_t *event_rec, snd_seq_event_t *ev);
36static int chn_common_event(seq_oss_devinfo_t *dp, evrec_t *event_rec, snd_seq_event_t *ev);
37static int timing_event(seq_oss_devinfo_t *dp, evrec_t *event_rec, snd_seq_event_t *ev);
38static int local_event(seq_oss_devinfo_t *dp, evrec_t *event_rec, snd_seq_event_t *ev);
39static int old_event(seq_oss_devinfo_t *dp, evrec_t *q, snd_seq_event_t *ev);
40static int note_on_event(seq_oss_devinfo_t *dp, int dev, int ch, int note, int vel, snd_seq_event_t *ev);
41static int note_off_event(seq_oss_devinfo_t *dp, int dev, int ch, int note, int vel, snd_seq_event_t *ev);
42static int set_note_event(seq_oss_devinfo_t *dp, int dev, int type, int ch, int note, int vel, snd_seq_event_t *ev);
43static int set_control_event(seq_oss_devinfo_t *dp, int dev, int type, int ch, int param, int val, snd_seq_event_t *ev);
44static int set_echo_event(seq_oss_devinfo_t *dp, evrec_t *rec, snd_seq_event_t *ev);
45
46
47/*
48 * convert an OSS event to ALSA event
49 * return 0 : enqueued
50 * non-zero : invalid - ignored
51 */
52
53int
54snd_seq_oss_process_event(seq_oss_devinfo_t *dp, evrec_t *q, snd_seq_event_t *ev)
55{
56 switch (q->s.code) {
57 case SEQ_EXTENDED:
58 return extended_event(dp, q, ev);
59
60 case EV_CHN_VOICE:
61 return chn_voice_event(dp, q, ev);
62
63 case EV_CHN_COMMON:
64 return chn_common_event(dp, q, ev);
65
66 case EV_TIMING:
67 return timing_event(dp, q, ev);
68
69 case EV_SEQ_LOCAL:
70 return local_event(dp, q, ev);
71
72 case EV_SYSEX:
73 return snd_seq_oss_synth_sysex(dp, q->x.dev, q->x.buf, ev);
74
75 case SEQ_MIDIPUTC:
76 if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC)
77 return -EINVAL;
78 /* put a midi byte */
79 if (! is_write_mode(dp->file_mode))
80 break;
81 if (snd_seq_oss_midi_open(dp, q->s.dev, SNDRV_SEQ_OSS_FILE_WRITE))
82 break;
83 if (snd_seq_oss_midi_filemode(dp, q->s.dev) & SNDRV_SEQ_OSS_FILE_WRITE)
84 return snd_seq_oss_midi_putc(dp, q->s.dev, q->s.parm1, ev);
85 break;
86
87 case SEQ_ECHO:
88 if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC)
89 return -EINVAL;
90 return set_echo_event(dp, q, ev);
91
92 case SEQ_PRIVATE:
93 if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC)
94 return -EINVAL;
95 return snd_seq_oss_synth_raw_event(dp, q->c[1], q->c, ev);
96
97 default:
98 if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC)
99 return -EINVAL;
100 return old_event(dp, q, ev);
101 }
102 return -EINVAL;
103}
104
105/* old type events: mode1 only */
106static int
107old_event(seq_oss_devinfo_t *dp, evrec_t *q, snd_seq_event_t *ev)
108{
109 switch (q->s.code) {
110 case SEQ_NOTEOFF:
111 return note_off_event(dp, 0, q->n.chn, q->n.note, q->n.vel, ev);
112
113 case SEQ_NOTEON:
114 return note_on_event(dp, 0, q->n.chn, q->n.note, q->n.vel, ev);
115
116 case SEQ_WAIT:
117 /* skip */
118 break;
119
120 case SEQ_PGMCHANGE:
121 return set_control_event(dp, 0, SNDRV_SEQ_EVENT_PGMCHANGE,
122 q->n.chn, 0, q->n.note, ev);
123
124 case SEQ_SYNCTIMER:
125 return snd_seq_oss_timer_reset(dp->timer);
126 }
127
128 return -EINVAL;
129}
130
131/* 8bytes extended event: mode1 only */
132static int
133extended_event(seq_oss_devinfo_t *dp, evrec_t *q, snd_seq_event_t *ev)
134{
135 int val;
136
137 switch (q->e.cmd) {
138 case SEQ_NOTEOFF:
139 return note_off_event(dp, q->e.dev, q->e.chn, q->e.p1, q->e.p2, ev);
140
141 case SEQ_NOTEON:
142 return note_on_event(dp, q->e.dev, q->e.chn, q->e.p1, q->e.p2, ev);
143
144 case SEQ_PGMCHANGE:
145 return set_control_event(dp, q->e.dev, SNDRV_SEQ_EVENT_PGMCHANGE,
146 q->e.chn, 0, q->e.p1, ev);
147
148 case SEQ_AFTERTOUCH:
149 return set_control_event(dp, q->e.dev, SNDRV_SEQ_EVENT_CHANPRESS,
150 q->e.chn, 0, q->e.p1, ev);
151
152 case SEQ_BALANCE:
153 /* convert -128:127 to 0:127 */
154 val = (char)q->e.p1;
155 val = (val + 128) / 2;
156 return set_control_event(dp, q->e.dev, SNDRV_SEQ_EVENT_CONTROLLER,
157 q->e.chn, CTL_PAN, val, ev);
158
159 case SEQ_CONTROLLER:
160 val = ((short)q->e.p3 << 8) | (short)q->e.p2;
161 switch (q->e.p1) {
162 case CTRL_PITCH_BENDER: /* SEQ1 V2 control */
163 /* -0x2000:0x1fff */
164 return set_control_event(dp, q->e.dev,
165 SNDRV_SEQ_EVENT_PITCHBEND,
166 q->e.chn, 0, val, ev);
167 case CTRL_PITCH_BENDER_RANGE:
168 /* conversion: 100/semitone -> 128/semitone */
169 return set_control_event(dp, q->e.dev,
170 SNDRV_SEQ_EVENT_REGPARAM,
171 q->e.chn, 0, val*128/100, ev);
172 default:
173 return set_control_event(dp, q->e.dev,
174 SNDRV_SEQ_EVENT_CONTROL14,
175 q->e.chn, q->e.p1, val, ev);
176 }
177
178 case SEQ_VOLMODE:
179 return snd_seq_oss_synth_raw_event(dp, q->e.dev, q->c, ev);
180
181 }
182 return -EINVAL;
183}
184
185/* channel voice events: mode1 and 2 */
186static int
187chn_voice_event(seq_oss_devinfo_t *dp, evrec_t *q, snd_seq_event_t *ev)
188{
189 if (q->v.chn >= 32)
190 return -EINVAL;
191 switch (q->v.cmd) {
192 case MIDI_NOTEON:
193 return note_on_event(dp, q->v.dev, q->v.chn, q->v.note, q->v.parm, ev);
194
195 case MIDI_NOTEOFF:
196 return note_off_event(dp, q->v.dev, q->v.chn, q->v.note, q->v.parm, ev);
197
198 case MIDI_KEY_PRESSURE:
199 return set_note_event(dp, q->v.dev, SNDRV_SEQ_EVENT_KEYPRESS,
200 q->v.chn, q->v.note, q->v.parm, ev);
201
202 }
203 return -EINVAL;
204}
205
206/* channel common events: mode1 and 2 */
207static int
208chn_common_event(seq_oss_devinfo_t *dp, evrec_t *q, snd_seq_event_t *ev)
209{
210 if (q->l.chn >= 32)
211 return -EINVAL;
212 switch (q->l.cmd) {
213 case MIDI_PGM_CHANGE:
214 return set_control_event(dp, q->l.dev, SNDRV_SEQ_EVENT_PGMCHANGE,
215 q->l.chn, 0, q->l.p1, ev);
216
217 case MIDI_CTL_CHANGE:
218 return set_control_event(dp, q->l.dev, SNDRV_SEQ_EVENT_CONTROLLER,
219 q->l.chn, q->l.p1, q->l.val, ev);
220
221 case MIDI_PITCH_BEND:
222 /* conversion: 0:0x3fff -> -0x2000:0x1fff */
223 return set_control_event(dp, q->l.dev, SNDRV_SEQ_EVENT_PITCHBEND,
224 q->l.chn, 0, q->l.val - 8192, ev);
225
226 case MIDI_CHN_PRESSURE:
227 return set_control_event(dp, q->l.dev, SNDRV_SEQ_EVENT_CHANPRESS,
228 q->l.chn, 0, q->l.val, ev);
229 }
230 return -EINVAL;
231}
232
233/* timer events: mode1 and mode2 */
234static int
235timing_event(seq_oss_devinfo_t *dp, evrec_t *q, snd_seq_event_t *ev)
236{
237 switch (q->t.cmd) {
238 case TMR_ECHO:
239 if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC)
240 return set_echo_event(dp, q, ev);
241 else {
242 evrec_t tmp;
243 memset(&tmp, 0, sizeof(tmp));
244 /* XXX: only for little-endian! */
245 tmp.echo = (q->t.time << 8) | SEQ_ECHO;
246 return set_echo_event(dp, &tmp, ev);
247 }
248
249 case TMR_STOP:
250 if (dp->seq_mode)
251 return snd_seq_oss_timer_stop(dp->timer);
252 return 0;
253
254 case TMR_CONTINUE:
255 if (dp->seq_mode)
256 return snd_seq_oss_timer_continue(dp->timer);
257 return 0;
258
259 case TMR_TEMPO:
260 if (dp->seq_mode)
261 return snd_seq_oss_timer_tempo(dp->timer, q->t.time);
262 return 0;
263 }
264
265 return -EINVAL;
266}
267
268/* local events: mode1 and 2 */
269static int
270local_event(seq_oss_devinfo_t *dp, evrec_t *q, snd_seq_event_t *ev)
271{
272 return -EINVAL;
273}
274
275/*
276 * process note-on event for OSS synth
277 * three different modes are available:
278 * - SNDRV_SEQ_OSS_PROCESS_EVENTS (for one-voice per channel mode)
279 * Accept note 255 as volume change.
280 * - SNDRV_SEQ_OSS_PASS_EVENTS
281 * Pass all events to lowlevel driver anyway
282 * - SNDRV_SEQ_OSS_PROCESS_KEYPRESS (mostly for Emu8000)
283 * Use key-pressure if note >= 128
284 */
285static int
286note_on_event(seq_oss_devinfo_t *dp, int dev, int ch, int note, int vel, snd_seq_event_t *ev)
287{
288 seq_oss_synthinfo_t *info = &dp->synths[dev];
289 switch (info->arg.event_passing) {
290 case SNDRV_SEQ_OSS_PROCESS_EVENTS:
291 if (! info->ch || ch < 0 || ch >= info->nr_voices) {
292 /* pass directly */
293 return set_note_event(dp, dev, SNDRV_SEQ_EVENT_NOTEON, ch, note, vel, ev);
294 }
295
296 if (note == 255 && info->ch[ch].note >= 0) {
297 /* volume control */
298 int type;
299 //if (! vel)
300 /* set volume to zero -- note off */
301 // type = SNDRV_SEQ_EVENT_NOTEOFF;
302 //else
303 if (info->ch[ch].vel)
304 /* sample already started -- volume change */
305 type = SNDRV_SEQ_EVENT_KEYPRESS;
306 else
307 /* sample not started -- start now */
308 type = SNDRV_SEQ_EVENT_NOTEON;
309 info->ch[ch].vel = vel;
310 return set_note_event(dp, dev, type, ch, info->ch[ch].note, vel, ev);
311 } else if (note >= 128)
312 return -EINVAL; /* invalid */
313
314 if (note != info->ch[ch].note && info->ch[ch].note >= 0)
315 /* note changed - note off at beginning */
316 set_note_event(dp, dev, SNDRV_SEQ_EVENT_NOTEOFF, ch, info->ch[ch].note, 0, ev);
317 /* set current status */
318 info->ch[ch].note = note;
319 info->ch[ch].vel = vel;
320 if (vel) /* non-zero velocity - start the note now */
321 return set_note_event(dp, dev, SNDRV_SEQ_EVENT_NOTEON, ch, note, vel, ev);
322 return -EINVAL;
323
324 case SNDRV_SEQ_OSS_PASS_EVENTS:
325 /* pass the event anyway */
326 return set_note_event(dp, dev, SNDRV_SEQ_EVENT_NOTEON, ch, note, vel, ev);
327
328 case SNDRV_SEQ_OSS_PROCESS_KEYPRESS:
329 if (note >= 128) /* key pressure: shifted by 128 */
330 return set_note_event(dp, dev, SNDRV_SEQ_EVENT_KEYPRESS, ch, note - 128, vel, ev);
331 else /* normal note-on event */
332 return set_note_event(dp, dev, SNDRV_SEQ_EVENT_NOTEON, ch, note, vel, ev);
333 }
334 return -EINVAL;
335}
336
337/*
338 * process note-off event for OSS synth
339 */
340static int
341note_off_event(seq_oss_devinfo_t *dp, int dev, int ch, int note, int vel, snd_seq_event_t *ev)
342{
343 seq_oss_synthinfo_t *info = &dp->synths[dev];
344 switch (info->arg.event_passing) {
345 case SNDRV_SEQ_OSS_PROCESS_EVENTS:
346 if (! info->ch || ch < 0 || ch >= info->nr_voices) {
347 /* pass directly */
348 return set_note_event(dp, dev, SNDRV_SEQ_EVENT_NOTEON, ch, note, vel, ev);
349 }
350
351 if (info->ch[ch].note >= 0) {
352 note = info->ch[ch].note;
353 info->ch[ch].vel = 0;
354 info->ch[ch].note = -1;
355 return set_note_event(dp, dev, SNDRV_SEQ_EVENT_NOTEOFF, ch, note, vel, ev);
356 }
357 return -EINVAL; /* invalid */
358
359 case SNDRV_SEQ_OSS_PASS_EVENTS:
360 case SNDRV_SEQ_OSS_PROCESS_KEYPRESS:
361 /* pass the event anyway */
362 return set_note_event(dp, dev, SNDRV_SEQ_EVENT_NOTEOFF, ch, note, vel, ev);
363
364 }
365 return -EINVAL;
366}
367
368/*
369 * create a note event
370 */
371static int
372set_note_event(seq_oss_devinfo_t *dp, int dev, int type, int ch, int note, int vel, snd_seq_event_t *ev)
373{
374 if (! snd_seq_oss_synth_is_valid(dp, dev))
375 return -ENXIO;
376
377 ev->type = type;
378 snd_seq_oss_synth_addr(dp, dev, ev);
379 ev->data.note.channel = ch;
380 ev->data.note.note = note;
381 ev->data.note.velocity = vel;
382
383 return 0;
384}
385
386/*
387 * create a control event
388 */
389static int
390set_control_event(seq_oss_devinfo_t *dp, int dev, int type, int ch, int param, int val, snd_seq_event_t *ev)
391{
392 if (! snd_seq_oss_synth_is_valid(dp, dev))
393 return -ENXIO;
394
395 ev->type = type;
396 snd_seq_oss_synth_addr(dp, dev, ev);
397 ev->data.control.channel = ch;
398 ev->data.control.param = param;
399 ev->data.control.value = val;
400
401 return 0;
402}
403
404/*
405 * create an echo event
406 */
407static int
408set_echo_event(seq_oss_devinfo_t *dp, evrec_t *rec, snd_seq_event_t *ev)
409{
410 ev->type = SNDRV_SEQ_EVENT_ECHO;
411 /* echo back to itself */
412 snd_seq_oss_fill_addr(dp, ev, dp->addr.client, dp->addr.port);
413 memcpy(&ev->data, rec, LONG_EVENT_SIZE);
414 return 0;
415}
416
417/*
418 * event input callback from ALSA sequencer:
419 * the echo event is processed here.
420 */
421int
422snd_seq_oss_event_input(snd_seq_event_t *ev, int direct, void *private_data,
423 int atomic, int hop)
424{
425 seq_oss_devinfo_t *dp = (seq_oss_devinfo_t *)private_data;
426 evrec_t *rec;
427
428 if (ev->type != SNDRV_SEQ_EVENT_ECHO)
429 return snd_seq_oss_midi_input(ev, direct, private_data);
430
431 if (ev->source.client != dp->cseq)
432 return 0; /* ignored */
433
434 rec = (evrec_t*)&ev->data;
435 if (rec->s.code == SEQ_SYNCTIMER) {
436 /* sync echo back */
437 snd_seq_oss_writeq_wakeup(dp->writeq, rec->t.time);
438
439 } else {
440 /* echo back event */
441 if (dp->readq == NULL)
442 return 0;
443 snd_seq_oss_readq_put_event(dp->readq, rec);
444 }
445 return 0;
446}
447
diff --git a/sound/core/seq/oss/seq_oss_event.h b/sound/core/seq/oss/seq_oss_event.h
new file mode 100644
index 000000000000..bf1d4d3f53c9
--- /dev/null
+++ b/sound/core/seq/oss/seq_oss_event.h
@@ -0,0 +1,112 @@
1/*
2 * OSS compatible sequencer driver
3 *
4 * seq_oss_event.h - OSS event queue record
5 *
6 * Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (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 General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 */
22
23#ifndef __SEQ_OSS_EVENT_H
24#define __SEQ_OSS_EVENT_H
25
26#include "seq_oss_device.h"
27
28#define SHORT_EVENT_SIZE 4
29#define LONG_EVENT_SIZE 8
30
31/* short event (4bytes) */
32typedef struct evrec_short_t {
33 unsigned char code;
34 unsigned char parm1;
35 unsigned char dev;
36 unsigned char parm2;
37} evrec_short_t;
38
39/* short note events (4bytes) */
40typedef struct evrec_note_t {
41 unsigned char code;
42 unsigned char chn;
43 unsigned char note;
44 unsigned char vel;
45} evrec_note_t;
46
47/* long timer events (8bytes) */
48typedef struct evrec_timer_t {
49 unsigned char code;
50 unsigned char cmd;
51 unsigned char dummy1, dummy2;
52 unsigned int time;
53} evrec_timer_t;
54
55/* long extended events (8bytes) */
56typedef struct evrec_extended_t {
57 unsigned char code;
58 unsigned char cmd;
59 unsigned char dev;
60 unsigned char chn;
61 unsigned char p1, p2, p3, p4;
62} evrec_extended_t;
63
64/* long channel events (8bytes) */
65typedef struct evrec_long_t {
66 unsigned char code;
67 unsigned char dev;
68 unsigned char cmd;
69 unsigned char chn;
70 unsigned char p1, p2;
71 unsigned short val;
72} evrec_long_t;
73
74/* channel voice events (8bytes) */
75typedef struct evrec_voice_t {
76 unsigned char code;
77 unsigned char dev;
78 unsigned char cmd;
79 unsigned char chn;
80 unsigned char note, parm;
81 unsigned short dummy;
82} evrec_voice_t;
83
84/* sysex events (8bytes) */
85typedef struct evrec_sysex_t {
86 unsigned char code;
87 unsigned char dev;
88 unsigned char buf[6];
89} evrec_sysex_t;
90
91/* event record */
92union evrec_t {
93 evrec_short_t s;
94 evrec_note_t n;
95 evrec_long_t l;
96 evrec_voice_t v;
97 evrec_timer_t t;
98 evrec_extended_t e;
99 evrec_sysex_t x;
100 unsigned int echo;
101 unsigned char c[LONG_EVENT_SIZE];
102};
103
104#define ev_is_long(ev) ((ev)->s.code >= 128)
105#define ev_length(ev) ((ev)->s.code >= 128 ? LONG_EVENT_SIZE : SHORT_EVENT_SIZE)
106
107int snd_seq_oss_process_event(seq_oss_devinfo_t *dp, evrec_t *q, snd_seq_event_t *ev);
108int snd_seq_oss_process_timer_event(seq_oss_timer_t *rec, evrec_t *q);
109int snd_seq_oss_event_input(snd_seq_event_t *ev, int direct, void *private_data, int atomic, int hop);
110
111
112#endif /* __SEQ_OSS_EVENT_H */
diff --git a/sound/core/seq/oss/seq_oss_init.c b/sound/core/seq/oss/seq_oss_init.c
new file mode 100644
index 000000000000..bac4b4f1a94e
--- /dev/null
+++ b/sound/core/seq/oss/seq_oss_init.c
@@ -0,0 +1,555 @@
1/*
2 * OSS compatible sequencer driver
3 *
4 * open/close and reset interface
5 *
6 * Copyright (C) 1998-1999 Takashi Iwai <tiwai@suse.de>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (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 General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 */
22
23#include "seq_oss_device.h"
24#include "seq_oss_synth.h"
25#include "seq_oss_midi.h"
26#include "seq_oss_writeq.h"
27#include "seq_oss_readq.h"
28#include "seq_oss_timer.h"
29#include "seq_oss_event.h"
30#include <linux/init.h>
31#include <linux/moduleparam.h>
32
33/*
34 * common variables
35 */
36static int maxqlen = SNDRV_SEQ_OSS_MAX_QLEN;
37module_param(maxqlen, int, 0444);
38MODULE_PARM_DESC(maxqlen, "maximum queue length");
39
40static int system_client = -1; /* ALSA sequencer client number */
41static int system_port = -1;
42
43static int num_clients;
44static seq_oss_devinfo_t *client_table[SNDRV_SEQ_OSS_MAX_CLIENTS];
45
46
47/*
48 * prototypes
49 */
50static int receive_announce(snd_seq_event_t *ev, int direct, void *private, int atomic, int hop);
51static int translate_mode(struct file *file);
52static int create_port(seq_oss_devinfo_t *dp);
53static int delete_port(seq_oss_devinfo_t *dp);
54static int alloc_seq_queue(seq_oss_devinfo_t *dp);
55static int delete_seq_queue(int queue);
56static void free_devinfo(void *private);
57
58#define call_ctl(type,rec) snd_seq_kernel_client_ctl(system_client, type, rec)
59
60
61/*
62 * create sequencer client for OSS sequencer
63 */
64int __init
65snd_seq_oss_create_client(void)
66{
67 int rc;
68 snd_seq_client_callback_t callback;
69 snd_seq_client_info_t *info;
70 snd_seq_port_info_t *port;
71 snd_seq_port_callback_t port_callback;
72
73 info = kmalloc(sizeof(*info), GFP_KERNEL);
74 port = kmalloc(sizeof(*port), GFP_KERNEL);
75 if (!info || !port) {
76 rc = -ENOMEM;
77 goto __error;
78 }
79
80 /* create ALSA client */
81 memset(&callback, 0, sizeof(callback));
82
83 callback.private_data = NULL;
84 callback.allow_input = 1;
85 callback.allow_output = 1;
86
87 rc = snd_seq_create_kernel_client(NULL, SNDRV_SEQ_CLIENT_OSS, &callback);
88 if (rc < 0)
89 goto __error;
90
91 system_client = rc;
92 debug_printk(("new client = %d\n", rc));
93
94 /* set client information */
95 memset(info, 0, sizeof(*info));
96 info->client = system_client;
97 info->type = KERNEL_CLIENT;
98 strcpy(info->name, "OSS sequencer");
99
100 rc = call_ctl(SNDRV_SEQ_IOCTL_SET_CLIENT_INFO, info);
101
102 /* look up midi devices */
103 snd_seq_oss_midi_lookup_ports(system_client);
104
105 /* create annoucement receiver port */
106 memset(port, 0, sizeof(*port));
107 strcpy(port->name, "Receiver");
108 port->addr.client = system_client;
109 port->capability = SNDRV_SEQ_PORT_CAP_WRITE; /* receive only */
110 port->type = 0;
111
112 memset(&port_callback, 0, sizeof(port_callback));
113 /* don't set port_callback.owner here. otherwise the module counter
114 * is incremented and we can no longer release the module..
115 */
116 port_callback.event_input = receive_announce;
117 port->kernel = &port_callback;
118
119 call_ctl(SNDRV_SEQ_IOCTL_CREATE_PORT, port);
120 if ((system_port = port->addr.port) >= 0) {
121 snd_seq_port_subscribe_t subs;
122
123 memset(&subs, 0, sizeof(subs));
124 subs.sender.client = SNDRV_SEQ_CLIENT_SYSTEM;
125 subs.sender.port = SNDRV_SEQ_PORT_SYSTEM_ANNOUNCE;
126 subs.dest.client = system_client;
127 subs.dest.port = system_port;
128 call_ctl(SNDRV_SEQ_IOCTL_SUBSCRIBE_PORT, &subs);
129 }
130 rc = 0;
131
132 __error:
133 kfree(port);
134 kfree(info);
135 return rc;
136}
137
138
139/*
140 * receive annoucement from system port, and check the midi device
141 */
142static int
143receive_announce(snd_seq_event_t *ev, int direct, void *private, int atomic, int hop)
144{
145 snd_seq_port_info_t pinfo;
146
147 if (atomic)
148 return 0; /* it must not happen */
149
150 switch (ev->type) {
151 case SNDRV_SEQ_EVENT_PORT_START:
152 case SNDRV_SEQ_EVENT_PORT_CHANGE:
153 if (ev->data.addr.client == system_client)
154 break; /* ignore myself */
155 memset(&pinfo, 0, sizeof(pinfo));
156 pinfo.addr = ev->data.addr;
157 if (call_ctl(SNDRV_SEQ_IOCTL_GET_PORT_INFO, &pinfo) >= 0)
158 snd_seq_oss_midi_check_new_port(&pinfo);
159 break;
160
161 case SNDRV_SEQ_EVENT_PORT_EXIT:
162 if (ev->data.addr.client == system_client)
163 break; /* ignore myself */
164 snd_seq_oss_midi_check_exit_port(ev->data.addr.client,
165 ev->data.addr.port);
166 break;
167 }
168 return 0;
169}
170
171
172/*
173 * delete OSS sequencer client
174 */
175int
176snd_seq_oss_delete_client(void)
177{
178 if (system_client >= 0)
179 snd_seq_delete_kernel_client(system_client);
180
181 snd_seq_oss_midi_clear_all();
182
183 return 0;
184}
185
186
187/*
188 * open sequencer device
189 */
190int
191snd_seq_oss_open(struct file *file, int level)
192{
193 int i, rc;
194 seq_oss_devinfo_t *dp;
195
196 if ((dp = kcalloc(1, sizeof(*dp), GFP_KERNEL)) == NULL) {
197 snd_printk(KERN_ERR "can't malloc device info\n");
198 return -ENOMEM;
199 }
200 debug_printk(("oss_open: dp = %p\n", dp));
201
202 for (i = 0; i < SNDRV_SEQ_OSS_MAX_CLIENTS; i++) {
203 if (client_table[i] == NULL)
204 break;
205 }
206 if (i >= SNDRV_SEQ_OSS_MAX_CLIENTS) {
207 snd_printk(KERN_ERR "too many applications\n");
208 kfree(dp);
209 return -ENOMEM;
210 }
211
212 dp->index = i;
213 dp->cseq = system_client;
214 dp->port = -1;
215 dp->queue = -1;
216 dp->readq = NULL;
217 dp->writeq = NULL;
218
219 /* look up synth and midi devices */
220 snd_seq_oss_synth_setup(dp);
221 snd_seq_oss_midi_setup(dp);
222
223 if (dp->synth_opened == 0 && dp->max_mididev == 0) {
224 /* snd_printk(KERN_ERR "no device found\n"); */
225 rc = -ENODEV;
226 goto _error;
227 }
228
229 /* create port */
230 debug_printk(("create new port\n"));
231 if ((rc = create_port(dp)) < 0) {
232 snd_printk(KERN_ERR "can't create port\n");
233 goto _error;
234 }
235
236 /* allocate queue */
237 debug_printk(("allocate queue\n"));
238 if ((rc = alloc_seq_queue(dp)) < 0)
239 goto _error;
240
241 /* set address */
242 dp->addr.client = dp->cseq;
243 dp->addr.port = dp->port;
244 /*dp->addr.queue = dp->queue;*/
245 /*dp->addr.channel = 0;*/
246
247 dp->seq_mode = level;
248
249 /* set up file mode */
250 dp->file_mode = translate_mode(file);
251
252 /* initialize read queue */
253 debug_printk(("initialize read queue\n"));
254 if (is_read_mode(dp->file_mode)) {
255 if ((dp->readq = snd_seq_oss_readq_new(dp, maxqlen)) == NULL) {
256 rc = -ENOMEM;
257 goto _error;
258 }
259 }
260
261 /* initialize write queue */
262 debug_printk(("initialize write queue\n"));
263 if (is_write_mode(dp->file_mode)) {
264 dp->writeq = snd_seq_oss_writeq_new(dp, maxqlen);
265 if (dp->writeq == NULL) {
266 rc = -ENOMEM;
267 goto _error;
268 }
269 }
270
271 /* initialize timer */
272 debug_printk(("initialize timer\n"));
273 if ((dp->timer = snd_seq_oss_timer_new(dp)) == NULL) {
274 snd_printk(KERN_ERR "can't alloc timer\n");
275 rc = -ENOMEM;
276 goto _error;
277 }
278 debug_printk(("timer initialized\n"));
279
280 /* set private data pointer */
281 file->private_data = dp;
282
283 /* set up for mode2 */
284 if (level == SNDRV_SEQ_OSS_MODE_MUSIC)
285 snd_seq_oss_synth_setup_midi(dp);
286 else if (is_read_mode(dp->file_mode))
287 snd_seq_oss_midi_open_all(dp, SNDRV_SEQ_OSS_FILE_READ);
288
289 client_table[dp->index] = dp;
290 num_clients++;
291
292 debug_printk(("open done\n"));
293 return 0;
294
295 _error:
296 snd_seq_oss_synth_cleanup(dp);
297 snd_seq_oss_midi_cleanup(dp);
298 i = dp->queue;
299 delete_port(dp);
300 delete_seq_queue(i);
301
302 return rc;
303}
304
305/*
306 * translate file flags to private mode
307 */
308static int
309translate_mode(struct file *file)
310{
311 int file_mode = 0;
312 if ((file->f_flags & O_ACCMODE) != O_RDONLY)
313 file_mode |= SNDRV_SEQ_OSS_FILE_WRITE;
314 if ((file->f_flags & O_ACCMODE) != O_WRONLY)
315 file_mode |= SNDRV_SEQ_OSS_FILE_READ;
316 if (file->f_flags & O_NONBLOCK)
317 file_mode |= SNDRV_SEQ_OSS_FILE_NONBLOCK;
318 return file_mode;
319}
320
321
322/*
323 * create sequencer port
324 */
325static int
326create_port(seq_oss_devinfo_t *dp)
327{
328 int rc;
329 snd_seq_port_info_t port;
330 snd_seq_port_callback_t callback;
331
332 memset(&port, 0, sizeof(port));
333 port.addr.client = dp->cseq;
334 sprintf(port.name, "Sequencer-%d", dp->index);
335 port.capability = SNDRV_SEQ_PORT_CAP_READ|SNDRV_SEQ_PORT_CAP_WRITE; /* no subscription */
336 port.type = SNDRV_SEQ_PORT_TYPE_SPECIFIC;
337 port.midi_channels = 128;
338 port.synth_voices = 128;
339
340 memset(&callback, 0, sizeof(callback));
341 callback.owner = THIS_MODULE;
342 callback.private_data = dp;
343 callback.event_input = snd_seq_oss_event_input;
344 callback.private_free = free_devinfo;
345 port.kernel = &callback;
346
347 rc = call_ctl(SNDRV_SEQ_IOCTL_CREATE_PORT, &port);
348 if (rc < 0)
349 return rc;
350
351 dp->port = port.addr.port;
352 debug_printk(("new port = %d\n", port.addr.port));
353
354 return 0;
355}
356
357/*
358 * delete ALSA port
359 */
360static int
361delete_port(seq_oss_devinfo_t *dp)
362{
363 if (dp->port < 0)
364 return 0;
365
366 debug_printk(("delete_port %i\n", dp->port));
367 return snd_seq_event_port_detach(dp->cseq, dp->port);
368}
369
370/*
371 * allocate a queue
372 */
373static int
374alloc_seq_queue(seq_oss_devinfo_t *dp)
375{
376 snd_seq_queue_info_t qinfo;
377 int rc;
378
379 memset(&qinfo, 0, sizeof(qinfo));
380 qinfo.owner = system_client;
381 qinfo.locked = 1;
382 strcpy(qinfo.name, "OSS Sequencer Emulation");
383 if ((rc = call_ctl(SNDRV_SEQ_IOCTL_CREATE_QUEUE, &qinfo)) < 0)
384 return rc;
385 dp->queue = qinfo.queue;
386 return 0;
387}
388
389/*
390 * release queue
391 */
392static int
393delete_seq_queue(int queue)
394{
395 snd_seq_queue_info_t qinfo;
396 int rc;
397
398 if (queue < 0)
399 return 0;
400 memset(&qinfo, 0, sizeof(qinfo));
401 qinfo.queue = queue;
402 rc = call_ctl(SNDRV_SEQ_IOCTL_DELETE_QUEUE, &qinfo);
403 if (rc < 0)
404 printk(KERN_ERR "seq-oss: unable to delete queue %d (%d)\n", queue, rc);
405 return rc;
406}
407
408
409/*
410 * free device informations - private_free callback of port
411 */
412static void
413free_devinfo(void *private)
414{
415 seq_oss_devinfo_t *dp = (seq_oss_devinfo_t *)private;
416
417 if (dp->timer)
418 snd_seq_oss_timer_delete(dp->timer);
419
420 if (dp->writeq)
421 snd_seq_oss_writeq_delete(dp->writeq);
422
423 if (dp->readq)
424 snd_seq_oss_readq_delete(dp->readq);
425
426 kfree(dp);
427}
428
429
430/*
431 * close sequencer device
432 */
433void
434snd_seq_oss_release(seq_oss_devinfo_t *dp)
435{
436 int queue;
437
438 client_table[dp->index] = NULL;
439 num_clients--;
440
441 debug_printk(("resetting..\n"));
442 snd_seq_oss_reset(dp);
443
444 debug_printk(("cleaning up..\n"));
445 snd_seq_oss_synth_cleanup(dp);
446 snd_seq_oss_midi_cleanup(dp);
447
448 /* clear slot */
449 debug_printk(("releasing resource..\n"));
450 queue = dp->queue;
451 if (dp->port >= 0)
452 delete_port(dp);
453 delete_seq_queue(queue);
454
455 debug_printk(("release done\n"));
456}
457
458
459/*
460 * Wait until the queue is empty (if we don't have nonblock)
461 */
462void
463snd_seq_oss_drain_write(seq_oss_devinfo_t *dp)
464{
465 if (! dp->timer->running)
466 return;
467 if (is_write_mode(dp->file_mode) && !is_nonblock_mode(dp->file_mode) &&
468 dp->writeq) {
469 debug_printk(("syncing..\n"));
470 while (snd_seq_oss_writeq_sync(dp->writeq))
471 ;
472 }
473}
474
475
476/*
477 * reset sequencer devices
478 */
479void
480snd_seq_oss_reset(seq_oss_devinfo_t *dp)
481{
482 int i;
483
484 /* reset all synth devices */
485 for (i = 0; i < dp->max_synthdev; i++)
486 snd_seq_oss_synth_reset(dp, i);
487
488 /* reset all midi devices */
489 if (dp->seq_mode != SNDRV_SEQ_OSS_MODE_MUSIC) {
490 for (i = 0; i < dp->max_mididev; i++)
491 snd_seq_oss_midi_reset(dp, i);
492 }
493
494 /* remove queues */
495 if (dp->readq)
496 snd_seq_oss_readq_clear(dp->readq);
497 if (dp->writeq)
498 snd_seq_oss_writeq_clear(dp->writeq);
499
500 /* reset timer */
501 snd_seq_oss_timer_stop(dp->timer);
502}
503
504
505/*
506 * misc. functions for proc interface
507 */
508char *
509enabled_str(int bool)
510{
511 return bool ? "enabled" : "disabled";
512}
513
514static char *
515filemode_str(int val)
516{
517 static char *str[] = {
518 "none", "read", "write", "read/write",
519 };
520 return str[val & SNDRV_SEQ_OSS_FILE_ACMODE];
521}
522
523
524/*
525 * proc interface
526 */
527void
528snd_seq_oss_system_info_read(snd_info_buffer_t *buf)
529{
530 int i;
531 seq_oss_devinfo_t *dp;
532
533 snd_iprintf(buf, "ALSA client number %d\n", system_client);
534 snd_iprintf(buf, "ALSA receiver port %d\n", system_port);
535
536 snd_iprintf(buf, "\nNumber of applications: %d\n", num_clients);
537 for (i = 0; i < num_clients; i++) {
538 snd_iprintf(buf, "\nApplication %d: ", i);
539 if ((dp = client_table[i]) == NULL) {
540 snd_iprintf(buf, "*empty*\n");
541 continue;
542 }
543 snd_iprintf(buf, "port %d : queue %d\n", dp->port, dp->queue);
544 snd_iprintf(buf, " sequencer mode = %s : file open mode = %s\n",
545 (dp->seq_mode ? "music" : "synth"),
546 filemode_str(dp->file_mode));
547 if (dp->seq_mode)
548 snd_iprintf(buf, " timer tempo = %d, timebase = %d\n",
549 dp->timer->oss_tempo, dp->timer->oss_timebase);
550 snd_iprintf(buf, " max queue length %d\n", maxqlen);
551 if (is_read_mode(dp->file_mode) && dp->readq)
552 snd_seq_oss_readq_info_read(dp->readq, buf);
553 }
554}
555
diff --git a/sound/core/seq/oss/seq_oss_ioctl.c b/sound/core/seq/oss/seq_oss_ioctl.c
new file mode 100644
index 000000000000..e86f18d00f39
--- /dev/null
+++ b/sound/core/seq/oss/seq_oss_ioctl.c
@@ -0,0 +1,209 @@
1/*
2 * OSS compatible sequencer driver
3 *
4 * OSS compatible i/o control
5 *
6 * Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (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 General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 */
22
23#include "seq_oss_device.h"
24#include "seq_oss_readq.h"
25#include "seq_oss_writeq.h"
26#include "seq_oss_timer.h"
27#include "seq_oss_synth.h"
28#include "seq_oss_midi.h"
29#include "seq_oss_event.h"
30
31static int snd_seq_oss_synth_info_user(seq_oss_devinfo_t *dp, void __user *arg)
32{
33 struct synth_info info;
34
35 if (copy_from_user(&info, arg, sizeof(info)))
36 return -EFAULT;
37 if (snd_seq_oss_synth_make_info(dp, info.device, &info) < 0)
38 return -EINVAL;
39 if (copy_to_user(arg, &info, sizeof(info)))
40 return -EFAULT;
41 return 0;
42}
43
44static int snd_seq_oss_midi_info_user(seq_oss_devinfo_t *dp, void __user *arg)
45{
46 struct midi_info info;
47
48 if (copy_from_user(&info, arg, sizeof(info)))
49 return -EFAULT;
50 if (snd_seq_oss_midi_make_info(dp, info.device, &info) < 0)
51 return -EINVAL;
52 if (copy_to_user(arg, &info, sizeof(info)))
53 return -EFAULT;
54 return 0;
55}
56
57static int snd_seq_oss_oob_user(seq_oss_devinfo_t *dp, void __user *arg)
58{
59 unsigned char ev[8];
60 snd_seq_event_t tmpev;
61
62 if (copy_from_user(ev, arg, 8))
63 return -EFAULT;
64 memset(&tmpev, 0, sizeof(tmpev));
65 snd_seq_oss_fill_addr(dp, &tmpev, dp->addr.port, dp->addr.client);
66 tmpev.time.tick = 0;
67 if (! snd_seq_oss_process_event(dp, (evrec_t*)ev, &tmpev)) {
68 snd_seq_oss_dispatch(dp, &tmpev, 0, 0);
69 }
70 return 0;
71}
72
73int
74snd_seq_oss_ioctl(seq_oss_devinfo_t *dp, unsigned int cmd, unsigned long carg)
75{
76 int dev, val;
77 void __user *arg = (void __user *)carg;
78 int __user *p = arg;
79
80 switch (cmd) {
81 case SNDCTL_TMR_TIMEBASE:
82 case SNDCTL_TMR_TEMPO:
83 case SNDCTL_TMR_START:
84 case SNDCTL_TMR_STOP:
85 case SNDCTL_TMR_CONTINUE:
86 case SNDCTL_TMR_METRONOME:
87 case SNDCTL_TMR_SOURCE:
88 case SNDCTL_TMR_SELECT:
89 case SNDCTL_SEQ_CTRLRATE:
90 return snd_seq_oss_timer_ioctl(dp->timer, cmd, arg);
91
92 case SNDCTL_SEQ_PANIC:
93 debug_printk(("panic\n"));
94 snd_seq_oss_reset(dp);
95 return -EINVAL;
96
97 case SNDCTL_SEQ_SYNC:
98 debug_printk(("sync\n"));
99 if (! is_write_mode(dp->file_mode) || dp->writeq == NULL)
100 return 0;
101 while (snd_seq_oss_writeq_sync(dp->writeq))
102 ;
103 if (signal_pending(current))
104 return -ERESTARTSYS;
105 return 0;
106
107 case SNDCTL_SEQ_RESET:
108 debug_printk(("reset\n"));
109 snd_seq_oss_reset(dp);
110 return 0;
111
112 case SNDCTL_SEQ_TESTMIDI:
113 debug_printk(("test midi\n"));
114 if (get_user(dev, p))
115 return -EFAULT;
116 return snd_seq_oss_midi_open(dp, dev, dp->file_mode);
117
118 case SNDCTL_SEQ_GETINCOUNT:
119 debug_printk(("get in count\n"));
120 if (dp->readq == NULL || ! is_read_mode(dp->file_mode))
121 return 0;
122 return put_user(dp->readq->qlen, p) ? -EFAULT : 0;
123
124 case SNDCTL_SEQ_GETOUTCOUNT:
125 debug_printk(("get out count\n"));
126 if (! is_write_mode(dp->file_mode) || dp->writeq == NULL)
127 return 0;
128 return put_user(snd_seq_oss_writeq_get_free_size(dp->writeq), p) ? -EFAULT : 0;
129
130 case SNDCTL_SEQ_GETTIME:
131 debug_printk(("get time\n"));
132 return put_user(snd_seq_oss_timer_cur_tick(dp->timer), p) ? -EFAULT : 0;
133
134 case SNDCTL_SEQ_RESETSAMPLES:
135 debug_printk(("reset samples\n"));
136 if (get_user(dev, p))
137 return -EFAULT;
138 return snd_seq_oss_synth_ioctl(dp, dev, cmd, carg);
139
140 case SNDCTL_SEQ_NRSYNTHS:
141 debug_printk(("nr synths\n"));
142 return put_user(dp->max_synthdev, p) ? -EFAULT : 0;
143
144 case SNDCTL_SEQ_NRMIDIS:
145 debug_printk(("nr midis\n"));
146 return put_user(dp->max_mididev, p) ? -EFAULT : 0;
147
148 case SNDCTL_SYNTH_MEMAVL:
149 debug_printk(("mem avail\n"));
150 if (get_user(dev, p))
151 return -EFAULT;
152 val = snd_seq_oss_synth_ioctl(dp, dev, cmd, carg);
153 return put_user(val, p) ? -EFAULT : 0;
154
155 case SNDCTL_FM_4OP_ENABLE:
156 debug_printk(("4op\n"));
157 if (get_user(dev, p))
158 return -EFAULT;
159 snd_seq_oss_synth_ioctl(dp, dev, cmd, carg);
160 return 0;
161
162 case SNDCTL_SYNTH_INFO:
163 case SNDCTL_SYNTH_ID:
164 debug_printk(("synth info\n"));
165 return snd_seq_oss_synth_info_user(dp, arg);
166
167 case SNDCTL_SEQ_OUTOFBAND:
168 debug_printk(("out of band\n"));
169 return snd_seq_oss_oob_user(dp, arg);
170
171 case SNDCTL_MIDI_INFO:
172 debug_printk(("midi info\n"));
173 return snd_seq_oss_midi_info_user(dp, arg);
174
175 case SNDCTL_SEQ_THRESHOLD:
176 debug_printk(("threshold\n"));
177 if (! is_write_mode(dp->file_mode))
178 return 0;
179 if (get_user(val, p))
180 return -EFAULT;
181 if (val < 1)
182 val = 1;
183 if (val >= dp->writeq->maxlen)
184 val = dp->writeq->maxlen - 1;
185 snd_seq_oss_writeq_set_output(dp->writeq, val);
186 return 0;
187
188 case SNDCTL_MIDI_PRETIME:
189 debug_printk(("pretime\n"));
190 if (dp->readq == NULL || !is_read_mode(dp->file_mode))
191 return 0;
192 if (get_user(val, p))
193 return -EFAULT;
194 if (val <= 0)
195 val = -1;
196 else
197 val = (HZ * val) / 10;
198 dp->readq->pre_event_timeout = val;
199 return put_user(val, p) ? -EFAULT : 0;
200
201 default:
202 debug_printk(("others\n"));
203 if (! is_write_mode(dp->file_mode))
204 return -EIO;
205 return snd_seq_oss_synth_ioctl(dp, 0, cmd, carg);
206 }
207 return 0;
208}
209
diff --git a/sound/core/seq/oss/seq_oss_midi.c b/sound/core/seq/oss/seq_oss_midi.c
new file mode 100644
index 000000000000..9aece6c65dbc
--- /dev/null
+++ b/sound/core/seq/oss/seq_oss_midi.c
@@ -0,0 +1,710 @@
1/*
2 * OSS compatible sequencer driver
3 *
4 * MIDI device handlers
5 *
6 * Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (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 General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 */
22
23#include "seq_oss_midi.h"
24#include "seq_oss_readq.h"
25#include "seq_oss_timer.h"
26#include "seq_oss_event.h"
27#include <sound/seq_midi_event.h>
28#include "../seq_lock.h"
29#include <linux/init.h>
30
31
32/*
33 * constants
34 */
35#define SNDRV_SEQ_OSS_MAX_MIDI_NAME 30
36
37/*
38 * definition of midi device record
39 */
40struct seq_oss_midi_t {
41 int seq_device; /* device number */
42 int client; /* sequencer client number */
43 int port; /* sequencer port number */
44 unsigned int flags; /* port capability */
45 int opened; /* flag for opening */
46 unsigned char name[SNDRV_SEQ_OSS_MAX_MIDI_NAME];
47 snd_midi_event_t *coder; /* MIDI event coder */
48 seq_oss_devinfo_t *devinfo; /* assigned OSSseq device */
49 snd_use_lock_t use_lock;
50};
51
52
53/*
54 * midi device table
55 */
56static int max_midi_devs;
57static seq_oss_midi_t *midi_devs[SNDRV_SEQ_OSS_MAX_MIDI_DEVS];
58
59static DEFINE_SPINLOCK(register_lock);
60
61/*
62 * prototypes
63 */
64static seq_oss_midi_t *get_mdev(int dev);
65static seq_oss_midi_t *get_mididev(seq_oss_devinfo_t *dp, int dev);
66static int send_synth_event(seq_oss_devinfo_t *dp, snd_seq_event_t *ev, int dev);
67static int send_midi_event(seq_oss_devinfo_t *dp, snd_seq_event_t *ev, seq_oss_midi_t *mdev);
68
69/*
70 * look up the existing ports
71 * this looks a very exhausting job.
72 */
73int __init
74snd_seq_oss_midi_lookup_ports(int client)
75{
76 snd_seq_client_info_t *clinfo;
77 snd_seq_port_info_t *pinfo;
78
79 clinfo = kcalloc(1, sizeof(*clinfo), GFP_KERNEL);
80 pinfo = kcalloc(1, sizeof(*pinfo), GFP_KERNEL);
81 if (! clinfo || ! pinfo) {
82 kfree(clinfo);
83 kfree(pinfo);
84 return -ENOMEM;
85 }
86 clinfo->client = -1;
87 while (snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_QUERY_NEXT_CLIENT, clinfo) == 0) {
88 if (clinfo->client == client)
89 continue; /* ignore myself */
90 pinfo->addr.client = clinfo->client;
91 pinfo->addr.port = -1;
92 while (snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_QUERY_NEXT_PORT, pinfo) == 0)
93 snd_seq_oss_midi_check_new_port(pinfo);
94 }
95 kfree(clinfo);
96 kfree(pinfo);
97 return 0;
98}
99
100
101/*
102 */
103static seq_oss_midi_t *
104get_mdev(int dev)
105{
106 seq_oss_midi_t *mdev;
107 unsigned long flags;
108
109 spin_lock_irqsave(&register_lock, flags);
110 mdev = midi_devs[dev];
111 if (mdev)
112 snd_use_lock_use(&mdev->use_lock);
113 spin_unlock_irqrestore(&register_lock, flags);
114 return mdev;
115}
116
117/*
118 * look for the identical slot
119 */
120static seq_oss_midi_t *
121find_slot(int client, int port)
122{
123 int i;
124 seq_oss_midi_t *mdev;
125 unsigned long flags;
126
127 spin_lock_irqsave(&register_lock, flags);
128 for (i = 0; i < max_midi_devs; i++) {
129 mdev = midi_devs[i];
130 if (mdev && mdev->client == client && mdev->port == port) {
131 /* found! */
132 snd_use_lock_use(&mdev->use_lock);
133 spin_unlock_irqrestore(&register_lock, flags);
134 return mdev;
135 }
136 }
137 spin_unlock_irqrestore(&register_lock, flags);
138 return NULL;
139}
140
141
142#define PERM_WRITE (SNDRV_SEQ_PORT_CAP_WRITE|SNDRV_SEQ_PORT_CAP_SUBS_WRITE)
143#define PERM_READ (SNDRV_SEQ_PORT_CAP_READ|SNDRV_SEQ_PORT_CAP_SUBS_READ)
144/*
145 * register a new port if it doesn't exist yet
146 */
147int
148snd_seq_oss_midi_check_new_port(snd_seq_port_info_t *pinfo)
149{
150 int i;
151 seq_oss_midi_t *mdev;
152 unsigned long flags;
153
154 debug_printk(("check for MIDI client %d port %d\n", pinfo->addr.client, pinfo->addr.port));
155 /* the port must include generic midi */
156 if (! (pinfo->type & SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC))
157 return 0;
158 /* either read or write subscribable */
159 if ((pinfo->capability & PERM_WRITE) != PERM_WRITE &&
160 (pinfo->capability & PERM_READ) != PERM_READ)
161 return 0;
162
163 /*
164 * look for the identical slot
165 */
166 if ((mdev = find_slot(pinfo->addr.client, pinfo->addr.port)) != NULL) {
167 /* already exists */
168 snd_use_lock_free(&mdev->use_lock);
169 return 0;
170 }
171
172 /*
173 * allocate midi info record
174 */
175 if ((mdev = kcalloc(1, sizeof(*mdev), GFP_KERNEL)) == NULL) {
176 snd_printk(KERN_ERR "can't malloc midi info\n");
177 return -ENOMEM;
178 }
179
180 /* copy the port information */
181 mdev->client = pinfo->addr.client;
182 mdev->port = pinfo->addr.port;
183 mdev->flags = pinfo->capability;
184 mdev->opened = 0;
185 snd_use_lock_init(&mdev->use_lock);
186
187 /* copy and truncate the name of synth device */
188 strlcpy(mdev->name, pinfo->name, sizeof(mdev->name));
189
190 /* create MIDI coder */
191 if (snd_midi_event_new(MAX_MIDI_EVENT_BUF, &mdev->coder) < 0) {
192 snd_printk(KERN_ERR "can't malloc midi coder\n");
193 kfree(mdev);
194 return -ENOMEM;
195 }
196 /* OSS sequencer adds running status to all sequences */
197 snd_midi_event_no_status(mdev->coder, 1);
198
199 /*
200 * look for en empty slot
201 */
202 spin_lock_irqsave(&register_lock, flags);
203 for (i = 0; i < max_midi_devs; i++) {
204 if (midi_devs[i] == NULL)
205 break;
206 }
207 if (i >= max_midi_devs) {
208 if (max_midi_devs >= SNDRV_SEQ_OSS_MAX_MIDI_DEVS) {
209 spin_unlock_irqrestore(&register_lock, flags);
210 snd_midi_event_free(mdev->coder);
211 kfree(mdev);
212 return -ENOMEM;
213 }
214 max_midi_devs++;
215 }
216 mdev->seq_device = i;
217 midi_devs[mdev->seq_device] = mdev;
218 spin_unlock_irqrestore(&register_lock, flags);
219
220 return 0;
221}
222
223/*
224 * release the midi device if it was registered
225 */
226int
227snd_seq_oss_midi_check_exit_port(int client, int port)
228{
229 seq_oss_midi_t *mdev;
230 unsigned long flags;
231 int index;
232
233 if ((mdev = find_slot(client, port)) != NULL) {
234 spin_lock_irqsave(&register_lock, flags);
235 midi_devs[mdev->seq_device] = NULL;
236 spin_unlock_irqrestore(&register_lock, flags);
237 snd_use_lock_free(&mdev->use_lock);
238 snd_use_lock_sync(&mdev->use_lock);
239 if (mdev->coder)
240 snd_midi_event_free(mdev->coder);
241 kfree(mdev);
242 }
243 spin_lock_irqsave(&register_lock, flags);
244 for (index = max_midi_devs - 1; index >= 0; index--) {
245 if (midi_devs[index])
246 break;
247 }
248 max_midi_devs = index + 1;
249 spin_unlock_irqrestore(&register_lock, flags);
250 return 0;
251}
252
253
254/*
255 * release the midi device if it was registered
256 */
257void
258snd_seq_oss_midi_clear_all(void)
259{
260 int i;
261 seq_oss_midi_t *mdev;
262 unsigned long flags;
263
264 spin_lock_irqsave(&register_lock, flags);
265 for (i = 0; i < max_midi_devs; i++) {
266 if ((mdev = midi_devs[i]) != NULL) {
267 if (mdev->coder)
268 snd_midi_event_free(mdev->coder);
269 kfree(mdev);
270 midi_devs[i] = NULL;
271 }
272 }
273 max_midi_devs = 0;
274 spin_unlock_irqrestore(&register_lock, flags);
275}
276
277
278/*
279 * set up midi tables
280 */
281void
282snd_seq_oss_midi_setup(seq_oss_devinfo_t *dp)
283{
284 dp->max_mididev = max_midi_devs;
285}
286
287/*
288 * clean up midi tables
289 */
290void
291snd_seq_oss_midi_cleanup(seq_oss_devinfo_t *dp)
292{
293 int i;
294 for (i = 0; i < dp->max_mididev; i++)
295 snd_seq_oss_midi_close(dp, i);
296 dp->max_mididev = 0;
297}
298
299
300/*
301 * open all midi devices. ignore errors.
302 */
303void
304snd_seq_oss_midi_open_all(seq_oss_devinfo_t *dp, int file_mode)
305{
306 int i;
307 for (i = 0; i < dp->max_mididev; i++)
308 snd_seq_oss_midi_open(dp, i, file_mode);
309}
310
311
312/*
313 * get the midi device information
314 */
315static seq_oss_midi_t *
316get_mididev(seq_oss_devinfo_t *dp, int dev)
317{
318 if (dev < 0 || dev >= dp->max_mididev)
319 return NULL;
320 return get_mdev(dev);
321}
322
323
324/*
325 * open the midi device if not opened yet
326 */
327int
328snd_seq_oss_midi_open(seq_oss_devinfo_t *dp, int dev, int fmode)
329{
330 int perm;
331 seq_oss_midi_t *mdev;
332 snd_seq_port_subscribe_t subs;
333
334 if ((mdev = get_mididev(dp, dev)) == NULL)
335 return -ENODEV;
336
337 /* already used? */
338 if (mdev->opened && mdev->devinfo != dp) {
339 snd_use_lock_free(&mdev->use_lock);
340 return -EBUSY;
341 }
342
343 perm = 0;
344 if (is_write_mode(fmode))
345 perm |= PERM_WRITE;
346 if (is_read_mode(fmode))
347 perm |= PERM_READ;
348 perm &= mdev->flags;
349 if (perm == 0) {
350 snd_use_lock_free(&mdev->use_lock);
351 return -ENXIO;
352 }
353
354 /* already opened? */
355 if ((mdev->opened & perm) == perm) {
356 snd_use_lock_free(&mdev->use_lock);
357 return 0;
358 }
359
360 perm &= ~mdev->opened;
361
362 memset(&subs, 0, sizeof(subs));
363
364 if (perm & PERM_WRITE) {
365 subs.sender = dp->addr;
366 subs.dest.client = mdev->client;
367 subs.dest.port = mdev->port;
368 if (snd_seq_kernel_client_ctl(dp->cseq, SNDRV_SEQ_IOCTL_SUBSCRIBE_PORT, &subs) >= 0)
369 mdev->opened |= PERM_WRITE;
370 }
371 if (perm & PERM_READ) {
372 subs.sender.client = mdev->client;
373 subs.sender.port = mdev->port;
374 subs.dest = dp->addr;
375 subs.flags = SNDRV_SEQ_PORT_SUBS_TIMESTAMP;
376 subs.queue = dp->queue; /* queue for timestamps */
377 if (snd_seq_kernel_client_ctl(dp->cseq, SNDRV_SEQ_IOCTL_SUBSCRIBE_PORT, &subs) >= 0)
378 mdev->opened |= PERM_READ;
379 }
380
381 if (! mdev->opened) {
382 snd_use_lock_free(&mdev->use_lock);
383 return -ENXIO;
384 }
385
386 mdev->devinfo = dp;
387 snd_use_lock_free(&mdev->use_lock);
388 return 0;
389}
390
391/*
392 * close the midi device if already opened
393 */
394int
395snd_seq_oss_midi_close(seq_oss_devinfo_t *dp, int dev)
396{
397 seq_oss_midi_t *mdev;
398 snd_seq_port_subscribe_t subs;
399
400 if ((mdev = get_mididev(dp, dev)) == NULL)
401 return -ENODEV;
402 if (! mdev->opened || mdev->devinfo != dp) {
403 snd_use_lock_free(&mdev->use_lock);
404 return 0;
405 }
406
407 debug_printk(("closing client %d port %d mode %d\n", mdev->client, mdev->port, mdev->opened));
408 memset(&subs, 0, sizeof(subs));
409 if (mdev->opened & PERM_WRITE) {
410 subs.sender = dp->addr;
411 subs.dest.client = mdev->client;
412 subs.dest.port = mdev->port;
413 snd_seq_kernel_client_ctl(dp->cseq, SNDRV_SEQ_IOCTL_UNSUBSCRIBE_PORT, &subs);
414 }
415 if (mdev->opened & PERM_READ) {
416 subs.sender.client = mdev->client;
417 subs.sender.port = mdev->port;
418 subs.dest = dp->addr;
419 snd_seq_kernel_client_ctl(dp->cseq, SNDRV_SEQ_IOCTL_UNSUBSCRIBE_PORT, &subs);
420 }
421
422 mdev->opened = 0;
423 mdev->devinfo = NULL;
424
425 snd_use_lock_free(&mdev->use_lock);
426 return 0;
427}
428
429/*
430 * change seq capability flags to file mode flags
431 */
432int
433snd_seq_oss_midi_filemode(seq_oss_devinfo_t *dp, int dev)
434{
435 seq_oss_midi_t *mdev;
436 int mode;
437
438 if ((mdev = get_mididev(dp, dev)) == NULL)
439 return 0;
440
441 mode = 0;
442 if (mdev->opened & PERM_WRITE)
443 mode |= SNDRV_SEQ_OSS_FILE_WRITE;
444 if (mdev->opened & PERM_READ)
445 mode |= SNDRV_SEQ_OSS_FILE_READ;
446
447 snd_use_lock_free(&mdev->use_lock);
448 return mode;
449}
450
451/*
452 * reset the midi device and close it:
453 * so far, only close the device.
454 */
455void
456snd_seq_oss_midi_reset(seq_oss_devinfo_t *dp, int dev)
457{
458 seq_oss_midi_t *mdev;
459
460 if ((mdev = get_mididev(dp, dev)) == NULL)
461 return;
462 if (! mdev->opened) {
463 snd_use_lock_free(&mdev->use_lock);
464 return;
465 }
466
467 if (mdev->opened & PERM_WRITE) {
468 snd_seq_event_t ev;
469 int c;
470
471 debug_printk(("resetting client %d port %d\n", mdev->client, mdev->port));
472 memset(&ev, 0, sizeof(ev));
473 ev.dest.client = mdev->client;
474 ev.dest.port = mdev->port;
475 ev.queue = dp->queue;
476 ev.source.port = dp->port;
477 if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_SYNTH) {
478 ev.type = SNDRV_SEQ_EVENT_SENSING;
479 snd_seq_oss_dispatch(dp, &ev, 0, 0); /* active sensing */
480 }
481 for (c = 0; c < 16; c++) {
482 ev.type = SNDRV_SEQ_EVENT_CONTROLLER;
483 ev.data.control.channel = c;
484 ev.data.control.param = 123;
485 snd_seq_oss_dispatch(dp, &ev, 0, 0); /* all notes off */
486 if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC) {
487 ev.data.control.param = 121;
488 snd_seq_oss_dispatch(dp, &ev, 0, 0); /* reset all controllers */
489 ev.type = SNDRV_SEQ_EVENT_PITCHBEND;
490 ev.data.control.value = 0;
491 snd_seq_oss_dispatch(dp, &ev, 0, 0); /* bender off */
492 }
493 }
494 }
495 // snd_seq_oss_midi_close(dp, dev);
496 snd_use_lock_free(&mdev->use_lock);
497}
498
499
500/*
501 * get client/port of the specified MIDI device
502 */
503void
504snd_seq_oss_midi_get_addr(seq_oss_devinfo_t *dp, int dev, snd_seq_addr_t *addr)
505{
506 seq_oss_midi_t *mdev;
507
508 if ((mdev = get_mididev(dp, dev)) == NULL)
509 return;
510 addr->client = mdev->client;
511 addr->port = mdev->port;
512 snd_use_lock_free(&mdev->use_lock);
513}
514
515
516/*
517 * input callback - this can be atomic
518 */
519int
520snd_seq_oss_midi_input(snd_seq_event_t *ev, int direct, void *private_data)
521{
522 seq_oss_devinfo_t *dp = (seq_oss_devinfo_t *)private_data;
523 seq_oss_midi_t *mdev;
524 int rc;
525
526 if (dp->readq == NULL)
527 return 0;
528 if ((mdev = find_slot(ev->source.client, ev->source.port)) == NULL)
529 return 0;
530 if (! (mdev->opened & PERM_READ)) {
531 snd_use_lock_free(&mdev->use_lock);
532 return 0;
533 }
534
535 if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC)
536 rc = send_synth_event(dp, ev, mdev->seq_device);
537 else
538 rc = send_midi_event(dp, ev, mdev);
539
540 snd_use_lock_free(&mdev->use_lock);
541 return rc;
542}
543
544/*
545 * convert ALSA sequencer event to OSS synth event
546 */
547static int
548send_synth_event(seq_oss_devinfo_t *dp, snd_seq_event_t *ev, int dev)
549{
550 evrec_t ossev;
551
552 memset(&ossev, 0, sizeof(ossev));
553
554 switch (ev->type) {
555 case SNDRV_SEQ_EVENT_NOTEON:
556 ossev.v.cmd = MIDI_NOTEON; break;
557 case SNDRV_SEQ_EVENT_NOTEOFF:
558 ossev.v.cmd = MIDI_NOTEOFF; break;
559 case SNDRV_SEQ_EVENT_KEYPRESS:
560 ossev.v.cmd = MIDI_KEY_PRESSURE; break;
561 case SNDRV_SEQ_EVENT_CONTROLLER:
562 ossev.l.cmd = MIDI_CTL_CHANGE; break;
563 case SNDRV_SEQ_EVENT_PGMCHANGE:
564 ossev.l.cmd = MIDI_PGM_CHANGE; break;
565 case SNDRV_SEQ_EVENT_CHANPRESS:
566 ossev.l.cmd = MIDI_CHN_PRESSURE; break;
567 case SNDRV_SEQ_EVENT_PITCHBEND:
568 ossev.l.cmd = MIDI_PITCH_BEND; break;
569 default:
570 return 0; /* not supported */
571 }
572
573 ossev.v.dev = dev;
574
575 switch (ev->type) {
576 case SNDRV_SEQ_EVENT_NOTEON:
577 case SNDRV_SEQ_EVENT_NOTEOFF:
578 case SNDRV_SEQ_EVENT_KEYPRESS:
579 ossev.v.code = EV_CHN_VOICE;
580 ossev.v.note = ev->data.note.note;
581 ossev.v.parm = ev->data.note.velocity;
582 ossev.v.chn = ev->data.note.channel;
583 break;
584 case SNDRV_SEQ_EVENT_CONTROLLER:
585 case SNDRV_SEQ_EVENT_PGMCHANGE:
586 case SNDRV_SEQ_EVENT_CHANPRESS:
587 ossev.l.code = EV_CHN_COMMON;
588 ossev.l.p1 = ev->data.control.param;
589 ossev.l.val = ev->data.control.value;
590 ossev.l.chn = ev->data.control.channel;
591 break;
592 case SNDRV_SEQ_EVENT_PITCHBEND:
593 ossev.l.code = EV_CHN_COMMON;
594 ossev.l.val = ev->data.control.value + 8192;
595 ossev.l.chn = ev->data.control.channel;
596 break;
597 }
598
599 snd_seq_oss_readq_put_timestamp(dp->readq, ev->time.tick, dp->seq_mode);
600 snd_seq_oss_readq_put_event(dp->readq, &ossev);
601
602 return 0;
603}
604
605/*
606 * decode event and send MIDI bytes to read queue
607 */
608static int
609send_midi_event(seq_oss_devinfo_t *dp, snd_seq_event_t *ev, seq_oss_midi_t *mdev)
610{
611 char msg[32];
612 int len;
613
614 snd_seq_oss_readq_put_timestamp(dp->readq, ev->time.tick, dp->seq_mode);
615 if (!dp->timer->running)
616 len = snd_seq_oss_timer_start(dp->timer);
617 if (ev->type == SNDRV_SEQ_EVENT_SYSEX) {
618 if ((ev->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) == SNDRV_SEQ_EVENT_LENGTH_VARIABLE)
619 snd_seq_oss_readq_puts(dp->readq, mdev->seq_device,
620 ev->data.ext.ptr, ev->data.ext.len);
621 } else {
622 len = snd_midi_event_decode(mdev->coder, msg, sizeof(msg), ev);
623 if (len > 0)
624 snd_seq_oss_readq_puts(dp->readq, mdev->seq_device, msg, len);
625 }
626
627 return 0;
628}
629
630
631/*
632 * dump midi data
633 * return 0 : enqueued
634 * non-zero : invalid - ignored
635 */
636int
637snd_seq_oss_midi_putc(seq_oss_devinfo_t *dp, int dev, unsigned char c, snd_seq_event_t *ev)
638{
639 seq_oss_midi_t *mdev;
640
641 if ((mdev = get_mididev(dp, dev)) == NULL)
642 return -ENODEV;
643 if (snd_midi_event_encode_byte(mdev->coder, c, ev) > 0) {
644 snd_seq_oss_fill_addr(dp, ev, mdev->client, mdev->port);
645 snd_use_lock_free(&mdev->use_lock);
646 return 0;
647 }
648 snd_use_lock_free(&mdev->use_lock);
649 return -EINVAL;
650}
651
652/*
653 * create OSS compatible midi_info record
654 */
655int
656snd_seq_oss_midi_make_info(seq_oss_devinfo_t *dp, int dev, struct midi_info *inf)
657{
658 seq_oss_midi_t *mdev;
659
660 if ((mdev = get_mididev(dp, dev)) == NULL)
661 return -ENXIO;
662 inf->device = dev;
663 inf->dev_type = 0; /* FIXME: ?? */
664 inf->capabilities = 0; /* FIXME: ?? */
665 strlcpy(inf->name, mdev->name, sizeof(inf->name));
666 snd_use_lock_free(&mdev->use_lock);
667 return 0;
668}
669
670
671/*
672 * proc interface
673 */
674static char *
675capmode_str(int val)
676{
677 val &= PERM_READ|PERM_WRITE;
678 if (val == (PERM_READ|PERM_WRITE))
679 return "read/write";
680 else if (val == PERM_READ)
681 return "read";
682 else if (val == PERM_WRITE)
683 return "write";
684 else
685 return "none";
686}
687
688void
689snd_seq_oss_midi_info_read(snd_info_buffer_t *buf)
690{
691 int i;
692 seq_oss_midi_t *mdev;
693
694 snd_iprintf(buf, "\nNumber of MIDI devices: %d\n", max_midi_devs);
695 for (i = 0; i < max_midi_devs; i++) {
696 snd_iprintf(buf, "\nmidi %d: ", i);
697 mdev = get_mdev(i);
698 if (mdev == NULL) {
699 snd_iprintf(buf, "*empty*\n");
700 continue;
701 }
702 snd_iprintf(buf, "[%s] ALSA port %d:%d\n", mdev->name,
703 mdev->client, mdev->port);
704 snd_iprintf(buf, " capability %s / opened %s\n",
705 capmode_str(mdev->flags),
706 capmode_str(mdev->opened));
707 snd_use_lock_free(&mdev->use_lock);
708 }
709}
710
diff --git a/sound/core/seq/oss/seq_oss_midi.h b/sound/core/seq/oss/seq_oss_midi.h
new file mode 100644
index 000000000000..462484b2b6fe
--- /dev/null
+++ b/sound/core/seq/oss/seq_oss_midi.h
@@ -0,0 +1,49 @@
1/*
2 * OSS compatible sequencer driver
3 *
4 * midi device information
5 *
6 * Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (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 General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 */
22
23#ifndef __SEQ_OSS_MIDI_H
24#define __SEQ_OSS_MIDI_H
25
26#include "seq_oss_device.h"
27#include <sound/seq_oss_legacy.h>
28
29typedef struct seq_oss_midi_t seq_oss_midi_t;
30
31int snd_seq_oss_midi_lookup_ports(int client);
32int snd_seq_oss_midi_check_new_port(snd_seq_port_info_t *pinfo);
33int snd_seq_oss_midi_check_exit_port(int client, int port);
34void snd_seq_oss_midi_clear_all(void);
35
36void snd_seq_oss_midi_setup(seq_oss_devinfo_t *dp);
37void snd_seq_oss_midi_cleanup(seq_oss_devinfo_t *dp);
38
39int snd_seq_oss_midi_open(seq_oss_devinfo_t *dp, int dev, int file_mode);
40void snd_seq_oss_midi_open_all(seq_oss_devinfo_t *dp, int file_mode);
41int snd_seq_oss_midi_close(seq_oss_devinfo_t *dp, int dev);
42void snd_seq_oss_midi_reset(seq_oss_devinfo_t *dp, int dev);
43int snd_seq_oss_midi_putc(seq_oss_devinfo_t *dp, int dev, unsigned char c, snd_seq_event_t *ev);
44int snd_seq_oss_midi_input(snd_seq_event_t *ev, int direct, void *private);
45int snd_seq_oss_midi_filemode(seq_oss_devinfo_t *dp, int dev);
46int snd_seq_oss_midi_make_info(seq_oss_devinfo_t *dp, int dev, struct midi_info *inf);
47void snd_seq_oss_midi_get_addr(seq_oss_devinfo_t *dp, int dev, snd_seq_addr_t *addr);
48
49#endif
diff --git a/sound/core/seq/oss/seq_oss_readq.c b/sound/core/seq/oss/seq_oss_readq.c
new file mode 100644
index 000000000000..0a6f2a64f692
--- /dev/null
+++ b/sound/core/seq/oss/seq_oss_readq.c
@@ -0,0 +1,234 @@
1/*
2 * OSS compatible sequencer driver
3 *
4 * seq_oss_readq.c - MIDI input queue
5 *
6 * Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (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 General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 */
22
23#include "seq_oss_readq.h"
24#include "seq_oss_event.h"
25#include <sound/seq_oss_legacy.h>
26#include "../seq_lock.h"
27#include <linux/wait.h>
28
29/*
30 * constants
31 */
32//#define SNDRV_SEQ_OSS_MAX_TIMEOUT (unsigned long)(-1)
33#define SNDRV_SEQ_OSS_MAX_TIMEOUT (HZ * 3600)
34
35
36/*
37 * prototypes
38 */
39
40
41/*
42 * create a read queue
43 */
44seq_oss_readq_t *
45snd_seq_oss_readq_new(seq_oss_devinfo_t *dp, int maxlen)
46{
47 seq_oss_readq_t *q;
48
49 if ((q = kcalloc(1, sizeof(*q), GFP_KERNEL)) == NULL) {
50 snd_printk(KERN_ERR "can't malloc read queue\n");
51 return NULL;
52 }
53
54 if ((q->q = kcalloc(maxlen, sizeof(evrec_t), GFP_KERNEL)) == NULL) {
55 snd_printk(KERN_ERR "can't malloc read queue buffer\n");
56 kfree(q);
57 return NULL;
58 }
59
60 q->maxlen = maxlen;
61 q->qlen = 0;
62 q->head = q->tail = 0;
63 init_waitqueue_head(&q->midi_sleep);
64 spin_lock_init(&q->lock);
65 q->pre_event_timeout = SNDRV_SEQ_OSS_MAX_TIMEOUT;
66 q->input_time = (unsigned long)-1;
67
68 return q;
69}
70
71/*
72 * delete the read queue
73 */
74void
75snd_seq_oss_readq_delete(seq_oss_readq_t *q)
76{
77 if (q) {
78 kfree(q->q);
79 kfree(q);
80 }
81}
82
83/*
84 * reset the read queue
85 */
86void
87snd_seq_oss_readq_clear(seq_oss_readq_t *q)
88{
89 if (q->qlen) {
90 q->qlen = 0;
91 q->head = q->tail = 0;
92 }
93 /* if someone sleeping, wake'em up */
94 if (waitqueue_active(&q->midi_sleep))
95 wake_up(&q->midi_sleep);
96 q->input_time = (unsigned long)-1;
97}
98
99/*
100 * put a midi byte
101 */
102int
103snd_seq_oss_readq_puts(seq_oss_readq_t *q, int dev, unsigned char *data, int len)
104{
105 evrec_t rec;
106 int result;
107
108 memset(&rec, 0, sizeof(rec));
109 rec.c[0] = SEQ_MIDIPUTC;
110 rec.c[2] = dev;
111
112 while (len-- > 0) {
113 rec.c[1] = *data++;
114 result = snd_seq_oss_readq_put_event(q, &rec);
115 if (result < 0)
116 return result;
117 }
118 return 0;
119}
120
121/*
122 * copy an event to input queue:
123 * return zero if enqueued
124 */
125int
126snd_seq_oss_readq_put_event(seq_oss_readq_t *q, evrec_t *ev)
127{
128 unsigned long flags;
129
130 spin_lock_irqsave(&q->lock, flags);
131 if (q->qlen >= q->maxlen - 1) {
132 spin_unlock_irqrestore(&q->lock, flags);
133 return -ENOMEM;
134 }
135
136 memcpy(&q->q[q->tail], ev, sizeof(*ev));
137 q->tail = (q->tail + 1) % q->maxlen;
138 q->qlen++;
139
140 /* wake up sleeper */
141 if (waitqueue_active(&q->midi_sleep))
142 wake_up(&q->midi_sleep);
143
144 spin_unlock_irqrestore(&q->lock, flags);
145
146 return 0;
147}
148
149
150/*
151 * pop queue
152 * caller must hold lock
153 */
154int
155snd_seq_oss_readq_pick(seq_oss_readq_t *q, evrec_t *rec)
156{
157 if (q->qlen == 0)
158 return -EAGAIN;
159 memcpy(rec, &q->q[q->head], sizeof(*rec));
160 return 0;
161}
162
163/*
164 * sleep until ready
165 */
166void
167snd_seq_oss_readq_wait(seq_oss_readq_t *q)
168{
169 wait_event_interruptible_timeout(q->midi_sleep,
170 (q->qlen > 0 || q->head == q->tail),
171 q->pre_event_timeout);
172}
173
174/*
175 * drain one record
176 * caller must hold lock
177 */
178void
179snd_seq_oss_readq_free(seq_oss_readq_t *q)
180{
181 if (q->qlen > 0) {
182 q->head = (q->head + 1) % q->maxlen;
183 q->qlen--;
184 }
185}
186
187/*
188 * polling/select:
189 * return non-zero if readq is not empty.
190 */
191unsigned int
192snd_seq_oss_readq_poll(seq_oss_readq_t *q, struct file *file, poll_table *wait)
193{
194 poll_wait(file, &q->midi_sleep, wait);
195 return q->qlen;
196}
197
198/*
199 * put a timestamp
200 */
201int
202snd_seq_oss_readq_put_timestamp(seq_oss_readq_t *q, unsigned long curt, int seq_mode)
203{
204 if (curt != q->input_time) {
205 evrec_t rec;
206 memset(&rec, 0, sizeof(rec));
207 switch (seq_mode) {
208 case SNDRV_SEQ_OSS_MODE_SYNTH:
209 rec.echo = (curt << 8) | SEQ_WAIT;
210 snd_seq_oss_readq_put_event(q, &rec);
211 break;
212 case SNDRV_SEQ_OSS_MODE_MUSIC:
213 rec.t.code = EV_TIMING;
214 rec.t.cmd = TMR_WAIT_ABS;
215 rec.t.time = curt;
216 snd_seq_oss_readq_put_event(q, &rec);
217 break;
218 }
219 q->input_time = curt;
220 }
221 return 0;
222}
223
224
225/*
226 * proc interface
227 */
228void
229snd_seq_oss_readq_info_read(seq_oss_readq_t *q, snd_info_buffer_t *buf)
230{
231 snd_iprintf(buf, " read queue [%s] length = %d : tick = %ld\n",
232 (waitqueue_active(&q->midi_sleep) ? "sleeping":"running"),
233 q->qlen, q->input_time);
234}
diff --git a/sound/core/seq/oss/seq_oss_readq.h b/sound/core/seq/oss/seq_oss_readq.h
new file mode 100644
index 000000000000..303b9298f206
--- /dev/null
+++ b/sound/core/seq/oss/seq_oss_readq.h
@@ -0,0 +1,56 @@
1/*
2 * OSS compatible sequencer driver
3 * read fifo queue
4 *
5 * Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (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 General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21
22#ifndef __SEQ_OSS_READQ_H
23#define __SEQ_OSS_READQ_H
24
25#include "seq_oss_device.h"
26
27
28/*
29 * definition of read queue
30 */
31struct seq_oss_readq_t {
32 evrec_t *q;
33 int qlen;
34 int maxlen;
35 int head, tail;
36 unsigned long pre_event_timeout;
37 unsigned long input_time;
38 wait_queue_head_t midi_sleep;
39 spinlock_t lock;
40};
41
42seq_oss_readq_t *snd_seq_oss_readq_new(seq_oss_devinfo_t *dp, int maxlen);
43void snd_seq_oss_readq_delete(seq_oss_readq_t *q);
44void snd_seq_oss_readq_clear(seq_oss_readq_t *readq);
45unsigned int snd_seq_oss_readq_poll(seq_oss_readq_t *readq, struct file *file, poll_table *wait);
46int snd_seq_oss_readq_puts(seq_oss_readq_t *readq, int dev, unsigned char *data, int len);
47int snd_seq_oss_readq_put_event(seq_oss_readq_t *readq, evrec_t *ev);
48int snd_seq_oss_readq_put_timestamp(seq_oss_readq_t *readq, unsigned long curt, int seq_mode);
49int snd_seq_oss_readq_pick(seq_oss_readq_t *q, evrec_t *rec);
50void snd_seq_oss_readq_wait(seq_oss_readq_t *q);
51void snd_seq_oss_readq_free(seq_oss_readq_t *q);
52
53#define snd_seq_oss_readq_lock(q, flags) spin_lock_irqsave(&(q)->lock, flags)
54#define snd_seq_oss_readq_unlock(q, flags) spin_unlock_irqrestore(&(q)->lock, flags)
55
56#endif
diff --git a/sound/core/seq/oss/seq_oss_rw.c b/sound/core/seq/oss/seq_oss_rw.c
new file mode 100644
index 000000000000..1d8fbd22e3e3
--- /dev/null
+++ b/sound/core/seq/oss/seq_oss_rw.c
@@ -0,0 +1,216 @@
1/*
2 * OSS compatible sequencer driver
3 *
4 * read/write/select interface to device file
5 *
6 * Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (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 General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 */
22
23#include "seq_oss_device.h"
24#include "seq_oss_readq.h"
25#include "seq_oss_writeq.h"
26#include "seq_oss_synth.h"
27#include <sound/seq_oss_legacy.h>
28#include "seq_oss_event.h"
29#include "seq_oss_timer.h"
30#include "../seq_clientmgr.h"
31
32
33/*
34 * protoypes
35 */
36static int insert_queue(seq_oss_devinfo_t *dp, evrec_t *rec, struct file *opt);
37
38
39/*
40 * read interface
41 */
42
43int
44snd_seq_oss_read(seq_oss_devinfo_t *dp, char __user *buf, int count)
45{
46 seq_oss_readq_t *readq = dp->readq;
47 int result = 0, err = 0;
48 int ev_len;
49 evrec_t rec;
50 unsigned long flags;
51
52 if (readq == NULL || ! is_read_mode(dp->file_mode))
53 return -ENXIO;
54
55 while (count >= SHORT_EVENT_SIZE) {
56 snd_seq_oss_readq_lock(readq, flags);
57 err = snd_seq_oss_readq_pick(readq, &rec);
58 if (err == -EAGAIN &&
59 !is_nonblock_mode(dp->file_mode) && result == 0) {
60 snd_seq_oss_readq_unlock(readq, flags);
61 snd_seq_oss_readq_wait(readq);
62 snd_seq_oss_readq_lock(readq, flags);
63 if (signal_pending(current))
64 err = -ERESTARTSYS;
65 else
66 err = snd_seq_oss_readq_pick(readq, &rec);
67 }
68 if (err < 0) {
69 snd_seq_oss_readq_unlock(readq, flags);
70 break;
71 }
72 ev_len = ev_length(&rec);
73 if (ev_len < count) {
74 snd_seq_oss_readq_unlock(readq, flags);
75 break;
76 }
77 snd_seq_oss_readq_free(readq);
78 snd_seq_oss_readq_unlock(readq, flags);
79 if (copy_to_user(buf, &rec, ev_len)) {
80 err = -EFAULT;
81 break;
82 }
83 result += ev_len;
84 buf += ev_len;
85 count -= ev_len;
86 }
87 return result > 0 ? result : err;
88}
89
90
91/*
92 * write interface
93 */
94
95int
96snd_seq_oss_write(seq_oss_devinfo_t *dp, const char __user *buf, int count, struct file *opt)
97{
98 int result = 0, err = 0;
99 int ev_size, fmt;
100 evrec_t rec;
101
102 if (! is_write_mode(dp->file_mode) || dp->writeq == NULL)
103 return -ENXIO;
104
105 while (count >= SHORT_EVENT_SIZE) {
106 if (copy_from_user(&rec, buf, SHORT_EVENT_SIZE)) {
107 err = -EFAULT;
108 break;
109 }
110 if (rec.s.code == SEQ_FULLSIZE) {
111 /* load patch */
112 if (result > 0) {
113 err = -EINVAL;
114 break;
115 }
116 fmt = (*(unsigned short *)rec.c) & 0xffff;
117 /* FIXME the return value isn't correct */
118 return snd_seq_oss_synth_load_patch(dp, rec.s.dev,
119 fmt, buf, 0, count);
120 }
121 if (ev_is_long(&rec)) {
122 /* extended code */
123 if (rec.s.code == SEQ_EXTENDED &&
124 dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC) {
125 err = -EINVAL;
126 break;
127 }
128 ev_size = LONG_EVENT_SIZE;
129 if (count < ev_size)
130 break;
131 /* copy the reset 4 bytes */
132 if (copy_from_user(rec.c + SHORT_EVENT_SIZE,
133 buf + SHORT_EVENT_SIZE,
134 LONG_EVENT_SIZE - SHORT_EVENT_SIZE)) {
135 err = -EFAULT;
136 break;
137 }
138 } else {
139 /* old-type code */
140 if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC) {
141 err = -EINVAL;
142 break;
143 }
144 ev_size = SHORT_EVENT_SIZE;
145 }
146
147 /* insert queue */
148 if ((err = insert_queue(dp, &rec, opt)) < 0)
149 break;
150
151 result += ev_size;
152 buf += ev_size;
153 count -= ev_size;
154 }
155 return result > 0 ? result : err;
156}
157
158
159/*
160 * insert event record to write queue
161 * return: 0 = OK, non-zero = NG
162 */
163static int
164insert_queue(seq_oss_devinfo_t *dp, evrec_t *rec, struct file *opt)
165{
166 int rc = 0;
167 snd_seq_event_t event;
168
169 /* if this is a timing event, process the current time */
170 if (snd_seq_oss_process_timer_event(dp->timer, rec))
171 return 0; /* no need to insert queue */
172
173 /* parse this event */
174 memset(&event, 0, sizeof(event));
175 /* set dummy -- to be sure */
176 event.type = SNDRV_SEQ_EVENT_NOTEOFF;
177 snd_seq_oss_fill_addr(dp, &event, dp->addr.port, dp->addr.client);
178
179 if (snd_seq_oss_process_event(dp, rec, &event))
180 return 0; /* invalid event - no need to insert queue */
181
182 event.time.tick = snd_seq_oss_timer_cur_tick(dp->timer);
183 if (dp->timer->realtime || !dp->timer->running) {
184 snd_seq_oss_dispatch(dp, &event, 0, 0);
185 } else {
186 if (is_nonblock_mode(dp->file_mode))
187 rc = snd_seq_kernel_client_enqueue(dp->cseq, &event, 0, 0);
188 else
189 rc = snd_seq_kernel_client_enqueue_blocking(dp->cseq, &event, opt, 0, 0);
190 }
191 return rc;
192}
193
194
195/*
196 * select / poll
197 */
198
199unsigned int
200snd_seq_oss_poll(seq_oss_devinfo_t *dp, struct file *file, poll_table * wait)
201{
202 unsigned int mask = 0;
203
204 /* input */
205 if (dp->readq && is_read_mode(dp->file_mode)) {
206 if (snd_seq_oss_readq_poll(dp->readq, file, wait))
207 mask |= POLLIN | POLLRDNORM;
208 }
209
210 /* output */
211 if (dp->writeq && is_write_mode(dp->file_mode)) {
212 if (snd_seq_kernel_client_write_poll(dp->cseq, file, wait))
213 mask |= POLLOUT | POLLWRNORM;
214 }
215 return mask;
216}
diff --git a/sound/core/seq/oss/seq_oss_synth.c b/sound/core/seq/oss/seq_oss_synth.c
new file mode 100644
index 000000000000..638cc148706d
--- /dev/null
+++ b/sound/core/seq/oss/seq_oss_synth.c
@@ -0,0 +1,659 @@
1/*
2 * OSS compatible sequencer driver
3 *
4 * synth device handlers
5 *
6 * Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (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 General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 */
22
23#include "seq_oss_synth.h"
24#include "seq_oss_midi.h"
25#include "../seq_lock.h"
26#include <linux/init.h>
27
28/*
29 * constants
30 */
31#define SNDRV_SEQ_OSS_MAX_SYNTH_NAME 30
32#define MAX_SYSEX_BUFLEN 128
33
34
35/*
36 * definition of synth info records
37 */
38
39/* sysex buffer */
40struct seq_oss_synth_sysex_t {
41 int len;
42 int skip;
43 unsigned char buf[MAX_SYSEX_BUFLEN];
44};
45
46/* synth info */
47struct seq_oss_synth_t {
48 int seq_device;
49
50 /* for synth_info */
51 int synth_type;
52 int synth_subtype;
53 int nr_voices;
54
55 char name[SNDRV_SEQ_OSS_MAX_SYNTH_NAME];
56 snd_seq_oss_callback_t oper;
57
58 int opened;
59
60 void *private_data;
61 snd_use_lock_t use_lock;
62};
63
64
65/*
66 * device table
67 */
68static int max_synth_devs;
69static seq_oss_synth_t *synth_devs[SNDRV_SEQ_OSS_MAX_SYNTH_DEVS];
70static seq_oss_synth_t midi_synth_dev = {
71 -1, /* seq_device */
72 SYNTH_TYPE_MIDI, /* synth_type */
73 0, /* synth_subtype */
74 16, /* nr_voices */
75 "MIDI", /* name */
76};
77
78static DEFINE_SPINLOCK(register_lock);
79
80/*
81 * prototypes
82 */
83static seq_oss_synth_t *get_synthdev(seq_oss_devinfo_t *dp, int dev);
84static void reset_channels(seq_oss_synthinfo_t *info);
85
86/*
87 * global initialization
88 */
89void __init
90snd_seq_oss_synth_init(void)
91{
92 snd_use_lock_init(&midi_synth_dev.use_lock);
93}
94
95/*
96 * registration of the synth device
97 */
98int
99snd_seq_oss_synth_register(snd_seq_device_t *dev)
100{
101 int i;
102 seq_oss_synth_t *rec;
103 snd_seq_oss_reg_t *reg = SNDRV_SEQ_DEVICE_ARGPTR(dev);
104 unsigned long flags;
105
106 if ((rec = kcalloc(1, sizeof(*rec), GFP_KERNEL)) == NULL) {
107 snd_printk(KERN_ERR "can't malloc synth info\n");
108 return -ENOMEM;
109 }
110 rec->seq_device = -1;
111 rec->synth_type = reg->type;
112 rec->synth_subtype = reg->subtype;
113 rec->nr_voices = reg->nvoices;
114 rec->oper = reg->oper;
115 rec->private_data = reg->private_data;
116 rec->opened = 0;
117 snd_use_lock_init(&rec->use_lock);
118
119 /* copy and truncate the name of synth device */
120 strlcpy(rec->name, dev->name, sizeof(rec->name));
121
122 /* registration */
123 spin_lock_irqsave(&register_lock, flags);
124 for (i = 0; i < max_synth_devs; i++) {
125 if (synth_devs[i] == NULL)
126 break;
127 }
128 if (i >= max_synth_devs) {
129 if (max_synth_devs >= SNDRV_SEQ_OSS_MAX_SYNTH_DEVS) {
130 spin_unlock_irqrestore(&register_lock, flags);
131 snd_printk(KERN_ERR "no more synth slot\n");
132 kfree(rec);
133 return -ENOMEM;
134 }
135 max_synth_devs++;
136 }
137 rec->seq_device = i;
138 synth_devs[i] = rec;
139 debug_printk(("synth %s registered %d\n", rec->name, i));
140 spin_unlock_irqrestore(&register_lock, flags);
141 dev->driver_data = rec;
142#ifdef SNDRV_OSS_INFO_DEV_SYNTH
143 if (i < SNDRV_CARDS)
144 snd_oss_info_register(SNDRV_OSS_INFO_DEV_SYNTH, i, rec->name);
145#endif
146 return 0;
147}
148
149
150int
151snd_seq_oss_synth_unregister(snd_seq_device_t *dev)
152{
153 int index;
154 seq_oss_synth_t *rec = dev->driver_data;
155 unsigned long flags;
156
157 spin_lock_irqsave(&register_lock, flags);
158 for (index = 0; index < max_synth_devs; index++) {
159 if (synth_devs[index] == rec)
160 break;
161 }
162 if (index >= max_synth_devs) {
163 spin_unlock_irqrestore(&register_lock, flags);
164 snd_printk(KERN_ERR "can't unregister synth\n");
165 return -EINVAL;
166 }
167 synth_devs[index] = NULL;
168 if (index == max_synth_devs - 1) {
169 for (index--; index >= 0; index--) {
170 if (synth_devs[index])
171 break;
172 }
173 max_synth_devs = index + 1;
174 }
175 spin_unlock_irqrestore(&register_lock, flags);
176#ifdef SNDRV_OSS_INFO_DEV_SYNTH
177 if (rec->seq_device < SNDRV_CARDS)
178 snd_oss_info_unregister(SNDRV_OSS_INFO_DEV_SYNTH, rec->seq_device);
179#endif
180
181 snd_use_lock_sync(&rec->use_lock);
182 kfree(rec);
183
184 return 0;
185}
186
187
188/*
189 */
190static seq_oss_synth_t *
191get_sdev(int dev)
192{
193 seq_oss_synth_t *rec;
194 unsigned long flags;
195
196 spin_lock_irqsave(&register_lock, flags);
197 rec = synth_devs[dev];
198 if (rec)
199 snd_use_lock_use(&rec->use_lock);
200 spin_unlock_irqrestore(&register_lock, flags);
201 return rec;
202}
203
204
205/*
206 * set up synth tables
207 */
208
209void
210snd_seq_oss_synth_setup(seq_oss_devinfo_t *dp)
211{
212 int i;
213 seq_oss_synth_t *rec;
214 seq_oss_synthinfo_t *info;
215
216 dp->max_synthdev = max_synth_devs;
217 dp->synth_opened = 0;
218 memset(dp->synths, 0, sizeof(dp->synths));
219 for (i = 0; i < dp->max_synthdev; i++) {
220 rec = get_sdev(i);
221 if (rec == NULL)
222 continue;
223 if (rec->oper.open == NULL || rec->oper.close == NULL) {
224 snd_use_lock_free(&rec->use_lock);
225 continue;
226 }
227 info = &dp->synths[i];
228 info->arg.app_index = dp->port;
229 info->arg.file_mode = dp->file_mode;
230 info->arg.seq_mode = dp->seq_mode;
231 if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_SYNTH)
232 info->arg.event_passing = SNDRV_SEQ_OSS_PROCESS_EVENTS;
233 else
234 info->arg.event_passing = SNDRV_SEQ_OSS_PASS_EVENTS;
235 info->opened = 0;
236 if (!try_module_get(rec->oper.owner)) {
237 snd_use_lock_free(&rec->use_lock);
238 continue;
239 }
240 if (rec->oper.open(&info->arg, rec->private_data) < 0) {
241 module_put(rec->oper.owner);
242 snd_use_lock_free(&rec->use_lock);
243 continue;
244 }
245 info->nr_voices = rec->nr_voices;
246 if (info->nr_voices > 0) {
247 info->ch = kcalloc(info->nr_voices, sizeof(seq_oss_chinfo_t), GFP_KERNEL);
248 if (!info->ch)
249 BUG();
250 reset_channels(info);
251 }
252 debug_printk(("synth %d assigned\n", i));
253 info->opened++;
254 rec->opened++;
255 dp->synth_opened++;
256 snd_use_lock_free(&rec->use_lock);
257 }
258}
259
260
261/*
262 * set up synth tables for MIDI emulation - /dev/music mode only
263 */
264
265void
266snd_seq_oss_synth_setup_midi(seq_oss_devinfo_t *dp)
267{
268 int i;
269
270 if (dp->max_synthdev >= SNDRV_SEQ_OSS_MAX_SYNTH_DEVS)
271 return;
272
273 for (i = 0; i < dp->max_mididev; i++) {
274 seq_oss_synthinfo_t *info;
275 info = &dp->synths[dp->max_synthdev];
276 if (snd_seq_oss_midi_open(dp, i, dp->file_mode) < 0)
277 continue;
278 info->arg.app_index = dp->port;
279 info->arg.file_mode = dp->file_mode;
280 info->arg.seq_mode = dp->seq_mode;
281 info->arg.private_data = info;
282 info->is_midi = 1;
283 info->midi_mapped = i;
284 info->arg.event_passing = SNDRV_SEQ_OSS_PASS_EVENTS;
285 snd_seq_oss_midi_get_addr(dp, i, &info->arg.addr);
286 info->opened = 1;
287 midi_synth_dev.opened++;
288 dp->max_synthdev++;
289 if (dp->max_synthdev >= SNDRV_SEQ_OSS_MAX_SYNTH_DEVS)
290 break;
291 }
292}
293
294
295/*
296 * clean up synth tables
297 */
298
299void
300snd_seq_oss_synth_cleanup(seq_oss_devinfo_t *dp)
301{
302 int i;
303 seq_oss_synth_t *rec;
304 seq_oss_synthinfo_t *info;
305
306 snd_assert(dp->max_synthdev <= SNDRV_SEQ_OSS_MAX_SYNTH_DEVS, return);
307 for (i = 0; i < dp->max_synthdev; i++) {
308 info = &dp->synths[i];
309 if (! info->opened)
310 continue;
311 if (info->is_midi) {
312 if (midi_synth_dev.opened > 0) {
313 snd_seq_oss_midi_close(dp, info->midi_mapped);
314 midi_synth_dev.opened--;
315 }
316 } else {
317 rec = get_sdev(i);
318 if (rec == NULL)
319 continue;
320 if (rec->opened > 0) {
321 debug_printk(("synth %d closed\n", i));
322 rec->oper.close(&info->arg);
323 module_put(rec->oper.owner);
324 rec->opened = 0;
325 }
326 snd_use_lock_free(&rec->use_lock);
327 }
328 if (info->sysex) {
329 kfree(info->sysex);
330 info->sysex = NULL;
331 }
332 if (info->ch) {
333 kfree(info->ch);
334 info->ch = NULL;
335 }
336 }
337 dp->synth_opened = 0;
338 dp->max_synthdev = 0;
339}
340
341/*
342 * check if the specified device is MIDI mapped device
343 */
344static int
345is_midi_dev(seq_oss_devinfo_t *dp, int dev)
346{
347 if (dev < 0 || dev >= dp->max_synthdev)
348 return 0;
349 if (dp->synths[dev].is_midi)
350 return 1;
351 return 0;
352}
353
354/*
355 * return synth device information pointer
356 */
357static seq_oss_synth_t *
358get_synthdev(seq_oss_devinfo_t *dp, int dev)
359{
360 seq_oss_synth_t *rec;
361 if (dev < 0 || dev >= dp->max_synthdev)
362 return NULL;
363 if (! dp->synths[dev].opened)
364 return NULL;
365 if (dp->synths[dev].is_midi)
366 return &midi_synth_dev;
367 if ((rec = get_sdev(dev)) == NULL)
368 return NULL;
369 if (! rec->opened) {
370 snd_use_lock_free(&rec->use_lock);
371 return NULL;
372 }
373 return rec;
374}
375
376
377/*
378 * reset note and velocity on each channel.
379 */
380static void
381reset_channels(seq_oss_synthinfo_t *info)
382{
383 int i;
384 if (info->ch == NULL || ! info->nr_voices)
385 return;
386 for (i = 0; i < info->nr_voices; i++) {
387 info->ch[i].note = -1;
388 info->ch[i].vel = 0;
389 }
390}
391
392
393/*
394 * reset synth device:
395 * call reset callback. if no callback is defined, send a heartbeat
396 * event to the corresponding port.
397 */
398void
399snd_seq_oss_synth_reset(seq_oss_devinfo_t *dp, int dev)
400{
401 seq_oss_synth_t *rec;
402 seq_oss_synthinfo_t *info;
403
404 snd_assert(dev >= 0 && dev < dp->max_synthdev, return);
405 info = &dp->synths[dev];
406 if (! info->opened)
407 return;
408 if (info->sysex)
409 info->sysex->len = 0; /* reset sysex */
410 reset_channels(info);
411 if (info->is_midi) {
412 if (midi_synth_dev.opened <= 0)
413 return;
414 snd_seq_oss_midi_reset(dp, info->midi_mapped);
415 /* reopen the device */
416 snd_seq_oss_midi_close(dp, dev);
417 if (snd_seq_oss_midi_open(dp, info->midi_mapped,
418 dp->file_mode) < 0) {
419 midi_synth_dev.opened--;
420 info->opened = 0;
421 if (info->sysex) {
422 kfree(info->sysex);
423 info->sysex = NULL;
424 }
425 if (info->ch) {
426 kfree(info->ch);
427 info->ch = NULL;
428 }
429 }
430 return;
431 }
432
433 rec = get_sdev(dev);
434 if (rec == NULL)
435 return;
436 if (rec->oper.reset) {
437 rec->oper.reset(&info->arg);
438 } else {
439 snd_seq_event_t ev;
440 memset(&ev, 0, sizeof(ev));
441 snd_seq_oss_fill_addr(dp, &ev, info->arg.addr.client,
442 info->arg.addr.port);
443 ev.type = SNDRV_SEQ_EVENT_RESET;
444 snd_seq_oss_dispatch(dp, &ev, 0, 0);
445 }
446 snd_use_lock_free(&rec->use_lock);
447}
448
449
450/*
451 * load a patch record:
452 * call load_patch callback function
453 */
454int
455snd_seq_oss_synth_load_patch(seq_oss_devinfo_t *dp, int dev, int fmt,
456 const char __user *buf, int p, int c)
457{
458 seq_oss_synth_t *rec;
459 int rc;
460
461 if (dev < 0 || dev >= dp->max_synthdev)
462 return -ENXIO;
463
464 if (is_midi_dev(dp, dev))
465 return 0;
466 if ((rec = get_synthdev(dp, dev)) == NULL)
467 return -ENXIO;
468
469 if (rec->oper.load_patch == NULL)
470 rc = -ENXIO;
471 else
472 rc = rec->oper.load_patch(&dp->synths[dev].arg, fmt, buf, p, c);
473 snd_use_lock_free(&rec->use_lock);
474 return rc;
475}
476
477/*
478 * check if the device is valid synth device
479 */
480int
481snd_seq_oss_synth_is_valid(seq_oss_devinfo_t *dp, int dev)
482{
483 seq_oss_synth_t *rec;
484 rec = get_synthdev(dp, dev);
485 if (rec) {
486 snd_use_lock_free(&rec->use_lock);
487 return 1;
488 }
489 return 0;
490}
491
492
493/*
494 * receive OSS 6 byte sysex packet:
495 * the full sysex message will be sent if it reaches to the end of data
496 * (0xff).
497 */
498int
499snd_seq_oss_synth_sysex(seq_oss_devinfo_t *dp, int dev, unsigned char *buf, snd_seq_event_t *ev)
500{
501 int i, send;
502 unsigned char *dest;
503 seq_oss_synth_sysex_t *sysex;
504
505 if (! snd_seq_oss_synth_is_valid(dp, dev))
506 return -ENXIO;
507
508 sysex = dp->synths[dev].sysex;
509 if (sysex == NULL) {
510 sysex = kcalloc(1, sizeof(*sysex), GFP_KERNEL);
511 if (sysex == NULL)
512 return -ENOMEM;
513 dp->synths[dev].sysex = sysex;
514 }
515
516 send = 0;
517 dest = sysex->buf + sysex->len;
518 /* copy 6 byte packet to the buffer */
519 for (i = 0; i < 6; i++) {
520 if (buf[i] == 0xff) {
521 send = 1;
522 break;
523 }
524 dest[i] = buf[i];
525 sysex->len++;
526 if (sysex->len >= MAX_SYSEX_BUFLEN) {
527 sysex->len = 0;
528 sysex->skip = 1;
529 break;
530 }
531 }
532
533 if (sysex->len && send) {
534 if (sysex->skip) {
535 sysex->skip = 0;
536 sysex->len = 0;
537 return -EINVAL; /* skip */
538 }
539 /* copy the data to event record and send it */
540 ev->flags = SNDRV_SEQ_EVENT_LENGTH_VARIABLE;
541 if (snd_seq_oss_synth_addr(dp, dev, ev))
542 return -EINVAL;
543 ev->data.ext.len = sysex->len;
544 ev->data.ext.ptr = sysex->buf;
545 sysex->len = 0;
546 return 0;
547 }
548
549 return -EINVAL; /* skip */
550}
551
552/*
553 * fill the event source/destination addresses
554 */
555int
556snd_seq_oss_synth_addr(seq_oss_devinfo_t *dp, int dev, snd_seq_event_t *ev)
557{
558 if (! snd_seq_oss_synth_is_valid(dp, dev))
559 return -EINVAL;
560 snd_seq_oss_fill_addr(dp, ev, dp->synths[dev].arg.addr.client,
561 dp->synths[dev].arg.addr.port);
562 return 0;
563}
564
565
566/*
567 * OSS compatible ioctl
568 */
569int
570snd_seq_oss_synth_ioctl(seq_oss_devinfo_t *dp, int dev, unsigned int cmd, unsigned long addr)
571{
572 seq_oss_synth_t *rec;
573 int rc;
574
575 if (is_midi_dev(dp, dev))
576 return -ENXIO;
577 if ((rec = get_synthdev(dp, dev)) == NULL)
578 return -ENXIO;
579 if (rec->oper.ioctl == NULL)
580 rc = -ENXIO;
581 else
582 rc = rec->oper.ioctl(&dp->synths[dev].arg, cmd, addr);
583 snd_use_lock_free(&rec->use_lock);
584 return rc;
585}
586
587
588/*
589 * send OSS raw events - SEQ_PRIVATE and SEQ_VOLUME
590 */
591int
592snd_seq_oss_synth_raw_event(seq_oss_devinfo_t *dp, int dev, unsigned char *data, snd_seq_event_t *ev)
593{
594 if (! snd_seq_oss_synth_is_valid(dp, dev) || is_midi_dev(dp, dev))
595 return -ENXIO;
596 ev->type = SNDRV_SEQ_EVENT_OSS;
597 memcpy(ev->data.raw8.d, data, 8);
598 return snd_seq_oss_synth_addr(dp, dev, ev);
599}
600
601
602/*
603 * create OSS compatible synth_info record
604 */
605int
606snd_seq_oss_synth_make_info(seq_oss_devinfo_t *dp, int dev, struct synth_info *inf)
607{
608 seq_oss_synth_t *rec;
609
610 if (dp->synths[dev].is_midi) {
611 struct midi_info minf;
612 snd_seq_oss_midi_make_info(dp, dp->synths[dev].midi_mapped, &minf);
613 inf->synth_type = SYNTH_TYPE_MIDI;
614 inf->synth_subtype = 0;
615 inf->nr_voices = 16;
616 inf->device = dev;
617 strlcpy(inf->name, minf.name, sizeof(inf->name));
618 } else {
619 if ((rec = get_synthdev(dp, dev)) == NULL)
620 return -ENXIO;
621 inf->synth_type = rec->synth_type;
622 inf->synth_subtype = rec->synth_subtype;
623 inf->nr_voices = rec->nr_voices;
624 inf->device = dev;
625 strlcpy(inf->name, rec->name, sizeof(inf->name));
626 snd_use_lock_free(&rec->use_lock);
627 }
628 return 0;
629}
630
631
632/*
633 * proc interface
634 */
635void
636snd_seq_oss_synth_info_read(snd_info_buffer_t *buf)
637{
638 int i;
639 seq_oss_synth_t *rec;
640
641 snd_iprintf(buf, "\nNumber of synth devices: %d\n", max_synth_devs);
642 for (i = 0; i < max_synth_devs; i++) {
643 snd_iprintf(buf, "\nsynth %d: ", i);
644 rec = get_sdev(i);
645 if (rec == NULL) {
646 snd_iprintf(buf, "*empty*\n");
647 continue;
648 }
649 snd_iprintf(buf, "[%s]\n", rec->name);
650 snd_iprintf(buf, " type 0x%x : subtype 0x%x : voices %d\n",
651 rec->synth_type, rec->synth_subtype,
652 rec->nr_voices);
653 snd_iprintf(buf, " capabilities : ioctl %s / load_patch %s\n",
654 enabled_str((long)rec->oper.ioctl),
655 enabled_str((long)rec->oper.load_patch));
656 snd_use_lock_free(&rec->use_lock);
657 }
658}
659
diff --git a/sound/core/seq/oss/seq_oss_synth.h b/sound/core/seq/oss/seq_oss_synth.h
new file mode 100644
index 000000000000..07bc0e2cfb82
--- /dev/null
+++ b/sound/core/seq/oss/seq_oss_synth.h
@@ -0,0 +1,49 @@
1/*
2 * OSS compatible sequencer driver
3 *
4 * synth device information
5 *
6 * Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (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 General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 */
22
23#ifndef __SEQ_OSS_SYNTH_H
24#define __SEQ_OSS_SYNTH_H
25
26#include "seq_oss_device.h"
27#include <sound/seq_oss_legacy.h>
28#include <sound/seq_device.h>
29
30typedef struct seq_oss_synth_t seq_oss_synth_t;
31
32void snd_seq_oss_synth_init(void);
33int snd_seq_oss_synth_register(snd_seq_device_t *dev);
34int snd_seq_oss_synth_unregister(snd_seq_device_t *dev);
35void snd_seq_oss_synth_setup(seq_oss_devinfo_t *dp);
36void snd_seq_oss_synth_setup_midi(seq_oss_devinfo_t *dp);
37void snd_seq_oss_synth_cleanup(seq_oss_devinfo_t *dp);
38
39void snd_seq_oss_synth_reset(seq_oss_devinfo_t *dp, int dev);
40int snd_seq_oss_synth_load_patch(seq_oss_devinfo_t *dp, int dev, int fmt, const char __user *buf, int p, int c);
41int snd_seq_oss_synth_is_valid(seq_oss_devinfo_t *dp, int dev);
42int snd_seq_oss_synth_sysex(seq_oss_devinfo_t *dp, int dev, unsigned char *buf, snd_seq_event_t *ev);
43int snd_seq_oss_synth_addr(seq_oss_devinfo_t *dp, int dev, snd_seq_event_t *ev);
44int snd_seq_oss_synth_ioctl(seq_oss_devinfo_t *dp, int dev, unsigned int cmd, unsigned long addr);
45int snd_seq_oss_synth_raw_event(seq_oss_devinfo_t *dp, int dev, unsigned char *data, snd_seq_event_t *ev);
46
47int snd_seq_oss_synth_make_info(seq_oss_devinfo_t *dp, int dev, struct synth_info *inf);
48
49#endif
diff --git a/sound/core/seq/oss/seq_oss_timer.c b/sound/core/seq/oss/seq_oss_timer.c
new file mode 100644
index 000000000000..42ca9493fa60
--- /dev/null
+++ b/sound/core/seq/oss/seq_oss_timer.c
@@ -0,0 +1,283 @@
1/*
2 * OSS compatible sequencer driver
3 *
4 * Timer control routines
5 *
6 * Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (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 General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 */
22
23#include "seq_oss_timer.h"
24#include "seq_oss_event.h"
25#include <sound/seq_oss_legacy.h>
26
27/*
28 */
29#define MIN_OSS_TEMPO 8
30#define MAX_OSS_TEMPO 360
31#define MIN_OSS_TIMEBASE 1
32#define MAX_OSS_TIMEBASE 1000
33
34/*
35 */
36static void calc_alsa_tempo(seq_oss_timer_t *timer);
37static int send_timer_event(seq_oss_devinfo_t *dp, int type, int value);
38
39
40/*
41 * create and register a new timer.
42 * if queue is not started yet, start it.
43 */
44seq_oss_timer_t *
45snd_seq_oss_timer_new(seq_oss_devinfo_t *dp)
46{
47 seq_oss_timer_t *rec;
48
49 rec = kcalloc(1, sizeof(*rec), GFP_KERNEL);
50 if (rec == NULL)
51 return NULL;
52
53 rec->dp = dp;
54 rec->cur_tick = 0;
55 rec->realtime = 0;
56 rec->running = 0;
57 rec->oss_tempo = 60;
58 rec->oss_timebase = 100;
59 calc_alsa_tempo(rec);
60
61 return rec;
62}
63
64
65/*
66 * delete timer.
67 * if no more timer exists, stop the queue.
68 */
69void
70snd_seq_oss_timer_delete(seq_oss_timer_t *rec)
71{
72 if (rec) {
73 snd_seq_oss_timer_stop(rec);
74 kfree(rec);
75 }
76}
77
78
79/*
80 * process one timing event
81 * return 1 : event proceseed -- skip this event
82 * 0 : not a timer event -- enqueue this event
83 */
84int
85snd_seq_oss_process_timer_event(seq_oss_timer_t *rec, evrec_t *ev)
86{
87 abstime_t parm = ev->t.time;
88
89 if (ev->t.code == EV_TIMING) {
90 switch (ev->t.cmd) {
91 case TMR_WAIT_REL:
92 parm += rec->cur_tick;
93 rec->realtime = 0;
94 /* continue to next */
95 case TMR_WAIT_ABS:
96 if (parm == 0) {
97 rec->realtime = 1;
98 } else if (parm >= rec->cur_tick) {
99 rec->realtime = 0;
100 rec->cur_tick = parm;
101 }
102 return 1; /* skip this event */
103
104 case TMR_START:
105 snd_seq_oss_timer_start(rec);
106 return 1;
107
108 }
109 } else if (ev->s.code == SEQ_WAIT) {
110 /* time = from 1 to 3 bytes */
111 parm = (ev->echo >> 8) & 0xffffff;
112 if (parm > rec->cur_tick) {
113 /* set next event time */
114 rec->cur_tick = parm;
115 rec->realtime = 0;
116 }
117 return 1;
118 }
119
120 return 0;
121}
122
123
124/*
125 * convert tempo units
126 */
127static void
128calc_alsa_tempo(seq_oss_timer_t *timer)
129{
130 timer->tempo = (60 * 1000000) / timer->oss_tempo;
131 timer->ppq = timer->oss_timebase;
132}
133
134
135/*
136 * dispatch a timer event
137 */
138static int
139send_timer_event(seq_oss_devinfo_t *dp, int type, int value)
140{
141 snd_seq_event_t ev;
142
143 memset(&ev, 0, sizeof(ev));
144 ev.type = type;
145 ev.source.client = dp->cseq;
146 ev.source.port = 0;
147 ev.dest.client = SNDRV_SEQ_CLIENT_SYSTEM;
148 ev.dest.port = SNDRV_SEQ_PORT_SYSTEM_TIMER;
149 ev.queue = dp->queue;
150 ev.data.queue.queue = dp->queue;
151 ev.data.queue.param.value = value;
152 return snd_seq_kernel_client_dispatch(dp->cseq, &ev, 1, 0);
153}
154
155/*
156 * set queue tempo and start queue
157 */
158int
159snd_seq_oss_timer_start(seq_oss_timer_t *timer)
160{
161 seq_oss_devinfo_t *dp = timer->dp;
162 snd_seq_queue_tempo_t tmprec;
163
164 if (timer->running)
165 snd_seq_oss_timer_stop(timer);
166
167 memset(&tmprec, 0, sizeof(tmprec));
168 tmprec.queue = dp->queue;
169 tmprec.ppq = timer->ppq;
170 tmprec.tempo = timer->tempo;
171 snd_seq_set_queue_tempo(dp->cseq, &tmprec);
172
173 send_timer_event(dp, SNDRV_SEQ_EVENT_START, 0);
174 timer->running = 1;
175 timer->cur_tick = 0;
176 return 0;
177}
178
179
180/*
181 * stop queue
182 */
183int
184snd_seq_oss_timer_stop(seq_oss_timer_t *timer)
185{
186 if (! timer->running)
187 return 0;
188 send_timer_event(timer->dp, SNDRV_SEQ_EVENT_STOP, 0);
189 timer->running = 0;
190 return 0;
191}
192
193
194/*
195 * continue queue
196 */
197int
198snd_seq_oss_timer_continue(seq_oss_timer_t *timer)
199{
200 if (timer->running)
201 return 0;
202 send_timer_event(timer->dp, SNDRV_SEQ_EVENT_CONTINUE, 0);
203 timer->running = 1;
204 return 0;
205}
206
207
208/*
209 * change queue tempo
210 */
211int
212snd_seq_oss_timer_tempo(seq_oss_timer_t *timer, int value)
213{
214 if (value < MIN_OSS_TEMPO)
215 value = MIN_OSS_TEMPO;
216 else if (value > MAX_OSS_TEMPO)
217 value = MAX_OSS_TEMPO;
218 timer->oss_tempo = value;
219 calc_alsa_tempo(timer);
220 if (timer->running)
221 send_timer_event(timer->dp, SNDRV_SEQ_EVENT_TEMPO, timer->tempo);
222 return 0;
223}
224
225
226/*
227 * ioctls
228 */
229int
230snd_seq_oss_timer_ioctl(seq_oss_timer_t *timer, unsigned int cmd, int __user *arg)
231{
232 int value;
233
234 if (cmd == SNDCTL_SEQ_CTRLRATE) {
235 debug_printk(("ctrl rate\n"));
236 /* if *arg == 0, just return the current rate */
237 if (get_user(value, arg))
238 return -EFAULT;
239 if (value)
240 return -EINVAL;
241 value = ((timer->oss_tempo * timer->oss_timebase) + 30) / 60;
242 return put_user(value, arg) ? -EFAULT : 0;
243 }
244
245 if (timer->dp->seq_mode == SNDRV_SEQ_OSS_MODE_SYNTH)
246 return 0;
247
248 switch (cmd) {
249 case SNDCTL_TMR_START:
250 debug_printk(("timer start\n"));
251 return snd_seq_oss_timer_start(timer);
252 case SNDCTL_TMR_STOP:
253 debug_printk(("timer stop\n"));
254 return snd_seq_oss_timer_stop(timer);
255 case SNDCTL_TMR_CONTINUE:
256 debug_printk(("timer continue\n"));
257 return snd_seq_oss_timer_continue(timer);
258 case SNDCTL_TMR_TEMPO:
259 debug_printk(("timer tempo\n"));
260 if (get_user(value, arg))
261 return -EFAULT;
262 return snd_seq_oss_timer_tempo(timer, value);
263 case SNDCTL_TMR_TIMEBASE:
264 debug_printk(("timer timebase\n"));
265 if (get_user(value, arg))
266 return -EFAULT;
267 if (value < MIN_OSS_TIMEBASE)
268 value = MIN_OSS_TIMEBASE;
269 else if (value > MAX_OSS_TIMEBASE)
270 value = MAX_OSS_TIMEBASE;
271 timer->oss_timebase = value;
272 calc_alsa_tempo(timer);
273 return 0;
274
275 case SNDCTL_TMR_METRONOME:
276 case SNDCTL_TMR_SELECT:
277 case SNDCTL_TMR_SOURCE:
278 debug_printk(("timer XXX\n"));
279 /* not supported */
280 return 0;
281 }
282 return 0;
283}
diff --git a/sound/core/seq/oss/seq_oss_timer.h b/sound/core/seq/oss/seq_oss_timer.h
new file mode 100644
index 000000000000..6e4dbd8504c1
--- /dev/null
+++ b/sound/core/seq/oss/seq_oss_timer.h
@@ -0,0 +1,70 @@
1/*
2 * OSS compatible sequencer driver
3 * timer handling routines
4 *
5 * Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (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 General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21
22#ifndef __SEQ_OSS_TIMER_H
23#define __SEQ_OSS_TIMER_H
24
25#include "seq_oss_device.h"
26
27/*
28 * timer information definition
29 */
30struct seq_oss_timer_t {
31 seq_oss_devinfo_t *dp;
32 reltime_t cur_tick;
33 int realtime;
34 int running;
35 int tempo, ppq; /* ALSA queue */
36 int oss_tempo, oss_timebase;
37};
38
39
40seq_oss_timer_t *snd_seq_oss_timer_new(seq_oss_devinfo_t *dp);
41void snd_seq_oss_timer_delete(seq_oss_timer_t *dp);
42
43int snd_seq_oss_timer_start(seq_oss_timer_t *timer);
44int snd_seq_oss_timer_stop(seq_oss_timer_t *timer);
45int snd_seq_oss_timer_continue(seq_oss_timer_t *timer);
46int snd_seq_oss_timer_tempo(seq_oss_timer_t *timer, int value);
47#define snd_seq_oss_timer_reset snd_seq_oss_timer_start
48
49int snd_seq_oss_timer_ioctl(seq_oss_timer_t *timer, unsigned int cmd, int __user *arg);
50
51/*
52 * get current processed time
53 */
54static inline abstime_t
55snd_seq_oss_timer_cur_tick(seq_oss_timer_t *timer)
56{
57 return timer->cur_tick;
58}
59
60
61/*
62 * is realtime event?
63 */
64static inline int
65snd_seq_oss_timer_is_realtime(seq_oss_timer_t *timer)
66{
67 return timer->realtime;
68}
69
70#endif
diff --git a/sound/core/seq/oss/seq_oss_writeq.c b/sound/core/seq/oss/seq_oss_writeq.c
new file mode 100644
index 000000000000..87f85f7ee814
--- /dev/null
+++ b/sound/core/seq/oss/seq_oss_writeq.c
@@ -0,0 +1,170 @@
1/*
2 * OSS compatible sequencer driver
3 *
4 * seq_oss_writeq.c - write queue and sync
5 *
6 * Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (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 General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 */
22
23#include "seq_oss_writeq.h"
24#include "seq_oss_event.h"
25#include "seq_oss_timer.h"
26#include <sound/seq_oss_legacy.h>
27#include "../seq_lock.h"
28#include "../seq_clientmgr.h"
29#include <linux/wait.h>
30
31
32/*
33 * create a write queue record
34 */
35seq_oss_writeq_t *
36snd_seq_oss_writeq_new(seq_oss_devinfo_t *dp, int maxlen)
37{
38 seq_oss_writeq_t *q;
39 snd_seq_client_pool_t pool;
40
41 if ((q = kcalloc(1, sizeof(*q), GFP_KERNEL)) == NULL)
42 return NULL;
43 q->dp = dp;
44 q->maxlen = maxlen;
45 spin_lock_init(&q->sync_lock);
46 q->sync_event_put = 0;
47 q->sync_time = 0;
48 init_waitqueue_head(&q->sync_sleep);
49
50 memset(&pool, 0, sizeof(pool));
51 pool.client = dp->cseq;
52 pool.output_pool = maxlen;
53 pool.output_room = maxlen / 2;
54
55 snd_seq_oss_control(dp, SNDRV_SEQ_IOCTL_SET_CLIENT_POOL, &pool);
56
57 return q;
58}
59
60/*
61 * delete the write queue
62 */
63void
64snd_seq_oss_writeq_delete(seq_oss_writeq_t *q)
65{
66 snd_seq_oss_writeq_clear(q); /* to be sure */
67 kfree(q);
68}
69
70
71/*
72 * reset the write queue
73 */
74void
75snd_seq_oss_writeq_clear(seq_oss_writeq_t *q)
76{
77 snd_seq_remove_events_t reset;
78
79 memset(&reset, 0, sizeof(reset));
80 reset.remove_mode = SNDRV_SEQ_REMOVE_OUTPUT; /* remove all */
81 snd_seq_oss_control(q->dp, SNDRV_SEQ_IOCTL_REMOVE_EVENTS, &reset);
82
83 /* wake up sleepers if any */
84 snd_seq_oss_writeq_wakeup(q, 0);
85}
86
87/*
88 * wait until the write buffer has enough room
89 */
90int
91snd_seq_oss_writeq_sync(seq_oss_writeq_t *q)
92{
93 seq_oss_devinfo_t *dp = q->dp;
94 abstime_t time;
95
96 time = snd_seq_oss_timer_cur_tick(dp->timer);
97 if (q->sync_time >= time)
98 return 0; /* already finished */
99
100 if (! q->sync_event_put) {
101 snd_seq_event_t ev;
102 evrec_t *rec;
103
104 /* put echoback event */
105 memset(&ev, 0, sizeof(ev));
106 ev.flags = 0;
107 ev.type = SNDRV_SEQ_EVENT_ECHO;
108 ev.time.tick = time;
109 /* echo back to itself */
110 snd_seq_oss_fill_addr(dp, &ev, dp->addr.client, dp->addr.port);
111 rec = (evrec_t*)&ev.data;
112 rec->t.code = SEQ_SYNCTIMER;
113 rec->t.time = time;
114 q->sync_event_put = 1;
115 snd_seq_kernel_client_enqueue_blocking(dp->cseq, &ev, NULL, 0, 0);
116 }
117
118 wait_event_interruptible_timeout(q->sync_sleep, ! q->sync_event_put, HZ);
119 if (signal_pending(current))
120 /* interrupted - return 0 to finish sync */
121 q->sync_event_put = 0;
122 if (! q->sync_event_put || q->sync_time >= time)
123 return 0;
124 return 1;
125}
126
127/*
128 * wake up sync - echo event was catched
129 */
130void
131snd_seq_oss_writeq_wakeup(seq_oss_writeq_t *q, abstime_t time)
132{
133 unsigned long flags;
134
135 spin_lock_irqsave(&q->sync_lock, flags);
136 q->sync_time = time;
137 q->sync_event_put = 0;
138 if (waitqueue_active(&q->sync_sleep)) {
139 wake_up(&q->sync_sleep);
140 }
141 spin_unlock_irqrestore(&q->sync_lock, flags);
142}
143
144
145/*
146 * return the unused pool size
147 */
148int
149snd_seq_oss_writeq_get_free_size(seq_oss_writeq_t *q)
150{
151 snd_seq_client_pool_t pool;
152 pool.client = q->dp->cseq;
153 snd_seq_oss_control(q->dp, SNDRV_SEQ_IOCTL_GET_CLIENT_POOL, &pool);
154 return pool.output_free;
155}
156
157
158/*
159 * set output threshold size from ioctl
160 */
161void
162snd_seq_oss_writeq_set_output(seq_oss_writeq_t *q, int val)
163{
164 snd_seq_client_pool_t pool;
165 pool.client = q->dp->cseq;
166 snd_seq_oss_control(q->dp, SNDRV_SEQ_IOCTL_GET_CLIENT_POOL, &pool);
167 pool.output_room = val;
168 snd_seq_oss_control(q->dp, SNDRV_SEQ_IOCTL_SET_CLIENT_POOL, &pool);
169}
170
diff --git a/sound/core/seq/oss/seq_oss_writeq.h b/sound/core/seq/oss/seq_oss_writeq.h
new file mode 100644
index 000000000000..6a13c85e2399
--- /dev/null
+++ b/sound/core/seq/oss/seq_oss_writeq.h
@@ -0,0 +1,50 @@
1/*
2 * OSS compatible sequencer driver
3 * write priority queue
4 *
5 * Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (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 General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21
22#ifndef __SEQ_OSS_WRITEQ_H
23#define __SEQ_OSS_WRITEQ_H
24
25#include "seq_oss_device.h"
26
27
28struct seq_oss_writeq_t {
29 seq_oss_devinfo_t *dp;
30 int maxlen;
31 abstime_t sync_time;
32 int sync_event_put;
33 wait_queue_head_t sync_sleep;
34 spinlock_t sync_lock;
35};
36
37
38/*
39 * seq_oss_writeq.c
40 */
41seq_oss_writeq_t *snd_seq_oss_writeq_new(seq_oss_devinfo_t *dp, int maxlen);
42void snd_seq_oss_writeq_delete(seq_oss_writeq_t *q);
43void snd_seq_oss_writeq_clear(seq_oss_writeq_t *q);
44int snd_seq_oss_writeq_sync(seq_oss_writeq_t *q);
45void snd_seq_oss_writeq_wakeup(seq_oss_writeq_t *q, abstime_t time);
46int snd_seq_oss_writeq_get_free_size(seq_oss_writeq_t *q);
47void snd_seq_oss_writeq_set_output(seq_oss_writeq_t *q, int size);
48
49
50#endif
diff --git a/sound/core/seq/seq.c b/sound/core/seq/seq.c
new file mode 100644
index 000000000000..7449d2a62629
--- /dev/null
+++ b/sound/core/seq/seq.c
@@ -0,0 +1,147 @@
1/*
2 * ALSA sequencer main module
3 * Copyright (c) 1998-1999 by Frank van de Pol <fvdpol@coil.demon.nl>
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/moduleparam.h>
25#include <sound/core.h>
26#include <sound/initval.h>
27
28#include <sound/seq_kernel.h>
29#include "seq_clientmgr.h"
30#include "seq_memory.h"
31#include "seq_queue.h"
32#include "seq_lock.h"
33#include "seq_timer.h"
34#include "seq_system.h"
35#include "seq_info.h"
36#include <sound/seq_device.h>
37
38#if defined(CONFIG_SND_SEQ_DUMMY_MODULE)
39int seq_client_load[64] = {[0] = SNDRV_SEQ_CLIENT_DUMMY, [1 ... 63] = -1};
40#else
41int seq_client_load[64] = {[0 ... 63] = -1};
42#endif
43int seq_default_timer_class = SNDRV_TIMER_CLASS_GLOBAL;
44int seq_default_timer_sclass = SNDRV_TIMER_SCLASS_NONE;
45int seq_default_timer_card = -1;
46int seq_default_timer_device = SNDRV_TIMER_GLOBAL_SYSTEM;
47int seq_default_timer_subdevice = 0;
48int seq_default_timer_resolution = 0; /* Hz */
49
50MODULE_AUTHOR("Frank van de Pol <fvdpol@coil.demon.nl>, Jaroslav Kysela <perex@suse.cz>");
51MODULE_DESCRIPTION("Advanced Linux Sound Architecture sequencer.");
52MODULE_LICENSE("GPL");
53
54module_param_array(seq_client_load, int, NULL, 0444);
55MODULE_PARM_DESC(seq_client_load, "The numbers of global (system) clients to load through kmod.");
56module_param(seq_default_timer_class, int, 0644);
57MODULE_PARM_DESC(seq_default_timer_class, "The default timer class.");
58module_param(seq_default_timer_sclass, int, 0644);
59MODULE_PARM_DESC(seq_default_timer_sclass, "The default timer slave class.");
60module_param(seq_default_timer_card, int, 0644);
61MODULE_PARM_DESC(seq_default_timer_card, "The default timer card number.");
62module_param(seq_default_timer_device, int, 0644);
63MODULE_PARM_DESC(seq_default_timer_device, "The default timer device number.");
64module_param(seq_default_timer_subdevice, int, 0644);
65MODULE_PARM_DESC(seq_default_timer_subdevice, "The default timer subdevice number.");
66module_param(seq_default_timer_resolution, int, 0644);
67MODULE_PARM_DESC(seq_default_timer_resolution, "The default timer resolution in Hz.");
68
69/*
70 * INIT PART
71 */
72
73static int __init alsa_seq_init(void)
74{
75 int err;
76
77 snd_seq_autoload_lock();
78 if ((err = client_init_data()) < 0)
79 goto error;
80
81 /* init memory, room for selected events */
82 if ((err = snd_sequencer_memory_init()) < 0)
83 goto error;
84
85 /* init event queues */
86 if ((err = snd_seq_queues_init()) < 0)
87 goto error;
88
89 /* register sequencer device */
90 if ((err = snd_sequencer_device_init()) < 0)
91 goto error;
92
93 /* register proc interface */
94 if ((err = snd_seq_info_init()) < 0)
95 goto error;
96
97 /* register our internal client */
98 if ((err = snd_seq_system_client_init()) < 0)
99 goto error;
100
101 error:
102 snd_seq_autoload_unlock();
103 return err;
104}
105
106static void __exit alsa_seq_exit(void)
107{
108 /* unregister our internal client */
109 snd_seq_system_client_done();
110
111 /* unregister proc interface */
112 snd_seq_info_done();
113
114 /* delete timing queues */
115 snd_seq_queues_delete();
116
117 /* unregister sequencer device */
118 snd_sequencer_device_done();
119
120 /* release event memory */
121 snd_sequencer_memory_done();
122}
123
124module_init(alsa_seq_init)
125module_exit(alsa_seq_exit)
126
127 /* seq_clientmgr.c */
128EXPORT_SYMBOL(snd_seq_create_kernel_client);
129EXPORT_SYMBOL(snd_seq_delete_kernel_client);
130EXPORT_SYMBOL(snd_seq_kernel_client_enqueue);
131EXPORT_SYMBOL(snd_seq_kernel_client_enqueue_blocking);
132EXPORT_SYMBOL(snd_seq_kernel_client_dispatch);
133EXPORT_SYMBOL(snd_seq_kernel_client_ctl);
134EXPORT_SYMBOL(snd_seq_kernel_client_write_poll);
135EXPORT_SYMBOL(snd_seq_set_queue_tempo);
136 /* seq_memory.c */
137EXPORT_SYMBOL(snd_seq_expand_var_event);
138EXPORT_SYMBOL(snd_seq_dump_var_event);
139 /* seq_ports.c */
140EXPORT_SYMBOL(snd_seq_event_port_attach);
141EXPORT_SYMBOL(snd_seq_event_port_detach);
142 /* seq_lock.c */
143#if defined(CONFIG_SMP) || defined(CONFIG_SND_DEBUG)
144/*EXPORT_SYMBOL(snd_seq_sleep_in_lock);*/
145/*EXPORT_SYMBOL(snd_seq_sleep_timeout_in_lock);*/
146EXPORT_SYMBOL(snd_use_lock_sync_helper);
147#endif
diff --git a/sound/core/seq/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c
new file mode 100644
index 000000000000..d8f76afd284b
--- /dev/null
+++ b/sound/core/seq/seq_clientmgr.c
@@ -0,0 +1,2503 @@
1/*
2 * ALSA sequencer Client Manager
3 * Copyright (c) 1998-2001 by Frank van de Pol <fvdpol@coil.demon.nl>
4 * Jaroslav Kysela <perex@suse.cz>
5 * Takashi Iwai <tiwai@suse.de>
6 *
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (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 General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; 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/init.h>
26#include <linux/smp_lock.h>
27#include <linux/slab.h>
28#include <sound/core.h>
29#include <sound/minors.h>
30#include <linux/kmod.h>
31
32#include <sound/seq_kernel.h>
33#include "seq_clientmgr.h"
34#include "seq_memory.h"
35#include "seq_queue.h"
36#include "seq_timer.h"
37#include "seq_info.h"
38#include "seq_system.h"
39#include <sound/seq_device.h>
40#ifdef CONFIG_COMPAT
41#include <linux/compat.h>
42#endif
43
44/* Client Manager
45
46 * this module handles the connections of userland and kernel clients
47 *
48 */
49
50#define SNDRV_SEQ_LFLG_INPUT 0x0001
51#define SNDRV_SEQ_LFLG_OUTPUT 0x0002
52#define SNDRV_SEQ_LFLG_OPEN (SNDRV_SEQ_LFLG_INPUT|SNDRV_SEQ_LFLG_OUTPUT)
53
54static DEFINE_SPINLOCK(clients_lock);
55static DECLARE_MUTEX(register_mutex);
56
57/*
58 * client table
59 */
60static char clienttablock[SNDRV_SEQ_MAX_CLIENTS];
61static client_t *clienttab[SNDRV_SEQ_MAX_CLIENTS];
62static usage_t client_usage;
63
64/*
65 * prototypes
66 */
67static int bounce_error_event(client_t *client, snd_seq_event_t *event, int err, int atomic, int hop);
68static int snd_seq_deliver_single_event(client_t *client, snd_seq_event_t *event, int filter, int atomic, int hop);
69
70/*
71 */
72
73static inline mm_segment_t snd_enter_user(void)
74{
75 mm_segment_t fs = get_fs();
76 set_fs(get_ds());
77 return fs;
78}
79
80static inline void snd_leave_user(mm_segment_t fs)
81{
82 set_fs(fs);
83}
84
85/*
86 */
87static inline unsigned short snd_seq_file_flags(struct file *file)
88{
89 switch (file->f_mode & (FMODE_READ | FMODE_WRITE)) {
90 case FMODE_WRITE:
91 return SNDRV_SEQ_LFLG_OUTPUT;
92 case FMODE_READ:
93 return SNDRV_SEQ_LFLG_INPUT;
94 default:
95 return SNDRV_SEQ_LFLG_OPEN;
96 }
97}
98
99static inline int snd_seq_write_pool_allocated(client_t *client)
100{
101 return snd_seq_total_cells(client->pool) > 0;
102}
103
104/* return pointer to client structure for specified id */
105static client_t *clientptr(int clientid)
106{
107 if (clientid < 0 || clientid >= SNDRV_SEQ_MAX_CLIENTS) {
108 snd_printd("Seq: oops. Trying to get pointer to client %d\n", clientid);
109 return NULL;
110 }
111 return clienttab[clientid];
112}
113
114extern int seq_client_load[];
115
116client_t *snd_seq_client_use_ptr(int clientid)
117{
118 unsigned long flags;
119 client_t *client;
120
121 if (clientid < 0 || clientid >= SNDRV_SEQ_MAX_CLIENTS) {
122 snd_printd("Seq: oops. Trying to get pointer to client %d\n", clientid);
123 return NULL;
124 }
125 spin_lock_irqsave(&clients_lock, flags);
126 client = clientptr(clientid);
127 if (client)
128 goto __lock;
129 if (clienttablock[clientid]) {
130 spin_unlock_irqrestore(&clients_lock, flags);
131 return NULL;
132 }
133 spin_unlock_irqrestore(&clients_lock, flags);
134#ifdef CONFIG_KMOD
135 if (!in_interrupt() && current->fs->root) {
136 static char client_requested[64];
137 static char card_requested[SNDRV_CARDS];
138 if (clientid < 64) {
139 int idx;
140
141 if (! client_requested[clientid] && current->fs->root) {
142 client_requested[clientid] = 1;
143 for (idx = 0; idx < 64; idx++) {
144 if (seq_client_load[idx] < 0)
145 break;
146 if (seq_client_load[idx] == clientid) {
147 request_module("snd-seq-client-%i", clientid);
148 break;
149 }
150 }
151 }
152 } else if (clientid >= 64 && clientid < 128) {
153 int card = (clientid - 64) / 8;
154 if (card < snd_ecards_limit) {
155 if (! card_requested[card]) {
156 card_requested[card] = 1;
157 snd_request_card(card);
158 }
159 snd_seq_device_load_drivers();
160 }
161 }
162 spin_lock_irqsave(&clients_lock, flags);
163 client = clientptr(clientid);
164 if (client)
165 goto __lock;
166 spin_unlock_irqrestore(&clients_lock, flags);
167 }
168#endif
169 return NULL;
170
171 __lock:
172 snd_use_lock_use(&client->use_lock);
173 spin_unlock_irqrestore(&clients_lock, flags);
174 return client;
175}
176
177static void usage_alloc(usage_t * res, int num)
178{
179 res->cur += num;
180 if (res->cur > res->peak)
181 res->peak = res->cur;
182}
183
184static void usage_free(usage_t * res, int num)
185{
186 res->cur -= num;
187}
188
189/* initialise data structures */
190int __init client_init_data(void)
191{
192 /* zap out the client table */
193 memset(&clienttablock, 0, sizeof(clienttablock));
194 memset(&clienttab, 0, sizeof(clienttab));
195 return 0;
196}
197
198
199static client_t *seq_create_client1(int client_index, int poolsize)
200{
201 unsigned long flags;
202 int c;
203 client_t *client;
204
205 /* init client data */
206 client = kcalloc(1, sizeof(*client), GFP_KERNEL);
207 if (client == NULL)
208 return NULL;
209 client->pool = snd_seq_pool_new(poolsize);
210 if (client->pool == NULL) {
211 kfree(client);
212 return NULL;
213 }
214 client->type = NO_CLIENT;
215 snd_use_lock_init(&client->use_lock);
216 rwlock_init(&client->ports_lock);
217 init_MUTEX(&client->ports_mutex);
218 INIT_LIST_HEAD(&client->ports_list_head);
219
220 /* find free slot in the client table */
221 spin_lock_irqsave(&clients_lock, flags);
222 if (client_index < 0) {
223 for (c = 128; c < SNDRV_SEQ_MAX_CLIENTS; c++) {
224 if (clienttab[c] || clienttablock[c])
225 continue;
226 clienttab[client->number = c] = client;
227 spin_unlock_irqrestore(&clients_lock, flags);
228 return client;
229 }
230 } else {
231 if (clienttab[client_index] == NULL && !clienttablock[client_index]) {
232 clienttab[client->number = client_index] = client;
233 spin_unlock_irqrestore(&clients_lock, flags);
234 return client;
235 }
236 }
237 spin_unlock_irqrestore(&clients_lock, flags);
238 snd_seq_pool_delete(&client->pool);
239 kfree(client);
240 return NULL; /* no free slot found or busy, return failure code */
241}
242
243
244static int seq_free_client1(client_t *client)
245{
246 unsigned long flags;
247
248 snd_assert(client != NULL, return -EINVAL);
249 snd_seq_delete_all_ports(client);
250 snd_seq_queue_client_leave(client->number);
251 spin_lock_irqsave(&clients_lock, flags);
252 clienttablock[client->number] = 1;
253 clienttab[client->number] = NULL;
254 spin_unlock_irqrestore(&clients_lock, flags);
255 snd_use_lock_sync(&client->use_lock);
256 snd_seq_queue_client_termination(client->number);
257 if (client->pool)
258 snd_seq_pool_delete(&client->pool);
259 spin_lock_irqsave(&clients_lock, flags);
260 clienttablock[client->number] = 0;
261 spin_unlock_irqrestore(&clients_lock, flags);
262 return 0;
263}
264
265
266static void seq_free_client(client_t * client)
267{
268 down(&register_mutex);
269 switch (client->type) {
270 case NO_CLIENT:
271 snd_printk(KERN_WARNING "Seq: Trying to free unused client %d\n", client->number);
272 break;
273 case USER_CLIENT:
274 case KERNEL_CLIENT:
275 seq_free_client1(client);
276 usage_free(&client_usage, 1);
277 break;
278
279 default:
280 snd_printk(KERN_ERR "Seq: Trying to free client %d with undefined type = %d\n", client->number, client->type);
281 }
282 up(&register_mutex);
283
284 snd_seq_system_client_ev_client_exit(client->number);
285}
286
287
288
289/* -------------------------------------------------------- */
290
291/* create a user client */
292static int snd_seq_open(struct inode *inode, struct file *file)
293{
294 int c, mode; /* client id */
295 client_t *client;
296 user_client_t *user;
297
298 if (down_interruptible(&register_mutex))
299 return -ERESTARTSYS;
300 client = seq_create_client1(-1, SNDRV_SEQ_DEFAULT_EVENTS);
301 if (client == NULL) {
302 up(&register_mutex);
303 return -ENOMEM; /* failure code */
304 }
305
306 mode = snd_seq_file_flags(file);
307 if (mode & SNDRV_SEQ_LFLG_INPUT)
308 client->accept_input = 1;
309 if (mode & SNDRV_SEQ_LFLG_OUTPUT)
310 client->accept_output = 1;
311
312 user = &client->data.user;
313 user->fifo = NULL;
314 user->fifo_pool_size = 0;
315
316 if (mode & SNDRV_SEQ_LFLG_INPUT) {
317 user->fifo_pool_size = SNDRV_SEQ_DEFAULT_CLIENT_EVENTS;
318 user->fifo = snd_seq_fifo_new(user->fifo_pool_size);
319 if (user->fifo == NULL) {
320 seq_free_client1(client);
321 kfree(client);
322 up(&register_mutex);
323 return -ENOMEM;
324 }
325 }
326
327 usage_alloc(&client_usage, 1);
328 client->type = USER_CLIENT;
329 up(&register_mutex);
330
331 c = client->number;
332 file->private_data = client;
333
334 /* fill client data */
335 user->file = file;
336 sprintf(client->name, "Client-%d", c);
337
338 /* make others aware this new client */
339 snd_seq_system_client_ev_client_start(c);
340
341 return 0;
342}
343
344/* delete a user client */
345static int snd_seq_release(struct inode *inode, struct file *file)
346{
347 client_t *client = (client_t *) file->private_data;
348
349 if (client) {
350 seq_free_client(client);
351 if (client->data.user.fifo)
352 snd_seq_fifo_delete(&client->data.user.fifo);
353 kfree(client);
354 }
355
356 return 0;
357}
358
359
360/* handle client read() */
361/* possible error values:
362 * -ENXIO invalid client or file open mode
363 * -ENOSPC FIFO overflow (the flag is cleared after this error report)
364 * -EINVAL no enough user-space buffer to write the whole event
365 * -EFAULT seg. fault during copy to user space
366 */
367static ssize_t snd_seq_read(struct file *file, char __user *buf, size_t count, loff_t *offset)
368{
369 client_t *client = (client_t *) file->private_data;
370 fifo_t *fifo;
371 int err;
372 long result = 0;
373 snd_seq_event_cell_t *cell;
374
375 if (!(snd_seq_file_flags(file) & SNDRV_SEQ_LFLG_INPUT))
376 return -ENXIO;
377
378 if (!access_ok(VERIFY_WRITE, buf, count))
379 return -EFAULT;
380
381 /* check client structures are in place */
382 snd_assert(client != NULL, return -ENXIO);
383
384 if (!client->accept_input || (fifo = client->data.user.fifo) == NULL)
385 return -ENXIO;
386
387 if (atomic_read(&fifo->overflow) > 0) {
388 /* buffer overflow is detected */
389 snd_seq_fifo_clear(fifo);
390 /* return error code */
391 return -ENOSPC;
392 }
393
394 cell = NULL;
395 err = 0;
396 snd_seq_fifo_lock(fifo);
397
398 /* while data available in queue */
399 while (count >= sizeof(snd_seq_event_t)) {
400 int nonblock;
401
402 nonblock = (file->f_flags & O_NONBLOCK) || result > 0;
403 if ((err = snd_seq_fifo_cell_out(fifo, &cell, nonblock)) < 0) {
404 break;
405 }
406 if (snd_seq_ev_is_variable(&cell->event)) {
407 snd_seq_event_t tmpev;
408 tmpev = cell->event;
409 tmpev.data.ext.len &= ~SNDRV_SEQ_EXT_MASK;
410 if (copy_to_user(buf, &tmpev, sizeof(snd_seq_event_t))) {
411 err = -EFAULT;
412 break;
413 }
414 count -= sizeof(snd_seq_event_t);
415 buf += sizeof(snd_seq_event_t);
416 err = snd_seq_expand_var_event(&cell->event, count, (char *)buf, 0, sizeof(snd_seq_event_t));
417 if (err < 0)
418 break;
419 result += err;
420 count -= err;
421 buf += err;
422 } else {
423 if (copy_to_user(buf, &cell->event, sizeof(snd_seq_event_t))) {
424 err = -EFAULT;
425 break;
426 }
427 count -= sizeof(snd_seq_event_t);
428 buf += sizeof(snd_seq_event_t);
429 }
430 snd_seq_cell_free(cell);
431 cell = NULL; /* to be sure */
432 result += sizeof(snd_seq_event_t);
433 }
434
435 if (err < 0) {
436 if (cell)
437 snd_seq_fifo_cell_putback(fifo, cell);
438 if (err == -EAGAIN && result > 0)
439 err = 0;
440 }
441 snd_seq_fifo_unlock(fifo);
442
443 return (err < 0) ? err : result;
444}
445
446
447/*
448 * check access permission to the port
449 */
450static int check_port_perm(client_port_t *port, unsigned int flags)
451{
452 if ((port->capability & flags) != flags)
453 return 0;
454 return flags;
455}
456
457/*
458 * check if the destination client is available, and return the pointer
459 * if filter is non-zero, client filter bitmap is tested.
460 */
461static client_t *get_event_dest_client(snd_seq_event_t *event, int filter)
462{
463 client_t *dest;
464
465 dest = snd_seq_client_use_ptr(event->dest.client);
466 if (dest == NULL)
467 return NULL;
468 if (! dest->accept_input)
469 goto __not_avail;
470 if ((dest->filter & SNDRV_SEQ_FILTER_USE_EVENT) &&
471 ! test_bit(event->type, dest->event_filter))
472 goto __not_avail;
473 if (filter && !(dest->filter & filter))
474 goto __not_avail;
475
476 return dest; /* ok - accessible */
477__not_avail:
478 snd_seq_client_unlock(dest);
479 return NULL;
480}
481
482
483/*
484 * Return the error event.
485 *
486 * If the receiver client is a user client, the original event is
487 * encapsulated in SNDRV_SEQ_EVENT_BOUNCE as variable length event. If
488 * the original event is also variable length, the external data is
489 * copied after the event record.
490 * If the receiver client is a kernel client, the original event is
491 * quoted in SNDRV_SEQ_EVENT_KERNEL_ERROR, since this requires no extra
492 * kmalloc.
493 */
494static int bounce_error_event(client_t *client, snd_seq_event_t *event,
495 int err, int atomic, int hop)
496{
497 snd_seq_event_t bounce_ev;
498 int result;
499
500 if (client == NULL ||
501 ! (client->filter & SNDRV_SEQ_FILTER_BOUNCE) ||
502 ! client->accept_input)
503 return 0; /* ignored */
504
505 /* set up quoted error */
506 memset(&bounce_ev, 0, sizeof(bounce_ev));
507 bounce_ev.type = SNDRV_SEQ_EVENT_KERNEL_ERROR;
508 bounce_ev.flags = SNDRV_SEQ_EVENT_LENGTH_FIXED;
509 bounce_ev.queue = SNDRV_SEQ_QUEUE_DIRECT;
510 bounce_ev.source.client = SNDRV_SEQ_CLIENT_SYSTEM;
511 bounce_ev.source.port = SNDRV_SEQ_PORT_SYSTEM_ANNOUNCE;
512 bounce_ev.dest.client = client->number;
513 bounce_ev.dest.port = event->source.port;
514 bounce_ev.data.quote.origin = event->dest;
515 bounce_ev.data.quote.event = event;
516 bounce_ev.data.quote.value = -err; /* use positive value */
517 result = snd_seq_deliver_single_event(NULL, &bounce_ev, 0, atomic, hop + 1);
518 if (result < 0) {
519 client->event_lost++;
520 return result;
521 }
522
523 return result;
524}
525
526
527/*
528 * rewrite the time-stamp of the event record with the curren time
529 * of the given queue.
530 * return non-zero if updated.
531 */
532static int update_timestamp_of_queue(snd_seq_event_t *event, int queue, int real_time)
533{
534 queue_t *q;
535
536 q = queueptr(queue);
537 if (! q)
538 return 0;
539 event->queue = queue;
540 event->flags &= ~SNDRV_SEQ_TIME_STAMP_MASK;
541 if (real_time) {
542 event->time.time = snd_seq_timer_get_cur_time(q->timer);
543 event->flags |= SNDRV_SEQ_TIME_STAMP_REAL;
544 } else {
545 event->time.tick = snd_seq_timer_get_cur_tick(q->timer);
546 event->flags |= SNDRV_SEQ_TIME_STAMP_TICK;
547 }
548 queuefree(q);
549 return 1;
550}
551
552
553/*
554 * deliver an event to the specified destination.
555 * if filter is non-zero, client filter bitmap is tested.
556 *
557 * RETURN VALUE: 0 : if succeeded
558 * <0 : error
559 */
560static int snd_seq_deliver_single_event(client_t *client,
561 snd_seq_event_t *event,
562 int filter, int atomic, int hop)
563{
564 client_t *dest = NULL;
565 client_port_t *dest_port = NULL;
566 int result = -ENOENT;
567 int direct;
568
569 direct = snd_seq_ev_is_direct(event);
570
571 dest = get_event_dest_client(event, filter);
572 if (dest == NULL)
573 goto __skip;
574 dest_port = snd_seq_port_use_ptr(dest, event->dest.port);
575 if (dest_port == NULL)
576 goto __skip;
577
578 /* check permission */
579 if (! check_port_perm(dest_port, SNDRV_SEQ_PORT_CAP_WRITE)) {
580 result = -EPERM;
581 goto __skip;
582 }
583
584 if (dest_port->timestamping)
585 update_timestamp_of_queue(event, dest_port->time_queue,
586 dest_port->time_real);
587
588 switch (dest->type) {
589 case USER_CLIENT:
590 if (dest->data.user.fifo)
591 result = snd_seq_fifo_event_in(dest->data.user.fifo, event);
592 break;
593
594 case KERNEL_CLIENT:
595 if (dest_port->event_input == NULL)
596 break;
597 result = dest_port->event_input(event, direct, dest_port->private_data, atomic, hop);
598 break;
599 default:
600 break;
601 }
602
603 __skip:
604 if (dest_port)
605 snd_seq_port_unlock(dest_port);
606 if (dest)
607 snd_seq_client_unlock(dest);
608
609 if (result < 0 && !direct) {
610 result = bounce_error_event(client, event, result, atomic, hop);
611 }
612 return result;
613}
614
615
616/*
617 * send the event to all subscribers:
618 */
619static int deliver_to_subscribers(client_t *client,
620 snd_seq_event_t *event,
621 int atomic, int hop)
622{
623 subscribers_t *subs;
624 int err = 0, num_ev = 0;
625 snd_seq_event_t event_saved;
626 client_port_t *src_port;
627 struct list_head *p;
628 port_subs_info_t *grp;
629
630 src_port = snd_seq_port_use_ptr(client, event->source.port);
631 if (src_port == NULL)
632 return -EINVAL; /* invalid source port */
633 /* save original event record */
634 event_saved = *event;
635 grp = &src_port->c_src;
636
637 /* lock list */
638 if (atomic)
639 read_lock(&grp->list_lock);
640 else
641 down_read(&grp->list_mutex);
642 list_for_each(p, &grp->list_head) {
643 subs = list_entry(p, subscribers_t, src_list);
644 event->dest = subs->info.dest;
645 if (subs->info.flags & SNDRV_SEQ_PORT_SUBS_TIMESTAMP)
646 /* convert time according to flag with subscription */
647 update_timestamp_of_queue(event, subs->info.queue,
648 subs->info.flags & SNDRV_SEQ_PORT_SUBS_TIME_REAL);
649 err = snd_seq_deliver_single_event(client, event,
650 0, atomic, hop);
651 if (err < 0)
652 break;
653 num_ev++;
654 /* restore original event record */
655 *event = event_saved;
656 }
657 if (atomic)
658 read_unlock(&grp->list_lock);
659 else
660 up_read(&grp->list_mutex);
661 *event = event_saved; /* restore */
662 snd_seq_port_unlock(src_port);
663 return (err < 0) ? err : num_ev;
664}
665
666
667#ifdef SUPPORT_BROADCAST
668/*
669 * broadcast to all ports:
670 */
671static int port_broadcast_event(client_t *client,
672 snd_seq_event_t *event,
673 int atomic, int hop)
674{
675 int num_ev = 0, err = 0;
676 client_t *dest_client;
677 struct list_head *p;
678
679 dest_client = get_event_dest_client(event, SNDRV_SEQ_FILTER_BROADCAST);
680 if (dest_client == NULL)
681 return 0; /* no matching destination */
682
683 read_lock(&dest_client->ports_lock);
684 list_for_each(p, &dest_client->ports_list_head) {
685 client_port_t *port = list_entry(p, client_port_t, list);
686 event->dest.port = port->addr.port;
687 /* pass NULL as source client to avoid error bounce */
688 err = snd_seq_deliver_single_event(NULL, event,
689 SNDRV_SEQ_FILTER_BROADCAST,
690 atomic, hop);
691 if (err < 0)
692 break;
693 num_ev++;
694 }
695 read_unlock(&dest_client->ports_lock);
696 snd_seq_client_unlock(dest_client);
697 event->dest.port = SNDRV_SEQ_ADDRESS_BROADCAST; /* restore */
698 return (err < 0) ? err : num_ev;
699}
700
701/*
702 * send the event to all clients:
703 * if destination port is also ADDRESS_BROADCAST, deliver to all ports.
704 */
705static int broadcast_event(client_t *client,
706 snd_seq_event_t *event, int atomic, int hop)
707{
708 int err = 0, num_ev = 0;
709 int dest;
710 snd_seq_addr_t addr;
711
712 addr = event->dest; /* save */
713
714 for (dest = 0; dest < SNDRV_SEQ_MAX_CLIENTS; dest++) {
715 /* don't send to itself */
716 if (dest == client->number)
717 continue;
718 event->dest.client = dest;
719 event->dest.port = addr.port;
720 if (addr.port == SNDRV_SEQ_ADDRESS_BROADCAST)
721 err = port_broadcast_event(client, event, atomic, hop);
722 else
723 /* pass NULL as source client to avoid error bounce */
724 err = snd_seq_deliver_single_event(NULL, event,
725 SNDRV_SEQ_FILTER_BROADCAST,
726 atomic, hop);
727 if (err < 0)
728 break;
729 num_ev += err;
730 }
731 event->dest = addr; /* restore */
732 return (err < 0) ? err : num_ev;
733}
734
735
736/* multicast - not supported yet */
737static int multicast_event(client_t *client, snd_seq_event_t *event,
738 int atomic, int hop)
739{
740 snd_printd("seq: multicast not supported yet.\n");
741 return 0; /* ignored */
742}
743#endif /* SUPPORT_BROADCAST */
744
745
746/* deliver an event to the destination port(s).
747 * if the event is to subscribers or broadcast, the event is dispatched
748 * to multiple targets.
749 *
750 * RETURN VALUE: n > 0 : the number of delivered events.
751 * n == 0 : the event was not passed to any client.
752 * n < 0 : error - event was not processed.
753 */
754static int snd_seq_deliver_event(client_t *client, snd_seq_event_t *event,
755 int atomic, int hop)
756{
757 int result;
758
759 hop++;
760 if (hop >= SNDRV_SEQ_MAX_HOPS) {
761 snd_printd("too long delivery path (%d:%d->%d:%d)\n",
762 event->source.client, event->source.port,
763 event->dest.client, event->dest.port);
764 return -EMLINK;
765 }
766
767 if (event->queue == SNDRV_SEQ_ADDRESS_SUBSCRIBERS ||
768 event->dest.client == SNDRV_SEQ_ADDRESS_SUBSCRIBERS)
769 result = deliver_to_subscribers(client, event, atomic, hop);
770#ifdef SUPPORT_BROADCAST
771 else if (event->queue == SNDRV_SEQ_ADDRESS_BROADCAST ||
772 event->dest.client == SNDRV_SEQ_ADDRESS_BROADCAST)
773 result = broadcast_event(client, event, atomic, hop);
774 else if (event->dest.client >= SNDRV_SEQ_MAX_CLIENTS)
775 result = multicast_event(client, event, atomic, hop);
776 else if (event->dest.port == SNDRV_SEQ_ADDRESS_BROADCAST)
777 result = port_broadcast_event(client, event, atomic, hop);
778#endif
779 else
780 result = snd_seq_deliver_single_event(client, event, 0, atomic, hop);
781
782 return result;
783}
784
785/*
786 * dispatch an event cell:
787 * This function is called only from queue check routines in timer
788 * interrupts or after enqueued.
789 * The event cell shall be released or re-queued in this function.
790 *
791 * RETURN VALUE: n > 0 : the number of delivered events.
792 * n == 0 : the event was not passed to any client.
793 * n < 0 : error - event was not processed.
794 */
795int snd_seq_dispatch_event(snd_seq_event_cell_t *cell, int atomic, int hop)
796{
797 client_t *client;
798 int result;
799
800 snd_assert(cell != NULL, return -EINVAL);
801
802 client = snd_seq_client_use_ptr(cell->event.source.client);
803 if (client == NULL) {
804 snd_seq_cell_free(cell); /* release this cell */
805 return -EINVAL;
806 }
807
808 if (cell->event.type == SNDRV_SEQ_EVENT_NOTE) {
809 /* NOTE event:
810 * the event cell is re-used as a NOTE-OFF event and
811 * enqueued again.
812 */
813 snd_seq_event_t tmpev, *ev;
814
815 /* reserve this event to enqueue note-off later */
816 tmpev = cell->event;
817 tmpev.type = SNDRV_SEQ_EVENT_NOTEON;
818 result = snd_seq_deliver_event(client, &tmpev, atomic, hop);
819
820 /*
821 * This was originally a note event. We now re-use the
822 * cell for the note-off event.
823 */
824
825 ev = &cell->event;
826 ev->type = SNDRV_SEQ_EVENT_NOTEOFF;
827 ev->flags |= SNDRV_SEQ_PRIORITY_HIGH;
828
829 /* add the duration time */
830 switch (ev->flags & SNDRV_SEQ_TIME_STAMP_MASK) {
831 case SNDRV_SEQ_TIME_STAMP_TICK:
832 ev->time.tick += ev->data.note.duration;
833 break;
834 case SNDRV_SEQ_TIME_STAMP_REAL:
835 /* unit for duration is ms */
836 ev->time.time.tv_nsec += 1000000 * (ev->data.note.duration % 1000);
837 ev->time.time.tv_sec += ev->data.note.duration / 1000 +
838 ev->time.time.tv_nsec / 1000000000;
839 ev->time.time.tv_nsec %= 1000000000;
840 break;
841 }
842 ev->data.note.velocity = ev->data.note.off_velocity;
843
844 /* Now queue this cell as the note off event */
845 if (snd_seq_enqueue_event(cell, atomic, hop) < 0)
846 snd_seq_cell_free(cell); /* release this cell */
847
848 } else {
849 /* Normal events:
850 * event cell is freed after processing the event
851 */
852
853 result = snd_seq_deliver_event(client, &cell->event, atomic, hop);
854 snd_seq_cell_free(cell);
855 }
856
857 snd_seq_client_unlock(client);
858 return result;
859}
860
861
862/* Allocate a cell from client pool and enqueue it to queue:
863 * if pool is empty and blocking is TRUE, sleep until a new cell is
864 * available.
865 */
866static int snd_seq_client_enqueue_event(client_t *client,
867 snd_seq_event_t *event,
868 struct file *file, int blocking,
869 int atomic, int hop)
870{
871 snd_seq_event_cell_t *cell;
872 int err;
873
874 /* special queue values - force direct passing */
875 if (event->queue == SNDRV_SEQ_ADDRESS_SUBSCRIBERS) {
876 event->dest.client = SNDRV_SEQ_ADDRESS_SUBSCRIBERS;
877 event->queue = SNDRV_SEQ_QUEUE_DIRECT;
878 } else
879#ifdef SUPPORT_BROADCAST
880 if (event->queue == SNDRV_SEQ_ADDRESS_BROADCAST) {
881 event->dest.client = SNDRV_SEQ_ADDRESS_BROADCAST;
882 event->queue = SNDRV_SEQ_QUEUE_DIRECT;
883 }
884#endif
885 if (event->dest.client == SNDRV_SEQ_ADDRESS_SUBSCRIBERS) {
886 /* check presence of source port */
887 client_port_t *src_port = snd_seq_port_use_ptr(client, event->source.port);
888 if (src_port == NULL)
889 return -EINVAL;
890 snd_seq_port_unlock(src_port);
891 }
892
893 /* direct event processing without enqueued */
894 if (snd_seq_ev_is_direct(event)) {
895 if (event->type == SNDRV_SEQ_EVENT_NOTE)
896 return -EINVAL; /* this event must be enqueued! */
897 return snd_seq_deliver_event(client, event, atomic, hop);
898 }
899
900 /* Not direct, normal queuing */
901 if (snd_seq_queue_is_used(event->queue, client->number) <= 0)
902 return -EINVAL; /* invalid queue */
903 if (! snd_seq_write_pool_allocated(client))
904 return -ENXIO; /* queue is not allocated */
905
906 /* allocate an event cell */
907 err = snd_seq_event_dup(client->pool, event, &cell, !blocking || atomic, file);
908 if (err < 0)
909 return err;
910
911 /* we got a cell. enqueue it. */
912 if ((err = snd_seq_enqueue_event(cell, atomic, hop)) < 0) {
913 snd_seq_cell_free(cell);
914 return err;
915 }
916
917 return 0;
918}
919
920
921/*
922 * check validity of event type and data length.
923 * return non-zero if invalid.
924 */
925static int check_event_type_and_length(snd_seq_event_t *ev)
926{
927 switch (snd_seq_ev_length_type(ev)) {
928 case SNDRV_SEQ_EVENT_LENGTH_FIXED:
929 if (snd_seq_ev_is_variable_type(ev))
930 return -EINVAL;
931 break;
932 case SNDRV_SEQ_EVENT_LENGTH_VARIABLE:
933 if (! snd_seq_ev_is_variable_type(ev) ||
934 (ev->data.ext.len & ~SNDRV_SEQ_EXT_MASK) >= SNDRV_SEQ_MAX_EVENT_LEN)
935 return -EINVAL;
936 break;
937 case SNDRV_SEQ_EVENT_LENGTH_VARUSR:
938 if (! snd_seq_ev_is_instr_type(ev) ||
939 ! snd_seq_ev_is_direct(ev))
940 return -EINVAL;
941 break;
942 }
943 return 0;
944}
945
946
947/* handle write() */
948/* possible error values:
949 * -ENXIO invalid client or file open mode
950 * -ENOMEM malloc failed
951 * -EFAULT seg. fault during copy from user space
952 * -EINVAL invalid event
953 * -EAGAIN no space in output pool
954 * -EINTR interrupts while sleep
955 * -EMLINK too many hops
956 * others depends on return value from driver callback
957 */
958static ssize_t snd_seq_write(struct file *file, const char __user *buf, size_t count, loff_t *offset)
959{
960 client_t *client = (client_t *) file->private_data;
961 int written = 0, len;
962 int err = -EINVAL;
963 snd_seq_event_t event;
964
965 if (!(snd_seq_file_flags(file) & SNDRV_SEQ_LFLG_OUTPUT))
966 return -ENXIO;
967
968 /* check client structures are in place */
969 snd_assert(client != NULL, return -ENXIO);
970
971 if (!client->accept_output || client->pool == NULL)
972 return -ENXIO;
973
974 /* allocate the pool now if the pool is not allocated yet */
975 if (client->pool->size > 0 && !snd_seq_write_pool_allocated(client)) {
976 if (snd_seq_pool_init(client->pool) < 0)
977 return -ENOMEM;
978 }
979
980 /* only process whole events */
981 while (count >= sizeof(snd_seq_event_t)) {
982 /* Read in the event header from the user */
983 len = sizeof(event);
984 if (copy_from_user(&event, buf, len)) {
985 err = -EFAULT;
986 break;
987 }
988 event.source.client = client->number; /* fill in client number */
989 /* Check for extension data length */
990 if (check_event_type_and_length(&event)) {
991 err = -EINVAL;
992 break;
993 }
994
995 /* check for special events */
996 if (event.type == SNDRV_SEQ_EVENT_NONE)
997 goto __skip_event;
998 else if (snd_seq_ev_is_reserved(&event)) {
999 err = -EINVAL;
1000 break;
1001 }
1002
1003 if (snd_seq_ev_is_variable(&event)) {
1004 int extlen = event.data.ext.len & ~SNDRV_SEQ_EXT_MASK;
1005 if ((size_t)(extlen + len) > count) {
1006 /* back out, will get an error this time or next */
1007 err = -EINVAL;
1008 break;
1009 }
1010 /* set user space pointer */
1011 event.data.ext.len = extlen | SNDRV_SEQ_EXT_USRPTR;
1012 event.data.ext.ptr = (char*)buf + sizeof(snd_seq_event_t);
1013 len += extlen; /* increment data length */
1014 } else {
1015#ifdef CONFIG_COMPAT
1016 if (client->convert32 && snd_seq_ev_is_varusr(&event)) {
1017 void *ptr = compat_ptr(event.data.raw32.d[1]);
1018 event.data.ext.ptr = ptr;
1019 }
1020#endif
1021 }
1022
1023 /* ok, enqueue it */
1024 err = snd_seq_client_enqueue_event(client, &event, file,
1025 !(file->f_flags & O_NONBLOCK),
1026 0, 0);
1027 if (err < 0)
1028 break;
1029
1030 __skip_event:
1031 /* Update pointers and counts */
1032 count -= len;
1033 buf += len;
1034 written += len;
1035 }
1036
1037 return written ? written : err;
1038}
1039
1040
1041/*
1042 * handle polling
1043 */
1044static unsigned int snd_seq_poll(struct file *file, poll_table * wait)
1045{
1046 client_t *client = (client_t *) file->private_data;
1047 unsigned int mask = 0;
1048
1049 /* check client structures are in place */
1050 snd_assert(client != NULL, return -ENXIO);
1051
1052 if ((snd_seq_file_flags(file) & SNDRV_SEQ_LFLG_INPUT) &&
1053 client->data.user.fifo) {
1054
1055 /* check if data is available in the outqueue */
1056 if (snd_seq_fifo_poll_wait(client->data.user.fifo, file, wait))
1057 mask |= POLLIN | POLLRDNORM;
1058 }
1059
1060 if (snd_seq_file_flags(file) & SNDRV_SEQ_LFLG_OUTPUT) {
1061
1062 /* check if data is available in the pool */
1063 if (!snd_seq_write_pool_allocated(client) ||
1064 snd_seq_pool_poll_wait(client->pool, file, wait))
1065 mask |= POLLOUT | POLLWRNORM;
1066 }
1067
1068 return mask;
1069}
1070
1071
1072/*-----------------------------------------------------*/
1073
1074
1075/* SYSTEM_INFO ioctl() */
1076static int snd_seq_ioctl_system_info(client_t *client, void __user *arg)
1077{
1078 snd_seq_system_info_t info;
1079
1080 memset(&info, 0, sizeof(info));
1081 /* fill the info fields */
1082 info.queues = SNDRV_SEQ_MAX_QUEUES;
1083 info.clients = SNDRV_SEQ_MAX_CLIENTS;
1084 info.ports = 256; /* fixed limit */
1085 info.channels = 256; /* fixed limit */
1086 info.cur_clients = client_usage.cur;
1087 info.cur_queues = snd_seq_queue_get_cur_queues();
1088
1089 if (copy_to_user(arg, &info, sizeof(info)))
1090 return -EFAULT;
1091 return 0;
1092}
1093
1094
1095/* RUNNING_MODE ioctl() */
1096static int snd_seq_ioctl_running_mode(client_t *client, void __user *arg)
1097{
1098 struct sndrv_seq_running_info info;
1099 client_t *cptr;
1100 int err = 0;
1101
1102 if (copy_from_user(&info, arg, sizeof(info)))
1103 return -EFAULT;
1104
1105 /* requested client number */
1106 cptr = snd_seq_client_use_ptr(info.client);
1107 if (cptr == NULL)
1108 return -ENOENT; /* don't change !!! */
1109
1110#ifdef SNDRV_BIG_ENDIAN
1111 if (! info.big_endian) {
1112 err = -EINVAL;
1113 goto __err;
1114 }
1115#else
1116 if (info.big_endian) {
1117 err = -EINVAL;
1118 goto __err;
1119 }
1120
1121#endif
1122 if (info.cpu_mode > sizeof(long)) {
1123 err = -EINVAL;
1124 goto __err;
1125 }
1126 cptr->convert32 = (info.cpu_mode < sizeof(long));
1127 __err:
1128 snd_seq_client_unlock(cptr);
1129 return err;
1130}
1131
1132/* CLIENT_INFO ioctl() */
1133static void get_client_info(client_t *cptr, snd_seq_client_info_t *info)
1134{
1135 info->client = cptr->number;
1136
1137 /* fill the info fields */
1138 info->type = cptr->type;
1139 strcpy(info->name, cptr->name);
1140 info->filter = cptr->filter;
1141 info->event_lost = cptr->event_lost;
1142 memcpy(info->event_filter, cptr->event_filter, 32);
1143 info->num_ports = cptr->num_ports;
1144 memset(info->reserved, 0, sizeof(info->reserved));
1145}
1146
1147static int snd_seq_ioctl_get_client_info(client_t * client, void __user *arg)
1148{
1149 client_t *cptr;
1150 snd_seq_client_info_t client_info;
1151
1152 if (copy_from_user(&client_info, arg, sizeof(client_info)))
1153 return -EFAULT;
1154
1155 /* requested client number */
1156 cptr = snd_seq_client_use_ptr(client_info.client);
1157 if (cptr == NULL)
1158 return -ENOENT; /* don't change !!! */
1159
1160 get_client_info(cptr, &client_info);
1161 snd_seq_client_unlock(cptr);
1162
1163 if (copy_to_user(arg, &client_info, sizeof(client_info)))
1164 return -EFAULT;
1165 return 0;
1166}
1167
1168
1169/* CLIENT_INFO ioctl() */
1170static int snd_seq_ioctl_set_client_info(client_t * client, void __user *arg)
1171{
1172 snd_seq_client_info_t client_info;
1173
1174 if (copy_from_user(&client_info, arg, sizeof(client_info)))
1175 return -EFAULT;
1176
1177 /* it is not allowed to set the info fields for an another client */
1178 if (client->number != client_info.client)
1179 return -EPERM;
1180 /* also client type must be set now */
1181 if (client->type != client_info.type)
1182 return -EINVAL;
1183
1184 /* fill the info fields */
1185 if (client_info.name[0])
1186 strlcpy(client->name, client_info.name, sizeof(client->name));
1187
1188 client->filter = client_info.filter;
1189 client->event_lost = client_info.event_lost;
1190 memcpy(client->event_filter, client_info.event_filter, 32);
1191
1192 return 0;
1193}
1194
1195
1196/*
1197 * CREATE PORT ioctl()
1198 */
1199static int snd_seq_ioctl_create_port(client_t * client, void __user *arg)
1200{
1201 client_port_t *port;
1202 snd_seq_port_info_t info;
1203 snd_seq_port_callback_t *callback;
1204
1205 if (copy_from_user(&info, arg, sizeof(info)))
1206 return -EFAULT;
1207
1208 /* it is not allowed to create the port for an another client */
1209 if (info.addr.client != client->number)
1210 return -EPERM;
1211
1212 port = snd_seq_create_port(client, (info.flags & SNDRV_SEQ_PORT_FLG_GIVEN_PORT) ? info.addr.port : -1);
1213 if (port == NULL)
1214 return -ENOMEM;
1215
1216 if (client->type == USER_CLIENT && info.kernel) {
1217 snd_seq_delete_port(client, port->addr.port);
1218 return -EINVAL;
1219 }
1220 if (client->type == KERNEL_CLIENT) {
1221 if ((callback = info.kernel) != NULL) {
1222 if (callback->owner)
1223 port->owner = callback->owner;
1224 port->private_data = callback->private_data;
1225 port->private_free = callback->private_free;
1226 port->callback_all = callback->callback_all;
1227 port->event_input = callback->event_input;
1228 port->c_src.open = callback->subscribe;
1229 port->c_src.close = callback->unsubscribe;
1230 port->c_dest.open = callback->use;
1231 port->c_dest.close = callback->unuse;
1232 }
1233 }
1234
1235 info.addr = port->addr;
1236
1237 snd_seq_set_port_info(port, &info);
1238 snd_seq_system_client_ev_port_start(port->addr.client, port->addr.port);
1239
1240 if (copy_to_user(arg, &info, sizeof(info)))
1241 return -EFAULT;
1242
1243 return 0;
1244}
1245
1246/*
1247 * DELETE PORT ioctl()
1248 */
1249static int snd_seq_ioctl_delete_port(client_t * client, void __user *arg)
1250{
1251 snd_seq_port_info_t info;
1252 int err;
1253
1254 /* set passed parameters */
1255 if (copy_from_user(&info, arg, sizeof(info)))
1256 return -EFAULT;
1257
1258 /* it is not allowed to remove the port for an another client */
1259 if (info.addr.client != client->number)
1260 return -EPERM;
1261
1262 err = snd_seq_delete_port(client, info.addr.port);
1263 if (err >= 0)
1264 snd_seq_system_client_ev_port_exit(client->number, info.addr.port);
1265 return err;
1266}
1267
1268
1269/*
1270 * GET_PORT_INFO ioctl() (on any client)
1271 */
1272static int snd_seq_ioctl_get_port_info(client_t *client, void __user *arg)
1273{
1274 client_t *cptr;
1275 client_port_t *port;
1276 snd_seq_port_info_t info;
1277
1278 if (copy_from_user(&info, arg, sizeof(info)))
1279 return -EFAULT;
1280 cptr = snd_seq_client_use_ptr(info.addr.client);
1281 if (cptr == NULL)
1282 return -ENXIO;
1283
1284 port = snd_seq_port_use_ptr(cptr, info.addr.port);
1285 if (port == NULL) {
1286 snd_seq_client_unlock(cptr);
1287 return -ENOENT; /* don't change */
1288 }
1289
1290 /* get port info */
1291 snd_seq_get_port_info(port, &info);
1292 snd_seq_port_unlock(port);
1293 snd_seq_client_unlock(cptr);
1294
1295 if (copy_to_user(arg, &info, sizeof(info)))
1296 return -EFAULT;
1297 return 0;
1298}
1299
1300
1301/*
1302 * SET_PORT_INFO ioctl() (only ports on this/own client)
1303 */
1304static int snd_seq_ioctl_set_port_info(client_t * client, void __user *arg)
1305{
1306 client_port_t *port;
1307 snd_seq_port_info_t info;
1308
1309 if (copy_from_user(&info, arg, sizeof(info)))
1310 return -EFAULT;
1311
1312 if (info.addr.client != client->number) /* only set our own ports ! */
1313 return -EPERM;
1314 port = snd_seq_port_use_ptr(client, info.addr.port);
1315 if (port) {
1316 snd_seq_set_port_info(port, &info);
1317 snd_seq_port_unlock(port);
1318 }
1319 return 0;
1320}
1321
1322
1323/*
1324 * port subscription (connection)
1325 */
1326#define PERM_RD (SNDRV_SEQ_PORT_CAP_READ|SNDRV_SEQ_PORT_CAP_SUBS_READ)
1327#define PERM_WR (SNDRV_SEQ_PORT_CAP_WRITE|SNDRV_SEQ_PORT_CAP_SUBS_WRITE)
1328
1329static int check_subscription_permission(client_t *client, client_port_t *sport,
1330 client_port_t *dport,
1331 snd_seq_port_subscribe_t *subs)
1332{
1333 if (client->number != subs->sender.client &&
1334 client->number != subs->dest.client) {
1335 /* connection by third client - check export permission */
1336 if (check_port_perm(sport, SNDRV_SEQ_PORT_CAP_NO_EXPORT))
1337 return -EPERM;
1338 if (check_port_perm(dport, SNDRV_SEQ_PORT_CAP_NO_EXPORT))
1339 return -EPERM;
1340 }
1341
1342 /* check read permission */
1343 /* if sender or receiver is the subscribing client itself,
1344 * no permission check is necessary
1345 */
1346 if (client->number != subs->sender.client) {
1347 if (! check_port_perm(sport, PERM_RD))
1348 return -EPERM;
1349 }
1350 /* check write permission */
1351 if (client->number != subs->dest.client) {
1352 if (! check_port_perm(dport, PERM_WR))
1353 return -EPERM;
1354 }
1355 return 0;
1356}
1357
1358/*
1359 * send an subscription notify event to user client:
1360 * client must be user client.
1361 */
1362int snd_seq_client_notify_subscription(int client, int port,
1363 snd_seq_port_subscribe_t *info, int evtype)
1364{
1365 snd_seq_event_t event;
1366
1367 memset(&event, 0, sizeof(event));
1368 event.type = evtype;
1369 event.data.connect.dest = info->dest;
1370 event.data.connect.sender = info->sender;
1371
1372 return snd_seq_system_notify(client, port, &event); /* non-atomic */
1373}
1374
1375
1376/*
1377 * add to port's subscription list IOCTL interface
1378 */
1379static int snd_seq_ioctl_subscribe_port(client_t * client, void __user *arg)
1380{
1381 int result = -EINVAL;
1382 client_t *receiver = NULL, *sender = NULL;
1383 client_port_t *sport = NULL, *dport = NULL;
1384 snd_seq_port_subscribe_t subs;
1385
1386 if (copy_from_user(&subs, arg, sizeof(subs)))
1387 return -EFAULT;
1388
1389 if ((receiver = snd_seq_client_use_ptr(subs.dest.client)) == NULL)
1390 goto __end;
1391 if ((sender = snd_seq_client_use_ptr(subs.sender.client)) == NULL)
1392 goto __end;
1393 if ((sport = snd_seq_port_use_ptr(sender, subs.sender.port)) == NULL)
1394 goto __end;
1395 if ((dport = snd_seq_port_use_ptr(receiver, subs.dest.port)) == NULL)
1396 goto __end;
1397
1398 result = check_subscription_permission(client, sport, dport, &subs);
1399 if (result < 0)
1400 goto __end;
1401
1402 /* connect them */
1403 result = snd_seq_port_connect(client, sender, sport, receiver, dport, &subs);
1404 if (! result) /* broadcast announce */
1405 snd_seq_client_notify_subscription(SNDRV_SEQ_ADDRESS_SUBSCRIBERS, 0,
1406 &subs, SNDRV_SEQ_EVENT_PORT_SUBSCRIBED);
1407 __end:
1408 if (sport)
1409 snd_seq_port_unlock(sport);
1410 if (dport)
1411 snd_seq_port_unlock(dport);
1412 if (sender)
1413 snd_seq_client_unlock(sender);
1414 if (receiver)
1415 snd_seq_client_unlock(receiver);
1416 return result;
1417}
1418
1419
1420/*
1421 * remove from port's subscription list
1422 */
1423static int snd_seq_ioctl_unsubscribe_port(client_t * client, void __user *arg)
1424{
1425 int result = -ENXIO;
1426 client_t *receiver = NULL, *sender = NULL;
1427 client_port_t *sport = NULL, *dport = NULL;
1428 snd_seq_port_subscribe_t subs;
1429
1430 if (copy_from_user(&subs, arg, sizeof(subs)))
1431 return -EFAULT;
1432
1433 if ((receiver = snd_seq_client_use_ptr(subs.dest.client)) == NULL)
1434 goto __end;
1435 if ((sender = snd_seq_client_use_ptr(subs.sender.client)) == NULL)
1436 goto __end;
1437 if ((sport = snd_seq_port_use_ptr(sender, subs.sender.port)) == NULL)
1438 goto __end;
1439 if ((dport = snd_seq_port_use_ptr(receiver, subs.dest.port)) == NULL)
1440 goto __end;
1441
1442 result = check_subscription_permission(client, sport, dport, &subs);
1443 if (result < 0)
1444 goto __end;
1445
1446 result = snd_seq_port_disconnect(client, sender, sport, receiver, dport, &subs);
1447 if (! result) /* broadcast announce */
1448 snd_seq_client_notify_subscription(SNDRV_SEQ_ADDRESS_SUBSCRIBERS, 0,
1449 &subs, SNDRV_SEQ_EVENT_PORT_UNSUBSCRIBED);
1450 __end:
1451 if (sport)
1452 snd_seq_port_unlock(sport);
1453 if (dport)
1454 snd_seq_port_unlock(dport);
1455 if (sender)
1456 snd_seq_client_unlock(sender);
1457 if (receiver)
1458 snd_seq_client_unlock(receiver);
1459 return result;
1460}
1461
1462
1463/* CREATE_QUEUE ioctl() */
1464static int snd_seq_ioctl_create_queue(client_t *client, void __user *arg)
1465{
1466 snd_seq_queue_info_t info;
1467 int result;
1468 queue_t *q;
1469
1470 if (copy_from_user(&info, arg, sizeof(info)))
1471 return -EFAULT;
1472
1473 result = snd_seq_queue_alloc(client->number, info.locked, info.flags);
1474 if (result < 0)
1475 return result;
1476
1477 q = queueptr(result);
1478 if (q == NULL)
1479 return -EINVAL;
1480
1481 info.queue = q->queue;
1482 info.locked = q->locked;
1483 info.owner = q->owner;
1484
1485 /* set queue name */
1486 if (! info.name[0])
1487 snprintf(info.name, sizeof(info.name), "Queue-%d", q->queue);
1488 strlcpy(q->name, info.name, sizeof(q->name));
1489 queuefree(q);
1490
1491 if (copy_to_user(arg, &info, sizeof(info)))
1492 return -EFAULT;
1493
1494 return 0;
1495}
1496
1497/* DELETE_QUEUE ioctl() */
1498static int snd_seq_ioctl_delete_queue(client_t *client, void __user *arg)
1499{
1500 snd_seq_queue_info_t info;
1501
1502 if (copy_from_user(&info, arg, sizeof(info)))
1503 return -EFAULT;
1504
1505 return snd_seq_queue_delete(client->number, info.queue);
1506}
1507
1508/* GET_QUEUE_INFO ioctl() */
1509static int snd_seq_ioctl_get_queue_info(client_t *client, void __user *arg)
1510{
1511 snd_seq_queue_info_t info;
1512 queue_t *q;
1513
1514 if (copy_from_user(&info, arg, sizeof(info)))
1515 return -EFAULT;
1516
1517 q = queueptr(info.queue);
1518 if (q == NULL)
1519 return -EINVAL;
1520
1521 memset(&info, 0, sizeof(info));
1522 info.queue = q->queue;
1523 info.owner = q->owner;
1524 info.locked = q->locked;
1525 strlcpy(info.name, q->name, sizeof(info.name));
1526 queuefree(q);
1527
1528 if (copy_to_user(arg, &info, sizeof(info)))
1529 return -EFAULT;
1530
1531 return 0;
1532}
1533
1534/* SET_QUEUE_INFO ioctl() */
1535static int snd_seq_ioctl_set_queue_info(client_t *client, void __user *arg)
1536{
1537 snd_seq_queue_info_t info;
1538 queue_t *q;
1539
1540 if (copy_from_user(&info, arg, sizeof(info)))
1541 return -EFAULT;
1542
1543 if (info.owner != client->number)
1544 return -EINVAL;
1545
1546 /* change owner/locked permission */
1547 if (snd_seq_queue_check_access(info.queue, client->number)) {
1548 if (snd_seq_queue_set_owner(info.queue, client->number, info.locked) < 0)
1549 return -EPERM;
1550 if (info.locked)
1551 snd_seq_queue_use(info.queue, client->number, 1);
1552 } else {
1553 return -EPERM;
1554 }
1555
1556 q = queueptr(info.queue);
1557 if (! q)
1558 return -EINVAL;
1559 if (q->owner != client->number) {
1560 queuefree(q);
1561 return -EPERM;
1562 }
1563 strlcpy(q->name, info.name, sizeof(q->name));
1564 queuefree(q);
1565
1566 return 0;
1567}
1568
1569/* GET_NAMED_QUEUE ioctl() */
1570static int snd_seq_ioctl_get_named_queue(client_t *client, void __user *arg)
1571{
1572 snd_seq_queue_info_t info;
1573 queue_t *q;
1574
1575 if (copy_from_user(&info, arg, sizeof(info)))
1576 return -EFAULT;
1577
1578 q = snd_seq_queue_find_name(info.name);
1579 if (q == NULL)
1580 return -EINVAL;
1581 info.queue = q->queue;
1582 info.owner = q->owner;
1583 info.locked = q->locked;
1584 queuefree(q);
1585
1586 if (copy_to_user(arg, &info, sizeof(info)))
1587 return -EFAULT;
1588
1589 return 0;
1590}
1591
1592/* GET_QUEUE_STATUS ioctl() */
1593static int snd_seq_ioctl_get_queue_status(client_t * client, void __user *arg)
1594{
1595 snd_seq_queue_status_t status;
1596 queue_t *queue;
1597 seq_timer_t *tmr;
1598
1599 if (copy_from_user(&status, arg, sizeof(status)))
1600 return -EFAULT;
1601
1602 queue = queueptr(status.queue);
1603 if (queue == NULL)
1604 return -EINVAL;
1605 memset(&status, 0, sizeof(status));
1606 status.queue = queue->queue;
1607
1608 tmr = queue->timer;
1609 status.events = queue->tickq->cells + queue->timeq->cells;
1610
1611 status.time = snd_seq_timer_get_cur_time(tmr);
1612 status.tick = snd_seq_timer_get_cur_tick(tmr);
1613
1614 status.running = tmr->running;
1615
1616 status.flags = queue->flags;
1617 queuefree(queue);
1618
1619 if (copy_to_user(arg, &status, sizeof(status)))
1620 return -EFAULT;
1621 return 0;
1622}
1623
1624
1625/* GET_QUEUE_TEMPO ioctl() */
1626static int snd_seq_ioctl_get_queue_tempo(client_t * client, void __user *arg)
1627{
1628 snd_seq_queue_tempo_t tempo;
1629 queue_t *queue;
1630 seq_timer_t *tmr;
1631
1632 if (copy_from_user(&tempo, arg, sizeof(tempo)))
1633 return -EFAULT;
1634
1635 queue = queueptr(tempo.queue);
1636 if (queue == NULL)
1637 return -EINVAL;
1638 memset(&tempo, 0, sizeof(tempo));
1639 tempo.queue = queue->queue;
1640
1641 tmr = queue->timer;
1642
1643 tempo.tempo = tmr->tempo;
1644 tempo.ppq = tmr->ppq;
1645 tempo.skew_value = tmr->skew;
1646 tempo.skew_base = tmr->skew_base;
1647 queuefree(queue);
1648
1649 if (copy_to_user(arg, &tempo, sizeof(tempo)))
1650 return -EFAULT;
1651 return 0;
1652}
1653
1654
1655/* SET_QUEUE_TEMPO ioctl() */
1656int snd_seq_set_queue_tempo(int client, snd_seq_queue_tempo_t *tempo)
1657{
1658 if (!snd_seq_queue_check_access(tempo->queue, client))
1659 return -EPERM;
1660 return snd_seq_queue_timer_set_tempo(tempo->queue, client, tempo);
1661}
1662
1663static int snd_seq_ioctl_set_queue_tempo(client_t * client, void __user *arg)
1664{
1665 int result;
1666 snd_seq_queue_tempo_t tempo;
1667
1668 if (copy_from_user(&tempo, arg, sizeof(tempo)))
1669 return -EFAULT;
1670
1671 result = snd_seq_set_queue_tempo(client->number, &tempo);
1672 return result < 0 ? result : 0;
1673}
1674
1675
1676/* GET_QUEUE_TIMER ioctl() */
1677static int snd_seq_ioctl_get_queue_timer(client_t * client, void __user *arg)
1678{
1679 snd_seq_queue_timer_t timer;
1680 queue_t *queue;
1681 seq_timer_t *tmr;
1682
1683 if (copy_from_user(&timer, arg, sizeof(timer)))
1684 return -EFAULT;
1685
1686 queue = queueptr(timer.queue);
1687 if (queue == NULL)
1688 return -EINVAL;
1689
1690 if (down_interruptible(&queue->timer_mutex)) {
1691 queuefree(queue);
1692 return -ERESTARTSYS;
1693 }
1694 tmr = queue->timer;
1695 memset(&timer, 0, sizeof(timer));
1696 timer.queue = queue->queue;
1697
1698 timer.type = tmr->type;
1699 if (tmr->type == SNDRV_SEQ_TIMER_ALSA) {
1700 timer.u.alsa.id = tmr->alsa_id;
1701 timer.u.alsa.resolution = tmr->preferred_resolution;
1702 }
1703 up(&queue->timer_mutex);
1704 queuefree(queue);
1705
1706 if (copy_to_user(arg, &timer, sizeof(timer)))
1707 return -EFAULT;
1708 return 0;
1709}
1710
1711
1712/* SET_QUEUE_TIMER ioctl() */
1713static int snd_seq_ioctl_set_queue_timer(client_t * client, void __user *arg)
1714{
1715 int result = 0;
1716 snd_seq_queue_timer_t timer;
1717
1718 if (copy_from_user(&timer, arg, sizeof(timer)))
1719 return -EFAULT;
1720
1721 if (timer.type != SNDRV_SEQ_TIMER_ALSA)
1722 return -EINVAL;
1723
1724 if (snd_seq_queue_check_access(timer.queue, client->number)) {
1725 queue_t *q;
1726 seq_timer_t *tmr;
1727
1728 q = queueptr(timer.queue);
1729 if (q == NULL)
1730 return -ENXIO;
1731 if (down_interruptible(&q->timer_mutex)) {
1732 queuefree(q);
1733 return -ERESTARTSYS;
1734 }
1735 tmr = q->timer;
1736 snd_seq_queue_timer_close(timer.queue);
1737 tmr->type = timer.type;
1738 if (tmr->type == SNDRV_SEQ_TIMER_ALSA) {
1739 tmr->alsa_id = timer.u.alsa.id;
1740 tmr->preferred_resolution = timer.u.alsa.resolution;
1741 }
1742 result = snd_seq_queue_timer_open(timer.queue);
1743 up(&q->timer_mutex);
1744 queuefree(q);
1745 } else {
1746 return -EPERM;
1747 }
1748
1749 return result;
1750}
1751
1752
1753/* GET_QUEUE_CLIENT ioctl() */
1754static int snd_seq_ioctl_get_queue_client(client_t * client, void __user *arg)
1755{
1756 snd_seq_queue_client_t info;
1757 int used;
1758
1759 if (copy_from_user(&info, arg, sizeof(info)))
1760 return -EFAULT;
1761
1762 used = snd_seq_queue_is_used(info.queue, client->number);
1763 if (used < 0)
1764 return -EINVAL;
1765 info.used = used;
1766 info.client = client->number;
1767
1768 if (copy_to_user(arg, &info, sizeof(info)))
1769 return -EFAULT;
1770 return 0;
1771}
1772
1773
1774/* SET_QUEUE_CLIENT ioctl() */
1775static int snd_seq_ioctl_set_queue_client(client_t * client, void __user *arg)
1776{
1777 int err;
1778 snd_seq_queue_client_t info;
1779
1780 if (copy_from_user(&info, arg, sizeof(info)))
1781 return -EFAULT;
1782
1783 if (info.used >= 0) {
1784 err = snd_seq_queue_use(info.queue, client->number, info.used);
1785 if (err < 0)
1786 return err;
1787 }
1788
1789 return snd_seq_ioctl_get_queue_client(client, arg);
1790}
1791
1792
1793/* GET_CLIENT_POOL ioctl() */
1794static int snd_seq_ioctl_get_client_pool(client_t * client, void __user *arg)
1795{
1796 snd_seq_client_pool_t info;
1797 client_t *cptr;
1798
1799 if (copy_from_user(&info, arg, sizeof(info)))
1800 return -EFAULT;
1801
1802 cptr = snd_seq_client_use_ptr(info.client);
1803 if (cptr == NULL)
1804 return -ENOENT;
1805 memset(&info, 0, sizeof(info));
1806 info.output_pool = cptr->pool->size;
1807 info.output_room = cptr->pool->room;
1808 info.output_free = info.output_pool;
1809 if (cptr->pool)
1810 info.output_free = snd_seq_unused_cells(cptr->pool);
1811 if (cptr->type == USER_CLIENT) {
1812 info.input_pool = cptr->data.user.fifo_pool_size;
1813 info.input_free = info.input_pool;
1814 if (cptr->data.user.fifo)
1815 info.input_free = snd_seq_unused_cells(cptr->data.user.fifo->pool);
1816 } else {
1817 info.input_pool = 0;
1818 info.input_free = 0;
1819 }
1820 snd_seq_client_unlock(cptr);
1821
1822 if (copy_to_user(arg, &info, sizeof(info)))
1823 return -EFAULT;
1824 return 0;
1825}
1826
1827/* SET_CLIENT_POOL ioctl() */
1828static int snd_seq_ioctl_set_client_pool(client_t * client, void __user *arg)
1829{
1830 snd_seq_client_pool_t info;
1831 int rc;
1832
1833 if (copy_from_user(&info, arg, sizeof(info)))
1834 return -EFAULT;
1835
1836 if (client->number != info.client)
1837 return -EINVAL; /* can't change other clients */
1838
1839 if (info.output_pool >= 1 && info.output_pool <= SNDRV_SEQ_MAX_EVENTS &&
1840 (! snd_seq_write_pool_allocated(client) ||
1841 info.output_pool != client->pool->size)) {
1842 if (snd_seq_write_pool_allocated(client)) {
1843 /* remove all existing cells */
1844 snd_seq_queue_client_leave_cells(client->number);
1845 snd_seq_pool_done(client->pool);
1846 }
1847 client->pool->size = info.output_pool;
1848 rc = snd_seq_pool_init(client->pool);
1849 if (rc < 0)
1850 return rc;
1851 }
1852 if (client->type == USER_CLIENT && client->data.user.fifo != NULL &&
1853 info.input_pool >= 1 &&
1854 info.input_pool <= SNDRV_SEQ_MAX_CLIENT_EVENTS &&
1855 info.input_pool != client->data.user.fifo_pool_size) {
1856 /* change pool size */
1857 rc = snd_seq_fifo_resize(client->data.user.fifo, info.input_pool);
1858 if (rc < 0)
1859 return rc;
1860 client->data.user.fifo_pool_size = info.input_pool;
1861 }
1862 if (info.output_room >= 1 &&
1863 info.output_room <= client->pool->size) {
1864 client->pool->room = info.output_room;
1865 }
1866
1867 return snd_seq_ioctl_get_client_pool(client, arg);
1868}
1869
1870
1871/* REMOVE_EVENTS ioctl() */
1872static int snd_seq_ioctl_remove_events(client_t * client, void __user *arg)
1873{
1874 snd_seq_remove_events_t info;
1875
1876 if (copy_from_user(&info, arg, sizeof(info)))
1877 return -EFAULT;
1878
1879 /*
1880 * Input mostly not implemented XXX.
1881 */
1882 if (info.remove_mode & SNDRV_SEQ_REMOVE_INPUT) {
1883 /*
1884 * No restrictions so for a user client we can clear
1885 * the whole fifo
1886 */
1887 if (client->type == USER_CLIENT)
1888 snd_seq_fifo_clear(client->data.user.fifo);
1889 }
1890
1891 if (info.remove_mode & SNDRV_SEQ_REMOVE_OUTPUT)
1892 snd_seq_queue_remove_cells(client->number, &info);
1893
1894 return 0;
1895}
1896
1897
1898/*
1899 * get subscription info
1900 */
1901static int snd_seq_ioctl_get_subscription(client_t *client, void __user *arg)
1902{
1903 int result;
1904 client_t *sender = NULL;
1905 client_port_t *sport = NULL;
1906 snd_seq_port_subscribe_t subs;
1907 subscribers_t *p;
1908
1909 if (copy_from_user(&subs, arg, sizeof(subs)))
1910 return -EFAULT;
1911
1912 result = -EINVAL;
1913 if ((sender = snd_seq_client_use_ptr(subs.sender.client)) == NULL)
1914 goto __end;
1915 if ((sport = snd_seq_port_use_ptr(sender, subs.sender.port)) == NULL)
1916 goto __end;
1917 p = snd_seq_port_get_subscription(&sport->c_src, &subs.dest);
1918 if (p) {
1919 result = 0;
1920 subs = p->info;
1921 } else
1922 result = -ENOENT;
1923
1924 __end:
1925 if (sport)
1926 snd_seq_port_unlock(sport);
1927 if (sender)
1928 snd_seq_client_unlock(sender);
1929 if (result >= 0) {
1930 if (copy_to_user(arg, &subs, sizeof(subs)))
1931 return -EFAULT;
1932 }
1933 return result;
1934}
1935
1936
1937/*
1938 * get subscription info - check only its presence
1939 */
1940static int snd_seq_ioctl_query_subs(client_t *client, void __user *arg)
1941{
1942 int result = -ENXIO;
1943 client_t *cptr = NULL;
1944 client_port_t *port = NULL;
1945 snd_seq_query_subs_t subs;
1946 port_subs_info_t *group;
1947 struct list_head *p;
1948 int i;
1949
1950 if (copy_from_user(&subs, arg, sizeof(subs)))
1951 return -EFAULT;
1952
1953 if ((cptr = snd_seq_client_use_ptr(subs.root.client)) == NULL)
1954 goto __end;
1955 if ((port = snd_seq_port_use_ptr(cptr, subs.root.port)) == NULL)
1956 goto __end;
1957
1958 switch (subs.type) {
1959 case SNDRV_SEQ_QUERY_SUBS_READ:
1960 group = &port->c_src;
1961 break;
1962 case SNDRV_SEQ_QUERY_SUBS_WRITE:
1963 group = &port->c_dest;
1964 break;
1965 default:
1966 goto __end;
1967 }
1968
1969 down_read(&group->list_mutex);
1970 /* search for the subscriber */
1971 subs.num_subs = group->count;
1972 i = 0;
1973 result = -ENOENT;
1974 list_for_each(p, &group->list_head) {
1975 if (i++ == subs.index) {
1976 /* found! */
1977 subscribers_t *s;
1978 if (subs.type == SNDRV_SEQ_QUERY_SUBS_READ) {
1979 s = list_entry(p, subscribers_t, src_list);
1980 subs.addr = s->info.dest;
1981 } else {
1982 s = list_entry(p, subscribers_t, dest_list);
1983 subs.addr = s->info.sender;
1984 }
1985 subs.flags = s->info.flags;
1986 subs.queue = s->info.queue;
1987 result = 0;
1988 break;
1989 }
1990 }
1991 up_read(&group->list_mutex);
1992
1993 __end:
1994 if (port)
1995 snd_seq_port_unlock(port);
1996 if (cptr)
1997 snd_seq_client_unlock(cptr);
1998 if (result >= 0) {
1999 if (copy_to_user(arg, &subs, sizeof(subs)))
2000 return -EFAULT;
2001 }
2002 return result;
2003}
2004
2005
2006/*
2007 * query next client
2008 */
2009static int snd_seq_ioctl_query_next_client(client_t *client, void __user *arg)
2010{
2011 client_t *cptr = NULL;
2012 snd_seq_client_info_t info;
2013
2014 if (copy_from_user(&info, arg, sizeof(info)))
2015 return -EFAULT;
2016
2017 /* search for next client */
2018 info.client++;
2019 if (info.client < 0)
2020 info.client = 0;
2021 for (; info.client < SNDRV_SEQ_MAX_CLIENTS; info.client++) {
2022 cptr = snd_seq_client_use_ptr(info.client);
2023 if (cptr)
2024 break; /* found */
2025 }
2026 if (cptr == NULL)
2027 return -ENOENT;
2028
2029 get_client_info(cptr, &info);
2030 snd_seq_client_unlock(cptr);
2031
2032 if (copy_to_user(arg, &info, sizeof(info)))
2033 return -EFAULT;
2034 return 0;
2035}
2036
2037/*
2038 * query next port
2039 */
2040static int snd_seq_ioctl_query_next_port(client_t *client, void __user *arg)
2041{
2042 client_t *cptr;
2043 client_port_t *port = NULL;
2044 snd_seq_port_info_t info;
2045
2046 if (copy_from_user(&info, arg, sizeof(info)))
2047 return -EFAULT;
2048 cptr = snd_seq_client_use_ptr(info.addr.client);
2049 if (cptr == NULL)
2050 return -ENXIO;
2051
2052 /* search for next port */
2053 info.addr.port++;
2054 port = snd_seq_port_query_nearest(cptr, &info);
2055 if (port == NULL) {
2056 snd_seq_client_unlock(cptr);
2057 return -ENOENT;
2058 }
2059
2060 /* get port info */
2061 info.addr = port->addr;
2062 snd_seq_get_port_info(port, &info);
2063 snd_seq_port_unlock(port);
2064 snd_seq_client_unlock(cptr);
2065
2066 if (copy_to_user(arg, &info, sizeof(info)))
2067 return -EFAULT;
2068 return 0;
2069}
2070
2071/* -------------------------------------------------------- */
2072
2073static struct seq_ioctl_table {
2074 unsigned int cmd;
2075 int (*func)(client_t *client, void __user * arg);
2076} ioctl_tables[] = {
2077 { SNDRV_SEQ_IOCTL_SYSTEM_INFO, snd_seq_ioctl_system_info },
2078 { SNDRV_SEQ_IOCTL_RUNNING_MODE, snd_seq_ioctl_running_mode },
2079 { SNDRV_SEQ_IOCTL_GET_CLIENT_INFO, snd_seq_ioctl_get_client_info },
2080 { SNDRV_SEQ_IOCTL_SET_CLIENT_INFO, snd_seq_ioctl_set_client_info },
2081 { SNDRV_SEQ_IOCTL_CREATE_PORT, snd_seq_ioctl_create_port },
2082 { SNDRV_SEQ_IOCTL_DELETE_PORT, snd_seq_ioctl_delete_port },
2083 { SNDRV_SEQ_IOCTL_GET_PORT_INFO, snd_seq_ioctl_get_port_info },
2084 { SNDRV_SEQ_IOCTL_SET_PORT_INFO, snd_seq_ioctl_set_port_info },
2085 { SNDRV_SEQ_IOCTL_SUBSCRIBE_PORT, snd_seq_ioctl_subscribe_port },
2086 { SNDRV_SEQ_IOCTL_UNSUBSCRIBE_PORT, snd_seq_ioctl_unsubscribe_port },
2087 { SNDRV_SEQ_IOCTL_CREATE_QUEUE, snd_seq_ioctl_create_queue },
2088 { SNDRV_SEQ_IOCTL_DELETE_QUEUE, snd_seq_ioctl_delete_queue },
2089 { SNDRV_SEQ_IOCTL_GET_QUEUE_INFO, snd_seq_ioctl_get_queue_info },
2090 { SNDRV_SEQ_IOCTL_SET_QUEUE_INFO, snd_seq_ioctl_set_queue_info },
2091 { SNDRV_SEQ_IOCTL_GET_NAMED_QUEUE, snd_seq_ioctl_get_named_queue },
2092 { SNDRV_SEQ_IOCTL_GET_QUEUE_STATUS, snd_seq_ioctl_get_queue_status },
2093 { SNDRV_SEQ_IOCTL_GET_QUEUE_TEMPO, snd_seq_ioctl_get_queue_tempo },
2094 { SNDRV_SEQ_IOCTL_SET_QUEUE_TEMPO, snd_seq_ioctl_set_queue_tempo },
2095 { SNDRV_SEQ_IOCTL_GET_QUEUE_TIMER, snd_seq_ioctl_get_queue_timer },
2096 { SNDRV_SEQ_IOCTL_SET_QUEUE_TIMER, snd_seq_ioctl_set_queue_timer },
2097 { SNDRV_SEQ_IOCTL_GET_QUEUE_CLIENT, snd_seq_ioctl_get_queue_client },
2098 { SNDRV_SEQ_IOCTL_SET_QUEUE_CLIENT, snd_seq_ioctl_set_queue_client },
2099 { SNDRV_SEQ_IOCTL_GET_CLIENT_POOL, snd_seq_ioctl_get_client_pool },
2100 { SNDRV_SEQ_IOCTL_SET_CLIENT_POOL, snd_seq_ioctl_set_client_pool },
2101 { SNDRV_SEQ_IOCTL_GET_SUBSCRIPTION, snd_seq_ioctl_get_subscription },
2102 { SNDRV_SEQ_IOCTL_QUERY_NEXT_CLIENT, snd_seq_ioctl_query_next_client },
2103 { SNDRV_SEQ_IOCTL_QUERY_NEXT_PORT, snd_seq_ioctl_query_next_port },
2104 { SNDRV_SEQ_IOCTL_REMOVE_EVENTS, snd_seq_ioctl_remove_events },
2105 { SNDRV_SEQ_IOCTL_QUERY_SUBS, snd_seq_ioctl_query_subs },
2106 { 0, NULL },
2107};
2108
2109static int snd_seq_do_ioctl(client_t *client, unsigned int cmd, void __user *arg)
2110{
2111 struct seq_ioctl_table *p;
2112
2113 switch (cmd) {
2114 case SNDRV_SEQ_IOCTL_PVERSION:
2115 /* return sequencer version number */
2116 return put_user(SNDRV_SEQ_VERSION, (int __user *)arg) ? -EFAULT : 0;
2117 case SNDRV_SEQ_IOCTL_CLIENT_ID:
2118 /* return the id of this client */
2119 return put_user(client->number, (int __user *)arg) ? -EFAULT : 0;
2120 }
2121
2122 if (! arg)
2123 return -EFAULT;
2124 for (p = ioctl_tables; p->cmd; p++) {
2125 if (p->cmd == cmd)
2126 return p->func(client, arg);
2127 }
2128 snd_printd("seq unknown ioctl() 0x%x (type='%c', number=0x%2x)\n",
2129 cmd, _IOC_TYPE(cmd), _IOC_NR(cmd));
2130 return -ENOTTY;
2131}
2132
2133
2134static long snd_seq_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
2135{
2136 client_t *client = (client_t *) file->private_data;
2137
2138 snd_assert(client != NULL, return -ENXIO);
2139
2140 return snd_seq_do_ioctl(client, cmd, (void __user *) arg);
2141}
2142
2143#ifdef CONFIG_COMPAT
2144#include "seq_compat.c"
2145#else
2146#define snd_seq_ioctl_compat NULL
2147#endif
2148
2149/* -------------------------------------------------------- */
2150
2151
2152/* exported to kernel modules */
2153int snd_seq_create_kernel_client(snd_card_t *card, int client_index, snd_seq_client_callback_t * callback)
2154{
2155 client_t *client;
2156
2157 snd_assert(! in_interrupt(), return -EBUSY);
2158
2159 if (callback == NULL)
2160 return -EINVAL;
2161 if (card && client_index > 7)
2162 return -EINVAL;
2163 if (card == NULL && client_index > 63)
2164 return -EINVAL;
2165 if (card)
2166 client_index += 64 + (card->number << 3);
2167
2168 if (down_interruptible(&register_mutex))
2169 return -ERESTARTSYS;
2170 /* empty write queue as default */
2171 client = seq_create_client1(client_index, 0);
2172 if (client == NULL) {
2173 up(&register_mutex);
2174 return -EBUSY; /* failure code */
2175 }
2176 usage_alloc(&client_usage, 1);
2177
2178 client->accept_input = callback->allow_output;
2179 client->accept_output = callback->allow_input;
2180
2181 /* fill client data */
2182 client->data.kernel.card = card;
2183 client->data.kernel.private_data = callback->private_data;
2184 sprintf(client->name, "Client-%d", client->number);
2185
2186 client->type = KERNEL_CLIENT;
2187 up(&register_mutex);
2188
2189 /* make others aware this new client */
2190 snd_seq_system_client_ev_client_start(client->number);
2191
2192 /* return client number to caller */
2193 return client->number;
2194}
2195
2196/* exported to kernel modules */
2197int snd_seq_delete_kernel_client(int client)
2198{
2199 client_t *ptr;
2200
2201 snd_assert(! in_interrupt(), return -EBUSY);
2202
2203 ptr = clientptr(client);
2204 if (ptr == NULL)
2205 return -EINVAL;
2206
2207 seq_free_client(ptr);
2208 kfree(ptr);
2209 return 0;
2210}
2211
2212
2213/* skeleton to enqueue event, called from snd_seq_kernel_client_enqueue
2214 * and snd_seq_kernel_client_enqueue_blocking
2215 */
2216static int kernel_client_enqueue(int client, snd_seq_event_t *ev,
2217 struct file *file, int blocking,
2218 int atomic, int hop)
2219{
2220 client_t *cptr;
2221 int result;
2222
2223 snd_assert(ev != NULL, return -EINVAL);
2224
2225 if (ev->type == SNDRV_SEQ_EVENT_NONE)
2226 return 0; /* ignore this */
2227 if (ev->type == SNDRV_SEQ_EVENT_KERNEL_ERROR)
2228 return -EINVAL; /* quoted events can't be enqueued */
2229
2230 /* fill in client number */
2231 ev->source.client = client;
2232
2233 if (check_event_type_and_length(ev))
2234 return -EINVAL;
2235
2236 cptr = snd_seq_client_use_ptr(client);
2237 if (cptr == NULL)
2238 return -EINVAL;
2239
2240 if (! cptr->accept_output)
2241 result = -EPERM;
2242 else /* send it */
2243 result = snd_seq_client_enqueue_event(cptr, ev, file, blocking, atomic, hop);
2244
2245 snd_seq_client_unlock(cptr);
2246 return result;
2247}
2248
2249/*
2250 * exported, called by kernel clients to enqueue events (w/o blocking)
2251 *
2252 * RETURN VALUE: zero if succeed, negative if error
2253 */
2254int snd_seq_kernel_client_enqueue(int client, snd_seq_event_t * ev,
2255 int atomic, int hop)
2256{
2257 return kernel_client_enqueue(client, ev, NULL, 0, atomic, hop);
2258}
2259
2260/*
2261 * exported, called by kernel clients to enqueue events (with blocking)
2262 *
2263 * RETURN VALUE: zero if succeed, negative if error
2264 */
2265int snd_seq_kernel_client_enqueue_blocking(int client, snd_seq_event_t * ev,
2266 struct file *file,
2267 int atomic, int hop)
2268{
2269 return kernel_client_enqueue(client, ev, file, 1, atomic, hop);
2270}
2271
2272
2273/*
2274 * exported, called by kernel clients to dispatch events directly to other
2275 * clients, bypassing the queues. Event time-stamp will be updated.
2276 *
2277 * RETURN VALUE: negative = delivery failed,
2278 * zero, or positive: the number of delivered events
2279 */
2280int snd_seq_kernel_client_dispatch(int client, snd_seq_event_t * ev,
2281 int atomic, int hop)
2282{
2283 client_t *cptr;
2284 int result;
2285
2286 snd_assert(ev != NULL, return -EINVAL);
2287
2288 /* fill in client number */
2289 ev->queue = SNDRV_SEQ_QUEUE_DIRECT;
2290 ev->source.client = client;
2291
2292 if (check_event_type_and_length(ev))
2293 return -EINVAL;
2294
2295 cptr = snd_seq_client_use_ptr(client);
2296 if (cptr == NULL)
2297 return -EINVAL;
2298
2299 if (!cptr->accept_output)
2300 result = -EPERM;
2301 else
2302 result = snd_seq_deliver_event(cptr, ev, atomic, hop);
2303
2304 snd_seq_client_unlock(cptr);
2305 return result;
2306}
2307
2308
2309/*
2310 * exported, called by kernel clients to perform same functions as with
2311 * userland ioctl()
2312 */
2313int snd_seq_kernel_client_ctl(int clientid, unsigned int cmd, void *arg)
2314{
2315 client_t *client;
2316 mm_segment_t fs;
2317 int result;
2318
2319 client = clientptr(clientid);
2320 if (client == NULL)
2321 return -ENXIO;
2322 fs = snd_enter_user();
2323 result = snd_seq_do_ioctl(client, cmd, (void __user *)arg);
2324 snd_leave_user(fs);
2325 return result;
2326}
2327
2328
2329/* exported (for OSS emulator) */
2330int snd_seq_kernel_client_write_poll(int clientid, struct file *file, poll_table *wait)
2331{
2332 client_t *client;
2333
2334 client = clientptr(clientid);
2335 if (client == NULL)
2336 return -ENXIO;
2337
2338 if (! snd_seq_write_pool_allocated(client))
2339 return 1;
2340 if (snd_seq_pool_poll_wait(client->pool, file, wait))
2341 return 1;
2342 return 0;
2343}
2344
2345/*---------------------------------------------------------------------------*/
2346
2347/*
2348 * /proc interface
2349 */
2350static void snd_seq_info_dump_subscribers(snd_info_buffer_t *buffer, port_subs_info_t *group, int is_src, char *msg)
2351{
2352 struct list_head *p;
2353 subscribers_t *s;
2354 int count = 0;
2355
2356 down_read(&group->list_mutex);
2357 if (list_empty(&group->list_head)) {
2358 up_read(&group->list_mutex);
2359 return;
2360 }
2361 snd_iprintf(buffer, msg);
2362 list_for_each(p, &group->list_head) {
2363 if (is_src)
2364 s = list_entry(p, subscribers_t, src_list);
2365 else
2366 s = list_entry(p, subscribers_t, dest_list);
2367 if (count++)
2368 snd_iprintf(buffer, ", ");
2369 snd_iprintf(buffer, "%d:%d",
2370 is_src ? s->info.dest.client : s->info.sender.client,
2371 is_src ? s->info.dest.port : s->info.sender.port);
2372 if (s->info.flags & SNDRV_SEQ_PORT_SUBS_TIMESTAMP)
2373 snd_iprintf(buffer, "[%c:%d]", ((s->info.flags & SNDRV_SEQ_PORT_SUBS_TIME_REAL) ? 'r' : 't'), s->info.queue);
2374 if (group->exclusive)
2375 snd_iprintf(buffer, "[ex]");
2376 }
2377 up_read(&group->list_mutex);
2378 snd_iprintf(buffer, "\n");
2379}
2380
2381#define FLAG_PERM_RD(perm) ((perm) & SNDRV_SEQ_PORT_CAP_READ ? ((perm) & SNDRV_SEQ_PORT_CAP_SUBS_READ ? 'R' : 'r') : '-')
2382#define FLAG_PERM_WR(perm) ((perm) & SNDRV_SEQ_PORT_CAP_WRITE ? ((perm) & SNDRV_SEQ_PORT_CAP_SUBS_WRITE ? 'W' : 'w') : '-')
2383#define FLAG_PERM_EX(perm) ((perm) & SNDRV_SEQ_PORT_CAP_NO_EXPORT ? '-' : 'e')
2384
2385#define FLAG_PERM_DUPLEX(perm) ((perm) & SNDRV_SEQ_PORT_CAP_DUPLEX ? 'X' : '-')
2386
2387static void snd_seq_info_dump_ports(snd_info_buffer_t *buffer, client_t *client)
2388{
2389 struct list_head *l;
2390
2391 down(&client->ports_mutex);
2392 list_for_each(l, &client->ports_list_head) {
2393 client_port_t *p = list_entry(l, client_port_t, list);
2394 snd_iprintf(buffer, " Port %3d : \"%s\" (%c%c%c%c)\n",
2395 p->addr.port, p->name,
2396 FLAG_PERM_RD(p->capability),
2397 FLAG_PERM_WR(p->capability),
2398 FLAG_PERM_EX(p->capability),
2399 FLAG_PERM_DUPLEX(p->capability));
2400 snd_seq_info_dump_subscribers(buffer, &p->c_src, 1, " Connecting To: ");
2401 snd_seq_info_dump_subscribers(buffer, &p->c_dest, 0, " Connected From: ");
2402 }
2403 up(&client->ports_mutex);
2404}
2405
2406
2407/* exported to seq_info.c */
2408void snd_seq_info_clients_read(snd_info_entry_t *entry,
2409 snd_info_buffer_t * buffer)
2410{
2411 extern void snd_seq_info_pool(snd_info_buffer_t * buffer, pool_t * pool, char *space);
2412 int c;
2413 client_t *client;
2414
2415 snd_iprintf(buffer, "Client info\n");
2416 snd_iprintf(buffer, " cur clients : %d\n", client_usage.cur);
2417 snd_iprintf(buffer, " peak clients : %d\n", client_usage.peak);
2418 snd_iprintf(buffer, " max clients : %d\n", SNDRV_SEQ_MAX_CLIENTS);
2419 snd_iprintf(buffer, "\n");
2420
2421 /* list the client table */
2422 for (c = 0; c < SNDRV_SEQ_MAX_CLIENTS; c++) {
2423 client = snd_seq_client_use_ptr(c);
2424 if (client == NULL)
2425 continue;
2426 if (client->type == NO_CLIENT) {
2427 snd_seq_client_unlock(client);
2428 continue;
2429 }
2430
2431 snd_iprintf(buffer, "Client %3d : \"%s\" [%s]\n",
2432 c, client->name,
2433 client->type == USER_CLIENT ? "User" : "Kernel");
2434 snd_seq_info_dump_ports(buffer, client);
2435 if (snd_seq_write_pool_allocated(client)) {
2436 snd_iprintf(buffer, " Output pool :\n");
2437 snd_seq_info_pool(buffer, client->pool, " ");
2438 }
2439 if (client->type == USER_CLIENT && client->data.user.fifo &&
2440 client->data.user.fifo->pool) {
2441 snd_iprintf(buffer, " Input pool :\n");
2442 snd_seq_info_pool(buffer, client->data.user.fifo->pool, " ");
2443 }
2444 snd_seq_client_unlock(client);
2445 }
2446}
2447
2448
2449/*---------------------------------------------------------------------------*/
2450
2451
2452/*
2453 * REGISTRATION PART
2454 */
2455
2456static struct file_operations snd_seq_f_ops =
2457{
2458 .owner = THIS_MODULE,
2459 .read = snd_seq_read,
2460 .write = snd_seq_write,
2461 .open = snd_seq_open,
2462 .release = snd_seq_release,
2463 .poll = snd_seq_poll,
2464 .unlocked_ioctl = snd_seq_ioctl,
2465 .compat_ioctl = snd_seq_ioctl_compat,
2466};
2467
2468static snd_minor_t snd_seq_reg =
2469{
2470 .comment = "sequencer",
2471 .f_ops = &snd_seq_f_ops,
2472};
2473
2474
2475/*
2476 * register sequencer device
2477 */
2478int __init snd_sequencer_device_init(void)
2479{
2480 int err;
2481
2482 if (down_interruptible(&register_mutex))
2483 return -ERESTARTSYS;
2484
2485 if ((err = snd_register_device(SNDRV_DEVICE_TYPE_SEQUENCER, NULL, 0, &snd_seq_reg, "seq")) < 0) {
2486 up(&register_mutex);
2487 return err;
2488 }
2489
2490 up(&register_mutex);
2491
2492 return 0;
2493}
2494
2495
2496
2497/*
2498 * unregister sequencer device
2499 */
2500void __exit snd_sequencer_device_done(void)
2501{
2502 snd_unregister_device(SNDRV_DEVICE_TYPE_SEQUENCER, NULL, 0);
2503}
diff --git a/sound/core/seq/seq_clientmgr.h b/sound/core/seq/seq_clientmgr.h
new file mode 100644
index 000000000000..3715c36183d3
--- /dev/null
+++ b/sound/core/seq/seq_clientmgr.h
@@ -0,0 +1,104 @@
1/*
2 * ALSA sequencer Client Manager
3 * Copyright (c) 1998-1999 by Frank van de Pol <fvdpol@coil.demon.nl>
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#ifndef __SND_SEQ_CLIENTMGR_H
22#define __SND_SEQ_CLIENTMGR_H
23
24#include <sound/seq_kernel.h>
25#include <linux/bitops.h>
26#include "seq_fifo.h"
27#include "seq_ports.h"
28#include "seq_lock.h"
29
30
31/* client manager */
32
33struct _snd_seq_user_client {
34 struct file *file; /* file struct of client */
35 /* ... */
36
37 /* fifo */
38 fifo_t *fifo; /* queue for incoming events */
39 int fifo_pool_size;
40};
41
42struct _snd_seq_kernel_client {
43 snd_card_t *card;
44 /* pointer to client functions */
45 void *private_data; /* private data for client */
46 /* ... */
47};
48
49
50struct _snd_seq_client {
51 snd_seq_client_type_t type;
52 unsigned int accept_input: 1,
53 accept_output: 1;
54 char name[64]; /* client name */
55 int number; /* client number */
56 unsigned int filter; /* filter flags */
57 DECLARE_BITMAP(event_filter, 256);
58 snd_use_lock_t use_lock;
59 int event_lost;
60 /* ports */
61 int num_ports; /* number of ports */
62 struct list_head ports_list_head;
63 rwlock_t ports_lock;
64 struct semaphore ports_mutex;
65 int convert32; /* convert 32->64bit */
66
67 /* output pool */
68 pool_t *pool; /* memory pool for this client */
69
70 union {
71 user_client_t user;
72 kernel_client_t kernel;
73 } data;
74};
75
76/* usage statistics */
77typedef struct {
78 int cur;
79 int peak;
80} usage_t;
81
82
83extern int client_init_data(void);
84extern int snd_sequencer_device_init(void);
85extern void snd_sequencer_device_done(void);
86
87/* get locked pointer to client */
88extern client_t *snd_seq_client_use_ptr(int clientid);
89
90/* unlock pointer to client */
91#define snd_seq_client_unlock(client) snd_use_lock_free(&(client)->use_lock)
92
93/* dispatch event to client(s) */
94extern int snd_seq_dispatch_event(snd_seq_event_cell_t *cell, int atomic, int hop);
95
96/* exported to other modules */
97extern int snd_seq_register_kernel_client(snd_seq_client_callback_t *callback, void *private_data);
98extern int snd_seq_unregister_kernel_client(int client);
99extern int snd_seq_kernel_client_enqueue(int client, snd_seq_event_t *ev, int atomic, int hop);
100int snd_seq_kernel_client_enqueue_blocking(int client, snd_seq_event_t * ev, struct file *file, int atomic, int hop);
101int snd_seq_kernel_client_write_poll(int clientid, struct file *file, poll_table *wait);
102int snd_seq_client_notify_subscription(int client, int port, snd_seq_port_subscribe_t *info, int evtype);
103
104#endif
diff --git a/sound/core/seq/seq_compat.c b/sound/core/seq/seq_compat.c
new file mode 100644
index 000000000000..902ad8b0c355
--- /dev/null
+++ b/sound/core/seq/seq_compat.c
@@ -0,0 +1,137 @@
1/*
2 * 32bit -> 64bit ioctl wrapper for sequencer API
3 * Copyright (c) by Takashi Iwai <tiwai@suse.de>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 *
19 */
20
21/* This file included from seq.c */
22
23#include <linux/compat.h>
24
25struct sndrv_seq_port_info32 {
26 struct sndrv_seq_addr addr; /* client/port numbers */
27 char name[64]; /* port name */
28
29 u32 capability; /* port capability bits */
30 u32 type; /* port type bits */
31 s32 midi_channels; /* channels per MIDI port */
32 s32 midi_voices; /* voices per MIDI port */
33 s32 synth_voices; /* voices per SYNTH port */
34
35 s32 read_use; /* R/O: subscribers for output (from this port) */
36 s32 write_use; /* R/O: subscribers for input (to this port) */
37
38 u32 kernel; /* reserved for kernel use (must be NULL) */
39 u32 flags; /* misc. conditioning */
40 unsigned char time_queue; /* queue # for timestamping */
41 char reserved[59]; /* for future use */
42};
43
44static int snd_seq_call_port_info_ioctl(client_t *client, unsigned int cmd,
45 struct sndrv_seq_port_info32 __user *data32)
46{
47 int err = -EFAULT;
48 snd_seq_port_info_t *data;
49 mm_segment_t fs;
50
51 data = kmalloc(sizeof(*data), GFP_KERNEL);
52 if (! data)
53 return -ENOMEM;
54
55 if (copy_from_user(data, data32, sizeof(*data32)) ||
56 get_user(data->flags, &data32->flags) ||
57 get_user(data->time_queue, &data32->time_queue))
58 goto error;
59 data->kernel = NULL;
60
61 fs = snd_enter_user();
62 err = snd_seq_do_ioctl(client, cmd, data);
63 snd_leave_user(fs);
64 if (err < 0)
65 goto error;
66
67 if (copy_to_user(data32, data, sizeof(*data32)) ||
68 put_user(data->flags, &data32->flags) ||
69 put_user(data->time_queue, &data32->time_queue))
70 err = -EFAULT;
71
72 error:
73 kfree(data);
74 return err;
75}
76
77
78
79/*
80 */
81
82enum {
83 SNDRV_SEQ_IOCTL_CREATE_PORT32 = _IOWR('S', 0x20, struct sndrv_seq_port_info32),
84 SNDRV_SEQ_IOCTL_DELETE_PORT32 = _IOW ('S', 0x21, struct sndrv_seq_port_info32),
85 SNDRV_SEQ_IOCTL_GET_PORT_INFO32 = _IOWR('S', 0x22, struct sndrv_seq_port_info32),
86 SNDRV_SEQ_IOCTL_SET_PORT_INFO32 = _IOW ('S', 0x23, struct sndrv_seq_port_info32),
87 SNDRV_SEQ_IOCTL_QUERY_NEXT_PORT32 = _IOWR('S', 0x52, struct sndrv_seq_port_info32),
88};
89
90static long snd_seq_ioctl_compat(struct file *file, unsigned int cmd, unsigned long arg)
91{
92 client_t *client = (client_t *) file->private_data;
93 void __user *argp = compat_ptr(arg);
94
95 snd_assert(client != NULL, return -ENXIO);
96
97 switch (cmd) {
98 case SNDRV_SEQ_IOCTL_PVERSION:
99 case SNDRV_SEQ_IOCTL_CLIENT_ID:
100 case SNDRV_SEQ_IOCTL_SYSTEM_INFO:
101 case SNDRV_SEQ_IOCTL_GET_CLIENT_INFO:
102 case SNDRV_SEQ_IOCTL_SET_CLIENT_INFO:
103 case SNDRV_SEQ_IOCTL_SUBSCRIBE_PORT:
104 case SNDRV_SEQ_IOCTL_UNSUBSCRIBE_PORT:
105 case SNDRV_SEQ_IOCTL_CREATE_QUEUE:
106 case SNDRV_SEQ_IOCTL_DELETE_QUEUE:
107 case SNDRV_SEQ_IOCTL_GET_QUEUE_INFO:
108 case SNDRV_SEQ_IOCTL_SET_QUEUE_INFO:
109 case SNDRV_SEQ_IOCTL_GET_NAMED_QUEUE:
110 case SNDRV_SEQ_IOCTL_GET_QUEUE_STATUS:
111 case SNDRV_SEQ_IOCTL_GET_QUEUE_TEMPO:
112 case SNDRV_SEQ_IOCTL_SET_QUEUE_TEMPO:
113 case SNDRV_SEQ_IOCTL_GET_QUEUE_TIMER:
114 case SNDRV_SEQ_IOCTL_SET_QUEUE_TIMER:
115 case SNDRV_SEQ_IOCTL_GET_QUEUE_CLIENT:
116 case SNDRV_SEQ_IOCTL_SET_QUEUE_CLIENT:
117 case SNDRV_SEQ_IOCTL_GET_CLIENT_POOL:
118 case SNDRV_SEQ_IOCTL_SET_CLIENT_POOL:
119 case SNDRV_SEQ_IOCTL_REMOVE_EVENTS:
120 case SNDRV_SEQ_IOCTL_QUERY_SUBS:
121 case SNDRV_SEQ_IOCTL_GET_SUBSCRIPTION:
122 case SNDRV_SEQ_IOCTL_QUERY_NEXT_CLIENT:
123 case SNDRV_SEQ_IOCTL_RUNNING_MODE:
124 return snd_seq_do_ioctl(client, cmd, argp);
125 case SNDRV_SEQ_IOCTL_CREATE_PORT32:
126 return snd_seq_call_port_info_ioctl(client, SNDRV_SEQ_IOCTL_CREATE_PORT, argp);
127 case SNDRV_SEQ_IOCTL_DELETE_PORT32:
128 return snd_seq_call_port_info_ioctl(client, SNDRV_SEQ_IOCTL_DELETE_PORT, argp);
129 case SNDRV_SEQ_IOCTL_GET_PORT_INFO32:
130 return snd_seq_call_port_info_ioctl(client, SNDRV_SEQ_IOCTL_GET_PORT_INFO, argp);
131 case SNDRV_SEQ_IOCTL_SET_PORT_INFO32:
132 return snd_seq_call_port_info_ioctl(client, SNDRV_SEQ_IOCTL_SET_PORT_INFO, argp);
133 case SNDRV_SEQ_IOCTL_QUERY_NEXT_PORT32:
134 return snd_seq_call_port_info_ioctl(client, SNDRV_SEQ_IOCTL_QUERY_NEXT_PORT, argp);
135 }
136 return -ENOIOCTLCMD;
137}
diff --git a/sound/core/seq/seq_device.c b/sound/core/seq/seq_device.c
new file mode 100644
index 000000000000..4d80f39612e8
--- /dev/null
+++ b/sound/core/seq/seq_device.c
@@ -0,0 +1,575 @@
1/*
2 * ALSA sequencer device management
3 * Copyright (c) 1999 by Takashi Iwai <tiwai@suse.de>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 *
19 *
20 *----------------------------------------------------------------
21 *
22 * This device handler separates the card driver module from sequencer
23 * stuff (sequencer core, synth drivers, etc), so that user can avoid
24 * to spend unnecessary resources e.g. if he needs only listening to
25 * MP3s.
26 *
27 * The card (or lowlevel) driver creates a sequencer device entry
28 * via snd_seq_device_new(). This is an entry pointer to communicate
29 * with the sequencer device "driver", which is involved with the
30 * actual part to communicate with the sequencer core.
31 * Each sequencer device entry has an id string and the corresponding
32 * driver with the same id is loaded when required. For example,
33 * lowlevel codes to access emu8000 chip on sbawe card are included in
34 * emu8000-synth module. To activate this module, the hardware
35 * resources like i/o port are passed via snd_seq_device argument.
36 *
37 */
38
39#include <sound/driver.h>
40#include <linux/init.h>
41#include <sound/core.h>
42#include <sound/info.h>
43#include <sound/seq_device.h>
44#include <sound/seq_kernel.h>
45#include <sound/initval.h>
46#include <linux/kmod.h>
47#include <linux/slab.h>
48
49MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");
50MODULE_DESCRIPTION("ALSA sequencer device management");
51MODULE_LICENSE("GPL");
52
53/*
54 * driver list
55 */
56typedef struct ops_list ops_list_t;
57
58/* driver state */
59#define DRIVER_EMPTY 0
60#define DRIVER_LOADED (1<<0)
61#define DRIVER_REQUESTED (1<<1)
62#define DRIVER_LOCKED (1<<2)
63
64struct ops_list {
65 char id[ID_LEN]; /* driver id */
66 int driver; /* driver state */
67 int used; /* reference counter */
68 int argsize; /* argument size */
69
70 /* operators */
71 snd_seq_dev_ops_t ops;
72
73 /* registred devices */
74 struct list_head dev_list; /* list of devices */
75 int num_devices; /* number of associated devices */
76 int num_init_devices; /* number of initialized devices */
77 struct semaphore reg_mutex;
78
79 struct list_head list; /* next driver */
80};
81
82
83static LIST_HEAD(opslist);
84static int num_ops;
85static DECLARE_MUTEX(ops_mutex);
86static snd_info_entry_t *info_entry = NULL;
87
88/*
89 * prototypes
90 */
91static int snd_seq_device_free(snd_seq_device_t *dev);
92static int snd_seq_device_dev_free(snd_device_t *device);
93static int snd_seq_device_dev_register(snd_device_t *device);
94static int snd_seq_device_dev_disconnect(snd_device_t *device);
95static int snd_seq_device_dev_unregister(snd_device_t *device);
96
97static int init_device(snd_seq_device_t *dev, ops_list_t *ops);
98static int free_device(snd_seq_device_t *dev, ops_list_t *ops);
99static ops_list_t *find_driver(char *id, int create_if_empty);
100static ops_list_t *create_driver(char *id);
101static void unlock_driver(ops_list_t *ops);
102static void remove_drivers(void);
103
104/*
105 * show all drivers and their status
106 */
107
108static void snd_seq_device_info(snd_info_entry_t *entry, snd_info_buffer_t * buffer)
109{
110 struct list_head *head;
111
112 down(&ops_mutex);
113 list_for_each(head, &opslist) {
114 ops_list_t *ops = list_entry(head, ops_list_t, list);
115 snd_iprintf(buffer, "snd-%s%s%s%s,%d\n",
116 ops->id,
117 ops->driver & DRIVER_LOADED ? ",loaded" : (ops->driver == DRIVER_EMPTY ? ",empty" : ""),
118 ops->driver & DRIVER_REQUESTED ? ",requested" : "",
119 ops->driver & DRIVER_LOCKED ? ",locked" : "",
120 ops->num_devices);
121 }
122 up(&ops_mutex);
123}
124
125/*
126 * load all registered drivers (called from seq_clientmgr.c)
127 */
128
129#ifdef CONFIG_KMOD
130/* avoid auto-loading during module_init() */
131static int snd_seq_in_init;
132void snd_seq_autoload_lock(void)
133{
134 snd_seq_in_init++;
135}
136
137void snd_seq_autoload_unlock(void)
138{
139 snd_seq_in_init--;
140}
141#endif
142
143void snd_seq_device_load_drivers(void)
144{
145#ifdef CONFIG_KMOD
146 struct list_head *head;
147
148 /* Calling request_module during module_init()
149 * may cause blocking.
150 */
151 if (snd_seq_in_init)
152 return;
153
154 if (! current->fs->root)
155 return;
156
157 down(&ops_mutex);
158 list_for_each(head, &opslist) {
159 ops_list_t *ops = list_entry(head, ops_list_t, list);
160 if (! (ops->driver & DRIVER_LOADED) &&
161 ! (ops->driver & DRIVER_REQUESTED)) {
162 ops->used++;
163 up(&ops_mutex);
164 ops->driver |= DRIVER_REQUESTED;
165 request_module("snd-%s", ops->id);
166 down(&ops_mutex);
167 ops->used--;
168 }
169 }
170 up(&ops_mutex);
171#endif
172}
173
174/*
175 * register a sequencer device
176 * card = card info (NULL allowed)
177 * device = device number (if any)
178 * id = id of driver
179 * result = return pointer (NULL allowed if unnecessary)
180 */
181int snd_seq_device_new(snd_card_t *card, int device, char *id, int argsize,
182 snd_seq_device_t **result)
183{
184 snd_seq_device_t *dev;
185 ops_list_t *ops;
186 int err;
187 static snd_device_ops_t dops = {
188 .dev_free = snd_seq_device_dev_free,
189 .dev_register = snd_seq_device_dev_register,
190 .dev_disconnect = snd_seq_device_dev_disconnect,
191 .dev_unregister = snd_seq_device_dev_unregister
192 };
193
194 if (result)
195 *result = NULL;
196
197 snd_assert(id != NULL, return -EINVAL);
198
199 ops = find_driver(id, 1);
200 if (ops == NULL)
201 return -ENOMEM;
202
203 dev = kcalloc(1, sizeof(*dev)*2 + argsize, GFP_KERNEL);
204 if (dev == NULL) {
205 unlock_driver(ops);
206 return -ENOMEM;
207 }
208
209 /* set up device info */
210 dev->card = card;
211 dev->device = device;
212 strlcpy(dev->id, id, sizeof(dev->id));
213 dev->argsize = argsize;
214 dev->status = SNDRV_SEQ_DEVICE_FREE;
215
216 /* add this device to the list */
217 down(&ops->reg_mutex);
218 list_add_tail(&dev->list, &ops->dev_list);
219 ops->num_devices++;
220 up(&ops->reg_mutex);
221
222 unlock_driver(ops);
223
224 if ((err = snd_device_new(card, SNDRV_DEV_SEQUENCER, dev, &dops)) < 0) {
225 snd_seq_device_free(dev);
226 return err;
227 }
228
229 if (result)
230 *result = dev;
231
232 return 0;
233}
234
235/*
236 * free the existing device
237 */
238static int snd_seq_device_free(snd_seq_device_t *dev)
239{
240 ops_list_t *ops;
241
242 snd_assert(dev != NULL, return -EINVAL);
243
244 ops = find_driver(dev->id, 0);
245 if (ops == NULL)
246 return -ENXIO;
247
248 /* remove the device from the list */
249 down(&ops->reg_mutex);
250 list_del(&dev->list);
251 ops->num_devices--;
252 up(&ops->reg_mutex);
253
254 free_device(dev, ops);
255 if (dev->private_free)
256 dev->private_free(dev);
257 kfree(dev);
258
259 unlock_driver(ops);
260
261 return 0;
262}
263
264static int snd_seq_device_dev_free(snd_device_t *device)
265{
266 snd_seq_device_t *dev = device->device_data;
267 return snd_seq_device_free(dev);
268}
269
270/*
271 * register the device
272 */
273static int snd_seq_device_dev_register(snd_device_t *device)
274{
275 snd_seq_device_t *dev = device->device_data;
276 ops_list_t *ops;
277
278 ops = find_driver(dev->id, 0);
279 if (ops == NULL)
280 return -ENOENT;
281
282 /* initialize this device if the corresponding driver was
283 * already loaded
284 */
285 if (ops->driver & DRIVER_LOADED)
286 init_device(dev, ops);
287
288 unlock_driver(ops);
289 return 0;
290}
291
292/*
293 * disconnect the device
294 */
295static int snd_seq_device_dev_disconnect(snd_device_t *device)
296{
297 snd_seq_device_t *dev = device->device_data;
298 ops_list_t *ops;
299
300 ops = find_driver(dev->id, 0);
301 if (ops == NULL)
302 return -ENOENT;
303
304 free_device(dev, ops);
305
306 unlock_driver(ops);
307 return 0;
308}
309
310/*
311 * unregister the existing device
312 */
313static int snd_seq_device_dev_unregister(snd_device_t *device)
314{
315 snd_seq_device_t *dev = device->device_data;
316 return snd_seq_device_free(dev);
317}
318
319/*
320 * register device driver
321 * id = driver id
322 * entry = driver operators - duplicated to each instance
323 */
324int snd_seq_device_register_driver(char *id, snd_seq_dev_ops_t *entry, int argsize)
325{
326 struct list_head *head;
327 ops_list_t *ops;
328
329 if (id == NULL || entry == NULL ||
330 entry->init_device == NULL || entry->free_device == NULL)
331 return -EINVAL;
332
333 snd_seq_autoload_lock();
334 ops = find_driver(id, 1);
335 if (ops == NULL) {
336 snd_seq_autoload_unlock();
337 return -ENOMEM;
338 }
339 if (ops->driver & DRIVER_LOADED) {
340 snd_printk(KERN_WARNING "driver_register: driver '%s' already exists\n", id);
341 unlock_driver(ops);
342 snd_seq_autoload_unlock();
343 return -EBUSY;
344 }
345
346 down(&ops->reg_mutex);
347 /* copy driver operators */
348 ops->ops = *entry;
349 ops->driver |= DRIVER_LOADED;
350 ops->argsize = argsize;
351
352 /* initialize existing devices if necessary */
353 list_for_each(head, &ops->dev_list) {
354 snd_seq_device_t *dev = list_entry(head, snd_seq_device_t, list);
355 init_device(dev, ops);
356 }
357 up(&ops->reg_mutex);
358
359 unlock_driver(ops);
360 snd_seq_autoload_unlock();
361
362 return 0;
363}
364
365
366/*
367 * create driver record
368 */
369static ops_list_t * create_driver(char *id)
370{
371 ops_list_t *ops;
372
373 ops = kmalloc(sizeof(*ops), GFP_KERNEL);
374 if (ops == NULL)
375 return ops;
376 memset(ops, 0, sizeof(*ops));
377
378 /* set up driver entry */
379 strlcpy(ops->id, id, sizeof(ops->id));
380 init_MUTEX(&ops->reg_mutex);
381 ops->driver = DRIVER_EMPTY;
382 INIT_LIST_HEAD(&ops->dev_list);
383 /* lock this instance */
384 ops->used = 1;
385
386 /* register driver entry */
387 down(&ops_mutex);
388 list_add_tail(&ops->list, &opslist);
389 num_ops++;
390 up(&ops_mutex);
391
392 return ops;
393}
394
395
396/*
397 * unregister the specified driver
398 */
399int snd_seq_device_unregister_driver(char *id)
400{
401 struct list_head *head;
402 ops_list_t *ops;
403
404 ops = find_driver(id, 0);
405 if (ops == NULL)
406 return -ENXIO;
407 if (! (ops->driver & DRIVER_LOADED) ||
408 (ops->driver & DRIVER_LOCKED)) {
409 snd_printk(KERN_ERR "driver_unregister: cannot unload driver '%s': status=%x\n", id, ops->driver);
410 unlock_driver(ops);
411 return -EBUSY;
412 }
413
414 /* close and release all devices associated with this driver */
415 down(&ops->reg_mutex);
416 ops->driver |= DRIVER_LOCKED; /* do not remove this driver recursively */
417 list_for_each(head, &ops->dev_list) {
418 snd_seq_device_t *dev = list_entry(head, snd_seq_device_t, list);
419 free_device(dev, ops);
420 }
421
422 ops->driver = 0;
423 if (ops->num_init_devices > 0)
424 snd_printk(KERN_ERR "free_driver: init_devices > 0!! (%d)\n", ops->num_init_devices);
425 up(&ops->reg_mutex);
426
427 unlock_driver(ops);
428
429 /* remove empty driver entries */
430 remove_drivers();
431
432 return 0;
433}
434
435
436/*
437 * remove empty driver entries
438 */
439static void remove_drivers(void)
440{
441 struct list_head *head;
442
443 down(&ops_mutex);
444 head = opslist.next;
445 while (head != &opslist) {
446 ops_list_t *ops = list_entry(head, ops_list_t, list);
447 if (! (ops->driver & DRIVER_LOADED) &&
448 ops->used == 0 && ops->num_devices == 0) {
449 head = head->next;
450 list_del(&ops->list);
451 kfree(ops);
452 num_ops--;
453 } else
454 head = head->next;
455 }
456 up(&ops_mutex);
457}
458
459/*
460 * initialize the device - call init_device operator
461 */
462static int init_device(snd_seq_device_t *dev, ops_list_t *ops)
463{
464 if (! (ops->driver & DRIVER_LOADED))
465 return 0; /* driver is not loaded yet */
466 if (dev->status != SNDRV_SEQ_DEVICE_FREE)
467 return 0; /* already initialized */
468 if (ops->argsize != dev->argsize) {
469 snd_printk(KERN_ERR "incompatible device '%s' for plug-in '%s' (%d %d)\n", dev->name, ops->id, ops->argsize, dev->argsize);
470 return -EINVAL;
471 }
472 if (ops->ops.init_device(dev) >= 0) {
473 dev->status = SNDRV_SEQ_DEVICE_REGISTERED;
474 ops->num_init_devices++;
475 } else {
476 snd_printk(KERN_ERR "init_device failed: %s: %s\n", dev->name, dev->id);
477 }
478
479 return 0;
480}
481
482/*
483 * release the device - call free_device operator
484 */
485static int free_device(snd_seq_device_t *dev, ops_list_t *ops)
486{
487 int result;
488
489 if (! (ops->driver & DRIVER_LOADED))
490 return 0; /* driver is not loaded yet */
491 if (dev->status != SNDRV_SEQ_DEVICE_REGISTERED)
492 return 0; /* not registered */
493 if (ops->argsize != dev->argsize) {
494 snd_printk(KERN_ERR "incompatible device '%s' for plug-in '%s' (%d %d)\n", dev->name, ops->id, ops->argsize, dev->argsize);
495 return -EINVAL;
496 }
497 if ((result = ops->ops.free_device(dev)) >= 0 || result == -ENXIO) {
498 dev->status = SNDRV_SEQ_DEVICE_FREE;
499 dev->driver_data = NULL;
500 ops->num_init_devices--;
501 } else {
502 snd_printk(KERN_ERR "free_device failed: %s: %s\n", dev->name, dev->id);
503 }
504
505 return 0;
506}
507
508/*
509 * find the matching driver with given id
510 */
511static ops_list_t * find_driver(char *id, int create_if_empty)
512{
513 struct list_head *head;
514
515 down(&ops_mutex);
516 list_for_each(head, &opslist) {
517 ops_list_t *ops = list_entry(head, ops_list_t, list);
518 if (strcmp(ops->id, id) == 0) {
519 ops->used++;
520 up(&ops_mutex);
521 return ops;
522 }
523 }
524 up(&ops_mutex);
525 if (create_if_empty)
526 return create_driver(id);
527 return NULL;
528}
529
530static void unlock_driver(ops_list_t *ops)
531{
532 down(&ops_mutex);
533 ops->used--;
534 up(&ops_mutex);
535}
536
537
538/*
539 * module part
540 */
541
542static int __init alsa_seq_device_init(void)
543{
544 info_entry = snd_info_create_module_entry(THIS_MODULE, "drivers", snd_seq_root);
545 if (info_entry == NULL)
546 return -ENOMEM;
547 info_entry->content = SNDRV_INFO_CONTENT_TEXT;
548 info_entry->c.text.read_size = 2048;
549 info_entry->c.text.read = snd_seq_device_info;
550 if (snd_info_register(info_entry) < 0) {
551 snd_info_free_entry(info_entry);
552 return -ENOMEM;
553 }
554 return 0;
555}
556
557static void __exit alsa_seq_device_exit(void)
558{
559 remove_drivers();
560 snd_info_unregister(info_entry);
561 if (num_ops)
562 snd_printk(KERN_ERR "drivers not released (%d)\n", num_ops);
563}
564
565module_init(alsa_seq_device_init)
566module_exit(alsa_seq_device_exit)
567
568EXPORT_SYMBOL(snd_seq_device_load_drivers);
569EXPORT_SYMBOL(snd_seq_device_new);
570EXPORT_SYMBOL(snd_seq_device_register_driver);
571EXPORT_SYMBOL(snd_seq_device_unregister_driver);
572#ifdef CONFIG_KMOD
573EXPORT_SYMBOL(snd_seq_autoload_lock);
574EXPORT_SYMBOL(snd_seq_autoload_unlock);
575#endif
diff --git a/sound/core/seq/seq_dummy.c b/sound/core/seq/seq_dummy.c
new file mode 100644
index 000000000000..e88967c5b93d
--- /dev/null
+++ b/sound/core/seq/seq_dummy.c
@@ -0,0 +1,273 @@
1/*
2 * ALSA sequencer MIDI-through client
3 * Copyright (c) 1999-2000 by Takashi Iwai <tiwai@suse.de>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 *
19 */
20
21#include <sound/driver.h>
22#include <linux/init.h>
23#include <linux/slab.h>
24#include <linux/moduleparam.h>
25#include <sound/core.h>
26#include "seq_clientmgr.h"
27#include <sound/initval.h>
28#include <sound/asoundef.h>
29
30/*
31
32 Sequencer MIDI-through client
33
34 This gives a simple midi-through client. All the normal input events
35 are redirected to output port immediately.
36 The routing can be done via aconnect program in alsa-utils.
37
38 Each client has a static client number 62 (= SNDRV_SEQ_CLIENT_DUMMY).
39 If you want to auto-load this module, you may add the following alias
40 in your /etc/conf.modules file.
41
42 alias snd-seq-client-62 snd-seq-dummy
43
44 The module is loaded on demand for client 62, or /proc/asound/seq/
45 is accessed. If you don't need this module to be loaded, alias
46 snd-seq-client-62 as "off". This will help modprobe.
47
48 The number of ports to be created can be specified via the module
49 parameter "ports". For example, to create four ports, add the
50 following option in /etc/modprobe.conf:
51
52 option snd-seq-dummy ports=4
53
54 The modle option "duplex=1" enables duplex operation to the port.
55 In duplex mode, a pair of ports are created instead of single port,
56 and events are tunneled between pair-ports. For example, input to
57 port A is sent to output port of another port B and vice versa.
58 In duplex mode, each port has DUPLEX capability.
59
60 */
61
62
63MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");
64MODULE_DESCRIPTION("ALSA sequencer MIDI-through client");
65MODULE_LICENSE("GPL");
66MODULE_ALIAS("snd-seq-client-" __stringify(SNDRV_SEQ_CLIENT_DUMMY));
67
68static int ports = 1;
69static int duplex = 0;
70
71module_param(ports, int, 0444);
72MODULE_PARM_DESC(ports, "number of ports to be created");
73module_param(duplex, bool, 0444);
74MODULE_PARM_DESC(duplex, "create DUPLEX ports");
75
76typedef struct snd_seq_dummy_port {
77 int client;
78 int port;
79 int duplex;
80 int connect;
81} snd_seq_dummy_port_t;
82
83static int my_client = -1;
84
85/*
86 * unuse callback - send ALL_SOUNDS_OFF and RESET_CONTROLLERS events
87 * to subscribers.
88 * Note: this callback is called only after all subscribers are removed.
89 */
90static int
91dummy_unuse(void *private_data, snd_seq_port_subscribe_t *info)
92{
93 snd_seq_dummy_port_t *p;
94 int i;
95 snd_seq_event_t ev;
96
97 p = private_data;
98 memset(&ev, 0, sizeof(ev));
99 if (p->duplex)
100 ev.source.port = p->connect;
101 else
102 ev.source.port = p->port;
103 ev.dest.client = SNDRV_SEQ_ADDRESS_SUBSCRIBERS;
104 ev.type = SNDRV_SEQ_EVENT_CONTROLLER;
105 for (i = 0; i < 16; i++) {
106 ev.data.control.channel = i;
107 ev.data.control.param = MIDI_CTL_ALL_SOUNDS_OFF;
108 snd_seq_kernel_client_dispatch(p->client, &ev, 0, 0);
109 ev.data.control.param = MIDI_CTL_RESET_CONTROLLERS;
110 snd_seq_kernel_client_dispatch(p->client, &ev, 0, 0);
111 }
112 return 0;
113}
114
115/*
116 * event input callback - just redirect events to subscribers
117 */
118static int
119dummy_input(snd_seq_event_t *ev, int direct, void *private_data, int atomic, int hop)
120{
121 snd_seq_dummy_port_t *p;
122 snd_seq_event_t tmpev;
123
124 p = private_data;
125 if (ev->source.client == SNDRV_SEQ_CLIENT_SYSTEM ||
126 ev->type == SNDRV_SEQ_EVENT_KERNEL_ERROR)
127 return 0; /* ignore system messages */
128 tmpev = *ev;
129 if (p->duplex)
130 tmpev.source.port = p->connect;
131 else
132 tmpev.source.port = p->port;
133 tmpev.dest.client = SNDRV_SEQ_ADDRESS_SUBSCRIBERS;
134 return snd_seq_kernel_client_dispatch(p->client, &tmpev, atomic, hop);
135}
136
137/*
138 * free_private callback
139 */
140static void
141dummy_free(void *private_data)
142{
143 snd_seq_dummy_port_t *p;
144
145 p = private_data;
146 kfree(p);
147}
148
149/*
150 * create a port
151 */
152static snd_seq_dummy_port_t __init *
153create_port(int idx, int type)
154{
155 snd_seq_port_info_t pinfo;
156 snd_seq_port_callback_t pcb;
157 snd_seq_dummy_port_t *rec;
158
159 if ((rec = kcalloc(1, sizeof(*rec), GFP_KERNEL)) == NULL)
160 return NULL;
161
162 rec->client = my_client;
163 rec->duplex = duplex;
164 rec->connect = 0;
165 memset(&pinfo, 0, sizeof(pinfo));
166 pinfo.addr.client = my_client;
167 if (duplex)
168 sprintf(pinfo.name, "Midi Through Port-%d:%c", idx,
169 (type ? 'B' : 'A'));
170 else
171 sprintf(pinfo.name, "Midi Through Port-%d", idx);
172 pinfo.capability = SNDRV_SEQ_PORT_CAP_READ | SNDRV_SEQ_PORT_CAP_SUBS_READ;
173 pinfo.capability |= SNDRV_SEQ_PORT_CAP_WRITE | SNDRV_SEQ_PORT_CAP_SUBS_WRITE;
174 if (duplex)
175 pinfo.capability |= SNDRV_SEQ_PORT_CAP_DUPLEX;
176 pinfo.type = SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC;
177 memset(&pcb, 0, sizeof(pcb));
178 pcb.owner = THIS_MODULE;
179 pcb.unuse = dummy_unuse;
180 pcb.event_input = dummy_input;
181 pcb.private_free = dummy_free;
182 pcb.private_data = rec;
183 pinfo.kernel = &pcb;
184 if (snd_seq_kernel_client_ctl(my_client, SNDRV_SEQ_IOCTL_CREATE_PORT, &pinfo) < 0) {
185 kfree(rec);
186 return NULL;
187 }
188 rec->port = pinfo.addr.port;
189 return rec;
190}
191
192/*
193 * register client and create ports
194 */
195static int __init
196register_client(void)
197{
198 snd_seq_client_callback_t cb;
199 snd_seq_client_info_t cinfo;
200 snd_seq_dummy_port_t *rec1, *rec2;
201 int i;
202
203 if (ports < 1) {
204 snd_printk(KERN_ERR "invalid number of ports %d\n", ports);
205 return -EINVAL;
206 }
207
208 /* create client */
209 memset(&cb, 0, sizeof(cb));
210 cb.allow_input = 1;
211 cb.allow_output = 1;
212 my_client = snd_seq_create_kernel_client(NULL, SNDRV_SEQ_CLIENT_DUMMY, &cb);
213 if (my_client < 0)
214 return my_client;
215
216 /* set client name */
217 memset(&cinfo, 0, sizeof(cinfo));
218 cinfo.client = my_client;
219 cinfo.type = KERNEL_CLIENT;
220 strcpy(cinfo.name, "Midi Through");
221 snd_seq_kernel_client_ctl(my_client, SNDRV_SEQ_IOCTL_SET_CLIENT_INFO, &cinfo);
222
223 /* create ports */
224 for (i = 0; i < ports; i++) {
225 rec1 = create_port(i, 0);
226 if (rec1 == NULL) {
227 snd_seq_delete_kernel_client(my_client);
228 return -ENOMEM;
229 }
230 if (duplex) {
231 rec2 = create_port(i, 1);
232 if (rec2 == NULL) {
233 snd_seq_delete_kernel_client(my_client);
234 return -ENOMEM;
235 }
236 rec1->connect = rec2->port;
237 rec2->connect = rec1->port;
238 }
239 }
240
241 return 0;
242}
243
244/*
245 * delete client if exists
246 */
247static void __exit
248delete_client(void)
249{
250 if (my_client >= 0)
251 snd_seq_delete_kernel_client(my_client);
252}
253
254/*
255 * Init part
256 */
257
258static int __init alsa_seq_dummy_init(void)
259{
260 int err;
261 snd_seq_autoload_lock();
262 err = register_client();
263 snd_seq_autoload_unlock();
264 return err;
265}
266
267static void __exit alsa_seq_dummy_exit(void)
268{
269 delete_client();
270}
271
272module_init(alsa_seq_dummy_init)
273module_exit(alsa_seq_dummy_exit)
diff --git a/sound/core/seq/seq_fifo.c b/sound/core/seq/seq_fifo.c
new file mode 100644
index 000000000000..3b7647ca7ad9
--- /dev/null
+++ b/sound/core/seq/seq_fifo.c
@@ -0,0 +1,264 @@
1/*
2 * ALSA sequencer FIFO
3 * Copyright (c) 1998 by Frank van de Pol <fvdpol@coil.demon.nl>
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 <sound/core.h>
24#include <linux/slab.h>
25#include "seq_fifo.h"
26#include "seq_lock.h"
27
28
29/* FIFO */
30
31/* create new fifo */
32fifo_t *snd_seq_fifo_new(int poolsize)
33{
34 fifo_t *f;
35
36 f = kcalloc(1, sizeof(*f), GFP_KERNEL);
37 if (f == NULL) {
38 snd_printd("malloc failed for snd_seq_fifo_new() \n");
39 return NULL;
40 }
41
42 f->pool = snd_seq_pool_new(poolsize);
43 if (f->pool == NULL) {
44 kfree(f);
45 return NULL;
46 }
47 if (snd_seq_pool_init(f->pool) < 0) {
48 snd_seq_pool_delete(&f->pool);
49 kfree(f);
50 return NULL;
51 }
52
53 spin_lock_init(&f->lock);
54 snd_use_lock_init(&f->use_lock);
55 init_waitqueue_head(&f->input_sleep);
56 atomic_set(&f->overflow, 0);
57
58 f->head = NULL;
59 f->tail = NULL;
60 f->cells = 0;
61
62 return f;
63}
64
65void snd_seq_fifo_delete(fifo_t **fifo)
66{
67 fifo_t *f;
68
69 snd_assert(fifo != NULL, return);
70 f = *fifo;
71 snd_assert(f != NULL, return);
72 *fifo = NULL;
73
74 snd_seq_fifo_clear(f);
75
76 /* wake up clients if any */
77 if (waitqueue_active(&f->input_sleep))
78 wake_up(&f->input_sleep);
79
80 /* release resources...*/
81 /*....................*/
82
83 if (f->pool) {
84 snd_seq_pool_done(f->pool);
85 snd_seq_pool_delete(&f->pool);
86 }
87
88 kfree(f);
89}
90
91static snd_seq_event_cell_t *fifo_cell_out(fifo_t *f);
92
93/* clear queue */
94void snd_seq_fifo_clear(fifo_t *f)
95{
96 snd_seq_event_cell_t *cell;
97 unsigned long flags;
98
99 /* clear overflow flag */
100 atomic_set(&f->overflow, 0);
101
102 snd_use_lock_sync(&f->use_lock);
103 spin_lock_irqsave(&f->lock, flags);
104 /* drain the fifo */
105 while ((cell = fifo_cell_out(f)) != NULL) {
106 snd_seq_cell_free(cell);
107 }
108 spin_unlock_irqrestore(&f->lock, flags);
109}
110
111
112/* enqueue event to fifo */
113int snd_seq_fifo_event_in(fifo_t *f, snd_seq_event_t *event)
114{
115 snd_seq_event_cell_t *cell;
116 unsigned long flags;
117 int err;
118
119 snd_assert(f != NULL, return -EINVAL);
120
121 snd_use_lock_use(&f->use_lock);
122 err = snd_seq_event_dup(f->pool, event, &cell, 1, NULL); /* always non-blocking */
123 if (err < 0) {
124 if (err == -ENOMEM)
125 atomic_inc(&f->overflow);
126 snd_use_lock_free(&f->use_lock);
127 return err;
128 }
129
130 /* append new cells to fifo */
131 spin_lock_irqsave(&f->lock, flags);
132 if (f->tail != NULL)
133 f->tail->next = cell;
134 f->tail = cell;
135 if (f->head == NULL)
136 f->head = cell;
137 f->cells++;
138 spin_unlock_irqrestore(&f->lock, flags);
139
140 /* wakeup client */
141 if (waitqueue_active(&f->input_sleep))
142 wake_up(&f->input_sleep);
143
144 snd_use_lock_free(&f->use_lock);
145
146 return 0; /* success */
147
148}
149
150/* dequeue cell from fifo */
151static snd_seq_event_cell_t *fifo_cell_out(fifo_t *f)
152{
153 snd_seq_event_cell_t *cell;
154
155 if ((cell = f->head) != NULL) {
156 f->head = cell->next;
157
158 /* reset tail if this was the last element */
159 if (f->tail == cell)
160 f->tail = NULL;
161
162 cell->next = NULL;
163 f->cells--;
164 }
165
166 return cell;
167}
168
169/* dequeue cell from fifo and copy on user space */
170int snd_seq_fifo_cell_out(fifo_t *f, snd_seq_event_cell_t **cellp, int nonblock)
171{
172 snd_seq_event_cell_t *cell;
173 unsigned long flags;
174 wait_queue_t wait;
175
176 snd_assert(f != NULL, return -EINVAL);
177
178 *cellp = NULL;
179 init_waitqueue_entry(&wait, current);
180 spin_lock_irqsave(&f->lock, flags);
181 while ((cell = fifo_cell_out(f)) == NULL) {
182 if (nonblock) {
183 /* non-blocking - return immediately */
184 spin_unlock_irqrestore(&f->lock, flags);
185 return -EAGAIN;
186 }
187 set_current_state(TASK_INTERRUPTIBLE);
188 add_wait_queue(&f->input_sleep, &wait);
189 spin_unlock_irq(&f->lock);
190 schedule();
191 spin_lock_irq(&f->lock);
192 remove_wait_queue(&f->input_sleep, &wait);
193 if (signal_pending(current)) {
194 spin_unlock_irqrestore(&f->lock, flags);
195 return -ERESTARTSYS;
196 }
197 }
198 spin_unlock_irqrestore(&f->lock, flags);
199 *cellp = cell;
200
201 return 0;
202}
203
204
205void snd_seq_fifo_cell_putback(fifo_t *f, snd_seq_event_cell_t *cell)
206{
207 unsigned long flags;
208
209 if (cell) {
210 spin_lock_irqsave(&f->lock, flags);
211 cell->next = f->head;
212 f->head = cell;
213 f->cells++;
214 spin_unlock_irqrestore(&f->lock, flags);
215 }
216}
217
218
219/* polling; return non-zero if queue is available */
220int snd_seq_fifo_poll_wait(fifo_t *f, struct file *file, poll_table *wait)
221{
222 poll_wait(file, &f->input_sleep, wait);
223 return (f->cells > 0);
224}
225
226/* change the size of pool; all old events are removed */
227int snd_seq_fifo_resize(fifo_t *f, int poolsize)
228{
229 unsigned long flags;
230 pool_t *newpool, *oldpool;
231 snd_seq_event_cell_t *cell, *next, *oldhead;
232
233 snd_assert(f != NULL && f->pool != NULL, return -EINVAL);
234
235 /* allocate new pool */
236 newpool = snd_seq_pool_new(poolsize);
237 if (newpool == NULL)
238 return -ENOMEM;
239 if (snd_seq_pool_init(newpool) < 0) {
240 snd_seq_pool_delete(&newpool);
241 return -ENOMEM;
242 }
243
244 spin_lock_irqsave(&f->lock, flags);
245 /* remember old pool */
246 oldpool = f->pool;
247 oldhead = f->head;
248 /* exchange pools */
249 f->pool = newpool;
250 f->head = NULL;
251 f->tail = NULL;
252 f->cells = 0;
253 /* NOTE: overflow flag is not cleared */
254 spin_unlock_irqrestore(&f->lock, flags);
255
256 /* release cells in old pool */
257 for (cell = oldhead; cell; cell = next) {
258 next = cell->next;
259 snd_seq_cell_free(cell);
260 }
261 snd_seq_pool_delete(&oldpool);
262
263 return 0;
264}
diff --git a/sound/core/seq/seq_fifo.h b/sound/core/seq/seq_fifo.h
new file mode 100644
index 000000000000..d677c261b0a4
--- /dev/null
+++ b/sound/core/seq/seq_fifo.h
@@ -0,0 +1,72 @@
1/*
2 * ALSA sequencer FIFO
3 * Copyright (c) 1998 by Frank van de Pol <fvdpol@coil.demon.nl>
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#ifndef __SND_SEQ_FIFO_H
22#define __SND_SEQ_FIFO_H
23
24#include "seq_memory.h"
25#include "seq_lock.h"
26
27
28/* === FIFO === */
29
30typedef struct {
31 pool_t *pool; /* FIFO pool */
32 snd_seq_event_cell_t* head; /* pointer to head of fifo */
33 snd_seq_event_cell_t* tail; /* pointer to tail of fifo */
34 int cells;
35 spinlock_t lock;
36 snd_use_lock_t use_lock;
37 wait_queue_head_t input_sleep;
38 atomic_t overflow;
39
40} fifo_t;
41
42/* create new fifo (constructor) */
43extern fifo_t *snd_seq_fifo_new(int poolsize);
44
45/* delete fifo (destructor) */
46extern void snd_seq_fifo_delete(fifo_t **f);
47
48
49/* enqueue event to fifo */
50extern int snd_seq_fifo_event_in(fifo_t *f, snd_seq_event_t *event);
51
52/* lock fifo from release */
53#define snd_seq_fifo_lock(fifo) snd_use_lock_use(&(fifo)->use_lock)
54#define snd_seq_fifo_unlock(fifo) snd_use_lock_free(&(fifo)->use_lock)
55
56/* get a cell from fifo - fifo should be locked */
57int snd_seq_fifo_cell_out(fifo_t *f, snd_seq_event_cell_t **cellp, int nonblock);
58
59/* free dequeued cell - fifo should be locked */
60extern void snd_seq_fifo_cell_putback(fifo_t *f, snd_seq_event_cell_t *cell);
61
62/* clean up queue */
63extern void snd_seq_fifo_clear(fifo_t *f);
64
65/* polling */
66extern int snd_seq_fifo_poll_wait(fifo_t *f, struct file *file, poll_table *wait);
67
68/* resize pool in fifo */
69int snd_seq_fifo_resize(fifo_t *f, int poolsize);
70
71
72#endif
diff --git a/sound/core/seq/seq_info.c b/sound/core/seq/seq_info.c
new file mode 100644
index 000000000000..b50b695c41c4
--- /dev/null
+++ b/sound/core/seq/seq_info.c
@@ -0,0 +1,75 @@
1/*
2 * ALSA sequencer /proc interface
3 * Copyright (c) 1998 by Frank van de Pol <fvdpol@coil.demon.nl>
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 <sound/core.h>
25
26#include "seq_info.h"
27#include "seq_clientmgr.h"
28#include "seq_timer.h"
29
30
31static snd_info_entry_t *queues_entry;
32static snd_info_entry_t *clients_entry;
33static snd_info_entry_t *timer_entry;
34
35
36static snd_info_entry_t * __init
37create_info_entry(char *name, int size, void (*read)(snd_info_entry_t *, snd_info_buffer_t *))
38{
39 snd_info_entry_t *entry;
40
41 entry = snd_info_create_module_entry(THIS_MODULE, name, snd_seq_root);
42 if (entry == NULL)
43 return NULL;
44 entry->content = SNDRV_INFO_CONTENT_TEXT;
45 entry->c.text.read_size = size;
46 entry->c.text.read = read;
47 if (snd_info_register(entry) < 0) {
48 snd_info_free_entry(entry);
49 return NULL;
50 }
51 return entry;
52}
53
54
55/* create all our /proc entries */
56int __init snd_seq_info_init(void)
57{
58 queues_entry = create_info_entry("queues", 512 + (256 * SNDRV_SEQ_MAX_QUEUES),
59 snd_seq_info_queues_read);
60 clients_entry = create_info_entry("clients", 512 + (256 * SNDRV_SEQ_MAX_CLIENTS),
61 snd_seq_info_clients_read);
62 timer_entry = create_info_entry("timer", 1024, snd_seq_info_timer_read);
63 return 0;
64}
65
66int __exit snd_seq_info_done(void)
67{
68 if (queues_entry)
69 snd_info_unregister(queues_entry);
70 if (clients_entry)
71 snd_info_unregister(clients_entry);
72 if (timer_entry)
73 snd_info_unregister(timer_entry);
74 return 0;
75}
diff --git a/sound/core/seq/seq_info.h b/sound/core/seq/seq_info.h
new file mode 100644
index 000000000000..efd099a858e4
--- /dev/null
+++ b/sound/core/seq/seq_info.h
@@ -0,0 +1,36 @@
1/*
2 * ALSA sequencer /proc info
3 * Copyright (c) 1998 by Frank van de Pol <fvdpol@coil.demon.nl>
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#ifndef __SND_SEQ_INFO_H
22#define __SND_SEQ_INFO_H
23
24#include <sound/info.h>
25#include <sound/seq_kernel.h>
26
27void snd_seq_info_clients_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer);
28void snd_seq_info_timer_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer);
29void snd_seq_info_queues_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer);
30
31
32int snd_seq_info_init( void );
33int snd_seq_info_done( void );
34
35
36#endif
diff --git a/sound/core/seq/seq_instr.c b/sound/core/seq/seq_instr.c
new file mode 100644
index 000000000000..5b40ea2ba8f4
--- /dev/null
+++ b/sound/core/seq/seq_instr.c
@@ -0,0 +1,653 @@
1/*
2 * Generic Instrument routines for ALSA sequencer
3 * Copyright (c) 1999 by Jaroslav Kysela <perex@suse.cz>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 *
19 */
20
21#include <sound/driver.h>
22#include <linux/init.h>
23#include <linux/slab.h>
24#include <sound/core.h>
25#include "seq_clientmgr.h"
26#include <sound/seq_instr.h>
27#include <sound/initval.h>
28
29MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
30MODULE_DESCRIPTION("Advanced Linux Sound Architecture sequencer instrument library.");
31MODULE_LICENSE("GPL");
32
33
34static void snd_instr_lock_ops(snd_seq_kinstr_list_t *list)
35{
36 if (!(list->flags & SNDRV_SEQ_INSTR_FLG_DIRECT)) {
37 spin_lock_irqsave(&list->ops_lock, list->ops_flags);
38 } else {
39 down(&list->ops_mutex);
40 }
41}
42
43static void snd_instr_unlock_ops(snd_seq_kinstr_list_t *list)
44{
45 if (!(list->flags & SNDRV_SEQ_INSTR_FLG_DIRECT)) {
46 spin_unlock_irqrestore(&list->ops_lock, list->ops_flags);
47 } else {
48 up(&list->ops_mutex);
49 }
50}
51
52static snd_seq_kinstr_t *snd_seq_instr_new(int add_len, int atomic)
53{
54 snd_seq_kinstr_t *instr;
55
56 instr = kcalloc(1, sizeof(snd_seq_kinstr_t) + add_len, atomic ? GFP_ATOMIC : GFP_KERNEL);
57 if (instr == NULL)
58 return NULL;
59 instr->add_len = add_len;
60 return instr;
61}
62
63static int snd_seq_instr_free(snd_seq_kinstr_t *instr, int atomic)
64{
65 int result = 0;
66
67 if (instr == NULL)
68 return -EINVAL;
69 if (instr->ops && instr->ops->remove)
70 result = instr->ops->remove(instr->ops->private_data, instr, 1);
71 if (!result)
72 kfree(instr);
73 return result;
74}
75
76snd_seq_kinstr_list_t *snd_seq_instr_list_new(void)
77{
78 snd_seq_kinstr_list_t *list;
79
80 list = kcalloc(1, sizeof(snd_seq_kinstr_list_t), GFP_KERNEL);
81 if (list == NULL)
82 return NULL;
83 spin_lock_init(&list->lock);
84 spin_lock_init(&list->ops_lock);
85 init_MUTEX(&list->ops_mutex);
86 list->owner = -1;
87 return list;
88}
89
90void snd_seq_instr_list_free(snd_seq_kinstr_list_t **list_ptr)
91{
92 snd_seq_kinstr_list_t *list;
93 snd_seq_kinstr_t *instr;
94 snd_seq_kcluster_t *cluster;
95 int idx;
96 unsigned long flags;
97
98 if (list_ptr == NULL)
99 return;
100 list = *list_ptr;
101 *list_ptr = NULL;
102 if (list == NULL)
103 return;
104
105 for (idx = 0; idx < SNDRV_SEQ_INSTR_HASH_SIZE; idx++) {
106 while ((instr = list->hash[idx]) != NULL) {
107 list->hash[idx] = instr->next;
108 list->count--;
109 spin_lock_irqsave(&list->lock, flags);
110 while (instr->use) {
111 spin_unlock_irqrestore(&list->lock, flags);
112 set_current_state(TASK_INTERRUPTIBLE);
113 schedule_timeout(1);
114 spin_lock_irqsave(&list->lock, flags);
115 }
116 spin_unlock_irqrestore(&list->lock, flags);
117 if (snd_seq_instr_free(instr, 0)<0)
118 snd_printk(KERN_WARNING "instrument free problem\n");
119 }
120 while ((cluster = list->chash[idx]) != NULL) {
121 list->chash[idx] = cluster->next;
122 list->ccount--;
123 kfree(cluster);
124 }
125 }
126 kfree(list);
127}
128
129static int instr_free_compare(snd_seq_kinstr_t *instr,
130 snd_seq_instr_header_t *ifree,
131 unsigned int client)
132{
133 switch (ifree->cmd) {
134 case SNDRV_SEQ_INSTR_FREE_CMD_ALL:
135 /* all, except private for other clients */
136 if ((instr->instr.std & 0xff000000) == 0)
137 return 0;
138 if (((instr->instr.std >> 24) & 0xff) == client)
139 return 0;
140 return 1;
141 case SNDRV_SEQ_INSTR_FREE_CMD_PRIVATE:
142 /* all my private instruments */
143 if ((instr->instr.std & 0xff000000) == 0)
144 return 1;
145 if (((instr->instr.std >> 24) & 0xff) == client)
146 return 0;
147 return 1;
148 case SNDRV_SEQ_INSTR_FREE_CMD_CLUSTER:
149 /* all my private instruments */
150 if ((instr->instr.std & 0xff000000) == 0) {
151 if (instr->instr.cluster == ifree->id.cluster)
152 return 0;
153 return 1;
154 }
155 if (((instr->instr.std >> 24) & 0xff) == client) {
156 if (instr->instr.cluster == ifree->id.cluster)
157 return 0;
158 }
159 return 1;
160 }
161 return 1;
162}
163
164int snd_seq_instr_list_free_cond(snd_seq_kinstr_list_t *list,
165 snd_seq_instr_header_t *ifree,
166 int client,
167 int atomic)
168{
169 snd_seq_kinstr_t *instr, *prev, *next, *flist;
170 int idx;
171 unsigned long flags;
172
173 snd_instr_lock_ops(list);
174 for (idx = 0; idx < SNDRV_SEQ_INSTR_HASH_SIZE; idx++) {
175 spin_lock_irqsave(&list->lock, flags);
176 instr = list->hash[idx];
177 prev = flist = NULL;
178 while (instr) {
179 while (instr && instr_free_compare(instr, ifree, (unsigned int)client)) {
180 prev = instr;
181 instr = instr->next;
182 }
183 if (instr == NULL)
184 continue;
185 if (instr->ops && instr->ops->notify)
186 instr->ops->notify(instr->ops->private_data, instr, SNDRV_SEQ_INSTR_NOTIFY_REMOVE);
187 next = instr->next;
188 if (prev == NULL) {
189 list->hash[idx] = next;
190 } else {
191 prev->next = next;
192 }
193 list->count--;
194 instr->next = flist;
195 flist = instr;
196 instr = next;
197 }
198 spin_unlock_irqrestore(&list->lock, flags);
199 while (flist) {
200 instr = flist;
201 flist = instr->next;
202 while (instr->use) {
203 set_current_state(TASK_INTERRUPTIBLE);
204 schedule_timeout(1);
205 }
206 if (snd_seq_instr_free(instr, atomic)<0)
207 snd_printk(KERN_WARNING "instrument free problem\n");
208 instr = next;
209 }
210 }
211 snd_instr_unlock_ops(list);
212 return 0;
213}
214
215static int compute_hash_instr_key(snd_seq_instr_t *instr)
216{
217 int result;
218
219 result = instr->bank | (instr->prg << 16);
220 result += result >> 24;
221 result += result >> 16;
222 result += result >> 8;
223 return result & (SNDRV_SEQ_INSTR_HASH_SIZE-1);
224}
225
226#if 0
227static int compute_hash_cluster_key(snd_seq_instr_cluster_t cluster)
228{
229 int result;
230
231 result = cluster;
232 result += result >> 24;
233 result += result >> 16;
234 result += result >> 8;
235 return result & (SNDRV_SEQ_INSTR_HASH_SIZE-1);
236}
237#endif
238
239static int compare_instr(snd_seq_instr_t *i1, snd_seq_instr_t *i2, int exact)
240{
241 if (exact) {
242 if (i1->cluster != i2->cluster ||
243 i1->bank != i2->bank ||
244 i1->prg != i2->prg)
245 return 1;
246 if ((i1->std & 0xff000000) != (i2->std & 0xff000000))
247 return 1;
248 if (!(i1->std & i2->std))
249 return 1;
250 return 0;
251 } else {
252 unsigned int client_check;
253
254 if (i2->cluster && i1->cluster != i2->cluster)
255 return 1;
256 client_check = i2->std & 0xff000000;
257 if (client_check) {
258 if ((i1->std & 0xff000000) != client_check)
259 return 1;
260 } else {
261 if ((i1->std & i2->std) != i2->std)
262 return 1;
263 }
264 return i1->bank != i2->bank || i1->prg != i2->prg;
265 }
266}
267
268snd_seq_kinstr_t *snd_seq_instr_find(snd_seq_kinstr_list_t *list,
269 snd_seq_instr_t *instr,
270 int exact,
271 int follow_alias)
272{
273 unsigned long flags;
274 int depth = 0;
275 snd_seq_kinstr_t *result;
276
277 if (list == NULL || instr == NULL)
278 return NULL;
279 spin_lock_irqsave(&list->lock, flags);
280 __again:
281 result = list->hash[compute_hash_instr_key(instr)];
282 while (result) {
283 if (!compare_instr(&result->instr, instr, exact)) {
284 if (follow_alias && (result->type == SNDRV_SEQ_INSTR_ATYPE_ALIAS)) {
285 instr = (snd_seq_instr_t *)KINSTR_DATA(result);
286 if (++depth > 10)
287 goto __not_found;
288 goto __again;
289 }
290 result->use++;
291 spin_unlock_irqrestore(&list->lock, flags);
292 return result;
293 }
294 result = result->next;
295 }
296 __not_found:
297 spin_unlock_irqrestore(&list->lock, flags);
298 return NULL;
299}
300
301void snd_seq_instr_free_use(snd_seq_kinstr_list_t *list,
302 snd_seq_kinstr_t *instr)
303{
304 unsigned long flags;
305
306 if (list == NULL || instr == NULL)
307 return;
308 spin_lock_irqsave(&list->lock, flags);
309 if (instr->use <= 0) {
310 snd_printk(KERN_ERR "free_use: fatal!!! use = %i, name = '%s'\n", instr->use, instr->name);
311 } else {
312 instr->use--;
313 }
314 spin_unlock_irqrestore(&list->lock, flags);
315}
316
317static snd_seq_kinstr_ops_t *instr_ops(snd_seq_kinstr_ops_t *ops, char *instr_type)
318{
319 while (ops) {
320 if (!strcmp(ops->instr_type, instr_type))
321 return ops;
322 ops = ops->next;
323 }
324 return NULL;
325}
326
327static int instr_result(snd_seq_event_t *ev,
328 int type, int result,
329 int atomic)
330{
331 snd_seq_event_t sev;
332
333 memset(&sev, 0, sizeof(sev));
334 sev.type = SNDRV_SEQ_EVENT_RESULT;
335 sev.flags = SNDRV_SEQ_TIME_STAMP_REAL | SNDRV_SEQ_EVENT_LENGTH_FIXED |
336 SNDRV_SEQ_PRIORITY_NORMAL;
337 sev.source = ev->dest;
338 sev.dest = ev->source;
339 sev.data.result.event = type;
340 sev.data.result.result = result;
341#if 0
342 printk("instr result - type = %i, result = %i, queue = %i, source.client:port = %i:%i, dest.client:port = %i:%i\n",
343 type, result,
344 sev.queue,
345 sev.source.client, sev.source.port,
346 sev.dest.client, sev.dest.port);
347#endif
348 return snd_seq_kernel_client_dispatch(sev.source.client, &sev, atomic, 0);
349}
350
351static int instr_begin(snd_seq_kinstr_ops_t *ops,
352 snd_seq_kinstr_list_t *list,
353 snd_seq_event_t *ev,
354 int atomic, int hop)
355{
356 unsigned long flags;
357
358 spin_lock_irqsave(&list->lock, flags);
359 if (list->owner >= 0 && list->owner != ev->source.client) {
360 spin_unlock_irqrestore(&list->lock, flags);
361 return instr_result(ev, SNDRV_SEQ_EVENT_INSTR_BEGIN, -EBUSY, atomic);
362 }
363 list->owner = ev->source.client;
364 spin_unlock_irqrestore(&list->lock, flags);
365 return instr_result(ev, SNDRV_SEQ_EVENT_INSTR_BEGIN, 0, atomic);
366}
367
368static int instr_end(snd_seq_kinstr_ops_t *ops,
369 snd_seq_kinstr_list_t *list,
370 snd_seq_event_t *ev,
371 int atomic, int hop)
372{
373 unsigned long flags;
374
375 /* TODO: timeout handling */
376 spin_lock_irqsave(&list->lock, flags);
377 if (list->owner == ev->source.client) {
378 list->owner = -1;
379 spin_unlock_irqrestore(&list->lock, flags);
380 return instr_result(ev, SNDRV_SEQ_EVENT_INSTR_END, 0, atomic);
381 }
382 spin_unlock_irqrestore(&list->lock, flags);
383 return instr_result(ev, SNDRV_SEQ_EVENT_INSTR_END, -EINVAL, atomic);
384}
385
386static int instr_info(snd_seq_kinstr_ops_t *ops,
387 snd_seq_kinstr_list_t *list,
388 snd_seq_event_t *ev,
389 int atomic, int hop)
390{
391 return -ENXIO;
392}
393
394static int instr_format_info(snd_seq_kinstr_ops_t *ops,
395 snd_seq_kinstr_list_t *list,
396 snd_seq_event_t *ev,
397 int atomic, int hop)
398{
399 return -ENXIO;
400}
401
402static int instr_reset(snd_seq_kinstr_ops_t *ops,
403 snd_seq_kinstr_list_t *list,
404 snd_seq_event_t *ev,
405 int atomic, int hop)
406{
407 return -ENXIO;
408}
409
410static int instr_status(snd_seq_kinstr_ops_t *ops,
411 snd_seq_kinstr_list_t *list,
412 snd_seq_event_t *ev,
413 int atomic, int hop)
414{
415 return -ENXIO;
416}
417
418static int instr_put(snd_seq_kinstr_ops_t *ops,
419 snd_seq_kinstr_list_t *list,
420 snd_seq_event_t *ev,
421 int atomic, int hop)
422{
423 unsigned long flags;
424 snd_seq_instr_header_t put;
425 snd_seq_kinstr_t *instr;
426 int result = -EINVAL, len, key;
427
428 if ((ev->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) != SNDRV_SEQ_EVENT_LENGTH_VARUSR)
429 goto __return;
430
431 if (ev->data.ext.len < sizeof(snd_seq_instr_header_t))
432 goto __return;
433 if (copy_from_user(&put, (void __user *)ev->data.ext.ptr, sizeof(snd_seq_instr_header_t))) {
434 result = -EFAULT;
435 goto __return;
436 }
437 snd_instr_lock_ops(list);
438 if (put.id.instr.std & 0xff000000) { /* private instrument */
439 put.id.instr.std &= 0x00ffffff;
440 put.id.instr.std |= (unsigned int)ev->source.client << 24;
441 }
442 if ((instr = snd_seq_instr_find(list, &put.id.instr, 1, 0))) {
443 snd_seq_instr_free_use(list, instr);
444 snd_instr_unlock_ops(list);
445 result = -EBUSY;
446 goto __return;
447 }
448 ops = instr_ops(ops, put.data.data.format);
449 if (ops == NULL) {
450 snd_instr_unlock_ops(list);
451 goto __return;
452 }
453 len = ops->add_len;
454 if (put.data.type == SNDRV_SEQ_INSTR_ATYPE_ALIAS)
455 len = sizeof(snd_seq_instr_t);
456 instr = snd_seq_instr_new(len, atomic);
457 if (instr == NULL) {
458 snd_instr_unlock_ops(list);
459 result = -ENOMEM;
460 goto __return;
461 }
462 instr->ops = ops;
463 instr->instr = put.id.instr;
464 strlcpy(instr->name, put.data.name, sizeof(instr->name));
465 instr->type = put.data.type;
466 if (instr->type == SNDRV_SEQ_INSTR_ATYPE_DATA) {
467 result = ops->put(ops->private_data,
468 instr,
469 (void __user *)ev->data.ext.ptr + sizeof(snd_seq_instr_header_t),
470 ev->data.ext.len - sizeof(snd_seq_instr_header_t),
471 atomic,
472 put.cmd);
473 if (result < 0) {
474 snd_seq_instr_free(instr, atomic);
475 snd_instr_unlock_ops(list);
476 goto __return;
477 }
478 }
479 key = compute_hash_instr_key(&instr->instr);
480 spin_lock_irqsave(&list->lock, flags);
481 instr->next = list->hash[key];
482 list->hash[key] = instr;
483 list->count++;
484 spin_unlock_irqrestore(&list->lock, flags);
485 snd_instr_unlock_ops(list);
486 result = 0;
487 __return:
488 instr_result(ev, SNDRV_SEQ_EVENT_INSTR_PUT, result, atomic);
489 return result;
490}
491
492static int instr_get(snd_seq_kinstr_ops_t *ops,
493 snd_seq_kinstr_list_t *list,
494 snd_seq_event_t *ev,
495 int atomic, int hop)
496{
497 return -ENXIO;
498}
499
500static int instr_free(snd_seq_kinstr_ops_t *ops,
501 snd_seq_kinstr_list_t *list,
502 snd_seq_event_t *ev,
503 int atomic, int hop)
504{
505 snd_seq_instr_header_t ifree;
506 snd_seq_kinstr_t *instr, *prev;
507 int result = -EINVAL;
508 unsigned long flags;
509 unsigned int hash;
510
511 if ((ev->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) != SNDRV_SEQ_EVENT_LENGTH_VARUSR)
512 goto __return;
513
514 if (ev->data.ext.len < sizeof(snd_seq_instr_header_t))
515 goto __return;
516 if (copy_from_user(&ifree, (void __user *)ev->data.ext.ptr, sizeof(snd_seq_instr_header_t))) {
517 result = -EFAULT;
518 goto __return;
519 }
520 if (ifree.cmd == SNDRV_SEQ_INSTR_FREE_CMD_ALL ||
521 ifree.cmd == SNDRV_SEQ_INSTR_FREE_CMD_PRIVATE ||
522 ifree.cmd == SNDRV_SEQ_INSTR_FREE_CMD_CLUSTER) {
523 result = snd_seq_instr_list_free_cond(list, &ifree, ev->dest.client, atomic);
524 goto __return;
525 }
526 if (ifree.cmd == SNDRV_SEQ_INSTR_FREE_CMD_SINGLE) {
527 if (ifree.id.instr.std & 0xff000000) {
528 ifree.id.instr.std &= 0x00ffffff;
529 ifree.id.instr.std |= (unsigned int)ev->source.client << 24;
530 }
531 hash = compute_hash_instr_key(&ifree.id.instr);
532 snd_instr_lock_ops(list);
533 spin_lock_irqsave(&list->lock, flags);
534 instr = list->hash[hash];
535 prev = NULL;
536 while (instr) {
537 if (!compare_instr(&instr->instr, &ifree.id.instr, 1))
538 goto __free_single;
539 prev = instr;
540 instr = instr->next;
541 }
542 result = -ENOENT;
543 spin_unlock_irqrestore(&list->lock, flags);
544 snd_instr_unlock_ops(list);
545 goto __return;
546
547 __free_single:
548 if (prev) {
549 prev->next = instr->next;
550 } else {
551 list->hash[hash] = instr->next;
552 }
553 if (instr->ops && instr->ops->notify)
554 instr->ops->notify(instr->ops->private_data, instr, SNDRV_SEQ_INSTR_NOTIFY_REMOVE);
555 while (instr->use) {
556 spin_unlock_irqrestore(&list->lock, flags);
557 set_current_state(TASK_INTERRUPTIBLE);
558 schedule_timeout(1);
559 spin_lock_irqsave(&list->lock, flags);
560 }
561 spin_unlock_irqrestore(&list->lock, flags);
562 result = snd_seq_instr_free(instr, atomic);
563 snd_instr_unlock_ops(list);
564 goto __return;
565 }
566
567 __return:
568 instr_result(ev, SNDRV_SEQ_EVENT_INSTR_FREE, result, atomic);
569 return result;
570}
571
572static int instr_list(snd_seq_kinstr_ops_t *ops,
573 snd_seq_kinstr_list_t *list,
574 snd_seq_event_t *ev,
575 int atomic, int hop)
576{
577 return -ENXIO;
578}
579
580static int instr_cluster(snd_seq_kinstr_ops_t *ops,
581 snd_seq_kinstr_list_t *list,
582 snd_seq_event_t *ev,
583 int atomic, int hop)
584{
585 return -ENXIO;
586}
587
588int snd_seq_instr_event(snd_seq_kinstr_ops_t *ops,
589 snd_seq_kinstr_list_t *list,
590 snd_seq_event_t *ev,
591 int client,
592 int atomic,
593 int hop)
594{
595 int direct = 0;
596
597 snd_assert(ops != NULL && list != NULL && ev != NULL, return -EINVAL);
598 if (snd_seq_ev_is_direct(ev)) {
599 direct = 1;
600 switch (ev->type) {
601 case SNDRV_SEQ_EVENT_INSTR_BEGIN:
602 return instr_begin(ops, list, ev, atomic, hop);
603 case SNDRV_SEQ_EVENT_INSTR_END:
604 return instr_end(ops, list, ev, atomic, hop);
605 }
606 }
607 if ((list->flags & SNDRV_SEQ_INSTR_FLG_DIRECT) && !direct)
608 return -EINVAL;
609 switch (ev->type) {
610 case SNDRV_SEQ_EVENT_INSTR_INFO:
611 return instr_info(ops, list, ev, atomic, hop);
612 case SNDRV_SEQ_EVENT_INSTR_FINFO:
613 return instr_format_info(ops, list, ev, atomic, hop);
614 case SNDRV_SEQ_EVENT_INSTR_RESET:
615 return instr_reset(ops, list, ev, atomic, hop);
616 case SNDRV_SEQ_EVENT_INSTR_STATUS:
617 return instr_status(ops, list, ev, atomic, hop);
618 case SNDRV_SEQ_EVENT_INSTR_PUT:
619 return instr_put(ops, list, ev, atomic, hop);
620 case SNDRV_SEQ_EVENT_INSTR_GET:
621 return instr_get(ops, list, ev, atomic, hop);
622 case SNDRV_SEQ_EVENT_INSTR_FREE:
623 return instr_free(ops, list, ev, atomic, hop);
624 case SNDRV_SEQ_EVENT_INSTR_LIST:
625 return instr_list(ops, list, ev, atomic, hop);
626 case SNDRV_SEQ_EVENT_INSTR_CLUSTER:
627 return instr_cluster(ops, list, ev, atomic, hop);
628 }
629 return -EINVAL;
630}
631
632/*
633 * Init part
634 */
635
636static int __init alsa_seq_instr_init(void)
637{
638 return 0;
639}
640
641static void __exit alsa_seq_instr_exit(void)
642{
643}
644
645module_init(alsa_seq_instr_init)
646module_exit(alsa_seq_instr_exit)
647
648EXPORT_SYMBOL(snd_seq_instr_list_new);
649EXPORT_SYMBOL(snd_seq_instr_list_free);
650EXPORT_SYMBOL(snd_seq_instr_list_free_cond);
651EXPORT_SYMBOL(snd_seq_instr_find);
652EXPORT_SYMBOL(snd_seq_instr_free_use);
653EXPORT_SYMBOL(snd_seq_instr_event);
diff --git a/sound/core/seq/seq_lock.c b/sound/core/seq/seq_lock.c
new file mode 100644
index 000000000000..b09cee058fa7
--- /dev/null
+++ b/sound/core/seq/seq_lock.c
@@ -0,0 +1,48 @@
1/*
2 * Do sleep inside a spin-lock
3 * Copyright (c) 1999 by Takashi Iwai <tiwai@suse.de>
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 <sound/core.h>
24#include "seq_lock.h"
25
26#if defined(CONFIG_SMP) || defined(CONFIG_SND_DEBUG)
27
28/* wait until all locks are released */
29void snd_use_lock_sync_helper(snd_use_lock_t *lockp, const char *file, int line)
30{
31 int max_count = 5 * HZ;
32
33 if (atomic_read(lockp) < 0) {
34 printk(KERN_WARNING "seq_lock: lock trouble [counter = %d] in %s:%d\n", atomic_read(lockp), file, line);
35 return;
36 }
37 while (atomic_read(lockp) > 0) {
38 if (max_count == 0) {
39 snd_printk(KERN_WARNING "seq_lock: timeout [%d left] in %s:%d\n", atomic_read(lockp), file, line);
40 break;
41 }
42 set_current_state(TASK_UNINTERRUPTIBLE);
43 schedule_timeout(1);
44 max_count--;
45 }
46}
47
48#endif
diff --git a/sound/core/seq/seq_lock.h b/sound/core/seq/seq_lock.h
new file mode 100644
index 000000000000..54044bc2c9ef
--- /dev/null
+++ b/sound/core/seq/seq_lock.h
@@ -0,0 +1,33 @@
1#ifndef __SND_SEQ_LOCK_H
2#define __SND_SEQ_LOCK_H
3
4#include <linux/sched.h>
5
6#if defined(CONFIG_SMP) || defined(CONFIG_SND_DEBUG)
7
8typedef atomic_t snd_use_lock_t;
9
10/* initialize lock */
11#define snd_use_lock_init(lockp) atomic_set(lockp, 0)
12
13/* increment lock */
14#define snd_use_lock_use(lockp) atomic_inc(lockp)
15
16/* release lock */
17#define snd_use_lock_free(lockp) atomic_dec(lockp)
18
19/* wait until all locks are released */
20void snd_use_lock_sync_helper(snd_use_lock_t *lock, const char *file, int line);
21#define snd_use_lock_sync(lockp) snd_use_lock_sync_helper(lockp, __BASE_FILE__, __LINE__)
22
23#else /* SMP || CONFIG_SND_DEBUG */
24
25typedef spinlock_t snd_use_lock_t; /* dummy */
26#define snd_use_lock_init(lockp) /**/
27#define snd_use_lock_use(lockp) /**/
28#define snd_use_lock_free(lockp) /**/
29#define snd_use_lock_sync(lockp) /**/
30
31#endif /* SMP || CONFIG_SND_DEBUG */
32
33#endif /* __SND_SEQ_LOCK_H */
diff --git a/sound/core/seq/seq_memory.c b/sound/core/seq/seq_memory.c
new file mode 100644
index 000000000000..00d841e82fbc
--- /dev/null
+++ b/sound/core/seq/seq_memory.c
@@ -0,0 +1,510 @@
1/*
2 * ALSA sequencer Memory Manager
3 * Copyright (c) 1998 by Frank van de Pol <fvdpol@coil.demon.nl>
4 * Jaroslav Kysela <perex@suse.cz>
5 * 2000 by Takashi Iwai <tiwai@suse.de>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (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 General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; 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/init.h>
25#include <linux/slab.h>
26#include <linux/vmalloc.h>
27#include <sound/core.h>
28
29#include <sound/seq_kernel.h>
30#include "seq_memory.h"
31#include "seq_queue.h"
32#include "seq_info.h"
33#include "seq_lock.h"
34
35/* semaphore in struct file record */
36#define semaphore_of(fp) ((fp)->f_dentry->d_inode->i_sem)
37
38
39inline static int snd_seq_pool_available(pool_t *pool)
40{
41 return pool->total_elements - atomic_read(&pool->counter);
42}
43
44inline static int snd_seq_output_ok(pool_t *pool)
45{
46 return snd_seq_pool_available(pool) >= pool->room;
47}
48
49/*
50 * Variable length event:
51 * The event like sysex uses variable length type.
52 * The external data may be stored in three different formats.
53 * 1) kernel space
54 * This is the normal case.
55 * ext.data.len = length
56 * ext.data.ptr = buffer pointer
57 * 2) user space
58 * When an event is generated via read(), the external data is
59 * kept in user space until expanded.
60 * ext.data.len = length | SNDRV_SEQ_EXT_USRPTR
61 * ext.data.ptr = userspace pointer
62 * 3) chained cells
63 * When the variable length event is enqueued (in prioq or fifo),
64 * the external data is decomposed to several cells.
65 * ext.data.len = length | SNDRV_SEQ_EXT_CHAINED
66 * ext.data.ptr = the additiona cell head
67 * -> cell.next -> cell.next -> ..
68 */
69
70/*
71 * exported:
72 * call dump function to expand external data.
73 */
74
75static int get_var_len(const snd_seq_event_t *event)
76{
77 if ((event->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) != SNDRV_SEQ_EVENT_LENGTH_VARIABLE)
78 return -EINVAL;
79
80 return event->data.ext.len & ~SNDRV_SEQ_EXT_MASK;
81}
82
83int snd_seq_dump_var_event(const snd_seq_event_t *event, snd_seq_dump_func_t func, void *private_data)
84{
85 int len, err;
86 snd_seq_event_cell_t *cell;
87
88 if ((len = get_var_len(event)) <= 0)
89 return len;
90
91 if (event->data.ext.len & SNDRV_SEQ_EXT_USRPTR) {
92 char buf[32];
93 char __user *curptr = (char __user *)event->data.ext.ptr;
94 while (len > 0) {
95 int size = sizeof(buf);
96 if (len < size)
97 size = len;
98 if (copy_from_user(buf, curptr, size))
99 return -EFAULT;
100 err = func(private_data, buf, size);
101 if (err < 0)
102 return err;
103 curptr += size;
104 len -= size;
105 }
106 return 0;
107 } if (! (event->data.ext.len & SNDRV_SEQ_EXT_CHAINED)) {
108 return func(private_data, event->data.ext.ptr, len);
109 }
110
111 cell = (snd_seq_event_cell_t*)event->data.ext.ptr;
112 for (; len > 0 && cell; cell = cell->next) {
113 int size = sizeof(snd_seq_event_t);
114 if (len < size)
115 size = len;
116 err = func(private_data, &cell->event, size);
117 if (err < 0)
118 return err;
119 len -= size;
120 }
121 return 0;
122}
123
124
125/*
126 * exported:
127 * expand the variable length event to linear buffer space.
128 */
129
130static int seq_copy_in_kernel(char **bufptr, const void *src, int size)
131{
132 memcpy(*bufptr, src, size);
133 *bufptr += size;
134 return 0;
135}
136
137static int seq_copy_in_user(char __user **bufptr, const void *src, int size)
138{
139 if (copy_to_user(*bufptr, src, size))
140 return -EFAULT;
141 *bufptr += size;
142 return 0;
143}
144
145int snd_seq_expand_var_event(const snd_seq_event_t *event, int count, char *buf, int in_kernel, int size_aligned)
146{
147 int len, newlen;
148 int err;
149
150 if ((len = get_var_len(event)) < 0)
151 return len;
152 newlen = len;
153 if (size_aligned > 0)
154 newlen = ((len + size_aligned - 1) / size_aligned) * size_aligned;
155 if (count < newlen)
156 return -EAGAIN;
157
158 if (event->data.ext.len & SNDRV_SEQ_EXT_USRPTR) {
159 if (! in_kernel)
160 return -EINVAL;
161 if (copy_from_user(buf, (void __user *)event->data.ext.ptr, len))
162 return -EFAULT;
163 return newlen;
164 }
165 err = snd_seq_dump_var_event(event,
166 in_kernel ? (snd_seq_dump_func_t)seq_copy_in_kernel :
167 (snd_seq_dump_func_t)seq_copy_in_user,
168 &buf);
169 return err < 0 ? err : newlen;
170}
171
172
173/*
174 * release this cell, free extended data if available
175 */
176
177static inline void free_cell(pool_t *pool, snd_seq_event_cell_t *cell)
178{
179 cell->next = pool->free;
180 pool->free = cell;
181 atomic_dec(&pool->counter);
182}
183
184void snd_seq_cell_free(snd_seq_event_cell_t * cell)
185{
186 unsigned long flags;
187 pool_t *pool;
188
189 snd_assert(cell != NULL, return);
190 pool = cell->pool;
191 snd_assert(pool != NULL, return);
192
193 spin_lock_irqsave(&pool->lock, flags);
194 free_cell(pool, cell);
195 if (snd_seq_ev_is_variable(&cell->event)) {
196 if (cell->event.data.ext.len & SNDRV_SEQ_EXT_CHAINED) {
197 snd_seq_event_cell_t *curp, *nextptr;
198 curp = cell->event.data.ext.ptr;
199 for (; curp; curp = nextptr) {
200 nextptr = curp->next;
201 curp->next = pool->free;
202 free_cell(pool, curp);
203 }
204 }
205 }
206 if (waitqueue_active(&pool->output_sleep)) {
207 /* has enough space now? */
208 if (snd_seq_output_ok(pool))
209 wake_up(&pool->output_sleep);
210 }
211 spin_unlock_irqrestore(&pool->lock, flags);
212}
213
214
215/*
216 * allocate an event cell.
217 */
218static int snd_seq_cell_alloc(pool_t *pool, snd_seq_event_cell_t **cellp, int nonblock, struct file *file)
219{
220 snd_seq_event_cell_t *cell;
221 unsigned long flags;
222 int err = -EAGAIN;
223 wait_queue_t wait;
224
225 if (pool == NULL)
226 return -EINVAL;
227
228 *cellp = NULL;
229
230 init_waitqueue_entry(&wait, current);
231 spin_lock_irqsave(&pool->lock, flags);
232 if (pool->ptr == NULL) { /* not initialized */
233 snd_printd("seq: pool is not initialized\n");
234 err = -EINVAL;
235 goto __error;
236 }
237 while (pool->free == NULL && ! nonblock && ! pool->closing) {
238
239 set_current_state(TASK_INTERRUPTIBLE);
240 add_wait_queue(&pool->output_sleep, &wait);
241 spin_unlock_irq(&pool->lock);
242 schedule();
243 spin_lock_irq(&pool->lock);
244 remove_wait_queue(&pool->output_sleep, &wait);
245 /* interrupted? */
246 if (signal_pending(current)) {
247 err = -ERESTARTSYS;
248 goto __error;
249 }
250 }
251 if (pool->closing) { /* closing.. */
252 err = -ENOMEM;
253 goto __error;
254 }
255
256 cell = pool->free;
257 if (cell) {
258 int used;
259 pool->free = cell->next;
260 atomic_inc(&pool->counter);
261 used = atomic_read(&pool->counter);
262 if (pool->max_used < used)
263 pool->max_used = used;
264 pool->event_alloc_success++;
265 /* clear cell pointers */
266 cell->next = NULL;
267 err = 0;
268 } else
269 pool->event_alloc_failures++;
270 *cellp = cell;
271
272__error:
273 spin_unlock_irqrestore(&pool->lock, flags);
274 return err;
275}
276
277
278/*
279 * duplicate the event to a cell.
280 * if the event has external data, the data is decomposed to additional
281 * cells.
282 */
283int snd_seq_event_dup(pool_t *pool, snd_seq_event_t *event, snd_seq_event_cell_t **cellp, int nonblock, struct file *file)
284{
285 int ncells, err;
286 unsigned int extlen;
287 snd_seq_event_cell_t *cell;
288
289 *cellp = NULL;
290
291 ncells = 0;
292 extlen = 0;
293 if (snd_seq_ev_is_variable(event)) {
294 extlen = event->data.ext.len & ~SNDRV_SEQ_EXT_MASK;
295 ncells = (extlen + sizeof(snd_seq_event_t) - 1) / sizeof(snd_seq_event_t);
296 }
297 if (ncells >= pool->total_elements)
298 return -ENOMEM;
299
300 err = snd_seq_cell_alloc(pool, &cell, nonblock, file);
301 if (err < 0)
302 return err;
303
304 /* copy the event */
305 cell->event = *event;
306
307 /* decompose */
308 if (snd_seq_ev_is_variable(event)) {
309 int len = extlen;
310 int is_chained = event->data.ext.len & SNDRV_SEQ_EXT_CHAINED;
311 int is_usrptr = event->data.ext.len & SNDRV_SEQ_EXT_USRPTR;
312 snd_seq_event_cell_t *src, *tmp, *tail;
313 char *buf;
314
315 cell->event.data.ext.len = extlen | SNDRV_SEQ_EXT_CHAINED;
316 cell->event.data.ext.ptr = NULL;
317
318 src = (snd_seq_event_cell_t*)event->data.ext.ptr;
319 buf = (char *)event->data.ext.ptr;
320 tail = NULL;
321
322 while (ncells-- > 0) {
323 int size = sizeof(snd_seq_event_t);
324 if (len < size)
325 size = len;
326 err = snd_seq_cell_alloc(pool, &tmp, nonblock, file);
327 if (err < 0)
328 goto __error;
329 if (cell->event.data.ext.ptr == NULL)
330 cell->event.data.ext.ptr = tmp;
331 if (tail)
332 tail->next = tmp;
333 tail = tmp;
334 /* copy chunk */
335 if (is_chained && src) {
336 tmp->event = src->event;
337 src = src->next;
338 } else if (is_usrptr) {
339 if (copy_from_user(&tmp->event, (char __user *)buf, size)) {
340 err = -EFAULT;
341 goto __error;
342 }
343 } else {
344 memcpy(&tmp->event, buf, size);
345 }
346 buf += size;
347 len -= size;
348 }
349 }
350
351 *cellp = cell;
352 return 0;
353
354__error:
355 snd_seq_cell_free(cell);
356 return err;
357}
358
359
360/* poll wait */
361int snd_seq_pool_poll_wait(pool_t *pool, struct file *file, poll_table *wait)
362{
363 poll_wait(file, &pool->output_sleep, wait);
364 return snd_seq_output_ok(pool);
365}
366
367
368/* allocate room specified number of events */
369int snd_seq_pool_init(pool_t *pool)
370{
371 int cell;
372 snd_seq_event_cell_t *cellptr;
373 unsigned long flags;
374
375 snd_assert(pool != NULL, return -EINVAL);
376 if (pool->ptr) /* should be atomic? */
377 return 0;
378
379 pool->ptr = vmalloc(sizeof(snd_seq_event_cell_t) * pool->size);
380 if (pool->ptr == NULL) {
381 snd_printd("seq: malloc for sequencer events failed\n");
382 return -ENOMEM;
383 }
384
385 /* add new cells to the free cell list */
386 spin_lock_irqsave(&pool->lock, flags);
387 pool->free = NULL;
388
389 for (cell = 0; cell < pool->size; cell++) {
390 cellptr = pool->ptr + cell;
391 cellptr->pool = pool;
392 cellptr->next = pool->free;
393 pool->free = cellptr;
394 }
395 pool->room = (pool->size + 1) / 2;
396
397 /* init statistics */
398 pool->max_used = 0;
399 pool->total_elements = pool->size;
400 spin_unlock_irqrestore(&pool->lock, flags);
401 return 0;
402}
403
404/* remove events */
405int snd_seq_pool_done(pool_t *pool)
406{
407 unsigned long flags;
408 snd_seq_event_cell_t *ptr;
409 int max_count = 5 * HZ;
410
411 snd_assert(pool != NULL, return -EINVAL);
412
413 /* wait for closing all threads */
414 spin_lock_irqsave(&pool->lock, flags);
415 pool->closing = 1;
416 spin_unlock_irqrestore(&pool->lock, flags);
417
418 if (waitqueue_active(&pool->output_sleep))
419 wake_up(&pool->output_sleep);
420
421 while (atomic_read(&pool->counter) > 0) {
422 if (max_count == 0) {
423 snd_printk(KERN_WARNING "snd_seq_pool_done timeout: %d cells remain\n", atomic_read(&pool->counter));
424 break;
425 }
426 set_current_state(TASK_UNINTERRUPTIBLE);
427 schedule_timeout(1);
428 max_count--;
429 }
430
431 /* release all resources */
432 spin_lock_irqsave(&pool->lock, flags);
433 ptr = pool->ptr;
434 pool->ptr = NULL;
435 pool->free = NULL;
436 pool->total_elements = 0;
437 spin_unlock_irqrestore(&pool->lock, flags);
438
439 vfree(ptr);
440
441 spin_lock_irqsave(&pool->lock, flags);
442 pool->closing = 0;
443 spin_unlock_irqrestore(&pool->lock, flags);
444
445 return 0;
446}
447
448
449/* init new memory pool */
450pool_t *snd_seq_pool_new(int poolsize)
451{
452 pool_t *pool;
453
454 /* create pool block */
455 pool = kcalloc(1, sizeof(*pool), GFP_KERNEL);
456 if (pool == NULL) {
457 snd_printd("seq: malloc failed for pool\n");
458 return NULL;
459 }
460 spin_lock_init(&pool->lock);
461 pool->ptr = NULL;
462 pool->free = NULL;
463 pool->total_elements = 0;
464 atomic_set(&pool->counter, 0);
465 pool->closing = 0;
466 init_waitqueue_head(&pool->output_sleep);
467
468 pool->size = poolsize;
469
470 /* init statistics */
471 pool->max_used = 0;
472 return pool;
473}
474
475/* remove memory pool */
476int snd_seq_pool_delete(pool_t **ppool)
477{
478 pool_t *pool = *ppool;
479
480 *ppool = NULL;
481 if (pool == NULL)
482 return 0;
483 snd_seq_pool_done(pool);
484 kfree(pool);
485 return 0;
486}
487
488/* initialize sequencer memory */
489int __init snd_sequencer_memory_init(void)
490{
491 return 0;
492}
493
494/* release sequencer memory */
495void __exit snd_sequencer_memory_done(void)
496{
497}
498
499
500/* exported to seq_clientmgr.c */
501void snd_seq_info_pool(snd_info_buffer_t * buffer, pool_t *pool, char *space)
502{
503 if (pool == NULL)
504 return;
505 snd_iprintf(buffer, "%sPool size : %d\n", space, pool->total_elements);
506 snd_iprintf(buffer, "%sCells in use : %d\n", space, atomic_read(&pool->counter));
507 snd_iprintf(buffer, "%sPeak cells in use : %d\n", space, pool->max_used);
508 snd_iprintf(buffer, "%sAlloc success : %d\n", space, pool->event_alloc_success);
509 snd_iprintf(buffer, "%sAlloc failures : %d\n", space, pool->event_alloc_failures);
510}
diff --git a/sound/core/seq/seq_memory.h b/sound/core/seq/seq_memory.h
new file mode 100644
index 000000000000..6c4dde5d3d6f
--- /dev/null
+++ b/sound/core/seq/seq_memory.h
@@ -0,0 +1,104 @@
1/*
2 * ALSA sequencer Memory Manager
3 * Copyright (c) 1998 by Frank van de Pol <fvdpol@coil.demon.nl>
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#ifndef __SND_SEQ_MEMORYMGR_H
22#define __SND_SEQ_MEMORYMGR_H
23
24#include <sound/seq_kernel.h>
25#include <linux/poll.h>
26
27typedef struct pool pool_t;
28
29/* container for sequencer event (internal use) */
30typedef struct snd_seq_event_cell_t {
31 snd_seq_event_t event;
32 pool_t *pool; /* used pool */
33 struct snd_seq_event_cell_t *next; /* next cell */
34} snd_seq_event_cell_t;
35
36/* design note: the pool is a contigious block of memory, if we dynamicly
37 want to add additional cells to the pool be better store this in another
38 pool as we need to know the base address of the pool when releasing
39 memory. */
40
41struct pool {
42 snd_seq_event_cell_t *ptr; /* pointer to first event chunk */
43 snd_seq_event_cell_t *free; /* pointer to the head of the free list */
44
45 int total_elements; /* pool size actually allocated */
46 atomic_t counter; /* cells free */
47
48 int size; /* pool size to be allocated */
49 int room; /* watermark for sleep/wakeup */
50
51 int closing;
52
53 /* statistics */
54 int max_used;
55 int event_alloc_nopool;
56 int event_alloc_failures;
57 int event_alloc_success;
58
59 /* Write locking */
60 wait_queue_head_t output_sleep;
61
62 /* Pool lock */
63 spinlock_t lock;
64};
65
66extern void snd_seq_cell_free(snd_seq_event_cell_t* cell);
67
68int snd_seq_event_dup(pool_t *pool, snd_seq_event_t *event, snd_seq_event_cell_t **cellp, int nonblock, struct file *file);
69
70/* return number of unused (free) cells */
71static inline int snd_seq_unused_cells(pool_t *pool)
72{
73 return pool ? pool->total_elements - atomic_read(&pool->counter) : 0;
74}
75
76/* return total number of allocated cells */
77static inline int snd_seq_total_cells(pool_t *pool)
78{
79 return pool ? pool->total_elements : 0;
80}
81
82/* init pool - allocate events */
83int snd_seq_pool_init(pool_t *pool);
84
85/* done pool - free events */
86int snd_seq_pool_done(pool_t *pool);
87
88/* create pool */
89pool_t *snd_seq_pool_new(int poolsize);
90
91/* remove pool */
92int snd_seq_pool_delete(pool_t **pool);
93
94/* init memory */
95int snd_sequencer_memory_init(void);
96
97/* release event memory */
98void snd_sequencer_memory_done(void);
99
100/* polling */
101int snd_seq_pool_poll_wait(pool_t *pool, struct file *file, poll_table *wait);
102
103
104#endif
diff --git a/sound/core/seq/seq_midi.c b/sound/core/seq/seq_midi.c
new file mode 100644
index 000000000000..18247db45db6
--- /dev/null
+++ b/sound/core/seq/seq_midi.c
@@ -0,0 +1,489 @@
1/*
2 * Generic MIDI synth driver for ALSA sequencer
3 * Copyright (c) 1998 by Frank van de Pol <fvdpol@coil.demon.nl>
4 * Jaroslav Kysela <perex@suse.cz>
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/*
23Possible options for midisynth module:
24 - automatic opening of midi ports on first received event or subscription
25 (close will be performed when client leaves)
26*/
27
28
29#include <sound/driver.h>
30#include <linux/init.h>
31#include <linux/slab.h>
32#include <linux/errno.h>
33#include <linux/string.h>
34#include <linux/moduleparam.h>
35#include <asm/semaphore.h>
36#include <sound/core.h>
37#include <sound/rawmidi.h>
38#include <sound/seq_kernel.h>
39#include <sound/seq_device.h>
40#include <sound/seq_midi_event.h>
41#include <sound/initval.h>
42
43MODULE_AUTHOR("Frank van de Pol <fvdpol@coil.demon.nl>, Jaroslav Kysela <perex@suse.cz>");
44MODULE_DESCRIPTION("Advanced Linux Sound Architecture sequencer MIDI synth.");
45MODULE_LICENSE("GPL");
46static int output_buffer_size = PAGE_SIZE;
47module_param(output_buffer_size, int, 0644);
48MODULE_PARM_DESC(output_buffer_size, "Output buffer size in bytes.");
49static int input_buffer_size = PAGE_SIZE;
50module_param(input_buffer_size, int, 0644);
51MODULE_PARM_DESC(input_buffer_size, "Input buffer size in bytes.");
52
53/* data for this midi synth driver */
54typedef struct {
55 snd_card_t *card;
56 int device;
57 int subdevice;
58 snd_rawmidi_file_t input_rfile;
59 snd_rawmidi_file_t output_rfile;
60 int seq_client;
61 int seq_port;
62 snd_midi_event_t *parser;
63} seq_midisynth_t;
64
65typedef struct {
66 int seq_client;
67 int num_ports;
68 int ports_per_device[SNDRV_RAWMIDI_DEVICES];
69 seq_midisynth_t *ports[SNDRV_RAWMIDI_DEVICES];
70} seq_midisynth_client_t;
71
72static seq_midisynth_client_t *synths[SNDRV_CARDS];
73static DECLARE_MUTEX(register_mutex);
74
75/* handle rawmidi input event (MIDI v1.0 stream) */
76static void snd_midi_input_event(snd_rawmidi_substream_t * substream)
77{
78 snd_rawmidi_runtime_t *runtime;
79 seq_midisynth_t *msynth;
80 snd_seq_event_t ev;
81 char buf[16], *pbuf;
82 long res, count;
83
84 if (substream == NULL)
85 return;
86 runtime = substream->runtime;
87 msynth = (seq_midisynth_t *) runtime->private_data;
88 if (msynth == NULL)
89 return;
90 memset(&ev, 0, sizeof(ev));
91 while (runtime->avail > 0) {
92 res = snd_rawmidi_kernel_read(substream, buf, sizeof(buf));
93 if (res <= 0)
94 continue;
95 if (msynth->parser == NULL)
96 continue;
97 pbuf = buf;
98 while (res > 0) {
99 count = snd_midi_event_encode(msynth->parser, pbuf, res, &ev);
100 if (count < 0)
101 break;
102 pbuf += count;
103 res -= count;
104 if (ev.type != SNDRV_SEQ_EVENT_NONE) {
105 ev.source.port = msynth->seq_port;
106 ev.dest.client = SNDRV_SEQ_ADDRESS_SUBSCRIBERS;
107 snd_seq_kernel_client_dispatch(msynth->seq_client, &ev, 1, 0);
108 /* clear event and reset header */
109 memset(&ev, 0, sizeof(ev));
110 }
111 }
112 }
113}
114
115static int dump_midi(snd_rawmidi_substream_t *substream, const char *buf, int count)
116{
117 snd_rawmidi_runtime_t *runtime;
118 int tmp;
119
120 snd_assert(substream != NULL || buf != NULL, return -EINVAL);
121 runtime = substream->runtime;
122 if ((tmp = runtime->avail) < count) {
123 snd_printd("warning, output event was lost (count = %i, available = %i)\n", count, tmp);
124 return -ENOMEM;
125 }
126 if (snd_rawmidi_kernel_write(substream, buf, count) < count)
127 return -EINVAL;
128 return 0;
129}
130
131static int event_process_midi(snd_seq_event_t * ev, int direct,
132 void *private_data, int atomic, int hop)
133{
134 seq_midisynth_t *msynth = (seq_midisynth_t *) private_data;
135 unsigned char msg[10]; /* buffer for constructing midi messages */
136 snd_rawmidi_substream_t *substream;
137 int res;
138
139 snd_assert(msynth != NULL, return -EINVAL);
140 substream = msynth->output_rfile.output;
141 if (substream == NULL)
142 return -ENODEV;
143 if (ev->type == SNDRV_SEQ_EVENT_SYSEX) { /* special case, to save space */
144 if ((ev->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) != SNDRV_SEQ_EVENT_LENGTH_VARIABLE) {
145 /* invalid event */
146 snd_printd("seq_midi: invalid sysex event flags = 0x%x\n", ev->flags);
147 return 0;
148 }
149 res = snd_seq_dump_var_event(ev, (snd_seq_dump_func_t)dump_midi, substream);
150 snd_midi_event_reset_decode(msynth->parser);
151 if (res < 0)
152 return res;
153 } else {
154 if (msynth->parser == NULL)
155 return -EIO;
156 res = snd_midi_event_decode(msynth->parser, msg, sizeof(msg), ev);
157 if (res < 0)
158 return res;
159 if ((res = dump_midi(substream, msg, res)) < 0) {
160 snd_midi_event_reset_decode(msynth->parser);
161 return res;
162 }
163 }
164 return 0;
165}
166
167
168static int snd_seq_midisynth_new(seq_midisynth_t *msynth,
169 snd_card_t *card,
170 int device,
171 int subdevice)
172{
173 if (snd_midi_event_new(MAX_MIDI_EVENT_BUF, &msynth->parser) < 0)
174 return -ENOMEM;
175 msynth->card = card;
176 msynth->device = device;
177 msynth->subdevice = subdevice;
178 return 0;
179}
180
181/* open associated midi device for input */
182static int midisynth_subscribe(void *private_data, snd_seq_port_subscribe_t *info)
183{
184 int err;
185 seq_midisynth_t *msynth = (seq_midisynth_t *)private_data;
186 snd_rawmidi_runtime_t *runtime;
187 snd_rawmidi_params_t params;
188
189 /* open midi port */
190 if ((err = snd_rawmidi_kernel_open(msynth->card->number, msynth->device, msynth->subdevice, SNDRV_RAWMIDI_LFLG_INPUT, &msynth->input_rfile)) < 0) {
191 snd_printd("midi input open failed!!!\n");
192 return err;
193 }
194 runtime = msynth->input_rfile.input->runtime;
195 memset(&params, 0, sizeof(params));
196 params.avail_min = 1;
197 params.buffer_size = input_buffer_size;
198 if ((err = snd_rawmidi_input_params(msynth->input_rfile.input, &params)) < 0) {
199 snd_rawmidi_kernel_release(&msynth->input_rfile);
200 return err;
201 }
202 snd_midi_event_reset_encode(msynth->parser);
203 runtime->event = snd_midi_input_event;
204 runtime->private_data = msynth;
205 snd_rawmidi_kernel_read(msynth->input_rfile.input, NULL, 0);
206 return 0;
207}
208
209/* close associated midi device for input */
210static int midisynth_unsubscribe(void *private_data, snd_seq_port_subscribe_t *info)
211{
212 int err;
213 seq_midisynth_t *msynth = (seq_midisynth_t *)private_data;
214
215 snd_assert(msynth->input_rfile.input != NULL, return -EINVAL);
216 err = snd_rawmidi_kernel_release(&msynth->input_rfile);
217 return err;
218}
219
220/* open associated midi device for output */
221static int midisynth_use(void *private_data, snd_seq_port_subscribe_t *info)
222{
223 int err;
224 seq_midisynth_t *msynth = (seq_midisynth_t *)private_data;
225 snd_rawmidi_params_t params;
226
227 /* open midi port */
228 if ((err = snd_rawmidi_kernel_open(msynth->card->number, msynth->device, msynth->subdevice, SNDRV_RAWMIDI_LFLG_OUTPUT, &msynth->output_rfile)) < 0) {
229 snd_printd("midi output open failed!!!\n");
230 return err;
231 }
232 memset(&params, 0, sizeof(params));
233 params.avail_min = 1;
234 params.buffer_size = output_buffer_size;
235 if ((err = snd_rawmidi_output_params(msynth->output_rfile.output, &params)) < 0) {
236 snd_rawmidi_kernel_release(&msynth->output_rfile);
237 return err;
238 }
239 snd_midi_event_reset_decode(msynth->parser);
240 return 0;
241}
242
243/* close associated midi device for output */
244static int midisynth_unuse(void *private_data, snd_seq_port_subscribe_t *info)
245{
246 seq_midisynth_t *msynth = (seq_midisynth_t *)private_data;
247 unsigned char buf = 0xff; /* MIDI reset */
248
249 snd_assert(msynth->output_rfile.output != NULL, return -EINVAL);
250 /* sending single MIDI reset message to shut the device up */
251 snd_rawmidi_kernel_write(msynth->output_rfile.output, &buf, 1);
252 snd_rawmidi_drain_output(msynth->output_rfile.output);
253 return snd_rawmidi_kernel_release(&msynth->output_rfile);
254}
255
256/* delete given midi synth port */
257static void snd_seq_midisynth_delete(seq_midisynth_t *msynth)
258{
259 if (msynth == NULL)
260 return;
261
262 if (msynth->seq_client > 0) {
263 /* delete port */
264 snd_seq_event_port_detach(msynth->seq_client, msynth->seq_port);
265 }
266
267 if (msynth->parser)
268 snd_midi_event_free(msynth->parser);
269}
270
271/* set our client name */
272static int set_client_name(seq_midisynth_client_t *client, snd_card_t *card,
273 snd_rawmidi_info_t *rmidi)
274{
275 snd_seq_client_info_t cinfo;
276 const char *name;
277
278 memset(&cinfo, 0, sizeof(cinfo));
279 cinfo.client = client->seq_client;
280 cinfo.type = KERNEL_CLIENT;
281 name = rmidi->name[0] ? (const char *)rmidi->name : "External MIDI";
282 strlcpy(cinfo.name, name, sizeof(cinfo.name));
283 return snd_seq_kernel_client_ctl(client->seq_client, SNDRV_SEQ_IOCTL_SET_CLIENT_INFO, &cinfo);
284}
285
286/* register new midi synth port */
287static int
288snd_seq_midisynth_register_port(snd_seq_device_t *dev)
289{
290 seq_midisynth_client_t *client;
291 seq_midisynth_t *msynth, *ms;
292 snd_seq_port_info_t *port;
293 snd_rawmidi_info_t *info;
294 int newclient = 0;
295 unsigned int p, ports;
296 snd_seq_client_callback_t callbacks;
297 snd_seq_port_callback_t pcallbacks;
298 snd_card_t *card = dev->card;
299 int device = dev->device;
300 unsigned int input_count = 0, output_count = 0;
301
302 snd_assert(card != NULL && device >= 0 && device < SNDRV_RAWMIDI_DEVICES, return -EINVAL);
303 info = kmalloc(sizeof(*info), GFP_KERNEL);
304 if (! info)
305 return -ENOMEM;
306 info->device = device;
307 info->stream = SNDRV_RAWMIDI_STREAM_OUTPUT;
308 info->subdevice = 0;
309 if (snd_rawmidi_info_select(card, info) >= 0)
310 output_count = info->subdevices_count;
311 info->stream = SNDRV_RAWMIDI_STREAM_INPUT;
312 if (snd_rawmidi_info_select(card, info) >= 0) {
313 input_count = info->subdevices_count;
314 }
315 ports = output_count;
316 if (ports < input_count)
317 ports = input_count;
318 if (ports == 0) {
319 kfree(info);
320 return -ENODEV;
321 }
322 if (ports > (256 / SNDRV_RAWMIDI_DEVICES))
323 ports = 256 / SNDRV_RAWMIDI_DEVICES;
324
325 down(&register_mutex);
326 client = synths[card->number];
327 if (client == NULL) {
328 newclient = 1;
329 client = kcalloc(1, sizeof(*client), GFP_KERNEL);
330 if (client == NULL) {
331 up(&register_mutex);
332 kfree(info);
333 return -ENOMEM;
334 }
335 memset(&callbacks, 0, sizeof(callbacks));
336 callbacks.private_data = client;
337 callbacks.allow_input = callbacks.allow_output = 1;
338 client->seq_client = snd_seq_create_kernel_client(card, 0, &callbacks);
339 if (client->seq_client < 0) {
340 kfree(client);
341 up(&register_mutex);
342 kfree(info);
343 return -ENOMEM;
344 }
345 set_client_name(client, card, info);
346 } else if (device == 0)
347 set_client_name(client, card, info); /* use the first device's name */
348
349 msynth = kcalloc(ports, sizeof(seq_midisynth_t), GFP_KERNEL);
350 port = kmalloc(sizeof(*port), GFP_KERNEL);
351 if (msynth == NULL || port == NULL)
352 goto __nomem;
353
354 for (p = 0; p < ports; p++) {
355 ms = &msynth[p];
356
357 if (snd_seq_midisynth_new(ms, card, device, p) < 0)
358 goto __nomem;
359
360 /* declare port */
361 memset(port, 0, sizeof(*port));
362 port->addr.client = client->seq_client;
363 port->addr.port = device * (256 / SNDRV_RAWMIDI_DEVICES) + p;
364 port->flags = SNDRV_SEQ_PORT_FLG_GIVEN_PORT;
365 memset(info, 0, sizeof(*info));
366 info->device = device;
367 if (p < output_count)
368 info->stream = SNDRV_RAWMIDI_STREAM_OUTPUT;
369 else
370 info->stream = SNDRV_RAWMIDI_STREAM_INPUT;
371 info->subdevice = p;
372 if (snd_rawmidi_info_select(card, info) >= 0)
373 strcpy(port->name, info->subname);
374 if (! port->name[0]) {
375 if (info->name[0]) {
376 if (ports > 1)
377 snprintf(port->name, sizeof(port->name), "%s-%d", info->name, p);
378 else
379 snprintf(port->name, sizeof(port->name), "%s", info->name);
380 } else {
381 /* last resort */
382 if (ports > 1)
383 sprintf(port->name, "MIDI %d-%d-%d", card->number, device, p);
384 else
385 sprintf(port->name, "MIDI %d-%d", card->number, device);
386 }
387 }
388 if ((info->flags & SNDRV_RAWMIDI_INFO_OUTPUT) && p < output_count)
389 port->capability |= SNDRV_SEQ_PORT_CAP_WRITE | SNDRV_SEQ_PORT_CAP_SYNC_WRITE | SNDRV_SEQ_PORT_CAP_SUBS_WRITE;
390 if ((info->flags & SNDRV_RAWMIDI_INFO_INPUT) && p < input_count)
391 port->capability |= SNDRV_SEQ_PORT_CAP_READ | SNDRV_SEQ_PORT_CAP_SYNC_READ | SNDRV_SEQ_PORT_CAP_SUBS_READ;
392 if ((port->capability & (SNDRV_SEQ_PORT_CAP_WRITE|SNDRV_SEQ_PORT_CAP_READ)) == (SNDRV_SEQ_PORT_CAP_WRITE|SNDRV_SEQ_PORT_CAP_READ) &&
393 info->flags & SNDRV_RAWMIDI_INFO_DUPLEX)
394 port->capability |= SNDRV_SEQ_PORT_CAP_DUPLEX;
395 port->type = SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC;
396 port->midi_channels = 16;
397 memset(&pcallbacks, 0, sizeof(pcallbacks));
398 pcallbacks.owner = THIS_MODULE;
399 pcallbacks.private_data = ms;
400 pcallbacks.subscribe = midisynth_subscribe;
401 pcallbacks.unsubscribe = midisynth_unsubscribe;
402 pcallbacks.use = midisynth_use;
403 pcallbacks.unuse = midisynth_unuse;
404 pcallbacks.event_input = event_process_midi;
405 port->kernel = &pcallbacks;
406 if (snd_seq_kernel_client_ctl(client->seq_client, SNDRV_SEQ_IOCTL_CREATE_PORT, port)<0)
407 goto __nomem;
408 ms->seq_client = client->seq_client;
409 ms->seq_port = port->addr.port;
410 }
411 client->ports_per_device[device] = ports;
412 client->ports[device] = msynth;
413 client->num_ports++;
414 if (newclient)
415 synths[card->number] = client;
416 up(&register_mutex);
417 return 0; /* success */
418
419 __nomem:
420 if (msynth != NULL) {
421 for (p = 0; p < ports; p++)
422 snd_seq_midisynth_delete(&msynth[p]);
423 kfree(msynth);
424 }
425 if (newclient) {
426 snd_seq_delete_kernel_client(client->seq_client);
427 kfree(client);
428 }
429 kfree(info);
430 kfree(port);
431 up(&register_mutex);
432 return -ENOMEM;
433}
434
435/* release midi synth port */
436static int
437snd_seq_midisynth_unregister_port(snd_seq_device_t *dev)
438{
439 seq_midisynth_client_t *client;
440 seq_midisynth_t *msynth;
441 snd_card_t *card = dev->card;
442 int device = dev->device, p, ports;
443
444 down(&register_mutex);
445 client = synths[card->number];
446 if (client == NULL || client->ports[device] == NULL) {
447 up(&register_mutex);
448 return -ENODEV;
449 }
450 ports = client->ports_per_device[device];
451 client->ports_per_device[device] = 0;
452 msynth = client->ports[device];
453 client->ports[device] = NULL;
454 snd_runtime_check(msynth != NULL || ports <= 0, goto __skip);
455 for (p = 0; p < ports; p++)
456 snd_seq_midisynth_delete(&msynth[p]);
457 kfree(msynth);
458 __skip:
459 client->num_ports--;
460 if (client->num_ports <= 0) {
461 snd_seq_delete_kernel_client(client->seq_client);
462 synths[card->number] = NULL;
463 kfree(client);
464 }
465 up(&register_mutex);
466 return 0;
467}
468
469
470static int __init alsa_seq_midi_init(void)
471{
472 static snd_seq_dev_ops_t ops = {
473 snd_seq_midisynth_register_port,
474 snd_seq_midisynth_unregister_port,
475 };
476 memset(&synths, 0, sizeof(synths));
477 snd_seq_autoload_lock();
478 snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_MIDISYNTH, &ops, 0);
479 snd_seq_autoload_unlock();
480 return 0;
481}
482
483static void __exit alsa_seq_midi_exit(void)
484{
485 snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_MIDISYNTH);
486}
487
488module_init(alsa_seq_midi_init)
489module_exit(alsa_seq_midi_exit)
diff --git a/sound/core/seq/seq_midi_emul.c b/sound/core/seq/seq_midi_emul.c
new file mode 100644
index 000000000000..35fe8a7e34bf
--- /dev/null
+++ b/sound/core/seq/seq_midi_emul.c
@@ -0,0 +1,735 @@
1/*
2 * GM/GS/XG midi module.
3 *
4 * Copyright (C) 1999 Steve Ratcliffe
5 *
6 * Based on awe_wave.c by Takashi Iwai
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (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 General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 *
22 */
23/*
24 * This module is used to keep track of the current midi state.
25 * It can be used for drivers that are required to emulate midi when
26 * the hardware doesn't.
27 *
28 * It was written for a AWE64 driver, but there should be no AWE specific
29 * code in here. If there is it should be reported as a bug.
30 */
31
32#include <sound/driver.h>
33#include <linux/init.h>
34#include <linux/slab.h>
35#include <linux/string.h>
36#include <sound/core.h>
37#include <sound/seq_kernel.h>
38#include <sound/seq_midi_emul.h>
39#include <sound/initval.h>
40#include <sound/asoundef.h>
41
42MODULE_AUTHOR("Takashi Iwai / Steve Ratcliffe");
43MODULE_DESCRIPTION("Advanced Linux Sound Architecture sequencer MIDI emulation.");
44MODULE_LICENSE("GPL");
45
46/* Prototypes for static functions */
47static void note_off(snd_midi_op_t *ops, void *drv, snd_midi_channel_t *chan, int note, int vel);
48static void do_control(snd_midi_op_t *ops, void *private,
49 snd_midi_channel_set_t *chset, snd_midi_channel_t *chan,
50 int control, int value);
51static void rpn(snd_midi_op_t *ops, void *drv, snd_midi_channel_t *chan, snd_midi_channel_set_t *chset);
52static void nrpn(snd_midi_op_t *ops, void *drv, snd_midi_channel_t *chan, snd_midi_channel_set_t *chset);
53static void sysex(snd_midi_op_t *ops, void *private, unsigned char *sysex, int len, snd_midi_channel_set_t *chset);
54static void all_sounds_off(snd_midi_op_t *ops, void *private, snd_midi_channel_t *chan);
55static void all_notes_off(snd_midi_op_t *ops, void *private, snd_midi_channel_t *chan);
56static void snd_midi_reset_controllers(snd_midi_channel_t *chan);
57static void reset_all_channels(snd_midi_channel_set_t *chset);
58
59
60/*
61 * Process an event in a driver independent way. This means dealing
62 * with RPN, NRPN, SysEx etc that are defined for common midi applications
63 * such as GM, GS and XG.
64 * There modes that this module will run in are:
65 * Generic MIDI - no interpretation at all, it will just save current values
66 * of controlers etc.
67 * GM - You can use all gm_ prefixed elements of chan. Controls, RPN, NRPN,
68 * SysEx will be interpreded as defined in General Midi.
69 * GS - You can use all gs_ prefixed elements of chan. Codes for GS will be
70 * interpreted.
71 * XG - You can use all xg_ prefixed elements of chan. Codes for XG will
72 * be interpreted.
73 */
74void
75snd_midi_process_event(snd_midi_op_t *ops,
76 snd_seq_event_t *ev, snd_midi_channel_set_t *chanset)
77{
78 snd_midi_channel_t *chan;
79 void *drv;
80 int dest_channel = 0;
81
82 if (ev == NULL || chanset == NULL) {
83 snd_printd("ev or chanbase NULL (snd_midi_process_event)\n");
84 return;
85 }
86 if (chanset->channels == NULL)
87 return;
88
89 if (snd_seq_ev_is_channel_type(ev)) {
90 dest_channel = ev->data.note.channel;
91 if (dest_channel >= chanset->max_channels) {
92 snd_printd("dest channel is %d, max is %d\n", dest_channel, chanset->max_channels);
93 return;
94 }
95 }
96
97 chan = chanset->channels + dest_channel;
98 drv = chanset->private_data;
99
100 /* EVENT_NOTE should be processed before queued */
101 if (ev->type == SNDRV_SEQ_EVENT_NOTE)
102 return;
103
104 /* Make sure that we don't have a note on that should really be
105 * a note off */
106 if (ev->type == SNDRV_SEQ_EVENT_NOTEON && ev->data.note.velocity == 0)
107 ev->type = SNDRV_SEQ_EVENT_NOTEOFF;
108
109 /* Make sure the note is within array range */
110 if (ev->type == SNDRV_SEQ_EVENT_NOTEON ||
111 ev->type == SNDRV_SEQ_EVENT_NOTEOFF ||
112 ev->type == SNDRV_SEQ_EVENT_KEYPRESS) {
113 if (ev->data.note.note >= 128)
114 return;
115 }
116
117 switch (ev->type) {
118 case SNDRV_SEQ_EVENT_NOTEON:
119 if (chan->note[ev->data.note.note] & SNDRV_MIDI_NOTE_ON) {
120 if (ops->note_off)
121 ops->note_off(drv, ev->data.note.note, 0, chan);
122 }
123 chan->note[ev->data.note.note] = SNDRV_MIDI_NOTE_ON;
124 if (ops->note_on)
125 ops->note_on(drv, ev->data.note.note, ev->data.note.velocity, chan);
126 break;
127 case SNDRV_SEQ_EVENT_NOTEOFF:
128 if (! (chan->note[ev->data.note.note] & SNDRV_MIDI_NOTE_ON))
129 break;
130 if (ops->note_off)
131 note_off(ops, drv, chan, ev->data.note.note, ev->data.note.velocity);
132 break;
133 case SNDRV_SEQ_EVENT_KEYPRESS:
134 if (ops->key_press)
135 ops->key_press(drv, ev->data.note.note, ev->data.note.velocity, chan);
136 break;
137 case SNDRV_SEQ_EVENT_CONTROLLER:
138 do_control(ops, drv, chanset, chan,
139 ev->data.control.param, ev->data.control.value);
140 break;
141 case SNDRV_SEQ_EVENT_PGMCHANGE:
142 chan->midi_program = ev->data.control.value;
143 break;
144 case SNDRV_SEQ_EVENT_PITCHBEND:
145 chan->midi_pitchbend = ev->data.control.value;
146 if (ops->control)
147 ops->control(drv, MIDI_CTL_PITCHBEND, chan);
148 break;
149 case SNDRV_SEQ_EVENT_CHANPRESS:
150 chan->midi_pressure = ev->data.control.value;
151 if (ops->control)
152 ops->control(drv, MIDI_CTL_CHAN_PRESSURE, chan);
153 break;
154 case SNDRV_SEQ_EVENT_CONTROL14:
155 /* Best guess is that this is any of the 14 bit controller values */
156 if (ev->data.control.param < 32) {
157 /* set low part first */
158 chan->control[ev->data.control.param + 32] =
159 ev->data.control.value & 0x7f;
160 do_control(ops, drv, chanset, chan,
161 ev->data.control.param,
162 ((ev->data.control.value>>7) & 0x7f));
163 } else
164 do_control(ops, drv, chanset, chan,
165 ev->data.control.param,
166 ev->data.control.value);
167 break;
168 case SNDRV_SEQ_EVENT_NONREGPARAM:
169 /* Break it back into its controler values */
170 chan->param_type = SNDRV_MIDI_PARAM_TYPE_NONREGISTERED;
171 chan->control[MIDI_CTL_MSB_DATA_ENTRY]
172 = (ev->data.control.value >> 7) & 0x7f;
173 chan->control[MIDI_CTL_LSB_DATA_ENTRY]
174 = ev->data.control.value & 0x7f;
175 chan->control[MIDI_CTL_NONREG_PARM_NUM_MSB]
176 = (ev->data.control.param >> 7) & 0x7f;
177 chan->control[MIDI_CTL_NONREG_PARM_NUM_LSB]
178 = ev->data.control.param & 0x7f;
179 nrpn(ops, drv, chan, chanset);
180 break;
181 case SNDRV_SEQ_EVENT_REGPARAM:
182 /* Break it back into its controler values */
183 chan->param_type = SNDRV_MIDI_PARAM_TYPE_REGISTERED;
184 chan->control[MIDI_CTL_MSB_DATA_ENTRY]
185 = (ev->data.control.value >> 7) & 0x7f;
186 chan->control[MIDI_CTL_LSB_DATA_ENTRY]
187 = ev->data.control.value & 0x7f;
188 chan->control[MIDI_CTL_REGIST_PARM_NUM_MSB]
189 = (ev->data.control.param >> 7) & 0x7f;
190 chan->control[MIDI_CTL_REGIST_PARM_NUM_LSB]
191 = ev->data.control.param & 0x7f;
192 rpn(ops, drv, chan, chanset);
193 break;
194 case SNDRV_SEQ_EVENT_SYSEX:
195 if ((ev->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) == SNDRV_SEQ_EVENT_LENGTH_VARIABLE) {
196 unsigned char sysexbuf[64];
197 int len;
198 len = snd_seq_expand_var_event(ev, sizeof(sysexbuf), sysexbuf, 1, 0);
199 if (len > 0)
200 sysex(ops, drv, sysexbuf, len, chanset);
201 }
202 break;
203 case SNDRV_SEQ_EVENT_SONGPOS:
204 case SNDRV_SEQ_EVENT_SONGSEL:
205 case SNDRV_SEQ_EVENT_CLOCK:
206 case SNDRV_SEQ_EVENT_START:
207 case SNDRV_SEQ_EVENT_CONTINUE:
208 case SNDRV_SEQ_EVENT_STOP:
209 case SNDRV_SEQ_EVENT_QFRAME:
210 case SNDRV_SEQ_EVENT_TEMPO:
211 case SNDRV_SEQ_EVENT_TIMESIGN:
212 case SNDRV_SEQ_EVENT_KEYSIGN:
213 goto not_yet;
214 case SNDRV_SEQ_EVENT_SENSING:
215 break;
216 case SNDRV_SEQ_EVENT_CLIENT_START:
217 case SNDRV_SEQ_EVENT_CLIENT_EXIT:
218 case SNDRV_SEQ_EVENT_CLIENT_CHANGE:
219 case SNDRV_SEQ_EVENT_PORT_START:
220 case SNDRV_SEQ_EVENT_PORT_EXIT:
221 case SNDRV_SEQ_EVENT_PORT_CHANGE:
222 case SNDRV_SEQ_EVENT_SAMPLE:
223 case SNDRV_SEQ_EVENT_SAMPLE_START:
224 case SNDRV_SEQ_EVENT_SAMPLE_STOP:
225 case SNDRV_SEQ_EVENT_SAMPLE_FREQ:
226 case SNDRV_SEQ_EVENT_SAMPLE_VOLUME:
227 case SNDRV_SEQ_EVENT_SAMPLE_LOOP:
228 case SNDRV_SEQ_EVENT_SAMPLE_POSITION:
229 case SNDRV_SEQ_EVENT_ECHO:
230 not_yet:
231 default:
232 /*snd_printd("Unimplemented event %d\n", ev->type);*/
233 break;
234 }
235}
236
237
238/*
239 * release note
240 */
241static void
242note_off(snd_midi_op_t *ops, void *drv, snd_midi_channel_t *chan, int note, int vel)
243{
244 if (chan->gm_hold) {
245 /* Hold this note until pedal is turned off */
246 chan->note[note] |= SNDRV_MIDI_NOTE_RELEASED;
247 } else if (chan->note[note] & SNDRV_MIDI_NOTE_SOSTENUTO) {
248 /* Mark this note as release; it will be turned off when sostenuto
249 * is turned off */
250 chan->note[note] |= SNDRV_MIDI_NOTE_RELEASED;
251 } else {
252 chan->note[note] = 0;
253 if (ops->note_off)
254 ops->note_off(drv, note, vel, chan);
255 }
256}
257
258/*
259 * Do all driver independent operations for this controler and pass
260 * events that need to take place immediately to the driver.
261 */
262static void
263do_control(snd_midi_op_t *ops, void *drv, snd_midi_channel_set_t *chset,
264 snd_midi_channel_t *chan, int control, int value)
265{
266 int i;
267
268 /* Switches */
269 if ((control >=64 && control <=69) || (control >= 80 && control <= 83)) {
270 /* These are all switches; either off or on so set to 0 or 127 */
271 value = (value >= 64)? 127: 0;
272 }
273 chan->control[control] = value;
274
275 switch (control) {
276 case MIDI_CTL_SUSTAIN:
277 if (value == 0) {
278 /* Sustain has been released, turn off held notes */
279 for (i = 0; i < 128; i++) {
280 if (chan->note[i] & SNDRV_MIDI_NOTE_RELEASED) {
281 chan->note[i] = SNDRV_MIDI_NOTE_OFF;
282 if (ops->note_off)
283 ops->note_off(drv, i, 0, chan);
284 }
285 }
286 }
287 break;
288 case MIDI_CTL_PORTAMENTO:
289 break;
290 case MIDI_CTL_SOSTENUTO:
291 if (value) {
292 /* Mark each note that is currently held down */
293 for (i = 0; i < 128; i++) {
294 if (chan->note[i] & SNDRV_MIDI_NOTE_ON)
295 chan->note[i] |= SNDRV_MIDI_NOTE_SOSTENUTO;
296 }
297 } else {
298 /* release all notes that were held */
299 for (i = 0; i < 128; i++) {
300 if (chan->note[i] & SNDRV_MIDI_NOTE_SOSTENUTO) {
301 chan->note[i] &= ~SNDRV_MIDI_NOTE_SOSTENUTO;
302 if (chan->note[i] & SNDRV_MIDI_NOTE_RELEASED) {
303 chan->note[i] = SNDRV_MIDI_NOTE_OFF;
304 if (ops->note_off)
305 ops->note_off(drv, i, 0, chan);
306 }
307 }
308 }
309 }
310 break;
311 case MIDI_CTL_MSB_DATA_ENTRY:
312 chan->control[MIDI_CTL_LSB_DATA_ENTRY] = 0;
313 /* go through here */
314 case MIDI_CTL_LSB_DATA_ENTRY:
315 if (chan->param_type == SNDRV_MIDI_PARAM_TYPE_REGISTERED)
316 rpn(ops, drv, chan, chset);
317 else
318 nrpn(ops, drv, chan, chset);
319 break;
320 case MIDI_CTL_REGIST_PARM_NUM_LSB:
321 case MIDI_CTL_REGIST_PARM_NUM_MSB:
322 chan->param_type = SNDRV_MIDI_PARAM_TYPE_REGISTERED;
323 break;
324 case MIDI_CTL_NONREG_PARM_NUM_LSB:
325 case MIDI_CTL_NONREG_PARM_NUM_MSB:
326 chan->param_type = SNDRV_MIDI_PARAM_TYPE_NONREGISTERED;
327 break;
328
329 case MIDI_CTL_ALL_SOUNDS_OFF:
330 all_sounds_off(ops, drv, chan);
331 break;
332
333 case MIDI_CTL_ALL_NOTES_OFF:
334 all_notes_off(ops, drv, chan);
335 break;
336
337 case MIDI_CTL_MSB_BANK:
338 if (chset->midi_mode == SNDRV_MIDI_MODE_XG) {
339 if (value == 127)
340 chan->drum_channel = 1;
341 else
342 chan->drum_channel = 0;
343 }
344 break;
345 case MIDI_CTL_LSB_BANK:
346 break;
347
348 case MIDI_CTL_RESET_CONTROLLERS:
349 snd_midi_reset_controllers(chan);
350 break;
351
352 case MIDI_CTL_SOFT_PEDAL:
353 case MIDI_CTL_LEGATO_FOOTSWITCH:
354 case MIDI_CTL_HOLD2:
355 case MIDI_CTL_SC1_SOUND_VARIATION:
356 case MIDI_CTL_SC2_TIMBRE:
357 case MIDI_CTL_SC3_RELEASE_TIME:
358 case MIDI_CTL_SC4_ATTACK_TIME:
359 case MIDI_CTL_SC5_BRIGHTNESS:
360 case MIDI_CTL_E1_REVERB_DEPTH:
361 case MIDI_CTL_E2_TREMOLO_DEPTH:
362 case MIDI_CTL_E3_CHORUS_DEPTH:
363 case MIDI_CTL_E4_DETUNE_DEPTH:
364 case MIDI_CTL_E5_PHASER_DEPTH:
365 goto notyet;
366 notyet:
367 default:
368 if (ops->control)
369 ops->control(drv, control, chan);
370 break;
371 }
372}
373
374
375/*
376 * initialize the MIDI status
377 */
378void
379snd_midi_channel_set_clear(snd_midi_channel_set_t *chset)
380{
381 int i;
382
383 chset->midi_mode = SNDRV_MIDI_MODE_GM;
384 chset->gs_master_volume = 127;
385
386 for (i = 0; i < chset->max_channels; i++) {
387 snd_midi_channel_t *chan = chset->channels + i;
388 memset(chan->note, 0, sizeof(chan->note));
389
390 chan->midi_aftertouch = 0;
391 chan->midi_pressure = 0;
392 chan->midi_program = 0;
393 chan->midi_pitchbend = 0;
394 snd_midi_reset_controllers(chan);
395 chan->gm_rpn_pitch_bend_range = 256; /* 2 semitones */
396 chan->gm_rpn_fine_tuning = 0;
397 chan->gm_rpn_coarse_tuning = 0;
398
399 if (i == 9)
400 chan->drum_channel = 1;
401 else
402 chan->drum_channel = 0;
403 }
404}
405
406/*
407 * Process a rpn message.
408 */
409static void
410rpn(snd_midi_op_t *ops, void *drv, snd_midi_channel_t *chan,
411 snd_midi_channel_set_t *chset)
412{
413 int type;
414 int val;
415
416 if (chset->midi_mode != SNDRV_MIDI_MODE_NONE) {
417 type = (chan->control[MIDI_CTL_REGIST_PARM_NUM_MSB] << 8) |
418 chan->control[MIDI_CTL_REGIST_PARM_NUM_LSB];
419 val = (chan->control[MIDI_CTL_MSB_DATA_ENTRY] << 7) |
420 chan->control[MIDI_CTL_LSB_DATA_ENTRY];
421
422 switch (type) {
423 case 0x0000: /* Pitch bend sensitivity */
424 /* MSB only / 1 semitone per 128 */
425 chan->gm_rpn_pitch_bend_range = val;
426 break;
427
428 case 0x0001: /* fine tuning: */
429 /* MSB/LSB, 8192=center, 100/8192 cent step */
430 chan->gm_rpn_fine_tuning = val - 8192;
431 break;
432
433 case 0x0002: /* coarse tuning */
434 /* MSB only / 8192=center, 1 semitone per 128 */
435 chan->gm_rpn_coarse_tuning = val - 8192;
436 break;
437
438 case 0x7F7F: /* "lock-in" RPN */
439 /* ignored */
440 break;
441 }
442 }
443 /* should call nrpn or rpn callback here.. */
444}
445
446/*
447 * Process an nrpn message.
448 */
449static void
450nrpn(snd_midi_op_t *ops, void *drv, snd_midi_channel_t *chan,
451 snd_midi_channel_set_t *chset)
452{
453 /* parse XG NRPNs here if possible */
454 if (ops->nrpn)
455 ops->nrpn(drv, chan, chset);
456}
457
458
459/*
460 * convert channel parameter in GS sysex
461 */
462static int
463get_channel(unsigned char cmd)
464{
465 int p = cmd & 0x0f;
466 if (p == 0)
467 p = 9;
468 else if (p < 10)
469 p--;
470 return p;
471}
472
473
474/*
475 * Process a sysex message.
476 */
477static void
478sysex(snd_midi_op_t *ops, void *private, unsigned char *buf, int len, snd_midi_channel_set_t *chset)
479{
480 /* GM on */
481 static unsigned char gm_on_macro[] = {
482 0x7e,0x7f,0x09,0x01,
483 };
484 /* XG on */
485 static unsigned char xg_on_macro[] = {
486 0x43,0x10,0x4c,0x00,0x00,0x7e,0x00,
487 };
488 /* GS prefix
489 * drum channel: XX=0x1?(channel), YY=0x15, ZZ=on/off
490 * reverb mode: XX=0x01, YY=0x30, ZZ=0-7
491 * chorus mode: XX=0x01, YY=0x38, ZZ=0-7
492 * master vol: XX=0x00, YY=0x04, ZZ=0-127
493 */
494 static unsigned char gs_pfx_macro[] = {
495 0x41,0x10,0x42,0x12,0x40,/*XX,YY,ZZ*/
496 };
497
498 int parsed = SNDRV_MIDI_SYSEX_NOT_PARSED;
499
500 if (len <= 0 || buf[0] != 0xf0)
501 return;
502 /* skip first byte */
503 buf++;
504 len--;
505
506 /* GM on */
507 if (len >= (int)sizeof(gm_on_macro) &&
508 memcmp(buf, gm_on_macro, sizeof(gm_on_macro)) == 0) {
509 if (chset->midi_mode != SNDRV_MIDI_MODE_GS &&
510 chset->midi_mode != SNDRV_MIDI_MODE_XG) {
511 chset->midi_mode = SNDRV_MIDI_MODE_GM;
512 reset_all_channels(chset);
513 parsed = SNDRV_MIDI_SYSEX_GM_ON;
514 }
515 }
516
517 /* GS macros */
518 else if (len >= 8 &&
519 memcmp(buf, gs_pfx_macro, sizeof(gs_pfx_macro)) == 0) {
520 if (chset->midi_mode != SNDRV_MIDI_MODE_GS &&
521 chset->midi_mode != SNDRV_MIDI_MODE_XG)
522 chset->midi_mode = SNDRV_MIDI_MODE_GS;
523
524 if (buf[5] == 0x00 && buf[6] == 0x7f && buf[7] == 0x00) {
525 /* GS reset */
526 parsed = SNDRV_MIDI_SYSEX_GS_RESET;
527 reset_all_channels(chset);
528 }
529
530 else if ((buf[5] & 0xf0) == 0x10 && buf[6] == 0x15) {
531 /* drum pattern */
532 int p = get_channel(buf[5]);
533 if (p < chset->max_channels) {
534 parsed = SNDRV_MIDI_SYSEX_GS_DRUM_CHANNEL;
535 if (buf[7])
536 chset->channels[p].drum_channel = 1;
537 else
538 chset->channels[p].drum_channel = 0;
539 }
540
541 } else if ((buf[5] & 0xf0) == 0x10 && buf[6] == 0x21) {
542 /* program */
543 int p = get_channel(buf[5]);
544 if (p < chset->max_channels &&
545 ! chset->channels[p].drum_channel) {
546 parsed = SNDRV_MIDI_SYSEX_GS_DRUM_CHANNEL;
547 chset->channels[p].midi_program = buf[7];
548 }
549
550 } else if (buf[5] == 0x01 && buf[6] == 0x30) {
551 /* reverb mode */
552 parsed = SNDRV_MIDI_SYSEX_GS_REVERB_MODE;
553 chset->gs_reverb_mode = buf[7];
554
555 } else if (buf[5] == 0x01 && buf[6] == 0x38) {
556 /* chorus mode */
557 parsed = SNDRV_MIDI_SYSEX_GS_CHORUS_MODE;
558 chset->gs_chorus_mode = buf[7];
559
560 } else if (buf[5] == 0x00 && buf[6] == 0x04) {
561 /* master volume */
562 parsed = SNDRV_MIDI_SYSEX_GS_MASTER_VOLUME;
563 chset->gs_master_volume = buf[7];
564
565 }
566 }
567
568 /* XG on */
569 else if (len >= (int)sizeof(xg_on_macro) &&
570 memcmp(buf, xg_on_macro, sizeof(xg_on_macro)) == 0) {
571 int i;
572 chset->midi_mode = SNDRV_MIDI_MODE_XG;
573 parsed = SNDRV_MIDI_SYSEX_XG_ON;
574 /* reset CC#0 for drums */
575 for (i = 0; i < chset->max_channels; i++) {
576 if (chset->channels[i].drum_channel)
577 chset->channels[i].control[MIDI_CTL_MSB_BANK] = 127;
578 else
579 chset->channels[i].control[MIDI_CTL_MSB_BANK] = 0;
580 }
581 }
582
583 if (ops->sysex)
584 ops->sysex(private, buf - 1, len + 1, parsed, chset);
585}
586
587/*
588 * all sound off
589 */
590static void
591all_sounds_off(snd_midi_op_t *ops, void *drv, snd_midi_channel_t *chan)
592{
593 int n;
594
595 if (! ops->note_terminate)
596 return;
597 for (n = 0; n < 128; n++) {
598 if (chan->note[n]) {
599 ops->note_terminate(drv, n, chan);
600 chan->note[n] = 0;
601 }
602 }
603}
604
605/*
606 * all notes off
607 */
608static void
609all_notes_off(snd_midi_op_t *ops, void *drv, snd_midi_channel_t *chan)
610{
611 int n;
612
613 if (! ops->note_off)
614 return;
615 for (n = 0; n < 128; n++) {
616 if (chan->note[n] == SNDRV_MIDI_NOTE_ON)
617 note_off(ops, drv, chan, n, 0);
618 }
619}
620
621/*
622 * Initialise a single midi channel control block.
623 */
624static void snd_midi_channel_init(snd_midi_channel_t *p, int n)
625{
626 if (p == NULL)
627 return;
628
629 memset(p, 0, sizeof(snd_midi_channel_t));
630 p->private = NULL;
631 p->number = n;
632
633 snd_midi_reset_controllers(p);
634 p->gm_rpn_pitch_bend_range = 256; /* 2 semitones */
635 p->gm_rpn_fine_tuning = 0;
636 p->gm_rpn_coarse_tuning = 0;
637
638 if (n == 9)
639 p->drum_channel = 1; /* Default ch 10 as drums */
640}
641
642/*
643 * Allocate and initialise a set of midi channel control blocks.
644 */
645static snd_midi_channel_t *snd_midi_channel_init_set(int n)
646{
647 snd_midi_channel_t *chan;
648 int i;
649
650 chan = kmalloc(n * sizeof(snd_midi_channel_t), GFP_KERNEL);
651 if (chan) {
652 for (i = 0; i < n; i++)
653 snd_midi_channel_init(chan+i, i);
654 }
655
656 return chan;
657}
658
659/*
660 * reset all midi channels
661 */
662static void
663reset_all_channels(snd_midi_channel_set_t *chset)
664{
665 int ch;
666 for (ch = 0; ch < chset->max_channels; ch++) {
667 snd_midi_channel_t *chan = chset->channels + ch;
668 snd_midi_reset_controllers(chan);
669 chan->gm_rpn_pitch_bend_range = 256; /* 2 semitones */
670 chan->gm_rpn_fine_tuning = 0;
671 chan->gm_rpn_coarse_tuning = 0;
672
673 if (ch == 9)
674 chan->drum_channel = 1;
675 else
676 chan->drum_channel = 0;
677 }
678}
679
680
681/*
682 * Allocate and initialise a midi channel set.
683 */
684snd_midi_channel_set_t *snd_midi_channel_alloc_set(int n)
685{
686 snd_midi_channel_set_t *chset;
687
688 chset = kmalloc(sizeof(*chset), GFP_KERNEL);
689 if (chset) {
690 chset->channels = snd_midi_channel_init_set(n);
691 chset->private_data = NULL;
692 chset->max_channels = n;
693 }
694 return chset;
695}
696
697/*
698 * Reset the midi controllers on a particular channel to default values.
699 */
700static void snd_midi_reset_controllers(snd_midi_channel_t *chan)
701{
702 memset(chan->control, 0, sizeof(chan->control));
703 chan->gm_volume = 127;
704 chan->gm_expression = 127;
705 chan->gm_pan = 64;
706}
707
708
709/*
710 * Free a midi channel set.
711 */
712void snd_midi_channel_free_set(snd_midi_channel_set_t *chset)
713{
714 if (chset == NULL)
715 return;
716 kfree(chset->channels);
717 kfree(chset);
718}
719
720static int __init alsa_seq_midi_emul_init(void)
721{
722 return 0;
723}
724
725static void __exit alsa_seq_midi_emul_exit(void)
726{
727}
728
729module_init(alsa_seq_midi_emul_init)
730module_exit(alsa_seq_midi_emul_exit)
731
732EXPORT_SYMBOL(snd_midi_process_event);
733EXPORT_SYMBOL(snd_midi_channel_set_clear);
734EXPORT_SYMBOL(snd_midi_channel_alloc_set);
735EXPORT_SYMBOL(snd_midi_channel_free_set);
diff --git a/sound/core/seq/seq_midi_event.c b/sound/core/seq/seq_midi_event.c
new file mode 100644
index 000000000000..21e569062bc3
--- /dev/null
+++ b/sound/core/seq/seq_midi_event.c
@@ -0,0 +1,539 @@
1/*
2 * MIDI byte <-> sequencer event coder
3 *
4 * Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>,
5 * Jaroslav Kysela <perex@suse.cz>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (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 General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21
22#include <sound/driver.h>
23#include <linux/slab.h>
24#include <linux/errno.h>
25#include <linux/string.h>
26#include <sound/core.h>
27#include <sound/seq_kernel.h>
28#include <sound/seq_midi_event.h>
29#include <sound/asoundef.h>
30
31MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>, Jaroslav Kysela <perex@suse.cz>");
32MODULE_DESCRIPTION("MIDI byte <-> sequencer event coder");
33MODULE_LICENSE("GPL");
34
35/* queue type */
36/* from 0 to 7 are normal commands (note off, on, etc.) */
37#define ST_NOTEOFF 0
38#define ST_NOTEON 1
39#define ST_SPECIAL 8
40#define ST_SYSEX ST_SPECIAL
41/* from 8 to 15 are events for 0xf0-0xf7 */
42
43
44/* status event types */
45typedef void (*event_encode_t)(snd_midi_event_t *dev, snd_seq_event_t *ev);
46typedef void (*event_decode_t)(snd_seq_event_t *ev, unsigned char *buf);
47
48/*
49 * prototypes
50 */
51static void note_event(snd_midi_event_t *dev, snd_seq_event_t *ev);
52static void one_param_ctrl_event(snd_midi_event_t *dev, snd_seq_event_t *ev);
53static void pitchbend_ctrl_event(snd_midi_event_t *dev, snd_seq_event_t *ev);
54static void two_param_ctrl_event(snd_midi_event_t *dev, snd_seq_event_t *ev);
55static void one_param_event(snd_midi_event_t *dev, snd_seq_event_t *ev);
56static void songpos_event(snd_midi_event_t *dev, snd_seq_event_t *ev);
57static void note_decode(snd_seq_event_t *ev, unsigned char *buf);
58static void one_param_decode(snd_seq_event_t *ev, unsigned char *buf);
59static void pitchbend_decode(snd_seq_event_t *ev, unsigned char *buf);
60static void two_param_decode(snd_seq_event_t *ev, unsigned char *buf);
61static void songpos_decode(snd_seq_event_t *ev, unsigned char *buf);
62
63/*
64 * event list
65 */
66static struct status_event_list_t {
67 int event;
68 int qlen;
69 event_encode_t encode;
70 event_decode_t decode;
71} status_event[] = {
72 /* 0x80 - 0xf0 */
73 {SNDRV_SEQ_EVENT_NOTEOFF, 2, note_event, note_decode},
74 {SNDRV_SEQ_EVENT_NOTEON, 2, note_event, note_decode},
75 {SNDRV_SEQ_EVENT_KEYPRESS, 2, note_event, note_decode},
76 {SNDRV_SEQ_EVENT_CONTROLLER, 2, two_param_ctrl_event, two_param_decode},
77 {SNDRV_SEQ_EVENT_PGMCHANGE, 1, one_param_ctrl_event, one_param_decode},
78 {SNDRV_SEQ_EVENT_CHANPRESS, 1, one_param_ctrl_event, one_param_decode},
79 {SNDRV_SEQ_EVENT_PITCHBEND, 2, pitchbend_ctrl_event, pitchbend_decode},
80 {SNDRV_SEQ_EVENT_NONE, 0, NULL, NULL}, /* 0xf0 */
81 /* 0xf0 - 0xff */
82 {SNDRV_SEQ_EVENT_SYSEX, 1, NULL, NULL}, /* sysex: 0xf0 */
83 {SNDRV_SEQ_EVENT_QFRAME, 1, one_param_event, one_param_decode}, /* 0xf1 */
84 {SNDRV_SEQ_EVENT_SONGPOS, 2, songpos_event, songpos_decode}, /* 0xf2 */
85 {SNDRV_SEQ_EVENT_SONGSEL, 1, one_param_event, one_param_decode}, /* 0xf3 */
86 {SNDRV_SEQ_EVENT_NONE, 0, NULL, NULL}, /* 0xf4 */
87 {SNDRV_SEQ_EVENT_NONE, 0, NULL, NULL}, /* 0xf5 */
88 {SNDRV_SEQ_EVENT_TUNE_REQUEST, 0, NULL, NULL}, /* 0xf6 */
89 {SNDRV_SEQ_EVENT_NONE, 0, NULL, NULL}, /* 0xf7 */
90 {SNDRV_SEQ_EVENT_CLOCK, 0, NULL, NULL}, /* 0xf8 */
91 {SNDRV_SEQ_EVENT_NONE, 0, NULL, NULL}, /* 0xf9 */
92 {SNDRV_SEQ_EVENT_START, 0, NULL, NULL}, /* 0xfa */
93 {SNDRV_SEQ_EVENT_CONTINUE, 0, NULL, NULL}, /* 0xfb */
94 {SNDRV_SEQ_EVENT_STOP, 0, NULL, NULL}, /* 0xfc */
95 {SNDRV_SEQ_EVENT_NONE, 0, NULL, NULL}, /* 0xfd */
96 {SNDRV_SEQ_EVENT_SENSING, 0, NULL, NULL}, /* 0xfe */
97 {SNDRV_SEQ_EVENT_RESET, 0, NULL, NULL}, /* 0xff */
98};
99
100static int extra_decode_ctrl14(snd_midi_event_t *dev, unsigned char *buf, int len, snd_seq_event_t *ev);
101static int extra_decode_xrpn(snd_midi_event_t *dev, unsigned char *buf, int count, snd_seq_event_t *ev);
102
103static struct extra_event_list_t {
104 int event;
105 int (*decode)(snd_midi_event_t *dev, unsigned char *buf, int len, snd_seq_event_t *ev);
106} extra_event[] = {
107 {SNDRV_SEQ_EVENT_CONTROL14, extra_decode_ctrl14},
108 {SNDRV_SEQ_EVENT_NONREGPARAM, extra_decode_xrpn},
109 {SNDRV_SEQ_EVENT_REGPARAM, extra_decode_xrpn},
110};
111
112/*
113 * new/delete record
114 */
115
116int snd_midi_event_new(int bufsize, snd_midi_event_t **rdev)
117{
118 snd_midi_event_t *dev;
119
120 *rdev = NULL;
121 dev = kcalloc(1, sizeof(*dev), GFP_KERNEL);
122 if (dev == NULL)
123 return -ENOMEM;
124 if (bufsize > 0) {
125 dev->buf = kmalloc(bufsize, GFP_KERNEL);
126 if (dev->buf == NULL) {
127 kfree(dev);
128 return -ENOMEM;
129 }
130 }
131 dev->bufsize = bufsize;
132 dev->lastcmd = 0xff;
133 spin_lock_init(&dev->lock);
134 *rdev = dev;
135 return 0;
136}
137
138void snd_midi_event_free(snd_midi_event_t *dev)
139{
140 if (dev != NULL) {
141 kfree(dev->buf);
142 kfree(dev);
143 }
144}
145
146/*
147 * initialize record
148 */
149inline static void reset_encode(snd_midi_event_t *dev)
150{
151 dev->read = 0;
152 dev->qlen = 0;
153 dev->type = 0;
154}
155
156void snd_midi_event_reset_encode(snd_midi_event_t *dev)
157{
158 unsigned long flags;
159
160 spin_lock_irqsave(&dev->lock, flags);
161 reset_encode(dev);
162 spin_unlock_irqrestore(&dev->lock, flags);
163}
164
165void snd_midi_event_reset_decode(snd_midi_event_t *dev)
166{
167 unsigned long flags;
168
169 spin_lock_irqsave(&dev->lock, flags);
170 dev->lastcmd = 0xff;
171 spin_unlock_irqrestore(&dev->lock, flags);
172}
173
174void snd_midi_event_init(snd_midi_event_t *dev)
175{
176 snd_midi_event_reset_encode(dev);
177 snd_midi_event_reset_decode(dev);
178}
179
180void snd_midi_event_no_status(snd_midi_event_t *dev, int on)
181{
182 dev->nostat = on ? 1 : 0;
183}
184
185/*
186 * resize buffer
187 */
188int snd_midi_event_resize_buffer(snd_midi_event_t *dev, int bufsize)
189{
190 unsigned char *new_buf, *old_buf;
191 unsigned long flags;
192
193 if (bufsize == dev->bufsize)
194 return 0;
195 new_buf = kmalloc(bufsize, GFP_KERNEL);
196 if (new_buf == NULL)
197 return -ENOMEM;
198 spin_lock_irqsave(&dev->lock, flags);
199 old_buf = dev->buf;
200 dev->buf = new_buf;
201 dev->bufsize = bufsize;
202 reset_encode(dev);
203 spin_unlock_irqrestore(&dev->lock, flags);
204 kfree(old_buf);
205 return 0;
206}
207
208/*
209 * read bytes and encode to sequencer event if finished
210 * return the size of encoded bytes
211 */
212long snd_midi_event_encode(snd_midi_event_t *dev, unsigned char *buf, long count, snd_seq_event_t *ev)
213{
214 long result = 0;
215 int rc;
216
217 ev->type = SNDRV_SEQ_EVENT_NONE;
218
219 while (count-- > 0) {
220 rc = snd_midi_event_encode_byte(dev, *buf++, ev);
221 result++;
222 if (rc < 0)
223 return rc;
224 else if (rc > 0)
225 return result;
226 }
227
228 return result;
229}
230
231/*
232 * read one byte and encode to sequencer event:
233 * return 1 if MIDI bytes are encoded to an event
234 * 0 data is not finished
235 * negative for error
236 */
237int snd_midi_event_encode_byte(snd_midi_event_t *dev, int c, snd_seq_event_t *ev)
238{
239 int rc = 0;
240 unsigned long flags;
241
242 c &= 0xff;
243
244 if (c >= MIDI_CMD_COMMON_CLOCK) {
245 /* real-time event */
246 ev->type = status_event[ST_SPECIAL + c - 0xf0].event;
247 ev->flags &= ~SNDRV_SEQ_EVENT_LENGTH_MASK;
248 ev->flags |= SNDRV_SEQ_EVENT_LENGTH_FIXED;
249 return 1;
250 }
251
252 spin_lock_irqsave(&dev->lock, flags);
253 if (dev->qlen > 0) {
254 /* rest of command */
255 dev->buf[dev->read++] = c;
256 if (dev->type != ST_SYSEX)
257 dev->qlen--;
258 } else {
259 /* new command */
260 dev->read = 1;
261 if (c & 0x80) {
262 dev->buf[0] = c;
263 if ((c & 0xf0) == 0xf0) /* special events */
264 dev->type = (c & 0x0f) + ST_SPECIAL;
265 else
266 dev->type = (c >> 4) & 0x07;
267 dev->qlen = status_event[dev->type].qlen;
268 } else {
269 /* process this byte as argument */
270 dev->buf[dev->read++] = c;
271 dev->qlen = status_event[dev->type].qlen - 1;
272 }
273 }
274 if (dev->qlen == 0) {
275 ev->type = status_event[dev->type].event;
276 ev->flags &= ~SNDRV_SEQ_EVENT_LENGTH_MASK;
277 ev->flags |= SNDRV_SEQ_EVENT_LENGTH_FIXED;
278 if (status_event[dev->type].encode) /* set data values */
279 status_event[dev->type].encode(dev, ev);
280 rc = 1;
281 } else if (dev->type == ST_SYSEX) {
282 if (c == MIDI_CMD_COMMON_SYSEX_END ||
283 dev->read >= dev->bufsize) {
284 ev->flags &= ~SNDRV_SEQ_EVENT_LENGTH_MASK;
285 ev->flags |= SNDRV_SEQ_EVENT_LENGTH_VARIABLE;
286 ev->type = SNDRV_SEQ_EVENT_SYSEX;
287 ev->data.ext.len = dev->read;
288 ev->data.ext.ptr = dev->buf;
289 if (c != MIDI_CMD_COMMON_SYSEX_END)
290 dev->read = 0; /* continue to parse */
291 else
292 reset_encode(dev); /* all parsed */
293 rc = 1;
294 }
295 }
296
297 spin_unlock_irqrestore(&dev->lock, flags);
298 return rc;
299}
300
301/* encode note event */
302static void note_event(snd_midi_event_t *dev, snd_seq_event_t *ev)
303{
304 ev->data.note.channel = dev->buf[0] & 0x0f;
305 ev->data.note.note = dev->buf[1];
306 ev->data.note.velocity = dev->buf[2];
307}
308
309/* encode one parameter controls */
310static void one_param_ctrl_event(snd_midi_event_t *dev, snd_seq_event_t *ev)
311{
312 ev->data.control.channel = dev->buf[0] & 0x0f;
313 ev->data.control.value = dev->buf[1];
314}
315
316/* encode pitch wheel change */
317static void pitchbend_ctrl_event(snd_midi_event_t *dev, snd_seq_event_t *ev)
318{
319 ev->data.control.channel = dev->buf[0] & 0x0f;
320 ev->data.control.value = (int)dev->buf[2] * 128 + (int)dev->buf[1] - 8192;
321}
322
323/* encode midi control change */
324static void two_param_ctrl_event(snd_midi_event_t *dev, snd_seq_event_t *ev)
325{
326 ev->data.control.channel = dev->buf[0] & 0x0f;
327 ev->data.control.param = dev->buf[1];
328 ev->data.control.value = dev->buf[2];
329}
330
331/* encode one parameter value*/
332static void one_param_event(snd_midi_event_t *dev, snd_seq_event_t *ev)
333{
334 ev->data.control.value = dev->buf[1];
335}
336
337/* encode song position */
338static void songpos_event(snd_midi_event_t *dev, snd_seq_event_t *ev)
339{
340 ev->data.control.value = (int)dev->buf[2] * 128 + (int)dev->buf[1];
341}
342
343/*
344 * decode from a sequencer event to midi bytes
345 * return the size of decoded midi events
346 */
347long snd_midi_event_decode(snd_midi_event_t *dev, unsigned char *buf, long count, snd_seq_event_t *ev)
348{
349 unsigned int cmd, type;
350
351 if (ev->type == SNDRV_SEQ_EVENT_NONE)
352 return -ENOENT;
353
354 for (type = 0; type < ARRAY_SIZE(status_event); type++) {
355 if (ev->type == status_event[type].event)
356 goto __found;
357 }
358 for (type = 0; type < ARRAY_SIZE(extra_event); type++) {
359 if (ev->type == extra_event[type].event)
360 return extra_event[type].decode(dev, buf, count, ev);
361 }
362 return -ENOENT;
363
364 __found:
365 if (type >= ST_SPECIAL)
366 cmd = 0xf0 + (type - ST_SPECIAL);
367 else
368 /* data.note.channel and data.control.channel is identical */
369 cmd = 0x80 | (type << 4) | (ev->data.note.channel & 0x0f);
370
371
372 if (cmd == MIDI_CMD_COMMON_SYSEX) {
373 snd_midi_event_reset_decode(dev);
374 return snd_seq_expand_var_event(ev, count, buf, 1, 0);
375 } else {
376 int qlen;
377 unsigned char xbuf[4];
378 unsigned long flags;
379
380 spin_lock_irqsave(&dev->lock, flags);
381 if ((cmd & 0xf0) == 0xf0 || dev->lastcmd != cmd || dev->nostat) {
382 dev->lastcmd = cmd;
383 spin_unlock_irqrestore(&dev->lock, flags);
384 xbuf[0] = cmd;
385 if (status_event[type].decode)
386 status_event[type].decode(ev, xbuf + 1);
387 qlen = status_event[type].qlen + 1;
388 } else {
389 spin_unlock_irqrestore(&dev->lock, flags);
390 if (status_event[type].decode)
391 status_event[type].decode(ev, xbuf + 0);
392 qlen = status_event[type].qlen;
393 }
394 if (count < qlen)
395 return -ENOMEM;
396 memcpy(buf, xbuf, qlen);
397 return qlen;
398 }
399}
400
401
402/* decode note event */
403static void note_decode(snd_seq_event_t *ev, unsigned char *buf)
404{
405 buf[0] = ev->data.note.note & 0x7f;
406 buf[1] = ev->data.note.velocity & 0x7f;
407}
408
409/* decode one parameter controls */
410static void one_param_decode(snd_seq_event_t *ev, unsigned char *buf)
411{
412 buf[0] = ev->data.control.value & 0x7f;
413}
414
415/* decode pitch wheel change */
416static void pitchbend_decode(snd_seq_event_t *ev, unsigned char *buf)
417{
418 int value = ev->data.control.value + 8192;
419 buf[0] = value & 0x7f;
420 buf[1] = (value >> 7) & 0x7f;
421}
422
423/* decode midi control change */
424static void two_param_decode(snd_seq_event_t *ev, unsigned char *buf)
425{
426 buf[0] = ev->data.control.param & 0x7f;
427 buf[1] = ev->data.control.value & 0x7f;
428}
429
430/* decode song position */
431static void songpos_decode(snd_seq_event_t *ev, unsigned char *buf)
432{
433 buf[0] = ev->data.control.value & 0x7f;
434 buf[1] = (ev->data.control.value >> 7) & 0x7f;
435}
436
437/* decode 14bit control */
438static int extra_decode_ctrl14(snd_midi_event_t *dev, unsigned char *buf, int count, snd_seq_event_t *ev)
439{
440 unsigned char cmd;
441 int idx = 0;
442
443 cmd = MIDI_CMD_CONTROL|(ev->data.control.channel & 0x0f);
444 if (ev->data.control.param < 0x20) {
445 if (count < 4)
446 return -ENOMEM;
447 if (dev->nostat && count < 6)
448 return -ENOMEM;
449 if (cmd != dev->lastcmd || dev->nostat) {
450 if (count < 5)
451 return -ENOMEM;
452 buf[idx++] = dev->lastcmd = cmd;
453 }
454 buf[idx++] = ev->data.control.param;
455 buf[idx++] = (ev->data.control.value >> 7) & 0x7f;
456 if (dev->nostat)
457 buf[idx++] = cmd;
458 buf[idx++] = ev->data.control.param + 0x20;
459 buf[idx++] = ev->data.control.value & 0x7f;
460 } else {
461 if (count < 2)
462 return -ENOMEM;
463 if (cmd != dev->lastcmd || dev->nostat) {
464 if (count < 3)
465 return -ENOMEM;
466 buf[idx++] = dev->lastcmd = cmd;
467 }
468 buf[idx++] = ev->data.control.param & 0x7f;
469 buf[idx++] = ev->data.control.value & 0x7f;
470 }
471 return idx;
472}
473
474/* decode reg/nonreg param */
475static int extra_decode_xrpn(snd_midi_event_t *dev, unsigned char *buf, int count, snd_seq_event_t *ev)
476{
477 unsigned char cmd;
478 char *cbytes;
479 static char cbytes_nrpn[4] = { MIDI_CTL_NONREG_PARM_NUM_MSB,
480 MIDI_CTL_NONREG_PARM_NUM_LSB,
481 MIDI_CTL_MSB_DATA_ENTRY,
482 MIDI_CTL_LSB_DATA_ENTRY };
483 static char cbytes_rpn[4] = { MIDI_CTL_REGIST_PARM_NUM_MSB,
484 MIDI_CTL_REGIST_PARM_NUM_LSB,
485 MIDI_CTL_MSB_DATA_ENTRY,
486 MIDI_CTL_LSB_DATA_ENTRY };
487 unsigned char bytes[4];
488 int idx = 0, i;
489
490 if (count < 8)
491 return -ENOMEM;
492 if (dev->nostat && count < 12)
493 return -ENOMEM;
494 cmd = MIDI_CMD_CONTROL|(ev->data.control.channel & 0x0f);
495 bytes[0] = ev->data.control.param & 0x007f;
496 bytes[1] = (ev->data.control.param & 0x3f80) >> 7;
497 bytes[2] = ev->data.control.value & 0x007f;
498 bytes[3] = (ev->data.control.value & 0x3f80) >> 7;
499 if (cmd != dev->lastcmd && !dev->nostat) {
500 if (count < 9)
501 return -ENOMEM;
502 buf[idx++] = dev->lastcmd = cmd;
503 }
504 cbytes = ev->type == SNDRV_SEQ_EVENT_NONREGPARAM ? cbytes_nrpn : cbytes_rpn;
505 for (i = 0; i < 4; i++) {
506 if (dev->nostat)
507 buf[idx++] = dev->lastcmd = cmd;
508 buf[idx++] = cbytes[i];
509 buf[idx++] = bytes[i];
510 }
511 return idx;
512}
513
514/*
515 * exports
516 */
517
518EXPORT_SYMBOL(snd_midi_event_new);
519EXPORT_SYMBOL(snd_midi_event_free);
520EXPORT_SYMBOL(snd_midi_event_resize_buffer);
521EXPORT_SYMBOL(snd_midi_event_init);
522EXPORT_SYMBOL(snd_midi_event_reset_encode);
523EXPORT_SYMBOL(snd_midi_event_reset_decode);
524EXPORT_SYMBOL(snd_midi_event_no_status);
525EXPORT_SYMBOL(snd_midi_event_encode);
526EXPORT_SYMBOL(snd_midi_event_encode_byte);
527EXPORT_SYMBOL(snd_midi_event_decode);
528
529static int __init alsa_seq_midi_event_init(void)
530{
531 return 0;
532}
533
534static void __exit alsa_seq_midi_event_exit(void)
535{
536}
537
538module_init(alsa_seq_midi_event_init)
539module_exit(alsa_seq_midi_event_exit)
diff --git a/sound/core/seq/seq_ports.c b/sound/core/seq/seq_ports.c
new file mode 100644
index 000000000000..b976951fc100
--- /dev/null
+++ b/sound/core/seq/seq_ports.c
@@ -0,0 +1,674 @@
1/*
2 * ALSA sequencer Ports
3 * Copyright (c) 1998 by Frank van de Pol <fvdpol@coil.demon.nl>
4 * Jaroslav Kysela <perex@suse.cz>
5 *
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (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 General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; 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 <sound/core.h>
25#include <linux/slab.h>
26#include "seq_system.h"
27#include "seq_ports.h"
28#include "seq_clientmgr.h"
29
30/*
31
32 registration of client ports
33
34 */
35
36
37/*
38
39NOTE: the current implementation of the port structure as a linked list is
40not optimal for clients that have many ports. For sending messages to all
41subscribers of a port we first need to find the address of the port
42structure, which means we have to traverse the list. A direct access table
43(array) would be better, but big preallocated arrays waste memory.
44
45Possible actions:
46
471) leave it this way, a client does normaly does not have more than a few
48ports
49
502) replace the linked list of ports by a array of pointers which is
51dynamicly kmalloced. When a port is added or deleted we can simply allocate
52a new array, copy the corresponding pointers, and delete the old one. We
53then only need a pointer to this array, and an integer that tells us how
54much elements are in array.
55
56*/
57
58/* return pointer to port structure - port is locked if found */
59client_port_t *snd_seq_port_use_ptr(client_t *client, int num)
60{
61 struct list_head *p;
62 client_port_t *port;
63
64 if (client == NULL)
65 return NULL;
66 read_lock(&client->ports_lock);
67 list_for_each(p, &client->ports_list_head) {
68 port = list_entry(p, client_port_t, list);
69 if (port->addr.port == num) {
70 if (port->closing)
71 break; /* deleting now */
72 snd_use_lock_use(&port->use_lock);
73 read_unlock(&client->ports_lock);
74 return port;
75 }
76 }
77 read_unlock(&client->ports_lock);
78 return NULL; /* not found */
79}
80
81
82/* search for the next port - port is locked if found */
83client_port_t *snd_seq_port_query_nearest(client_t *client, snd_seq_port_info_t *pinfo)
84{
85 int num;
86 struct list_head *p;
87 client_port_t *port, *found;
88
89 num = pinfo->addr.port;
90 found = NULL;
91 read_lock(&client->ports_lock);
92 list_for_each(p, &client->ports_list_head) {
93 port = list_entry(p, client_port_t, list);
94 if (port->addr.port < num)
95 continue;
96 if (port->addr.port == num) {
97 found = port;
98 break;
99 }
100 if (found == NULL || port->addr.port < found->addr.port)
101 found = port;
102 }
103 if (found) {
104 if (found->closing)
105 found = NULL;
106 else
107 snd_use_lock_use(&found->use_lock);
108 }
109 read_unlock(&client->ports_lock);
110 return found;
111}
112
113
114/* initialize port_subs_info_t */
115static void port_subs_info_init(port_subs_info_t *grp)
116{
117 INIT_LIST_HEAD(&grp->list_head);
118 grp->count = 0;
119 grp->exclusive = 0;
120 rwlock_init(&grp->list_lock);
121 init_rwsem(&grp->list_mutex);
122 grp->open = NULL;
123 grp->close = NULL;
124}
125
126
127/* create a port, port number is returned (-1 on failure) */
128client_port_t *snd_seq_create_port(client_t *client, int port)
129{
130 unsigned long flags;
131 client_port_t *new_port;
132 struct list_head *l;
133 int num = -1;
134
135 /* sanity check */
136 snd_assert(client, return NULL);
137
138 if (client->num_ports >= SNDRV_SEQ_MAX_PORTS - 1) {
139 snd_printk(KERN_WARNING "too many ports for client %d\n", client->number);
140 return NULL;
141 }
142
143 /* create a new port */
144 new_port = kcalloc(1, sizeof(*new_port), GFP_KERNEL);
145 if (! new_port) {
146 snd_printd("malloc failed for registering client port\n");
147 return NULL; /* failure, out of memory */
148 }
149 /* init port data */
150 new_port->addr.client = client->number;
151 new_port->addr.port = -1;
152 new_port->owner = THIS_MODULE;
153 sprintf(new_port->name, "port-%d", num);
154 snd_use_lock_init(&new_port->use_lock);
155 port_subs_info_init(&new_port->c_src);
156 port_subs_info_init(&new_port->c_dest);
157
158 num = port >= 0 ? port : 0;
159 down(&client->ports_mutex);
160 write_lock_irqsave(&client->ports_lock, flags);
161 list_for_each(l, &client->ports_list_head) {
162 client_port_t *p = list_entry(l, client_port_t, list);
163 if (p->addr.port > num)
164 break;
165 if (port < 0) /* auto-probe mode */
166 num = p->addr.port + 1;
167 }
168 /* insert the new port */
169 list_add_tail(&new_port->list, l);
170 client->num_ports++;
171 new_port->addr.port = num; /* store the port number in the port */
172 write_unlock_irqrestore(&client->ports_lock, flags);
173 up(&client->ports_mutex);
174 sprintf(new_port->name, "port-%d", num);
175
176 return new_port;
177}
178
179/* */
180enum group_type_t {
181 SRC_LIST, DEST_LIST
182};
183
184static int subscribe_port(client_t *client, client_port_t *port, port_subs_info_t *grp, snd_seq_port_subscribe_t *info, int send_ack);
185static int unsubscribe_port(client_t *client, client_port_t *port, port_subs_info_t *grp, snd_seq_port_subscribe_t *info, int send_ack);
186
187
188static client_port_t *get_client_port(snd_seq_addr_t *addr, client_t **cp)
189{
190 client_port_t *p;
191 *cp = snd_seq_client_use_ptr(addr->client);
192 if (*cp) {
193 p = snd_seq_port_use_ptr(*cp, addr->port);
194 if (! p) {
195 snd_seq_client_unlock(*cp);
196 *cp = NULL;
197 }
198 return p;
199 }
200 return NULL;
201}
202
203/*
204 * remove all subscribers on the list
205 * this is called from port_delete, for each src and dest list.
206 */
207static void clear_subscriber_list(client_t *client, client_port_t *port,
208 port_subs_info_t *grp, int grptype)
209{
210 struct list_head *p, *n;
211
212 down_write(&grp->list_mutex);
213 list_for_each_safe(p, n, &grp->list_head) {
214 subscribers_t *subs;
215 client_t *c;
216 client_port_t *aport;
217
218 if (grptype == SRC_LIST) {
219 subs = list_entry(p, subscribers_t, src_list);
220 aport = get_client_port(&subs->info.dest, &c);
221 } else {
222 subs = list_entry(p, subscribers_t, dest_list);
223 aport = get_client_port(&subs->info.sender, &c);
224 }
225 list_del(p);
226 unsubscribe_port(client, port, grp, &subs->info, 0);
227 if (!aport) {
228 /* looks like the connected port is being deleted.
229 * we decrease the counter, and when both ports are deleted
230 * remove the subscriber info
231 */
232 if (atomic_dec_and_test(&subs->ref_count))
233 kfree(subs);
234 } else {
235 /* ok we got the connected port */
236 port_subs_info_t *agrp;
237 agrp = (grptype == SRC_LIST) ? &aport->c_dest : &aport->c_src;
238 down_write(&agrp->list_mutex);
239 if (grptype == SRC_LIST)
240 list_del(&subs->dest_list);
241 else
242 list_del(&subs->src_list);
243 unsubscribe_port(c, aport, agrp, &subs->info, 1);
244 kfree(subs);
245 up_write(&agrp->list_mutex);
246 snd_seq_port_unlock(aport);
247 snd_seq_client_unlock(c);
248 }
249 }
250 up_write(&grp->list_mutex);
251}
252
253/* delete port data */
254static int port_delete(client_t *client, client_port_t *port)
255{
256 /* set closing flag and wait for all port access are gone */
257 port->closing = 1;
258 snd_use_lock_sync(&port->use_lock);
259
260 /* clear subscribers info */
261 clear_subscriber_list(client, port, &port->c_src, SRC_LIST);
262 clear_subscriber_list(client, port, &port->c_dest, DEST_LIST);
263
264 if (port->private_free)
265 port->private_free(port->private_data);
266
267 snd_assert(port->c_src.count == 0,);
268 snd_assert(port->c_dest.count == 0,);
269
270 kfree(port);
271 return 0;
272}
273
274
275/* delete a port with the given port id */
276int snd_seq_delete_port(client_t *client, int port)
277{
278 unsigned long flags;
279 struct list_head *l;
280 client_port_t *found = NULL;
281
282 down(&client->ports_mutex);
283 write_lock_irqsave(&client->ports_lock, flags);
284 list_for_each(l, &client->ports_list_head) {
285 client_port_t *p = list_entry(l, client_port_t, list);
286 if (p->addr.port == port) {
287 /* ok found. delete from the list at first */
288 list_del(l);
289 client->num_ports--;
290 found = p;
291 break;
292 }
293 }
294 write_unlock_irqrestore(&client->ports_lock, flags);
295 up(&client->ports_mutex);
296 if (found)
297 return port_delete(client, found);
298 else
299 return -ENOENT;
300}
301
302/* delete the all ports belonging to the given client */
303int snd_seq_delete_all_ports(client_t *client)
304{
305 unsigned long flags;
306 struct list_head deleted_list, *p, *n;
307
308 /* move the port list to deleted_list, and
309 * clear the port list in the client data.
310 */
311 down(&client->ports_mutex);
312 write_lock_irqsave(&client->ports_lock, flags);
313 if (! list_empty(&client->ports_list_head)) {
314 __list_add(&deleted_list,
315 client->ports_list_head.prev,
316 client->ports_list_head.next);
317 INIT_LIST_HEAD(&client->ports_list_head);
318 } else {
319 INIT_LIST_HEAD(&deleted_list);
320 }
321 client->num_ports = 0;
322 write_unlock_irqrestore(&client->ports_lock, flags);
323
324 /* remove each port in deleted_list */
325 list_for_each_safe(p, n, &deleted_list) {
326 client_port_t *port = list_entry(p, client_port_t, list);
327 list_del(p);
328 snd_seq_system_client_ev_port_exit(port->addr.client, port->addr.port);
329 port_delete(client, port);
330 }
331 up(&client->ports_mutex);
332 return 0;
333}
334
335/* set port info fields */
336int snd_seq_set_port_info(client_port_t * port, snd_seq_port_info_t * info)
337{
338 snd_assert(port && info, return -EINVAL);
339
340 /* set port name */
341 if (info->name[0])
342 strlcpy(port->name, info->name, sizeof(port->name));
343
344 /* set capabilities */
345 port->capability = info->capability;
346
347 /* get port type */
348 port->type = info->type;
349
350 /* information about supported channels/voices */
351 port->midi_channels = info->midi_channels;
352 port->midi_voices = info->midi_voices;
353 port->synth_voices = info->synth_voices;
354
355 /* timestamping */
356 port->timestamping = (info->flags & SNDRV_SEQ_PORT_FLG_TIMESTAMP) ? 1 : 0;
357 port->time_real = (info->flags & SNDRV_SEQ_PORT_FLG_TIME_REAL) ? 1 : 0;
358 port->time_queue = info->time_queue;
359
360 return 0;
361}
362
363/* get port info fields */
364int snd_seq_get_port_info(client_port_t * port, snd_seq_port_info_t * info)
365{
366 snd_assert(port && info, return -EINVAL);
367
368 /* get port name */
369 strlcpy(info->name, port->name, sizeof(info->name));
370
371 /* get capabilities */
372 info->capability = port->capability;
373
374 /* get port type */
375 info->type = port->type;
376
377 /* information about supported channels/voices */
378 info->midi_channels = port->midi_channels;
379 info->midi_voices = port->midi_voices;
380 info->synth_voices = port->synth_voices;
381
382 /* get subscriber counts */
383 info->read_use = port->c_src.count;
384 info->write_use = port->c_dest.count;
385
386 /* timestamping */
387 info->flags = 0;
388 if (port->timestamping) {
389 info->flags |= SNDRV_SEQ_PORT_FLG_TIMESTAMP;
390 if (port->time_real)
391 info->flags |= SNDRV_SEQ_PORT_FLG_TIME_REAL;
392 info->time_queue = port->time_queue;
393 }
394
395 return 0;
396}
397
398
399
400/*
401 * call callback functions (if any):
402 * the callbacks are invoked only when the first (for connection) or
403 * the last subscription (for disconnection) is done. Second or later
404 * subscription results in increment of counter, but no callback is
405 * invoked.
406 * This feature is useful if these callbacks are associated with
407 * initialization or termination of devices (see seq_midi.c).
408 *
409 * If callback_all option is set, the callback function is invoked
410 * at each connnection/disconnection.
411 */
412
413static int subscribe_port(client_t *client, client_port_t *port, port_subs_info_t *grp,
414 snd_seq_port_subscribe_t *info, int send_ack)
415{
416 int err = 0;
417
418 if (!try_module_get(port->owner))
419 return -EFAULT;
420 grp->count++;
421 if (grp->open && (port->callback_all || grp->count == 1)) {
422 err = grp->open(port->private_data, info);
423 if (err < 0) {
424 module_put(port->owner);
425 grp->count--;
426 }
427 }
428 if (err >= 0 && send_ack && client->type == USER_CLIENT)
429 snd_seq_client_notify_subscription(port->addr.client, port->addr.port,
430 info, SNDRV_SEQ_EVENT_PORT_SUBSCRIBED);
431
432 return err;
433}
434
435static int unsubscribe_port(client_t *client, client_port_t *port,
436 port_subs_info_t *grp,
437 snd_seq_port_subscribe_t *info, int send_ack)
438{
439 int err = 0;
440
441 if (! grp->count)
442 return -EINVAL;
443 grp->count--;
444 if (grp->close && (port->callback_all || grp->count == 0))
445 err = grp->close(port->private_data, info);
446 if (send_ack && client->type == USER_CLIENT)
447 snd_seq_client_notify_subscription(port->addr.client, port->addr.port,
448 info, SNDRV_SEQ_EVENT_PORT_UNSUBSCRIBED);
449 module_put(port->owner);
450 return err;
451}
452
453
454
455/* check if both addresses are identical */
456static inline int addr_match(snd_seq_addr_t *r, snd_seq_addr_t *s)
457{
458 return (r->client == s->client) && (r->port == s->port);
459}
460
461/* check the two subscribe info match */
462/* if flags is zero, checks only sender and destination addresses */
463static int match_subs_info(snd_seq_port_subscribe_t *r,
464 snd_seq_port_subscribe_t *s)
465{
466 if (addr_match(&r->sender, &s->sender) &&
467 addr_match(&r->dest, &s->dest)) {
468 if (r->flags && r->flags == s->flags)
469 return r->queue == s->queue;
470 else if (! r->flags)
471 return 1;
472 }
473 return 0;
474}
475
476
477/* connect two ports */
478int snd_seq_port_connect(client_t *connector,
479 client_t *src_client, client_port_t *src_port,
480 client_t *dest_client, client_port_t *dest_port,
481 snd_seq_port_subscribe_t *info)
482{
483 port_subs_info_t *src = &src_port->c_src;
484 port_subs_info_t *dest = &dest_port->c_dest;
485 subscribers_t *subs;
486 struct list_head *p;
487 int err, src_called = 0;
488 unsigned long flags;
489 int exclusive;
490
491 subs = kcalloc(1, sizeof(*subs), GFP_KERNEL);
492 if (! subs)
493 return -ENOMEM;
494
495 subs->info = *info;
496 atomic_set(&subs->ref_count, 2);
497
498 down_write(&src->list_mutex);
499 down_write(&dest->list_mutex);
500
501 exclusive = info->flags & SNDRV_SEQ_PORT_SUBS_EXCLUSIVE ? 1 : 0;
502 err = -EBUSY;
503 if (exclusive) {
504 if (! list_empty(&src->list_head) || ! list_empty(&dest->list_head))
505 goto __error;
506 } else {
507 if (src->exclusive || dest->exclusive)
508 goto __error;
509 /* check whether already exists */
510 list_for_each(p, &src->list_head) {
511 subscribers_t *s = list_entry(p, subscribers_t, src_list);
512 if (match_subs_info(info, &s->info))
513 goto __error;
514 }
515 list_for_each(p, &dest->list_head) {
516 subscribers_t *s = list_entry(p, subscribers_t, dest_list);
517 if (match_subs_info(info, &s->info))
518 goto __error;
519 }
520 }
521
522 if ((err = subscribe_port(src_client, src_port, src, info,
523 connector->number != src_client->number)) < 0)
524 goto __error;
525 src_called = 1;
526
527 if ((err = subscribe_port(dest_client, dest_port, dest, info,
528 connector->number != dest_client->number)) < 0)
529 goto __error;
530
531 /* add to list */
532 write_lock_irqsave(&src->list_lock, flags);
533 // write_lock(&dest->list_lock); // no other lock yet
534 list_add_tail(&subs->src_list, &src->list_head);
535 list_add_tail(&subs->dest_list, &dest->list_head);
536 // write_unlock(&dest->list_lock); // no other lock yet
537 write_unlock_irqrestore(&src->list_lock, flags);
538
539 src->exclusive = dest->exclusive = exclusive;
540
541 up_write(&dest->list_mutex);
542 up_write(&src->list_mutex);
543 return 0;
544
545 __error:
546 if (src_called)
547 unsubscribe_port(src_client, src_port, src, info,
548 connector->number != src_client->number);
549 kfree(subs);
550 up_write(&dest->list_mutex);
551 up_write(&src->list_mutex);
552 return err;
553}
554
555
556/* remove the connection */
557int snd_seq_port_disconnect(client_t *connector,
558 client_t *src_client, client_port_t *src_port,
559 client_t *dest_client, client_port_t *dest_port,
560 snd_seq_port_subscribe_t *info)
561{
562 port_subs_info_t *src = &src_port->c_src;
563 port_subs_info_t *dest = &dest_port->c_dest;
564 subscribers_t *subs;
565 struct list_head *p;
566 int err = -ENOENT;
567 unsigned long flags;
568
569 down_write(&src->list_mutex);
570 down_write(&dest->list_mutex);
571
572 /* look for the connection */
573 list_for_each(p, &src->list_head) {
574 subs = list_entry(p, subscribers_t, src_list);
575 if (match_subs_info(info, &subs->info)) {
576 write_lock_irqsave(&src->list_lock, flags);
577 // write_lock(&dest->list_lock); // no lock yet
578 list_del(&subs->src_list);
579 list_del(&subs->dest_list);
580 // write_unlock(&dest->list_lock);
581 write_unlock_irqrestore(&src->list_lock, flags);
582 src->exclusive = dest->exclusive = 0;
583 unsubscribe_port(src_client, src_port, src, info,
584 connector->number != src_client->number);
585 unsubscribe_port(dest_client, dest_port, dest, info,
586 connector->number != dest_client->number);
587 kfree(subs);
588 err = 0;
589 break;
590 }
591 }
592
593 up_write(&dest->list_mutex);
594 up_write(&src->list_mutex);
595 return err;
596}
597
598
599/* get matched subscriber */
600subscribers_t *snd_seq_port_get_subscription(port_subs_info_t *src_grp,
601 snd_seq_addr_t *dest_addr)
602{
603 struct list_head *p;
604 subscribers_t *s, *found = NULL;
605
606 down_read(&src_grp->list_mutex);
607 list_for_each(p, &src_grp->list_head) {
608 s = list_entry(p, subscribers_t, src_list);
609 if (addr_match(dest_addr, &s->info.dest)) {
610 found = s;
611 break;
612 }
613 }
614 up_read(&src_grp->list_mutex);
615 return found;
616}
617
618/*
619 * Attach a device driver that wants to receive events from the
620 * sequencer. Returns the new port number on success.
621 * A driver that wants to receive the events converted to midi, will
622 * use snd_seq_midisynth_register_port().
623 */
624/* exported */
625int snd_seq_event_port_attach(int client,
626 snd_seq_port_callback_t *pcbp,
627 int cap, int type, int midi_channels,
628 int midi_voices, char *portname)
629{
630 snd_seq_port_info_t portinfo;
631 int ret;
632
633 /* Set up the port */
634 memset(&portinfo, 0, sizeof(portinfo));
635 portinfo.addr.client = client;
636 strlcpy(portinfo.name, portname ? portname : "Unamed port",
637 sizeof(portinfo.name));
638
639 portinfo.capability = cap;
640 portinfo.type = type;
641 portinfo.kernel = pcbp;
642 portinfo.midi_channels = midi_channels;
643 portinfo.midi_voices = midi_voices;
644
645 /* Create it */
646 ret = snd_seq_kernel_client_ctl(client,
647 SNDRV_SEQ_IOCTL_CREATE_PORT,
648 &portinfo);
649
650 if (ret >= 0)
651 ret = portinfo.addr.port;
652
653 return ret;
654}
655
656
657/*
658 * Detach the driver from a port.
659 */
660/* exported */
661int snd_seq_event_port_detach(int client, int port)
662{
663 snd_seq_port_info_t portinfo;
664 int err;
665
666 memset(&portinfo, 0, sizeof(portinfo));
667 portinfo.addr.client = client;
668 portinfo.addr.port = port;
669 err = snd_seq_kernel_client_ctl(client,
670 SNDRV_SEQ_IOCTL_DELETE_PORT,
671 &portinfo);
672
673 return err;
674}
diff --git a/sound/core/seq/seq_ports.h b/sound/core/seq/seq_ports.h
new file mode 100644
index 000000000000..89fd4416f6fa
--- /dev/null
+++ b/sound/core/seq/seq_ports.h
@@ -0,0 +1,128 @@
1/*
2 * ALSA sequencer Ports
3 * Copyright (c) 1998 by Frank van de Pol <fvdpol@coil.demon.nl>
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#ifndef __SND_SEQ_PORTS_H
22#define __SND_SEQ_PORTS_H
23
24#include <sound/seq_kernel.h>
25#include "seq_lock.h"
26
27/* list of 'exported' ports */
28
29/* Client ports that are not exported are still accessible, but are
30 anonymous ports.
31
32 If a port supports SUBSCRIPTION, that port can send events to all
33 subscribersto a special address, with address
34 (queue==SNDRV_SEQ_ADDRESS_SUBSCRIBERS). The message is then send to all
35 recipients that are registered in the subscription list. A typical
36 application for these SUBSCRIPTION events is handling of incoming MIDI
37 data. The port doesn't 'know' what other clients are interested in this
38 message. If for instance a MIDI recording application would like to receive
39 the events from that port, it will first have to subscribe with that port.
40
41*/
42
43typedef struct subscribers_t {
44 snd_seq_port_subscribe_t info; /* additional info */
45 struct list_head src_list; /* link of sources */
46 struct list_head dest_list; /* link of destinations */
47 atomic_t ref_count;
48} subscribers_t;
49
50typedef struct port_subs_info_t {
51 struct list_head list_head; /* list of subscribed ports */
52 unsigned int count; /* count of subscribers */
53 unsigned int exclusive: 1; /* exclusive mode */
54 struct rw_semaphore list_mutex;
55 rwlock_t list_lock;
56 snd_seq_kernel_port_open_t *open;
57 snd_seq_kernel_port_close_t *close;
58} port_subs_info_t;
59
60typedef struct client_port_t {
61
62 snd_seq_addr_t addr; /* client/port number */
63 struct module *owner; /* owner of this port */
64 char name[64]; /* port name */
65 struct list_head list; /* port list */
66 snd_use_lock_t use_lock;
67
68 /* subscribers */
69 port_subs_info_t c_src; /* read (sender) list */
70 port_subs_info_t c_dest; /* write (dest) list */
71
72 snd_seq_kernel_port_input_t *event_input;
73 snd_seq_kernel_port_private_free_t *private_free;
74 void *private_data;
75 unsigned int callback_all : 1;
76 unsigned int closing : 1;
77 unsigned int timestamping: 1;
78 unsigned int time_real: 1;
79 int time_queue;
80
81 /* capability, inport, output, sync */
82 unsigned int capability; /* port capability bits */
83 unsigned int type; /* port type bits */
84
85 /* supported channels */
86 int midi_channels;
87 int midi_voices;
88 int synth_voices;
89
90} client_port_t;
91
92/* return pointer to port structure and lock port */
93client_port_t *snd_seq_port_use_ptr(client_t *client, int num);
94
95/* search for next port - port is locked if found */
96client_port_t *snd_seq_port_query_nearest(client_t *client, snd_seq_port_info_t *pinfo);
97
98/* unlock the port */
99#define snd_seq_port_unlock(port) snd_use_lock_free(&(port)->use_lock)
100
101/* create a port, port number is returned (-1 on failure) */
102client_port_t *snd_seq_create_port(client_t *client, int port_index);
103
104/* delete a port */
105int snd_seq_delete_port(client_t *client, int port);
106
107/* delete all ports */
108int snd_seq_delete_all_ports(client_t *client);
109
110/* set port info fields */
111int snd_seq_set_port_info(client_port_t *port, snd_seq_port_info_t *info);
112
113/* get port info fields */
114int snd_seq_get_port_info(client_port_t *port, snd_seq_port_info_t *info);
115
116/* add subscriber to subscription list */
117int snd_seq_port_connect(client_t *caller, client_t *s, client_port_t *sp, client_t *d, client_port_t *dp, snd_seq_port_subscribe_t *info);
118
119/* remove subscriber from subscription list */
120int snd_seq_port_disconnect(client_t *caller, client_t *s, client_port_t *sp, client_t *d, client_port_t *dp, snd_seq_port_subscribe_t *info);
121
122/* subscribe port */
123int snd_seq_port_subscribe(client_port_t *port, snd_seq_port_subscribe_t *info);
124
125/* get matched subscriber */
126subscribers_t *snd_seq_port_get_subscription(port_subs_info_t *src_grp, snd_seq_addr_t *dest_addr);
127
128#endif
diff --git a/sound/core/seq/seq_prioq.c b/sound/core/seq/seq_prioq.c
new file mode 100644
index 000000000000..a519732ed833
--- /dev/null
+++ b/sound/core/seq/seq_prioq.c
@@ -0,0 +1,449 @@
1/*
2 * ALSA sequencer Priority Queue
3 * Copyright (c) 1998-1999 by Frank van de Pol <fvdpol@coil.demon.nl>
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/time.h>
24#include <linux/slab.h>
25#include <sound/core.h>
26#include "seq_timer.h"
27#include "seq_prioq.h"
28
29
30/* Implementation is a simple linked list for now...
31
32 This priority queue orders the events on timestamp. For events with an
33 equeal timestamp the queue behaves as a FIFO.
34
35 *
36 * +-------+
37 * Head --> | first |
38 * +-------+
39 * |next
40 * +-----v-+
41 * | |
42 * +-------+
43 * |
44 * +-----v-+
45 * | |
46 * +-------+
47 * |
48 * +-----v-+
49 * Tail --> | last |
50 * +-------+
51 *
52
53 */
54
55
56
57/* create new prioq (constructor) */
58prioq_t *snd_seq_prioq_new(void)
59{
60 prioq_t *f;
61
62 f = kcalloc(1, sizeof(*f), GFP_KERNEL);
63 if (f == NULL) {
64 snd_printd("oops: malloc failed for snd_seq_prioq_new()\n");
65 return NULL;
66 }
67
68 spin_lock_init(&f->lock);
69 f->head = NULL;
70 f->tail = NULL;
71 f->cells = 0;
72
73 return f;
74}
75
76/* delete prioq (destructor) */
77void snd_seq_prioq_delete(prioq_t **fifo)
78{
79 prioq_t *f = *fifo;
80 *fifo = NULL;
81
82 if (f == NULL) {
83 snd_printd("oops: snd_seq_prioq_delete() called with NULL prioq\n");
84 return;
85 }
86
87 /* release resources...*/
88 /*....................*/
89
90 if (f->cells > 0) {
91 /* drain prioQ */
92 while (f->cells > 0)
93 snd_seq_cell_free(snd_seq_prioq_cell_out(f));
94 }
95
96 kfree(f);
97}
98
99
100
101
102/* compare timestamp between events */
103/* return 1 if a >= b; 0 */
104static inline int compare_timestamp(snd_seq_event_t * a, snd_seq_event_t * b)
105{
106 if ((a->flags & SNDRV_SEQ_TIME_STAMP_MASK) == SNDRV_SEQ_TIME_STAMP_TICK) {
107 /* compare ticks */
108 return (snd_seq_compare_tick_time(&a->time.tick, &b->time.tick));
109 } else {
110 /* compare real time */
111 return (snd_seq_compare_real_time(&a->time.time, &b->time.time));
112 }
113}
114
115/* compare timestamp between events */
116/* return negative if a < b;
117 * zero if a = b;
118 * positive if a > b;
119 */
120static inline int compare_timestamp_rel(snd_seq_event_t *a, snd_seq_event_t *b)
121{
122 if ((a->flags & SNDRV_SEQ_TIME_STAMP_MASK) == SNDRV_SEQ_TIME_STAMP_TICK) {
123 /* compare ticks */
124 if (a->time.tick > b->time.tick)
125 return 1;
126 else if (a->time.tick == b->time.tick)
127 return 0;
128 else
129 return -1;
130 } else {
131 /* compare real time */
132 if (a->time.time.tv_sec > b->time.time.tv_sec)
133 return 1;
134 else if (a->time.time.tv_sec == b->time.time.tv_sec) {
135 if (a->time.time.tv_nsec > b->time.time.tv_nsec)
136 return 1;
137 else if (a->time.time.tv_nsec == b->time.time.tv_nsec)
138 return 0;
139 else
140 return -1;
141 } else
142 return -1;
143 }
144}
145
146/* enqueue cell to prioq */
147int snd_seq_prioq_cell_in(prioq_t * f, snd_seq_event_cell_t * cell)
148{
149 snd_seq_event_cell_t *cur, *prev;
150 unsigned long flags;
151 int count;
152 int prior;
153
154 snd_assert(f, return -EINVAL);
155 snd_assert(cell, return -EINVAL);
156
157 /* check flags */
158 prior = (cell->event.flags & SNDRV_SEQ_PRIORITY_MASK);
159
160 spin_lock_irqsave(&f->lock, flags);
161
162 /* check if this element needs to inserted at the end (ie. ordered
163 data is inserted) This will be very likeley if a sequencer
164 application or midi file player is feeding us (sequential) data */
165 if (f->tail && !prior) {
166 if (compare_timestamp(&cell->event, &f->tail->event)) {
167 /* add new cell to tail of the fifo */
168 f->tail->next = cell;
169 f->tail = cell;
170 cell->next = NULL;
171 f->cells++;
172 spin_unlock_irqrestore(&f->lock, flags);
173 return 0;
174 }
175 }
176 /* traverse list of elements to find the place where the new cell is
177 to be inserted... Note that this is a order n process ! */
178
179 prev = NULL; /* previous cell */
180 cur = f->head; /* cursor */
181
182 count = 10000; /* FIXME: enough big, isn't it? */
183 while (cur != NULL) {
184 /* compare timestamps */
185 int rel = compare_timestamp_rel(&cell->event, &cur->event);
186 if (rel < 0)
187 /* new cell has earlier schedule time, */
188 break;
189 else if (rel == 0 && prior)
190 /* equal schedule time and prior to others */
191 break;
192 /* new cell has equal or larger schedule time, */
193 /* move cursor to next cell */
194 prev = cur;
195 cur = cur->next;
196 if (! --count) {
197 spin_unlock_irqrestore(&f->lock, flags);
198 snd_printk(KERN_ERR "cannot find a pointer.. infinite loop?\n");
199 return -EINVAL;
200 }
201 }
202
203 /* insert it before cursor */
204 if (prev != NULL)
205 prev->next = cell;
206 cell->next = cur;
207
208 if (f->head == cur) /* this is the first cell, set head to it */
209 f->head = cell;
210 if (cur == NULL) /* reached end of the list */
211 f->tail = cell;
212 f->cells++;
213 spin_unlock_irqrestore(&f->lock, flags);
214 return 0;
215}
216
217/* dequeue cell from prioq */
218snd_seq_event_cell_t *snd_seq_prioq_cell_out(prioq_t * f)
219{
220 snd_seq_event_cell_t *cell;
221 unsigned long flags;
222
223 if (f == NULL) {
224 snd_printd("oops: snd_seq_prioq_cell_in() called with NULL prioq\n");
225 return NULL;
226 }
227 spin_lock_irqsave(&f->lock, flags);
228
229 cell = f->head;
230 if (cell) {
231 f->head = cell->next;
232
233 /* reset tail if this was the last element */
234 if (f->tail == cell)
235 f->tail = NULL;
236
237 cell->next = NULL;
238 f->cells--;
239 }
240
241 spin_unlock_irqrestore(&f->lock, flags);
242 return cell;
243}
244
245/* return number of events available in prioq */
246int snd_seq_prioq_avail(prioq_t * f)
247{
248 if (f == NULL) {
249 snd_printd("oops: snd_seq_prioq_cell_in() called with NULL prioq\n");
250 return 0;
251 }
252 return f->cells;
253}
254
255
256/* peek at cell at the head of the prioq */
257snd_seq_event_cell_t *snd_seq_prioq_cell_peek(prioq_t * f)
258{
259 if (f == NULL) {
260 snd_printd("oops: snd_seq_prioq_cell_in() called with NULL prioq\n");
261 return NULL;
262 }
263 return f->head;
264}
265
266
267static inline int prioq_match(snd_seq_event_cell_t *cell, int client, int timestamp)
268{
269 if (cell->event.source.client == client ||
270 cell->event.dest.client == client)
271 return 1;
272 if (!timestamp)
273 return 0;
274 switch (cell->event.flags & SNDRV_SEQ_TIME_STAMP_MASK) {
275 case SNDRV_SEQ_TIME_STAMP_TICK:
276 if (cell->event.time.tick)
277 return 1;
278 break;
279 case SNDRV_SEQ_TIME_STAMP_REAL:
280 if (cell->event.time.time.tv_sec ||
281 cell->event.time.time.tv_nsec)
282 return 1;
283 break;
284 }
285 return 0;
286}
287
288/* remove cells for left client */
289void snd_seq_prioq_leave(prioq_t * f, int client, int timestamp)
290{
291 register snd_seq_event_cell_t *cell, *next;
292 unsigned long flags;
293 snd_seq_event_cell_t *prev = NULL;
294 snd_seq_event_cell_t *freefirst = NULL, *freeprev = NULL, *freenext;
295
296 /* collect all removed cells */
297 spin_lock_irqsave(&f->lock, flags);
298 cell = f->head;
299 while (cell) {
300 next = cell->next;
301 if (prioq_match(cell, client, timestamp)) {
302 /* remove cell from prioq */
303 if (cell == f->head) {
304 f->head = cell->next;
305 } else {
306 prev->next = cell->next;
307 }
308 if (cell == f->tail)
309 f->tail = cell->next;
310 f->cells--;
311 /* add cell to free list */
312 cell->next = NULL;
313 if (freefirst == NULL) {
314 freefirst = cell;
315 } else {
316 freeprev->next = cell;
317 }
318 freeprev = cell;
319 } else {
320#if 0
321 printk("type = %i, source = %i, dest = %i, client = %i\n",
322 cell->event.type,
323 cell->event.source.client,
324 cell->event.dest.client,
325 client);
326#endif
327 prev = cell;
328 }
329 cell = next;
330 }
331 spin_unlock_irqrestore(&f->lock, flags);
332
333 /* remove selected cells */
334 while (freefirst) {
335 freenext = freefirst->next;
336 snd_seq_cell_free(freefirst);
337 freefirst = freenext;
338 }
339}
340
341static int prioq_remove_match(snd_seq_remove_events_t *info,
342 snd_seq_event_t *ev)
343{
344 int res;
345
346 if (info->remove_mode & SNDRV_SEQ_REMOVE_DEST) {
347 if (ev->dest.client != info->dest.client ||
348 ev->dest.port != info->dest.port)
349 return 0;
350 }
351 if (info->remove_mode & SNDRV_SEQ_REMOVE_DEST_CHANNEL) {
352 if (! snd_seq_ev_is_channel_type(ev))
353 return 0;
354 /* data.note.channel and data.control.channel are identical */
355 if (ev->data.note.channel != info->channel)
356 return 0;
357 }
358 if (info->remove_mode & SNDRV_SEQ_REMOVE_TIME_AFTER) {
359 if (info->remove_mode & SNDRV_SEQ_REMOVE_TIME_TICK)
360 res = snd_seq_compare_tick_time(&ev->time.tick, &info->time.tick);
361 else
362 res = snd_seq_compare_real_time(&ev->time.time, &info->time.time);
363 if (!res)
364 return 0;
365 }
366 if (info->remove_mode & SNDRV_SEQ_REMOVE_TIME_BEFORE) {
367 if (info->remove_mode & SNDRV_SEQ_REMOVE_TIME_TICK)
368 res = snd_seq_compare_tick_time(&ev->time.tick, &info->time.tick);
369 else
370 res = snd_seq_compare_real_time(&ev->time.time, &info->time.time);
371 if (res)
372 return 0;
373 }
374 if (info->remove_mode & SNDRV_SEQ_REMOVE_EVENT_TYPE) {
375 if (ev->type != info->type)
376 return 0;
377 }
378 if (info->remove_mode & SNDRV_SEQ_REMOVE_IGNORE_OFF) {
379 /* Do not remove off events */
380 switch (ev->type) {
381 case SNDRV_SEQ_EVENT_NOTEOFF:
382 /* case SNDRV_SEQ_EVENT_SAMPLE_STOP: */
383 return 0;
384 default:
385 break;
386 }
387 }
388 if (info->remove_mode & SNDRV_SEQ_REMOVE_TAG_MATCH) {
389 if (info->tag != ev->tag)
390 return 0;
391 }
392
393 return 1;
394}
395
396/* remove cells matching remove criteria */
397void snd_seq_prioq_remove_events(prioq_t * f, int client,
398 snd_seq_remove_events_t *info)
399{
400 register snd_seq_event_cell_t *cell, *next;
401 unsigned long flags;
402 snd_seq_event_cell_t *prev = NULL;
403 snd_seq_event_cell_t *freefirst = NULL, *freeprev = NULL, *freenext;
404
405 /* collect all removed cells */
406 spin_lock_irqsave(&f->lock, flags);
407 cell = f->head;
408
409 while (cell) {
410 next = cell->next;
411 if (cell->event.source.client == client &&
412 prioq_remove_match(info, &cell->event)) {
413
414 /* remove cell from prioq */
415 if (cell == f->head) {
416 f->head = cell->next;
417 } else {
418 prev->next = cell->next;
419 }
420
421 if (cell == f->tail)
422 f->tail = cell->next;
423 f->cells--;
424
425 /* add cell to free list */
426 cell->next = NULL;
427 if (freefirst == NULL) {
428 freefirst = cell;
429 } else {
430 freeprev->next = cell;
431 }
432
433 freeprev = cell;
434 } else {
435 prev = cell;
436 }
437 cell = next;
438 }
439 spin_unlock_irqrestore(&f->lock, flags);
440
441 /* remove selected cells */
442 while (freefirst) {
443 freenext = freefirst->next;
444 snd_seq_cell_free(freefirst);
445 freefirst = freenext;
446 }
447}
448
449
diff --git a/sound/core/seq/seq_prioq.h b/sound/core/seq/seq_prioq.h
new file mode 100644
index 000000000000..f12af79308b8
--- /dev/null
+++ b/sound/core/seq/seq_prioq.h
@@ -0,0 +1,62 @@
1/*
2 * ALSA sequencer Priority Queue
3 * Copyright (c) 1998 by Frank van de Pol <fvdpol@coil.demon.nl>
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#ifndef __SND_SEQ_PRIOQ_H
22#define __SND_SEQ_PRIOQ_H
23
24#include "seq_memory.h"
25
26
27/* === PRIOQ === */
28
29typedef struct {
30 snd_seq_event_cell_t* head; /* pointer to head of prioq */
31 snd_seq_event_cell_t* tail; /* pointer to tail of prioq */
32 int cells;
33 spinlock_t lock;
34} prioq_t;
35
36
37/* create new prioq (constructor) */
38extern prioq_t *snd_seq_prioq_new(void);
39
40/* delete prioq (destructor) */
41extern void snd_seq_prioq_delete(prioq_t **fifo);
42
43/* enqueue cell to prioq */
44extern int snd_seq_prioq_cell_in(prioq_t *f, snd_seq_event_cell_t *cell);
45
46/* dequeue cell from prioq */
47extern snd_seq_event_cell_t *snd_seq_prioq_cell_out(prioq_t *f);
48
49/* return number of events available in prioq */
50extern int snd_seq_prioq_avail(prioq_t *f);
51
52/* peek at cell at the head of the prioq */
53extern snd_seq_event_cell_t *snd_seq_prioq_cell_peek(prioq_t *f);
54
55/* client left queue */
56extern void snd_seq_prioq_leave(prioq_t *f, int client, int timestamp);
57
58/* Remove events */
59void snd_seq_prioq_remove_events(prioq_t * f, int client,
60 snd_seq_remove_events_t *info);
61
62#endif
diff --git a/sound/core/seq/seq_queue.c b/sound/core/seq/seq_queue.c
new file mode 100644
index 000000000000..3afc7cc0c9a7
--- /dev/null
+++ b/sound/core/seq/seq_queue.c
@@ -0,0 +1,783 @@
1/*
2 * ALSA sequencer Timing queue handling
3 * Copyright (c) 1998-1999 by Frank van de Pol <fvdpol@coil.demon.nl>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 *
19 * MAJOR CHANGES
20 * Nov. 13, 1999 Takashi Iwai <iwai@ww.uni-erlangen.de>
21 * - Queues are allocated dynamically via ioctl.
22 * - When owner client is deleted, all owned queues are deleted, too.
23 * - Owner of unlocked queue is kept unmodified even if it is
24 * manipulated by other clients.
25 * - Owner field in SET_QUEUE_OWNER ioctl must be identical with the
26 * caller client. i.e. Changing owner to a third client is not
27 * allowed.
28 *
29 * Aug. 30, 2000 Takashi Iwai
30 * - Queues are managed in static array again, but with better way.
31 * The API itself is identical.
32 * - The queue is locked when queue_t pinter is returned via
33 * queueptr(). This pointer *MUST* be released afterward by
34 * queuefree(ptr).
35 * - Addition of experimental sync support.
36 */
37
38#include <sound/driver.h>
39#include <linux/init.h>
40#include <linux/slab.h>
41#include <sound/core.h>
42
43#include "seq_memory.h"
44#include "seq_queue.h"
45#include "seq_clientmgr.h"
46#include "seq_fifo.h"
47#include "seq_timer.h"
48#include "seq_info.h"
49
50/* list of allocated queues */
51static queue_t *queue_list[SNDRV_SEQ_MAX_QUEUES];
52static DEFINE_SPINLOCK(queue_list_lock);
53/* number of queues allocated */
54static int num_queues;
55
56int snd_seq_queue_get_cur_queues(void)
57{
58 return num_queues;
59}
60
61/*----------------------------------------------------------------*/
62
63/* assign queue id and insert to list */
64static int queue_list_add(queue_t *q)
65{
66 int i;
67 unsigned long flags;
68
69 spin_lock_irqsave(&queue_list_lock, flags);
70 for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) {
71 if (! queue_list[i]) {
72 queue_list[i] = q;
73 q->queue = i;
74 num_queues++;
75 spin_unlock_irqrestore(&queue_list_lock, flags);
76 return i;
77 }
78 }
79 spin_unlock_irqrestore(&queue_list_lock, flags);
80 return -1;
81}
82
83static queue_t *queue_list_remove(int id, int client)
84{
85 queue_t *q;
86 unsigned long flags;
87
88 spin_lock_irqsave(&queue_list_lock, flags);
89 q = queue_list[id];
90 if (q) {
91 spin_lock(&q->owner_lock);
92 if (q->owner == client) {
93 /* found */
94 q->klocked = 1;
95 spin_unlock(&q->owner_lock);
96 queue_list[id] = NULL;
97 num_queues--;
98 spin_unlock_irqrestore(&queue_list_lock, flags);
99 return q;
100 }
101 spin_unlock(&q->owner_lock);
102 }
103 spin_unlock_irqrestore(&queue_list_lock, flags);
104 return NULL;
105}
106
107/*----------------------------------------------------------------*/
108
109/* create new queue (constructor) */
110static queue_t *queue_new(int owner, int locked)
111{
112 queue_t *q;
113
114 q = kcalloc(1, sizeof(*q), GFP_KERNEL);
115 if (q == NULL) {
116 snd_printd("malloc failed for snd_seq_queue_new()\n");
117 return NULL;
118 }
119
120 spin_lock_init(&q->owner_lock);
121 spin_lock_init(&q->check_lock);
122 init_MUTEX(&q->timer_mutex);
123 snd_use_lock_init(&q->use_lock);
124 q->queue = -1;
125
126 q->tickq = snd_seq_prioq_new();
127 q->timeq = snd_seq_prioq_new();
128 q->timer = snd_seq_timer_new();
129 if (q->tickq == NULL || q->timeq == NULL || q->timer == NULL) {
130 snd_seq_prioq_delete(&q->tickq);
131 snd_seq_prioq_delete(&q->timeq);
132 snd_seq_timer_delete(&q->timer);
133 kfree(q);
134 return NULL;
135 }
136
137 q->owner = owner;
138 q->locked = locked;
139 q->klocked = 0;
140
141 return q;
142}
143
144/* delete queue (destructor) */
145static void queue_delete(queue_t *q)
146{
147 /* stop and release the timer */
148 snd_seq_timer_stop(q->timer);
149 snd_seq_timer_close(q);
150 /* wait until access free */
151 snd_use_lock_sync(&q->use_lock);
152 /* release resources... */
153 snd_seq_prioq_delete(&q->tickq);
154 snd_seq_prioq_delete(&q->timeq);
155 snd_seq_timer_delete(&q->timer);
156
157 kfree(q);
158}
159
160
161/*----------------------------------------------------------------*/
162
163/* setup queues */
164int __init snd_seq_queues_init(void)
165{
166 /*
167 memset(queue_list, 0, sizeof(queue_list));
168 num_queues = 0;
169 */
170 return 0;
171}
172
173/* delete all existing queues */
174void __exit snd_seq_queues_delete(void)
175{
176 int i;
177
178 /* clear list */
179 for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) {
180 if (queue_list[i])
181 queue_delete(queue_list[i]);
182 }
183}
184
185/* allocate a new queue -
186 * return queue index value or negative value for error
187 */
188int snd_seq_queue_alloc(int client, int locked, unsigned int info_flags)
189{
190 queue_t *q;
191
192 q = queue_new(client, locked);
193 if (q == NULL)
194 return -ENOMEM;
195 q->info_flags = info_flags;
196 if (queue_list_add(q) < 0) {
197 queue_delete(q);
198 return -ENOMEM;
199 }
200 snd_seq_queue_use(q->queue, client, 1); /* use this queue */
201 return q->queue;
202}
203
204/* delete a queue - queue must be owned by the client */
205int snd_seq_queue_delete(int client, int queueid)
206{
207 queue_t *q;
208
209 if (queueid < 0 || queueid >= SNDRV_SEQ_MAX_QUEUES)
210 return -EINVAL;
211 q = queue_list_remove(queueid, client);
212 if (q == NULL)
213 return -EINVAL;
214 queue_delete(q);
215
216 return 0;
217}
218
219
220/* return pointer to queue structure for specified id */
221queue_t *queueptr(int queueid)
222{
223 queue_t *q;
224 unsigned long flags;
225
226 if (queueid < 0 || queueid >= SNDRV_SEQ_MAX_QUEUES)
227 return NULL;
228 spin_lock_irqsave(&queue_list_lock, flags);
229 q = queue_list[queueid];
230 if (q)
231 snd_use_lock_use(&q->use_lock);
232 spin_unlock_irqrestore(&queue_list_lock, flags);
233 return q;
234}
235
236/* return the (first) queue matching with the specified name */
237queue_t *snd_seq_queue_find_name(char *name)
238{
239 int i;
240 queue_t *q;
241
242 for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) {
243 if ((q = queueptr(i)) != NULL) {
244 if (strncmp(q->name, name, sizeof(q->name)) == 0)
245 return q;
246 queuefree(q);
247 }
248 }
249 return NULL;
250}
251
252
253/* -------------------------------------------------------- */
254
255void snd_seq_check_queue(queue_t *q, int atomic, int hop)
256{
257 unsigned long flags;
258 snd_seq_event_cell_t *cell;
259
260 if (q == NULL)
261 return;
262
263 /* make this function non-reentrant */
264 spin_lock_irqsave(&q->check_lock, flags);
265 if (q->check_blocked) {
266 q->check_again = 1;
267 spin_unlock_irqrestore(&q->check_lock, flags);
268 return; /* other thread is already checking queues */
269 }
270 q->check_blocked = 1;
271 spin_unlock_irqrestore(&q->check_lock, flags);
272
273 __again:
274 /* Process tick queue... */
275 while ((cell = snd_seq_prioq_cell_peek(q->tickq)) != NULL) {
276 if (snd_seq_compare_tick_time(&q->timer->tick.cur_tick, &cell->event.time.tick)) {
277 cell = snd_seq_prioq_cell_out(q->tickq);
278 if (cell)
279 snd_seq_dispatch_event(cell, atomic, hop);
280 } else {
281 /* event remains in the queue */
282 break;
283 }
284 }
285
286
287 /* Process time queue... */
288 while ((cell = snd_seq_prioq_cell_peek(q->timeq)) != NULL) {
289 if (snd_seq_compare_real_time(&q->timer->cur_time, &cell->event.time.time)) {
290 cell = snd_seq_prioq_cell_out(q->timeq);
291 if (cell)
292 snd_seq_dispatch_event(cell, atomic, hop);
293 } else {
294 /* event remains in the queue */
295 break;
296 }
297 }
298
299 /* free lock */
300 spin_lock_irqsave(&q->check_lock, flags);
301 if (q->check_again) {
302 q->check_again = 0;
303 spin_unlock_irqrestore(&q->check_lock, flags);
304 goto __again;
305 }
306 q->check_blocked = 0;
307 spin_unlock_irqrestore(&q->check_lock, flags);
308}
309
310
311/* enqueue a event to singe queue */
312int snd_seq_enqueue_event(snd_seq_event_cell_t *cell, int atomic, int hop)
313{
314 int dest, err;
315 queue_t *q;
316
317 snd_assert(cell != NULL, return -EINVAL);
318 dest = cell->event.queue; /* destination queue */
319 q = queueptr(dest);
320 if (q == NULL)
321 return -EINVAL;
322 /* handle relative time stamps, convert them into absolute */
323 if ((cell->event.flags & SNDRV_SEQ_TIME_MODE_MASK) == SNDRV_SEQ_TIME_MODE_REL) {
324 switch (cell->event.flags & SNDRV_SEQ_TIME_STAMP_MASK) {
325 case SNDRV_SEQ_TIME_STAMP_TICK:
326 cell->event.time.tick += q->timer->tick.cur_tick;
327 break;
328
329 case SNDRV_SEQ_TIME_STAMP_REAL:
330 snd_seq_inc_real_time(&cell->event.time.time, &q->timer->cur_time);
331 break;
332 }
333 cell->event.flags &= ~SNDRV_SEQ_TIME_MODE_MASK;
334 cell->event.flags |= SNDRV_SEQ_TIME_MODE_ABS;
335 }
336 /* enqueue event in the real-time or midi queue */
337 switch (cell->event.flags & SNDRV_SEQ_TIME_STAMP_MASK) {
338 case SNDRV_SEQ_TIME_STAMP_TICK:
339 err = snd_seq_prioq_cell_in(q->tickq, cell);
340 break;
341
342 case SNDRV_SEQ_TIME_STAMP_REAL:
343 default:
344 err = snd_seq_prioq_cell_in(q->timeq, cell);
345 break;
346 }
347
348 if (err < 0) {
349 queuefree(q); /* unlock */
350 return err;
351 }
352
353 /* trigger dispatching */
354 snd_seq_check_queue(q, atomic, hop);
355
356 queuefree(q); /* unlock */
357
358 return 0;
359}
360
361
362/*----------------------------------------------------------------*/
363
364static inline int check_access(queue_t *q, int client)
365{
366 return (q->owner == client) || (!q->locked && !q->klocked);
367}
368
369/* check if the client has permission to modify queue parameters.
370 * if it does, lock the queue
371 */
372static int queue_access_lock(queue_t *q, int client)
373{
374 unsigned long flags;
375 int access_ok;
376
377 spin_lock_irqsave(&q->owner_lock, flags);
378 access_ok = check_access(q, client);
379 if (access_ok)
380 q->klocked = 1;
381 spin_unlock_irqrestore(&q->owner_lock, flags);
382 return access_ok;
383}
384
385/* unlock the queue */
386static inline void queue_access_unlock(queue_t *q)
387{
388 unsigned long flags;
389
390 spin_lock_irqsave(&q->owner_lock, flags);
391 q->klocked = 0;
392 spin_unlock_irqrestore(&q->owner_lock, flags);
393}
394
395/* exported - only checking permission */
396int snd_seq_queue_check_access(int queueid, int client)
397{
398 queue_t *q = queueptr(queueid);
399 int access_ok;
400 unsigned long flags;
401
402 if (! q)
403 return 0;
404 spin_lock_irqsave(&q->owner_lock, flags);
405 access_ok = check_access(q, client);
406 spin_unlock_irqrestore(&q->owner_lock, flags);
407 queuefree(q);
408 return access_ok;
409}
410
411/*----------------------------------------------------------------*/
412
413/*
414 * change queue's owner and permission
415 */
416int snd_seq_queue_set_owner(int queueid, int client, int locked)
417{
418 queue_t *q = queueptr(queueid);
419
420 if (q == NULL)
421 return -EINVAL;
422
423 if (! queue_access_lock(q, client)) {
424 queuefree(q);
425 return -EPERM;
426 }
427
428 q->locked = locked ? 1 : 0;
429 q->owner = client;
430 queue_access_unlock(q);
431 queuefree(q);
432
433 return 0;
434}
435
436
437/*----------------------------------------------------------------*/
438
439/* open timer -
440 * q->use mutex should be down before calling this function to avoid
441 * confliction with snd_seq_queue_use()
442 */
443int snd_seq_queue_timer_open(int queueid)
444{
445 int result = 0;
446 queue_t *queue;
447 seq_timer_t *tmr;
448
449 queue = queueptr(queueid);
450 if (queue == NULL)
451 return -EINVAL;
452 tmr = queue->timer;
453 if ((result = snd_seq_timer_open(queue)) < 0) {
454 snd_seq_timer_defaults(tmr);
455 result = snd_seq_timer_open(queue);
456 }
457 queuefree(queue);
458 return result;
459}
460
461/* close timer -
462 * q->use mutex should be down before calling this function
463 */
464int snd_seq_queue_timer_close(int queueid)
465{
466 queue_t *queue;
467 seq_timer_t *tmr;
468 int result = 0;
469
470 queue = queueptr(queueid);
471 if (queue == NULL)
472 return -EINVAL;
473 tmr = queue->timer;
474 snd_seq_timer_close(queue);
475 queuefree(queue);
476 return result;
477}
478
479/* change queue tempo and ppq */
480int snd_seq_queue_timer_set_tempo(int queueid, int client, snd_seq_queue_tempo_t *info)
481{
482 queue_t *q = queueptr(queueid);
483 int result;
484
485 if (q == NULL)
486 return -EINVAL;
487 if (! queue_access_lock(q, client)) {
488 queuefree(q);
489 return -EPERM;
490 }
491
492 result = snd_seq_timer_set_tempo(q->timer, info->tempo);
493 if (result >= 0)
494 result = snd_seq_timer_set_ppq(q->timer, info->ppq);
495 if (result >= 0 && info->skew_base > 0)
496 result = snd_seq_timer_set_skew(q->timer, info->skew_value, info->skew_base);
497 queue_access_unlock(q);
498 queuefree(q);
499 return result;
500}
501
502
503/* use or unuse this queue -
504 * if it is the first client, starts the timer.
505 * if it is not longer used by any clients, stop the timer.
506 */
507int snd_seq_queue_use(int queueid, int client, int use)
508{
509 queue_t *queue;
510
511 queue = queueptr(queueid);
512 if (queue == NULL)
513 return -EINVAL;
514 down(&queue->timer_mutex);
515 if (use) {
516 if (!test_and_set_bit(client, queue->clients_bitmap))
517 queue->clients++;
518 } else {
519 if (test_and_clear_bit(client, queue->clients_bitmap))
520 queue->clients--;
521 }
522 if (queue->clients) {
523 if (use && queue->clients == 1)
524 snd_seq_timer_defaults(queue->timer);
525 snd_seq_timer_open(queue);
526 } else {
527 snd_seq_timer_close(queue);
528 }
529 up(&queue->timer_mutex);
530 queuefree(queue);
531 return 0;
532}
533
534/*
535 * check if queue is used by the client
536 * return negative value if the queue is invalid.
537 * return 0 if not used, 1 if used.
538 */
539int snd_seq_queue_is_used(int queueid, int client)
540{
541 queue_t *q;
542 int result;
543
544 q = queueptr(queueid);
545 if (q == NULL)
546 return -EINVAL; /* invalid queue */
547 result = test_bit(client, q->clients_bitmap) ? 1 : 0;
548 queuefree(q);
549 return result;
550}
551
552
553/*----------------------------------------------------------------*/
554
555/* notification that client has left the system -
556 * stop the timer on all queues owned by this client
557 */
558void snd_seq_queue_client_termination(int client)
559{
560 unsigned long flags;
561 int i;
562 queue_t *q;
563
564 for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) {
565 if ((q = queueptr(i)) == NULL)
566 continue;
567 spin_lock_irqsave(&q->owner_lock, flags);
568 if (q->owner == client)
569 q->klocked = 1;
570 spin_unlock_irqrestore(&q->owner_lock, flags);
571 if (q->owner == client) {
572 if (q->timer->running)
573 snd_seq_timer_stop(q->timer);
574 snd_seq_timer_reset(q->timer);
575 }
576 queuefree(q);
577 }
578}
579
580/* final stage notification -
581 * remove cells for no longer exist client (for non-owned queue)
582 * or delete this queue (for owned queue)
583 */
584void snd_seq_queue_client_leave(int client)
585{
586 int i;
587 queue_t *q;
588
589 /* delete own queues from queue list */
590 for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) {
591 if ((q = queue_list_remove(i, client)) != NULL)
592 queue_delete(q);
593 }
594
595 /* remove cells from existing queues -
596 * they are not owned by this client
597 */
598 for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) {
599 if ((q = queueptr(i)) == NULL)
600 continue;
601 if (test_bit(client, q->clients_bitmap)) {
602 snd_seq_prioq_leave(q->tickq, client, 0);
603 snd_seq_prioq_leave(q->timeq, client, 0);
604 snd_seq_queue_use(q->queue, client, 0);
605 }
606 queuefree(q);
607 }
608}
609
610
611
612/*----------------------------------------------------------------*/
613
614/* remove cells from all queues */
615void snd_seq_queue_client_leave_cells(int client)
616{
617 int i;
618 queue_t *q;
619
620 for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) {
621 if ((q = queueptr(i)) == NULL)
622 continue;
623 snd_seq_prioq_leave(q->tickq, client, 0);
624 snd_seq_prioq_leave(q->timeq, client, 0);
625 queuefree(q);
626 }
627}
628
629/* remove cells based on flush criteria */
630void snd_seq_queue_remove_cells(int client, snd_seq_remove_events_t *info)
631{
632 int i;
633 queue_t *q;
634
635 for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) {
636 if ((q = queueptr(i)) == NULL)
637 continue;
638 if (test_bit(client, q->clients_bitmap) &&
639 (! (info->remove_mode & SNDRV_SEQ_REMOVE_DEST) ||
640 q->queue == info->queue)) {
641 snd_seq_prioq_remove_events(q->tickq, client, info);
642 snd_seq_prioq_remove_events(q->timeq, client, info);
643 }
644 queuefree(q);
645 }
646}
647
648/*----------------------------------------------------------------*/
649
650/*
651 * send events to all subscribed ports
652 */
653static void queue_broadcast_event(queue_t *q, snd_seq_event_t *ev, int atomic, int hop)
654{
655 snd_seq_event_t sev;
656
657 sev = *ev;
658
659 sev.flags = SNDRV_SEQ_TIME_STAMP_TICK|SNDRV_SEQ_TIME_MODE_ABS;
660 sev.time.tick = q->timer->tick.cur_tick;
661 sev.queue = q->queue;
662 sev.data.queue.queue = q->queue;
663
664 /* broadcast events from Timer port */
665 sev.source.client = SNDRV_SEQ_CLIENT_SYSTEM;
666 sev.source.port = SNDRV_SEQ_PORT_SYSTEM_TIMER;
667 sev.dest.client = SNDRV_SEQ_ADDRESS_SUBSCRIBERS;
668 snd_seq_kernel_client_dispatch(SNDRV_SEQ_CLIENT_SYSTEM, &sev, atomic, hop);
669}
670
671/*
672 * process a received queue-control event.
673 * this function is exported for seq_sync.c.
674 */
675void snd_seq_queue_process_event(queue_t *q, snd_seq_event_t *ev, int atomic, int hop)
676{
677 switch (ev->type) {
678 case SNDRV_SEQ_EVENT_START:
679 snd_seq_prioq_leave(q->tickq, ev->source.client, 1);
680 snd_seq_prioq_leave(q->timeq, ev->source.client, 1);
681 if (! snd_seq_timer_start(q->timer))
682 queue_broadcast_event(q, ev, atomic, hop);
683 break;
684
685 case SNDRV_SEQ_EVENT_CONTINUE:
686 if (! snd_seq_timer_continue(q->timer))
687 queue_broadcast_event(q, ev, atomic, hop);
688 break;
689
690 case SNDRV_SEQ_EVENT_STOP:
691 snd_seq_timer_stop(q->timer);
692 queue_broadcast_event(q, ev, atomic, hop);
693 break;
694
695 case SNDRV_SEQ_EVENT_TEMPO:
696 snd_seq_timer_set_tempo(q->timer, ev->data.queue.param.value);
697 queue_broadcast_event(q, ev, atomic, hop);
698 break;
699
700 case SNDRV_SEQ_EVENT_SETPOS_TICK:
701 if (snd_seq_timer_set_position_tick(q->timer, ev->data.queue.param.time.tick) == 0) {
702 queue_broadcast_event(q, ev, atomic, hop);
703 }
704 break;
705
706 case SNDRV_SEQ_EVENT_SETPOS_TIME:
707 if (snd_seq_timer_set_position_time(q->timer, ev->data.queue.param.time.time) == 0) {
708 queue_broadcast_event(q, ev, atomic, hop);
709 }
710 break;
711 case SNDRV_SEQ_EVENT_QUEUE_SKEW:
712 if (snd_seq_timer_set_skew(q->timer,
713 ev->data.queue.param.skew.value,
714 ev->data.queue.param.skew.base) == 0) {
715 queue_broadcast_event(q, ev, atomic, hop);
716 }
717 break;
718 }
719}
720
721
722/*
723 * Queue control via timer control port:
724 * this function is exported as a callback of timer port.
725 */
726int snd_seq_control_queue(snd_seq_event_t *ev, int atomic, int hop)
727{
728 queue_t *q;
729
730 snd_assert(ev != NULL, return -EINVAL);
731 q = queueptr(ev->data.queue.queue);
732
733 if (q == NULL)
734 return -EINVAL;
735
736 if (! queue_access_lock(q, ev->source.client)) {
737 queuefree(q);
738 return -EPERM;
739 }
740
741 snd_seq_queue_process_event(q, ev, atomic, hop);
742
743 queue_access_unlock(q);
744 queuefree(q);
745 return 0;
746}
747
748
749/*----------------------------------------------------------------*/
750
751/* exported to seq_info.c */
752void snd_seq_info_queues_read(snd_info_entry_t *entry,
753 snd_info_buffer_t * buffer)
754{
755 int i, bpm;
756 queue_t *q;
757 seq_timer_t *tmr;
758
759 for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) {
760 if ((q = queueptr(i)) == NULL)
761 continue;
762
763 tmr = q->timer;
764 if (tmr->tempo)
765 bpm = 60000000 / tmr->tempo;
766 else
767 bpm = 0;
768
769 snd_iprintf(buffer, "queue %d: [%s]\n", q->queue, q->name);
770 snd_iprintf(buffer, "owned by client : %d\n", q->owner);
771 snd_iprintf(buffer, "lock status : %s\n", q->locked ? "Locked" : "Free");
772 snd_iprintf(buffer, "queued time events : %d\n", snd_seq_prioq_avail(q->timeq));
773 snd_iprintf(buffer, "queued tick events : %d\n", snd_seq_prioq_avail(q->tickq));
774 snd_iprintf(buffer, "timer state : %s\n", tmr->running ? "Running" : "Stopped");
775 snd_iprintf(buffer, "timer PPQ : %d\n", tmr->ppq);
776 snd_iprintf(buffer, "current tempo : %d\n", tmr->tempo);
777 snd_iprintf(buffer, "current BPM : %d\n", bpm);
778 snd_iprintf(buffer, "current time : %d.%09d s\n", tmr->cur_time.tv_sec, tmr->cur_time.tv_nsec);
779 snd_iprintf(buffer, "current tick : %d\n", tmr->tick.cur_tick);
780 snd_iprintf(buffer, "\n");
781 queuefree(q);
782 }
783}
diff --git a/sound/core/seq/seq_queue.h b/sound/core/seq/seq_queue.h
new file mode 100644
index 000000000000..b1bf5519fb3b
--- /dev/null
+++ b/sound/core/seq/seq_queue.h
@@ -0,0 +1,140 @@
1/*
2 * ALSA sequencer Queue handling
3 * Copyright (c) 1998-1999 by Frank van de Pol <fvdpol@coil.demon.nl>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 *
19 */
20#ifndef __SND_SEQ_QUEUE_H
21#define __SND_SEQ_QUEUE_H
22
23#include "seq_memory.h"
24#include "seq_prioq.h"
25#include "seq_timer.h"
26#include "seq_lock.h"
27#include <linux/interrupt.h>
28#include <linux/list.h>
29#include <linux/bitops.h>
30
31#define SEQ_QUEUE_NO_OWNER (-1)
32
33struct _snd_seq_queue {
34 int queue; /* queue number */
35
36 char name[64]; /* name of this queue */
37
38 prioq_t *tickq; /* midi tick event queue */
39 prioq_t *timeq; /* real-time event queue */
40
41 seq_timer_t *timer; /* time keeper for this queue */
42 int owner; /* client that 'owns' the timer */
43 unsigned int locked:1, /* timer is only accesibble by owner if set */
44 klocked:1, /* kernel lock (after START) */
45 check_again:1,
46 check_blocked:1;
47
48 unsigned int flags; /* status flags */
49 unsigned int info_flags; /* info for sync */
50
51 spinlock_t owner_lock;
52 spinlock_t check_lock;
53
54 /* clients which uses this queue (bitmap) */
55 DECLARE_BITMAP(clients_bitmap, SNDRV_SEQ_MAX_CLIENTS);
56 unsigned int clients; /* users of this queue */
57 struct semaphore timer_mutex;
58
59 snd_use_lock_t use_lock;
60};
61
62
63/* get the number of current queues */
64int snd_seq_queue_get_cur_queues(void);
65
66/* init queues structure */
67int snd_seq_queues_init(void);
68
69/* delete queues */
70void snd_seq_queues_delete(void);
71
72
73/* create new queue (constructor) */
74int snd_seq_queue_alloc(int client, int locked, unsigned int flags);
75
76/* delete queue (destructor) */
77int snd_seq_queue_delete(int client, int queueid);
78
79/* notification that client has left the system */
80void snd_seq_queue_client_termination(int client);
81
82/* final stage */
83void snd_seq_queue_client_leave(int client);
84
85/* enqueue a event received from one the clients */
86int snd_seq_enqueue_event(snd_seq_event_cell_t *cell, int atomic, int hop);
87
88/* Remove events */
89void snd_seq_queue_client_leave_cells(int client);
90void snd_seq_queue_remove_cells(int client, snd_seq_remove_events_t *info);
91
92/* return pointer to queue structure for specified id */
93queue_t *queueptr(int queueid);
94/* unlock */
95#define queuefree(q) snd_use_lock_free(&(q)->use_lock)
96
97/* return the (first) queue matching with the specified name */
98queue_t *snd_seq_queue_find_name(char *name);
99
100/* check single queue and dispatch events */
101void snd_seq_check_queue(queue_t *q, int atomic, int hop);
102
103/* access to queue's parameters */
104int snd_seq_queue_check_access(int queueid, int client);
105int snd_seq_queue_timer_set_tempo(int queueid, int client, snd_seq_queue_tempo_t *info);
106int snd_seq_queue_set_owner(int queueid, int client, int locked);
107int snd_seq_queue_set_locked(int queueid, int client, int locked);
108int snd_seq_queue_timer_open(int queueid);
109int snd_seq_queue_timer_close(int queueid);
110int snd_seq_queue_use(int queueid, int client, int use);
111int snd_seq_queue_is_used(int queueid, int client);
112
113int snd_seq_control_queue(snd_seq_event_t *ev, int atomic, int hop);
114void snd_seq_queue_process_event(queue_t *q, snd_seq_event_t *ev, int atomic, int hop);
115
116/*
117 * 64bit division - for sync stuff..
118 */
119#if defined(i386) || defined(i486)
120
121#define udiv_qrnnd(q, r, n1, n0, d) \
122 __asm__ ("divl %4" \
123 : "=a" ((u32)(q)), \
124 "=d" ((u32)(r)) \
125 : "0" ((u32)(n0)), \
126 "1" ((u32)(n1)), \
127 "rm" ((u32)(d)))
128
129#define u64_div(x,y,q) do {u32 __tmp; udiv_qrnnd(q, __tmp, (x)>>32, x, y);} while (0)
130#define u64_mod(x,y,r) do {u32 __tmp; udiv_qrnnd(__tmp, q, (x)>>32, x, y);} while (0)
131#define u64_divmod(x,y,q,r) udiv_qrnnd(q, r, (x)>>32, x, y)
132
133#else
134#define u64_div(x,y,q) ((q) = (u32)((u64)(x) / (u64)(y)))
135#define u64_mod(x,y,r) ((r) = (u32)((u64)(x) % (u64)(y)))
136#define u64_divmod(x,y,q,r) (u64_div(x,y,q), u64_mod(x,y,r))
137#endif
138
139
140#endif
diff --git a/sound/core/seq/seq_system.c b/sound/core/seq/seq_system.c
new file mode 100644
index 000000000000..e8f0a6683d50
--- /dev/null
+++ b/sound/core/seq/seq_system.c
@@ -0,0 +1,190 @@
1/*
2 * ALSA sequencer System services Client
3 * Copyright (c) 1998-1999 by Frank van de Pol <fvdpol@coil.demon.nl>
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 <sound/core.h>
25#include "seq_system.h"
26#include "seq_timer.h"
27#include "seq_queue.h"
28
29/* internal client that provide system services, access to timer etc. */
30
31/*
32 * Port "Timer"
33 * - send tempo /start/stop etc. events to this port to manipulate the
34 * queue's timer. The queue address is specified in
35 * data.queue.queue.
36 * - this port supports subscription. The received timer events are
37 * broadcasted to all subscribed clients. The modified tempo
38 * value is stored on data.queue.value.
39 * The modifier client/port is not send.
40 *
41 * Port "Announce"
42 * - does not receive message
43 * - supports supscription. For each client or port attaching to or
44 * detaching from the system an announcement is send to the subscribed
45 * clients.
46 *
47 * Idea: the subscription mechanism might also work handy for distributing
48 * synchronisation and timing information. In this case we would ideally have
49 * a list of subscribers for each type of sync (time, tick), for each timing
50 * queue.
51 *
52 * NOTE: the queue to be started, stopped, etc. must be specified
53 * in data.queue.addr.queue field. queue is used only for
54 * scheduling, and no longer referred as affected queue.
55 * They are used only for timer broadcast (see above).
56 * -- iwai
57 */
58
59
60/* client id of our system client */
61static int sysclient = -1;
62
63/* port id numbers for this client */
64static int announce_port = -1;
65
66
67
68/* fill standard header data, source port & channel are filled in */
69static int setheader(snd_seq_event_t * ev, int client, int port)
70{
71 if (announce_port < 0)
72 return -ENODEV;
73
74 memset(ev, 0, sizeof(snd_seq_event_t));
75
76 ev->flags &= ~SNDRV_SEQ_EVENT_LENGTH_MASK;
77 ev->flags |= SNDRV_SEQ_EVENT_LENGTH_FIXED;
78
79 ev->source.client = sysclient;
80 ev->source.port = announce_port;
81 ev->dest.client = SNDRV_SEQ_ADDRESS_SUBSCRIBERS;
82
83 /* fill data */
84 /*ev->data.addr.queue = SNDRV_SEQ_ADDRESS_UNKNOWN;*/
85 ev->data.addr.client = client;
86 ev->data.addr.port = port;
87
88 return 0;
89}
90
91
92/* entry points for broadcasting system events */
93void snd_seq_system_broadcast(int client, int port, int type)
94{
95 snd_seq_event_t ev;
96
97 if (setheader(&ev, client, port) < 0)
98 return;
99 ev.type = type;
100 snd_seq_kernel_client_dispatch(sysclient, &ev, 0, 0);
101}
102
103/* entry points for broadcasting system events */
104int snd_seq_system_notify(int client, int port, snd_seq_event_t *ev)
105{
106 ev->flags = SNDRV_SEQ_EVENT_LENGTH_FIXED;
107 ev->source.client = sysclient;
108 ev->source.port = announce_port;
109 ev->dest.client = client;
110 ev->dest.port = port;
111 return snd_seq_kernel_client_dispatch(sysclient, ev, 0, 0);
112}
113
114/* call-back handler for timer events */
115static int event_input_timer(snd_seq_event_t * ev, int direct, void *private_data, int atomic, int hop)
116{
117 return snd_seq_control_queue(ev, atomic, hop);
118}
119
120/* register our internal client */
121int __init snd_seq_system_client_init(void)
122{
123
124 snd_seq_client_callback_t callbacks;
125 snd_seq_port_callback_t pcallbacks;
126 snd_seq_client_info_t *inf;
127 snd_seq_port_info_t *port;
128
129 inf = kcalloc(1, sizeof(*inf), GFP_KERNEL);
130 port = kcalloc(1, sizeof(*port), GFP_KERNEL);
131 if (! inf || ! port) {
132 kfree(inf);
133 kfree(port);
134 return -ENOMEM;
135 }
136
137 memset(&callbacks, 0, sizeof(callbacks));
138 memset(&pcallbacks, 0, sizeof(pcallbacks));
139 pcallbacks.owner = THIS_MODULE;
140 pcallbacks.event_input = event_input_timer;
141
142 /* register client */
143 callbacks.allow_input = callbacks.allow_output = 1;
144 sysclient = snd_seq_create_kernel_client(NULL, 0, &callbacks);
145
146 /* set our name */
147 inf->client = 0;
148 inf->type = KERNEL_CLIENT;
149 strcpy(inf->name, "System");
150 snd_seq_kernel_client_ctl(sysclient, SNDRV_SEQ_IOCTL_SET_CLIENT_INFO, inf);
151
152 /* register timer */
153 strcpy(port->name, "Timer");
154 port->capability = SNDRV_SEQ_PORT_CAP_WRITE; /* accept queue control */
155 port->capability |= SNDRV_SEQ_PORT_CAP_READ|SNDRV_SEQ_PORT_CAP_SUBS_READ; /* for broadcast */
156 port->kernel = &pcallbacks;
157 port->type = 0;
158 port->flags = SNDRV_SEQ_PORT_FLG_GIVEN_PORT;
159 port->addr.client = sysclient;
160 port->addr.port = SNDRV_SEQ_PORT_SYSTEM_TIMER;
161 snd_seq_kernel_client_ctl(sysclient, SNDRV_SEQ_IOCTL_CREATE_PORT, port);
162
163 /* register announcement port */
164 strcpy(port->name, "Announce");
165 port->capability = SNDRV_SEQ_PORT_CAP_READ|SNDRV_SEQ_PORT_CAP_SUBS_READ; /* for broadcast only */
166 port->kernel = NULL;
167 port->type = 0;
168 port->flags = SNDRV_SEQ_PORT_FLG_GIVEN_PORT;
169 port->addr.client = sysclient;
170 port->addr.port = SNDRV_SEQ_PORT_SYSTEM_ANNOUNCE;
171 snd_seq_kernel_client_ctl(sysclient, SNDRV_SEQ_IOCTL_CREATE_PORT, port);
172 announce_port = port->addr.port;
173
174 kfree(inf);
175 kfree(port);
176 return 0;
177}
178
179
180/* unregister our internal client */
181void __exit snd_seq_system_client_done(void)
182{
183 int oldsysclient = sysclient;
184
185 if (oldsysclient >= 0) {
186 sysclient = -1;
187 announce_port = -1;
188 snd_seq_delete_kernel_client(oldsysclient);
189 }
190}
diff --git a/sound/core/seq/seq_system.h b/sound/core/seq/seq_system.h
new file mode 100644
index 000000000000..900007255bb4
--- /dev/null
+++ b/sound/core/seq/seq_system.h
@@ -0,0 +1,46 @@
1/*
2 * ALSA sequencer System Client
3 * Copyright (c) 1998 by Frank van de Pol <fvdpol@coil.demon.nl>
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#ifndef __SND_SEQ_SYSTEM_H
22#define __SND_SEQ_SYSTEM_H
23
24#include <sound/seq_kernel.h>
25
26
27/* entry points for broadcasting system events */
28void snd_seq_system_broadcast(int client, int port, int type);
29
30#define snd_seq_system_client_ev_client_start(client) snd_seq_system_broadcast(client, 0, SNDRV_SEQ_EVENT_CLIENT_START)
31#define snd_seq_system_client_ev_client_exit(client) snd_seq_system_broadcast(client, 0, SNDRV_SEQ_EVENT_CLIENT_EXIT)
32#define snd_seq_system_client_ev_client_change(client) snd_seq_system_broadcast(client, 0, SNDRV_SEQ_EVENT_CLIENT_CHANGE)
33#define snd_seq_system_client_ev_port_start(client, port) snd_seq_system_broadcast(client, port, SNDRV_SEQ_EVENT_PORT_START)
34#define snd_seq_system_client_ev_port_exit(client, port) snd_seq_system_broadcast(client, port, SNDRV_SEQ_EVENT_PORT_EXIT)
35#define snd_seq_system_client_ev_port_change(client, port) snd_seq_system_broadcast(client, port, SNDRV_SEQ_EVENT_PORT_CHANGE)
36
37int snd_seq_system_notify(int client, int port, snd_seq_event_t *ev);
38
39/* register our internal client */
40int snd_seq_system_client_init(void);
41
42/* unregister our internal client */
43void snd_seq_system_client_done(void);
44
45
46#endif
diff --git a/sound/core/seq/seq_timer.c b/sound/core/seq/seq_timer.c
new file mode 100644
index 000000000000..753f1c0863cc
--- /dev/null
+++ b/sound/core/seq/seq_timer.c
@@ -0,0 +1,435 @@
1/*
2 * ALSA sequencer Timer
3 * Copyright (c) 1998-1999 by Frank van de Pol <fvdpol@coil.demon.nl>
4 * Jaroslav Kysela <perex@suse.cz>
5 *
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (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 General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; 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 <sound/core.h>
25#include <linux/slab.h>
26#include "seq_timer.h"
27#include "seq_queue.h"
28#include "seq_info.h"
29
30extern int seq_default_timer_class;
31extern int seq_default_timer_sclass;
32extern int seq_default_timer_card;
33extern int seq_default_timer_device;
34extern int seq_default_timer_subdevice;
35extern int seq_default_timer_resolution;
36
37#define SKEW_BASE 0x10000 /* 16bit shift */
38
39void snd_seq_timer_set_tick_resolution(seq_timer_tick_t *tick, int tempo, int ppq, int nticks)
40{
41 if (tempo < 1000000)
42 tick->resolution = (tempo * 1000) / ppq;
43 else {
44 /* might overflow.. */
45 unsigned int s;
46 s = tempo % ppq;
47 s = (s * 1000) / ppq;
48 tick->resolution = (tempo / ppq) * 1000;
49 tick->resolution += s;
50 }
51 if (tick->resolution <= 0)
52 tick->resolution = 1;
53 tick->resolution *= nticks;
54 snd_seq_timer_update_tick(tick, 0);
55}
56
57/* create new timer (constructor) */
58seq_timer_t *snd_seq_timer_new(void)
59{
60 seq_timer_t *tmr;
61
62 tmr = kcalloc(1, sizeof(*tmr), GFP_KERNEL);
63 if (tmr == NULL) {
64 snd_printd("malloc failed for snd_seq_timer_new() \n");
65 return NULL;
66 }
67 spin_lock_init(&tmr->lock);
68
69 /* reset setup to defaults */
70 snd_seq_timer_defaults(tmr);
71
72 /* reset time */
73 snd_seq_timer_reset(tmr);
74
75 return tmr;
76}
77
78/* delete timer (destructor) */
79void snd_seq_timer_delete(seq_timer_t **tmr)
80{
81 seq_timer_t *t = *tmr;
82 *tmr = NULL;
83
84 if (t == NULL) {
85 snd_printd("oops: snd_seq_timer_delete() called with NULL timer\n");
86 return;
87 }
88 t->running = 0;
89
90 /* reset time */
91 snd_seq_timer_stop(t);
92 snd_seq_timer_reset(t);
93
94 kfree(t);
95}
96
97void snd_seq_timer_defaults(seq_timer_t * tmr)
98{
99 /* setup defaults */
100 tmr->ppq = 96; /* 96 PPQ */
101 tmr->tempo = 500000; /* 120 BPM */
102 snd_seq_timer_set_tick_resolution(&tmr->tick, tmr->tempo, tmr->ppq, 1);
103 tmr->running = 0;
104
105 tmr->type = SNDRV_SEQ_TIMER_ALSA;
106 tmr->alsa_id.dev_class = seq_default_timer_class;
107 tmr->alsa_id.dev_sclass = seq_default_timer_sclass;
108 tmr->alsa_id.card = seq_default_timer_card;
109 tmr->alsa_id.device = seq_default_timer_device;
110 tmr->alsa_id.subdevice = seq_default_timer_subdevice;
111 tmr->preferred_resolution = seq_default_timer_resolution;
112
113 tmr->skew = tmr->skew_base = SKEW_BASE;
114}
115
116void snd_seq_timer_reset(seq_timer_t * tmr)
117{
118 unsigned long flags;
119
120 spin_lock_irqsave(&tmr->lock, flags);
121
122 /* reset time & songposition */
123 tmr->cur_time.tv_sec = 0;
124 tmr->cur_time.tv_nsec = 0;
125
126 tmr->tick.cur_tick = 0;
127 tmr->tick.fraction = 0;
128
129 spin_unlock_irqrestore(&tmr->lock, flags);
130}
131
132
133/* called by timer interrupt routine. the period time since previous invocation is passed */
134static void snd_seq_timer_interrupt(snd_timer_instance_t *timeri,
135 unsigned long resolution,
136 unsigned long ticks)
137{
138 unsigned long flags;
139 queue_t *q = (queue_t *)timeri->callback_data;
140 seq_timer_t *tmr;
141
142 if (q == NULL)
143 return;
144 tmr = q->timer;
145 if (tmr == NULL)
146 return;
147 if (!tmr->running)
148 return;
149
150 resolution *= ticks;
151 if (tmr->skew != tmr->skew_base) {
152 /* FIXME: assuming skew_base = 0x10000 */
153 resolution = (resolution >> 16) * tmr->skew +
154 (((resolution & 0xffff) * tmr->skew) >> 16);
155 }
156
157 spin_lock_irqsave(&tmr->lock, flags);
158
159 /* update timer */
160 snd_seq_inc_time_nsec(&tmr->cur_time, resolution);
161
162 /* calculate current tick */
163 snd_seq_timer_update_tick(&tmr->tick, resolution);
164
165 /* register actual time of this timer update */
166 do_gettimeofday(&tmr->last_update);
167
168 spin_unlock_irqrestore(&tmr->lock, flags);
169
170 /* check queues and dispatch events */
171 snd_seq_check_queue(q, 1, 0);
172}
173
174/* set current tempo */
175int snd_seq_timer_set_tempo(seq_timer_t * tmr, int tempo)
176{
177 unsigned long flags;
178
179 snd_assert(tmr, return -EINVAL);
180 if (tempo <= 0)
181 return -EINVAL;
182 spin_lock_irqsave(&tmr->lock, flags);
183 if ((unsigned int)tempo != tmr->tempo) {
184 tmr->tempo = tempo;
185 snd_seq_timer_set_tick_resolution(&tmr->tick, tmr->tempo, tmr->ppq, 1);
186 }
187 spin_unlock_irqrestore(&tmr->lock, flags);
188 return 0;
189}
190
191/* set current ppq */
192int snd_seq_timer_set_ppq(seq_timer_t * tmr, int ppq)
193{
194 unsigned long flags;
195
196 snd_assert(tmr, return -EINVAL);
197 if (ppq <= 0)
198 return -EINVAL;
199 spin_lock_irqsave(&tmr->lock, flags);
200 if (tmr->running && (ppq != tmr->ppq)) {
201 /* refuse to change ppq on running timers */
202 /* because it will upset the song position (ticks) */
203 spin_unlock_irqrestore(&tmr->lock, flags);
204 snd_printd("seq: cannot change ppq of a running timer\n");
205 return -EBUSY;
206 }
207
208 tmr->ppq = ppq;
209 snd_seq_timer_set_tick_resolution(&tmr->tick, tmr->tempo, tmr->ppq, 1);
210 spin_unlock_irqrestore(&tmr->lock, flags);
211 return 0;
212}
213
214/* set current tick position */
215int snd_seq_timer_set_position_tick(seq_timer_t *tmr, snd_seq_tick_time_t position)
216{
217 unsigned long flags;
218
219 snd_assert(tmr, return -EINVAL);
220
221 spin_lock_irqsave(&tmr->lock, flags);
222 tmr->tick.cur_tick = position;
223 tmr->tick.fraction = 0;
224 spin_unlock_irqrestore(&tmr->lock, flags);
225 return 0;
226}
227
228/* set current real-time position */
229int snd_seq_timer_set_position_time(seq_timer_t *tmr, snd_seq_real_time_t position)
230{
231 unsigned long flags;
232
233 snd_assert(tmr, return -EINVAL);
234
235 snd_seq_sanity_real_time(&position);
236 spin_lock_irqsave(&tmr->lock, flags);
237 tmr->cur_time = position;
238 spin_unlock_irqrestore(&tmr->lock, flags);
239 return 0;
240}
241
242/* set timer skew */
243int snd_seq_timer_set_skew(seq_timer_t *tmr, unsigned int skew, unsigned int base)
244{
245 unsigned long flags;
246
247 snd_assert(tmr, return -EINVAL);
248
249 /* FIXME */
250 if (base != SKEW_BASE) {
251 snd_printd("invalid skew base 0x%x\n", base);
252 return -EINVAL;
253 }
254 spin_lock_irqsave(&tmr->lock, flags);
255 tmr->skew = skew;
256 spin_unlock_irqrestore(&tmr->lock, flags);
257 return 0;
258}
259
260int snd_seq_timer_open(queue_t *q)
261{
262 snd_timer_instance_t *t;
263 seq_timer_t *tmr;
264 char str[32];
265 int err;
266
267 tmr = q->timer;
268 snd_assert(tmr != NULL, return -EINVAL);
269 if (tmr->timeri)
270 return -EBUSY;
271 sprintf(str, "sequencer queue %i", q->queue);
272 if (tmr->type != SNDRV_SEQ_TIMER_ALSA) /* standard ALSA timer */
273 return -EINVAL;
274 if (tmr->alsa_id.dev_class != SNDRV_TIMER_CLASS_SLAVE)
275 tmr->alsa_id.dev_sclass = SNDRV_TIMER_SCLASS_SEQUENCER;
276 err = snd_timer_open(&t, str, &tmr->alsa_id, q->queue);
277 if (err < 0 && tmr->alsa_id.dev_class != SNDRV_TIMER_CLASS_SLAVE) {
278 if (tmr->alsa_id.dev_class != SNDRV_TIMER_CLASS_GLOBAL ||
279 tmr->alsa_id.device != SNDRV_TIMER_GLOBAL_SYSTEM) {
280 snd_timer_id_t tid;
281 memset(&tid, 0, sizeof(tid));
282 tid.dev_class = SNDRV_TIMER_CLASS_GLOBAL;
283 tid.dev_sclass = SNDRV_TIMER_SCLASS_SEQUENCER;
284 tid.card = -1;
285 tid.device = SNDRV_TIMER_GLOBAL_SYSTEM;
286 err = snd_timer_open(&t, str, &tid, q->queue);
287 }
288 if (err < 0) {
289 snd_printk(KERN_ERR "seq fatal error: cannot create timer (%i)\n", err);
290 return err;
291 }
292 }
293 t->callback = snd_seq_timer_interrupt;
294 t->callback_data = q;
295 t->flags |= SNDRV_TIMER_IFLG_AUTO;
296 tmr->timeri = t;
297 return 0;
298}
299
300int snd_seq_timer_close(queue_t *q)
301{
302 seq_timer_t *tmr;
303
304 tmr = q->timer;
305 snd_assert(tmr != NULL, return -EINVAL);
306 if (tmr->timeri) {
307 snd_timer_stop(tmr->timeri);
308 snd_timer_close(tmr->timeri);
309 tmr->timeri = NULL;
310 }
311 return 0;
312}
313
314int snd_seq_timer_stop(seq_timer_t * tmr)
315{
316 if (! tmr->timeri)
317 return -EINVAL;
318 if (!tmr->running)
319 return 0;
320 tmr->running = 0;
321 snd_timer_pause(tmr->timeri);
322 return 0;
323}
324
325static int initialize_timer(seq_timer_t *tmr)
326{
327 snd_timer_t *t;
328 t = tmr->timeri->timer;
329 snd_assert(t, return -EINVAL);
330
331 tmr->ticks = 1;
332 if (tmr->preferred_resolution &&
333 ! (t->hw.flags & SNDRV_TIMER_HW_SLAVE)) {
334 unsigned long r = t->hw.resolution;
335 if (! r && t->hw.c_resolution)
336 r = t->hw.c_resolution(t);
337 if (r) {
338 tmr->ticks = (unsigned int)(1000000000uL / (r * tmr->preferred_resolution));
339 if (! tmr->ticks)
340 tmr->ticks = 1;
341 }
342 }
343 tmr->initialized = 1;
344 return 0;
345}
346
347int snd_seq_timer_start(seq_timer_t * tmr)
348{
349 if (! tmr->timeri)
350 return -EINVAL;
351 if (tmr->running)
352 snd_seq_timer_stop(tmr);
353 snd_seq_timer_reset(tmr);
354 if (initialize_timer(tmr) < 0)
355 return -EINVAL;
356 snd_timer_start(tmr->timeri, tmr->ticks);
357 tmr->running = 1;
358 do_gettimeofday(&tmr->last_update);
359 return 0;
360}
361
362int snd_seq_timer_continue(seq_timer_t * tmr)
363{
364 if (! tmr->timeri)
365 return -EINVAL;
366 if (tmr->running)
367 return -EBUSY;
368 if (! tmr->initialized) {
369 snd_seq_timer_reset(tmr);
370 if (initialize_timer(tmr) < 0)
371 return -EINVAL;
372 }
373 snd_timer_start(tmr->timeri, tmr->ticks);
374 tmr->running = 1;
375 do_gettimeofday(&tmr->last_update);
376 return 0;
377}
378
379/* return current 'real' time. use timeofday() to get better granularity. */
380snd_seq_real_time_t snd_seq_timer_get_cur_time(seq_timer_t *tmr)
381{
382 snd_seq_real_time_t cur_time;
383
384 cur_time = tmr->cur_time;
385 if (tmr->running) {
386 struct timeval tm;
387 int usec;
388 do_gettimeofday(&tm);
389 usec = (int)(tm.tv_usec - tmr->last_update.tv_usec);
390 if (usec < 0) {
391 cur_time.tv_nsec += (1000000 + usec) * 1000;
392 cur_time.tv_sec += tm.tv_sec - tmr->last_update.tv_sec - 1;
393 } else {
394 cur_time.tv_nsec += usec * 1000;
395 cur_time.tv_sec += tm.tv_sec - tmr->last_update.tv_sec;
396 }
397 snd_seq_sanity_real_time(&cur_time);
398 }
399
400 return cur_time;
401}
402
403/* TODO: use interpolation on tick queue (will only be useful for very
404 high PPQ values) */
405snd_seq_tick_time_t snd_seq_timer_get_cur_tick(seq_timer_t *tmr)
406{
407 return tmr->tick.cur_tick;
408}
409
410
411/* exported to seq_info.c */
412void snd_seq_info_timer_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer)
413{
414 int idx;
415 queue_t *q;
416 seq_timer_t *tmr;
417 snd_timer_instance_t *ti;
418 unsigned long resolution;
419
420 for (idx = 0; idx < SNDRV_SEQ_MAX_QUEUES; idx++) {
421 q = queueptr(idx);
422 if (q == NULL)
423 continue;
424 if ((tmr = q->timer) == NULL ||
425 (ti = tmr->timeri) == NULL) {
426 queuefree(q);
427 continue;
428 }
429 snd_iprintf(buffer, "Timer for queue %i : %s\n", q->queue, ti->timer->name);
430 resolution = snd_timer_resolution(ti) * tmr->ticks;
431 snd_iprintf(buffer, " Period time : %lu.%09lu\n", resolution / 1000000000, resolution % 1000000000);
432 snd_iprintf(buffer, " Skew : %u / %u\n", tmr->skew, tmr->skew_base);
433 queuefree(q);
434 }
435}
diff --git a/sound/core/seq/seq_timer.h b/sound/core/seq/seq_timer.h
new file mode 100644
index 000000000000..4c0872df8931
--- /dev/null
+++ b/sound/core/seq/seq_timer.h
@@ -0,0 +1,141 @@
1/*
2 * ALSA sequencer Timer
3 * Copyright (c) 1998-1999 by Frank van de Pol <fvdpol@coil.demon.nl>
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#ifndef __SND_SEQ_TIMER_H
22#define __SND_SEQ_TIMER_H
23
24#include <sound/timer.h>
25#include <sound/seq_kernel.h>
26
27typedef struct {
28 snd_seq_tick_time_t cur_tick; /* current tick */
29 unsigned long resolution; /* time per tick in nsec */
30 unsigned long fraction; /* current time per tick in nsec */
31} seq_timer_tick_t;
32
33typedef struct {
34 /* ... tempo / offset / running state */
35
36 unsigned int running:1, /* running state of queue */
37 initialized:1; /* timer is initialized */
38
39 unsigned int tempo; /* current tempo, us/tick */
40 int ppq; /* time resolution, ticks/quarter */
41
42 snd_seq_real_time_t cur_time; /* current time */
43 seq_timer_tick_t tick; /* current tick */
44 int tick_updated;
45
46 int type; /* timer type */
47 snd_timer_id_t alsa_id; /* ALSA's timer ID */
48 snd_timer_instance_t *timeri; /* timer instance */
49 unsigned int ticks;
50 unsigned long preferred_resolution; /* timer resolution, ticks/sec */
51
52 unsigned int skew;
53 unsigned int skew_base;
54
55 struct timeval last_update; /* time of last clock update, used for interpolation */
56
57 spinlock_t lock;
58} seq_timer_t;
59
60
61/* create new timer (constructor) */
62extern seq_timer_t *snd_seq_timer_new(void);
63
64/* delete timer (destructor) */
65extern void snd_seq_timer_delete(seq_timer_t **tmr);
66
67void snd_seq_timer_set_tick_resolution(seq_timer_tick_t *tick, int tempo, int ppq, int nticks);
68
69/* */
70static inline void snd_seq_timer_update_tick(seq_timer_tick_t *tick, unsigned long resolution)
71{
72 if (tick->resolution > 0) {
73 tick->fraction += resolution;
74 tick->cur_tick += (unsigned int)(tick->fraction / tick->resolution);
75 tick->fraction %= tick->resolution;
76 }
77}
78
79
80/* compare timestamp between events */
81/* return 1 if a >= b; otherwise return 0 */
82static inline int snd_seq_compare_tick_time(snd_seq_tick_time_t *a, snd_seq_tick_time_t *b)
83{
84 /* compare ticks */
85 return (*a >= *b);
86}
87
88static inline int snd_seq_compare_real_time(snd_seq_real_time_t *a, snd_seq_real_time_t *b)
89{
90 /* compare real time */
91 if (a->tv_sec > b->tv_sec)
92 return 1;
93 if ((a->tv_sec == b->tv_sec) && (a->tv_nsec >= b->tv_nsec))
94 return 1;
95 return 0;
96}
97
98
99static inline void snd_seq_sanity_real_time(snd_seq_real_time_t *tm)
100{
101 while (tm->tv_nsec >= 1000000000) {
102 /* roll-over */
103 tm->tv_nsec -= 1000000000;
104 tm->tv_sec++;
105 }
106}
107
108
109/* increment timestamp */
110static inline void snd_seq_inc_real_time(snd_seq_real_time_t *tm, snd_seq_real_time_t *inc)
111{
112 tm->tv_sec += inc->tv_sec;
113 tm->tv_nsec += inc->tv_nsec;
114 snd_seq_sanity_real_time(tm);
115}
116
117static inline void snd_seq_inc_time_nsec(snd_seq_real_time_t *tm, unsigned long nsec)
118{
119 tm->tv_nsec += nsec;
120 snd_seq_sanity_real_time(tm);
121}
122
123/* called by timer isr */
124int snd_seq_timer_open(queue_t *q);
125int snd_seq_timer_close(queue_t *q);
126int snd_seq_timer_midi_open(queue_t *q);
127int snd_seq_timer_midi_close(queue_t *q);
128void snd_seq_timer_defaults(seq_timer_t *tmr);
129void snd_seq_timer_reset(seq_timer_t *tmr);
130int snd_seq_timer_stop(seq_timer_t *tmr);
131int snd_seq_timer_start(seq_timer_t *tmr);
132int snd_seq_timer_continue(seq_timer_t *tmr);
133int snd_seq_timer_set_tempo(seq_timer_t *tmr, int tempo);
134int snd_seq_timer_set_ppq(seq_timer_t *tmr, int ppq);
135int snd_seq_timer_set_position_tick(seq_timer_t *tmr, snd_seq_tick_time_t position);
136int snd_seq_timer_set_position_time(seq_timer_t *tmr, snd_seq_real_time_t position);
137int snd_seq_timer_set_skew(seq_timer_t *tmr, unsigned int skew, unsigned int base);
138snd_seq_real_time_t snd_seq_timer_get_cur_time(seq_timer_t *tmr);
139snd_seq_tick_time_t snd_seq_timer_get_cur_tick(seq_timer_t *tmr);
140
141#endif
diff --git a/sound/core/seq/seq_virmidi.c b/sound/core/seq/seq_virmidi.c
new file mode 100644
index 000000000000..6b4e630ace54
--- /dev/null
+++ b/sound/core/seq/seq_virmidi.c
@@ -0,0 +1,551 @@
1/*
2 * Virtual Raw MIDI client on Sequencer
3 *
4 * Copyright (c) 2000 by Takashi Iwai <tiwai@suse.de>,
5 * Jaroslav Kysela <perex@perex.cz>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (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 General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 */
22
23/*
24 * Virtual Raw MIDI client
25 *
26 * The virtual rawmidi client is a sequencer client which associate
27 * a rawmidi device file. The created rawmidi device file can be
28 * accessed as a normal raw midi, but its MIDI source and destination
29 * are arbitrary. For example, a user-client software synth connected
30 * to this port can be used as a normal midi device as well.
31 *
32 * The virtual rawmidi device accepts also multiple opens. Each file
33 * has its own input buffer, so that no conflict would occur. The drain
34 * of input/output buffer acts only to the local buffer.
35 *
36 */
37
38#include <sound/driver.h>
39#include <linux/init.h>
40#include <linux/wait.h>
41#include <linux/sched.h>
42#include <linux/slab.h>
43#include <sound/core.h>
44#include <sound/rawmidi.h>
45#include <sound/info.h>
46#include <sound/control.h>
47#include <sound/minors.h>
48#include <sound/seq_kernel.h>
49#include <sound/seq_midi_event.h>
50#include <sound/seq_virmidi.h>
51
52MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");
53MODULE_DESCRIPTION("Virtual Raw MIDI client on Sequencer");
54MODULE_LICENSE("GPL");
55
56/*
57 * initialize an event record
58 */
59static void snd_virmidi_init_event(snd_virmidi_t *vmidi, snd_seq_event_t *ev)
60{
61 memset(ev, 0, sizeof(*ev));
62 ev->source.port = vmidi->port;
63 switch (vmidi->seq_mode) {
64 case SNDRV_VIRMIDI_SEQ_DISPATCH:
65 ev->dest.client = SNDRV_SEQ_ADDRESS_SUBSCRIBERS;
66 break;
67 case SNDRV_VIRMIDI_SEQ_ATTACH:
68 /* FIXME: source and destination are same - not good.. */
69 ev->dest.client = vmidi->client;
70 ev->dest.port = vmidi->port;
71 break;
72 }
73 ev->type = SNDRV_SEQ_EVENT_NONE;
74}
75
76/*
77 * decode input event and put to read buffer of each opened file
78 */
79static int snd_virmidi_dev_receive_event(snd_virmidi_dev_t *rdev, snd_seq_event_t *ev)
80{
81 snd_virmidi_t *vmidi;
82 struct list_head *list;
83 unsigned char msg[4];
84 int len;
85
86 read_lock(&rdev->filelist_lock);
87 list_for_each(list, &rdev->filelist) {
88 vmidi = list_entry(list, snd_virmidi_t, list);
89 if (!vmidi->trigger)
90 continue;
91 if (ev->type == SNDRV_SEQ_EVENT_SYSEX) {
92 if ((ev->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) != SNDRV_SEQ_EVENT_LENGTH_VARIABLE)
93 continue;
94 snd_seq_dump_var_event(ev, (snd_seq_dump_func_t)snd_rawmidi_receive, vmidi->substream);
95 } else {
96 len = snd_midi_event_decode(vmidi->parser, msg, sizeof(msg), ev);
97 if (len > 0)
98 snd_rawmidi_receive(vmidi->substream, msg, len);
99 }
100 }
101 read_unlock(&rdev->filelist_lock);
102
103 return 0;
104}
105
106/*
107 * receive an event from the remote virmidi port
108 *
109 * for rawmidi inputs, you can call this function from the event
110 * handler of a remote port which is attached to the virmidi via
111 * SNDRV_VIRMIDI_SEQ_ATTACH.
112 */
113/* exported */
114int snd_virmidi_receive(snd_rawmidi_t *rmidi, snd_seq_event_t *ev)
115{
116 snd_virmidi_dev_t *rdev;
117
118 rdev = rmidi->private_data;
119 return snd_virmidi_dev_receive_event(rdev, ev);
120}
121
122/*
123 * event handler of virmidi port
124 */
125static int snd_virmidi_event_input(snd_seq_event_t *ev, int direct,
126 void *private_data, int atomic, int hop)
127{
128 snd_virmidi_dev_t *rdev;
129
130 rdev = private_data;
131 if (!(rdev->flags & SNDRV_VIRMIDI_USE))
132 return 0; /* ignored */
133 return snd_virmidi_dev_receive_event(rdev, ev);
134}
135
136/*
137 * trigger rawmidi stream for input
138 */
139static void snd_virmidi_input_trigger(snd_rawmidi_substream_t * substream, int up)
140{
141 snd_virmidi_t *vmidi = substream->runtime->private_data;
142
143 if (up) {
144 vmidi->trigger = 1;
145 } else {
146 vmidi->trigger = 0;
147 }
148}
149
150/*
151 * trigger rawmidi stream for output
152 */
153static void snd_virmidi_output_trigger(snd_rawmidi_substream_t * substream, int up)
154{
155 snd_virmidi_t *vmidi = substream->runtime->private_data;
156 int count, res;
157 unsigned char buf[32], *pbuf;
158
159 if (up) {
160 vmidi->trigger = 1;
161 if (vmidi->seq_mode == SNDRV_VIRMIDI_SEQ_DISPATCH &&
162 !(vmidi->rdev->flags & SNDRV_VIRMIDI_SUBSCRIBE)) {
163 snd_rawmidi_transmit_ack(substream, substream->runtime->buffer_size - substream->runtime->avail);
164 return; /* ignored */
165 }
166 if (vmidi->event.type != SNDRV_SEQ_EVENT_NONE) {
167 if (snd_seq_kernel_client_dispatch(vmidi->client, &vmidi->event, 0, 0) < 0)
168 return;
169 vmidi->event.type = SNDRV_SEQ_EVENT_NONE;
170 }
171 while (1) {
172 count = snd_rawmidi_transmit_peek(substream, buf, sizeof(buf));
173 if (count <= 0)
174 break;
175 pbuf = buf;
176 while (count > 0) {
177 res = snd_midi_event_encode(vmidi->parser, pbuf, count, &vmidi->event);
178 if (res < 0) {
179 snd_midi_event_reset_encode(vmidi->parser);
180 continue;
181 }
182 snd_rawmidi_transmit_ack(substream, res);
183 pbuf += res;
184 count -= res;
185 if (vmidi->event.type != SNDRV_SEQ_EVENT_NONE) {
186 if (snd_seq_kernel_client_dispatch(vmidi->client, &vmidi->event, 0, 0) < 0)
187 return;
188 vmidi->event.type = SNDRV_SEQ_EVENT_NONE;
189 }
190 }
191 }
192 } else {
193 vmidi->trigger = 0;
194 }
195}
196
197/*
198 * open rawmidi handle for input
199 */
200static int snd_virmidi_input_open(snd_rawmidi_substream_t * substream)
201{
202 snd_virmidi_dev_t *rdev = substream->rmidi->private_data;
203 snd_rawmidi_runtime_t *runtime = substream->runtime;
204 snd_virmidi_t *vmidi;
205 unsigned long flags;
206
207 vmidi = kcalloc(1, sizeof(*vmidi), GFP_KERNEL);
208 if (vmidi == NULL)
209 return -ENOMEM;
210 vmidi->substream = substream;
211 if (snd_midi_event_new(0, &vmidi->parser) < 0) {
212 kfree(vmidi);
213 return -ENOMEM;
214 }
215 vmidi->seq_mode = rdev->seq_mode;
216 vmidi->client = rdev->client;
217 vmidi->port = rdev->port;
218 runtime->private_data = vmidi;
219 write_lock_irqsave(&rdev->filelist_lock, flags);
220 list_add_tail(&vmidi->list, &rdev->filelist);
221 write_unlock_irqrestore(&rdev->filelist_lock, flags);
222 vmidi->rdev = rdev;
223 return 0;
224}
225
226/*
227 * open rawmidi handle for output
228 */
229static int snd_virmidi_output_open(snd_rawmidi_substream_t * substream)
230{
231 snd_virmidi_dev_t *rdev = substream->rmidi->private_data;
232 snd_rawmidi_runtime_t *runtime = substream->runtime;
233 snd_virmidi_t *vmidi;
234
235 vmidi = kcalloc(1, sizeof(*vmidi), GFP_KERNEL);
236 if (vmidi == NULL)
237 return -ENOMEM;
238 vmidi->substream = substream;
239 if (snd_midi_event_new(MAX_MIDI_EVENT_BUF, &vmidi->parser) < 0) {
240 kfree(vmidi);
241 return -ENOMEM;
242 }
243 vmidi->seq_mode = rdev->seq_mode;
244 vmidi->client = rdev->client;
245 vmidi->port = rdev->port;
246 snd_virmidi_init_event(vmidi, &vmidi->event);
247 vmidi->rdev = rdev;
248 runtime->private_data = vmidi;
249 return 0;
250}
251
252/*
253 * close rawmidi handle for input
254 */
255static int snd_virmidi_input_close(snd_rawmidi_substream_t * substream)
256{
257 snd_virmidi_t *vmidi = substream->runtime->private_data;
258 snd_midi_event_free(vmidi->parser);
259 list_del(&vmidi->list);
260 substream->runtime->private_data = NULL;
261 kfree(vmidi);
262 return 0;
263}
264
265/*
266 * close rawmidi handle for output
267 */
268static int snd_virmidi_output_close(snd_rawmidi_substream_t * substream)
269{
270 snd_virmidi_t *vmidi = substream->runtime->private_data;
271 snd_midi_event_free(vmidi->parser);
272 substream->runtime->private_data = NULL;
273 kfree(vmidi);
274 return 0;
275}
276
277/*
278 * subscribe callback - allow output to rawmidi device
279 */
280static int snd_virmidi_subscribe(void *private_data, snd_seq_port_subscribe_t *info)
281{
282 snd_virmidi_dev_t *rdev;
283
284 rdev = private_data;
285 if (!try_module_get(rdev->card->module))
286 return -EFAULT;
287 rdev->flags |= SNDRV_VIRMIDI_SUBSCRIBE;
288 return 0;
289}
290
291/*
292 * unsubscribe callback - disallow output to rawmidi device
293 */
294static int snd_virmidi_unsubscribe(void *private_data, snd_seq_port_subscribe_t *info)
295{
296 snd_virmidi_dev_t *rdev;
297
298 rdev = private_data;
299 rdev->flags &= ~SNDRV_VIRMIDI_SUBSCRIBE;
300 module_put(rdev->card->module);
301 return 0;
302}
303
304
305/*
306 * use callback - allow input to rawmidi device
307 */
308static int snd_virmidi_use(void *private_data, snd_seq_port_subscribe_t *info)
309{
310 snd_virmidi_dev_t *rdev;
311
312 rdev = private_data;
313 if (!try_module_get(rdev->card->module))
314 return -EFAULT;
315 rdev->flags |= SNDRV_VIRMIDI_USE;
316 return 0;
317}
318
319/*
320 * unuse callback - disallow input to rawmidi device
321 */
322static int snd_virmidi_unuse(void *private_data, snd_seq_port_subscribe_t *info)
323{
324 snd_virmidi_dev_t *rdev;
325
326 rdev = private_data;
327 rdev->flags &= ~SNDRV_VIRMIDI_USE;
328 module_put(rdev->card->module);
329 return 0;
330}
331
332
333/*
334 * Register functions
335 */
336
337static snd_rawmidi_ops_t snd_virmidi_input_ops = {
338 .open = snd_virmidi_input_open,
339 .close = snd_virmidi_input_close,
340 .trigger = snd_virmidi_input_trigger,
341};
342
343static snd_rawmidi_ops_t snd_virmidi_output_ops = {
344 .open = snd_virmidi_output_open,
345 .close = snd_virmidi_output_close,
346 .trigger = snd_virmidi_output_trigger,
347};
348
349/*
350 * create a sequencer client and a port
351 */
352static int snd_virmidi_dev_attach_seq(snd_virmidi_dev_t *rdev)
353{
354 int client;
355 snd_seq_client_callback_t callbacks;
356 snd_seq_port_callback_t pcallbacks;
357 snd_seq_client_info_t *info;
358 snd_seq_port_info_t *pinfo;
359 int err;
360
361 if (rdev->client >= 0)
362 return 0;
363
364 info = kmalloc(sizeof(*info), GFP_KERNEL);
365 pinfo = kmalloc(sizeof(*pinfo), GFP_KERNEL);
366 if (! info || ! pinfo) {
367 err = -ENOMEM;
368 goto __error;
369 }
370
371 memset(&callbacks, 0, sizeof(callbacks));
372 callbacks.private_data = rdev;
373 callbacks.allow_input = 1;
374 callbacks.allow_output = 1;
375 client = snd_seq_create_kernel_client(rdev->card, rdev->device, &callbacks);
376 if (client < 0) {
377 err = client;
378 goto __error;
379 }
380 rdev->client = client;
381
382 /* set client name */
383 memset(info, 0, sizeof(*info));
384 info->client = client;
385 info->type = KERNEL_CLIENT;
386 sprintf(info->name, "%s %d-%d", rdev->rmidi->name, rdev->card->number, rdev->device);
387 snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_SET_CLIENT_INFO, &info);
388
389 /* create a port */
390 memset(pinfo, 0, sizeof(*pinfo));
391 pinfo->addr.client = client;
392 sprintf(pinfo->name, "VirMIDI %d-%d", rdev->card->number, rdev->device);
393 /* set all capabilities */
394 pinfo->capability |= SNDRV_SEQ_PORT_CAP_WRITE | SNDRV_SEQ_PORT_CAP_SYNC_WRITE | SNDRV_SEQ_PORT_CAP_SUBS_WRITE;
395 pinfo->capability |= SNDRV_SEQ_PORT_CAP_READ | SNDRV_SEQ_PORT_CAP_SYNC_READ | SNDRV_SEQ_PORT_CAP_SUBS_READ;
396 pinfo->capability |= SNDRV_SEQ_PORT_CAP_DUPLEX;
397 pinfo->type = SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC;
398 pinfo->midi_channels = 16;
399 memset(&pcallbacks, 0, sizeof(pcallbacks));
400 pcallbacks.owner = THIS_MODULE;
401 pcallbacks.private_data = rdev;
402 pcallbacks.subscribe = snd_virmidi_subscribe;
403 pcallbacks.unsubscribe = snd_virmidi_unsubscribe;
404 pcallbacks.use = snd_virmidi_use;
405 pcallbacks.unuse = snd_virmidi_unuse;
406 pcallbacks.event_input = snd_virmidi_event_input;
407 pinfo->kernel = &pcallbacks;
408 err = snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_CREATE_PORT, &pinfo);
409 if (err < 0) {
410 snd_seq_delete_kernel_client(client);
411 rdev->client = -1;
412 goto __error;
413 }
414
415 rdev->port = pinfo->addr.port;
416 err = 0; /* success */
417
418 __error:
419 kfree(info);
420 kfree(pinfo);
421 return err;
422}
423
424
425/*
426 * release the sequencer client
427 */
428static void snd_virmidi_dev_detach_seq(snd_virmidi_dev_t *rdev)
429{
430 if (rdev->client >= 0) {
431 snd_seq_delete_kernel_client(rdev->client);
432 rdev->client = -1;
433 }
434}
435
436/*
437 * register the device
438 */
439static int snd_virmidi_dev_register(snd_rawmidi_t *rmidi)
440{
441 snd_virmidi_dev_t *rdev = rmidi->private_data;
442 int err;
443
444 switch (rdev->seq_mode) {
445 case SNDRV_VIRMIDI_SEQ_DISPATCH:
446 err = snd_virmidi_dev_attach_seq(rdev);
447 if (err < 0)
448 return err;
449 break;
450 case SNDRV_VIRMIDI_SEQ_ATTACH:
451 if (rdev->client == 0)
452 return -EINVAL;
453 /* should check presence of port more strictly.. */
454 break;
455 default:
456 snd_printk(KERN_ERR "seq_mode is not set: %d\n", rdev->seq_mode);
457 return -EINVAL;
458 }
459 return 0;
460}
461
462
463/*
464 * unregister the device
465 */
466static int snd_virmidi_dev_unregister(snd_rawmidi_t *rmidi)
467{
468 snd_virmidi_dev_t *rdev = rmidi->private_data;
469
470 if (rdev->seq_mode == SNDRV_VIRMIDI_SEQ_DISPATCH)
471 snd_virmidi_dev_detach_seq(rdev);
472 return 0;
473}
474
475/*
476 *
477 */
478static snd_rawmidi_global_ops_t snd_virmidi_global_ops = {
479 .dev_register = snd_virmidi_dev_register,
480 .dev_unregister = snd_virmidi_dev_unregister,
481};
482
483/*
484 * free device
485 */
486static void snd_virmidi_free(snd_rawmidi_t *rmidi)
487{
488 snd_virmidi_dev_t *rdev = rmidi->private_data;
489 kfree(rdev);
490}
491
492/*
493 * create a new device
494 *
495 */
496/* exported */
497int snd_virmidi_new(snd_card_t *card, int device, snd_rawmidi_t **rrmidi)
498{
499 snd_rawmidi_t *rmidi;
500 snd_virmidi_dev_t *rdev;
501 int err;
502
503 *rrmidi = NULL;
504 if ((err = snd_rawmidi_new(card, "VirMidi", device,
505 16, /* may be configurable */
506 16, /* may be configurable */
507 &rmidi)) < 0)
508 return err;
509 strcpy(rmidi->name, rmidi->id);
510 rdev = kcalloc(1, sizeof(*rdev), GFP_KERNEL);
511 if (rdev == NULL) {
512 snd_device_free(card, rmidi);
513 return -ENOMEM;
514 }
515 rdev->card = card;
516 rdev->rmidi = rmidi;
517 rdev->device = device;
518 rdev->client = -1;
519 rwlock_init(&rdev->filelist_lock);
520 INIT_LIST_HEAD(&rdev->filelist);
521 rdev->seq_mode = SNDRV_VIRMIDI_SEQ_DISPATCH;
522 rmidi->private_data = rdev;
523 rmidi->private_free = snd_virmidi_free;
524 rmidi->ops = &snd_virmidi_global_ops;
525 snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_virmidi_input_ops);
526 snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_virmidi_output_ops);
527 rmidi->info_flags = SNDRV_RAWMIDI_INFO_INPUT |
528 SNDRV_RAWMIDI_INFO_OUTPUT |
529 SNDRV_RAWMIDI_INFO_DUPLEX;
530 *rrmidi = rmidi;
531 return 0;
532}
533
534/*
535 * ENTRY functions
536 */
537
538static int __init alsa_virmidi_init(void)
539{
540 return 0;
541}
542
543static void __exit alsa_virmidi_exit(void)
544{
545}
546
547module_init(alsa_virmidi_init)
548module_exit(alsa_virmidi_exit)
549
550EXPORT_SYMBOL(snd_virmidi_new);
551EXPORT_SYMBOL(snd_virmidi_receive);