diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /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')
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 | |||
6 | obj-$(CONFIG_SND) += instr/ | ||
7 | ifeq ($(CONFIG_SND_SEQUENCER_OSS),y) | ||
8 | obj-$(CONFIG_SND_SEQUENCER) += oss/ | ||
9 | endif | ||
10 | |||
11 | snd-seq-device-objs := seq_device.o | ||
12 | snd-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 | ||
15 | snd-seq-midi-objs := seq_midi.o | ||
16 | snd-seq-midi-emul-objs := seq_midi_emul.o | ||
17 | snd-seq-midi-event-objs := seq_midi_event.o | ||
18 | snd-seq-instr-objs := seq_instr.o | ||
19 | snd-seq-dummy-objs := seq_dummy.o | ||
20 | snd-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 | # | ||
28 | sequencer = $(if $(subst y,,$(CONFIG_SND_SEQUENCER)),$(if $(1),m),$(if $(CONFIG_SND_SEQUENCER),$(1))) | ||
29 | |||
30 | obj-$(CONFIG_SND_SEQUENCER) += snd-seq.o snd-seq-device.o | ||
31 | ifeq ($(CONFIG_SND_SEQUENCER_OSS),y) | ||
32 | obj-$(CONFIG_SND_SEQUENCER) += snd-seq-midi-event.o | ||
33 | endif | ||
34 | obj-$(CONFIG_SND_SEQ_DUMMY) += snd-seq-dummy.o | ||
35 | |||
36 | # Toplevel Module Dependency | ||
37 | obj-$(CONFIG_SND_VIRMIDI) += snd-seq-virmidi.o snd-seq-midi-event.o | ||
38 | obj-$(call sequencer,$(CONFIG_SND_RAWMIDI)) += snd-seq-midi.o snd-seq-midi-event.o | ||
39 | obj-$(call sequencer,$(CONFIG_SND_OPL3_LIB)) += snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o | ||
40 | obj-$(call sequencer,$(CONFIG_SND_OPL4_LIB)) += snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o | ||
41 | obj-$(call sequencer,$(CONFIG_SND_GUS_SYNTH)) += snd-seq-instr.o | ||
42 | obj-$(call sequencer,$(CONFIG_SND_SBAWE)) += snd-seq-midi-emul.o snd-seq-virmidi.o | ||
43 | obj-$(call sequencer,$(CONFIG_SND_EMU10K1)) += snd-seq-midi-emul.o snd-seq-virmidi.o | ||
44 | obj-$(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 | |||
6 | snd-ainstr-fm-objs := ainstr_fm.o | ||
7 | snd-ainstr-simple-objs := ainstr_simple.o | ||
8 | snd-ainstr-gf1-objs := ainstr_gf1.o | ||
9 | snd-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 | # | ||
17 | sequencer = $(if $(subst y,,$(CONFIG_SND_SEQUENCER)),$(if $(1),m),$(if $(CONFIG_SND_SEQUENCER),$(1))) | ||
18 | |||
19 | # Toplevel Module Dependency | ||
20 | obj-$(call sequencer,$(CONFIG_SND_OPL3_LIB)) += snd-ainstr-fm.o | ||
21 | obj-$(call sequencer,$(CONFIG_SND_OPL4_LIB)) += snd-ainstr-fm.o | ||
22 | obj-$(call sequencer,$(CONFIG_SND_GUS_SYNTH)) += snd-ainstr-gf1.o snd-ainstr-simple.o snd-ainstr-iw.o | ||
23 | obj-$(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 | |||
29 | MODULE_AUTHOR("Uros Bizjak <uros@kss-loka.si>"); | ||
30 | MODULE_DESCRIPTION("Advanced Linux Sound Architecture FM Instrument support."); | ||
31 | MODULE_LICENSE("GPL"); | ||
32 | |||
33 | static 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 | |||
75 | static 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 | |||
117 | static 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 | |||
124 | int 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 | |||
144 | static int __init alsa_ainstr_fm_init(void) | ||
145 | { | ||
146 | return 0; | ||
147 | } | ||
148 | |||
149 | static void __exit alsa_ainstr_fm_exit(void) | ||
150 | { | ||
151 | } | ||
152 | |||
153 | module_init(alsa_ainstr_fm_init) | ||
154 | module_exit(alsa_ainstr_fm_exit) | ||
155 | |||
156 | EXPORT_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 | |||
30 | MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>"); | ||
31 | MODULE_DESCRIPTION("Advanced Linux Sound Architecture GF1 (GUS) Patch support."); | ||
32 | MODULE_LICENSE("GPL"); | ||
33 | |||
34 | static 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 | |||
45 | static 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 | |||
118 | static 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 | |||
127 | static 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 | |||
139 | static 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 | |||
190 | static 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 | |||
252 | static 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 | |||
284 | static 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 | |||
302 | static 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 | |||
314 | static 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 | |||
324 | int 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 | |||
346 | static int __init alsa_ainstr_gf1_init(void) | ||
347 | { | ||
348 | return 0; | ||
349 | } | ||
350 | |||
351 | static void __exit alsa_ainstr_gf1_exit(void) | ||
352 | { | ||
353 | } | ||
354 | |||
355 | module_init(alsa_ainstr_gf1_init) | ||
356 | module_exit(alsa_ainstr_gf1_exit) | ||
357 | |||
358 | EXPORT_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 | |||
30 | MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>"); | ||
31 | MODULE_DESCRIPTION("Advanced Linux Sound Architecture IWFFFF support."); | ||
32 | MODULE_LICENSE("GPL"); | ||
33 | |||
34 | static 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 | |||
45 | static 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 | |||
55 | static 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 | |||
123 | static 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 | |||
188 | static 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 | |||
200 | static 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 | |||
209 | static 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 | |||
228 | static 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 | |||
336 | static 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 | |||
346 | static 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 | |||
393 | static 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 | |||
448 | static 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 | |||
521 | static 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 | |||
533 | static 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 | |||
546 | static 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 | |||
566 | static 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 | |||
578 | static 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 | |||
588 | int 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 | |||
610 | static int __init alsa_ainstr_iw_init(void) | ||
611 | { | ||
612 | return 0; | ||
613 | } | ||
614 | |||
615 | static void __exit alsa_ainstr_iw_exit(void) | ||
616 | { | ||
617 | } | ||
618 | |||
619 | module_init(alsa_ainstr_iw_init) | ||
620 | module_exit(alsa_ainstr_iw_exit) | ||
621 | |||
622 | EXPORT_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 | |||
30 | MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>"); | ||
31 | MODULE_DESCRIPTION("Advanced Linux Sound Architecture Simple Instrument support."); | ||
32 | MODULE_LICENSE("GPL"); | ||
33 | |||
34 | static 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 | |||
45 | static 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 | |||
53 | static 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 | |||
102 | static 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 | |||
149 | static 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 | |||
159 | static 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 | |||
171 | static 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 | |||
181 | int 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 | |||
203 | static int __init alsa_ainstr_simple_init(void) | ||
204 | { | ||
205 | return 0; | ||
206 | } | ||
207 | |||
208 | static void __exit alsa_ainstr_simple_exit(void) | ||
209 | { | ||
210 | } | ||
211 | |||
212 | module_init(alsa_ainstr_simple_init) | ||
213 | module_exit(alsa_ainstr_simple_exit) | ||
214 | |||
215 | EXPORT_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 | |||
6 | snd-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 | |||
10 | obj-$(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 | */ | ||
36 | MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>"); | ||
37 | MODULE_DESCRIPTION("OSS-compatible sequencer module"); | ||
38 | MODULE_LICENSE("GPL"); | ||
39 | /* Takashi says this is really only for sound-service-0-, but this is OK. */ | ||
40 | MODULE_ALIAS_SNDRV_MINOR(SNDRV_MINOR_OSS_SEQUENCER); | ||
41 | MODULE_ALIAS_SNDRV_MINOR(SNDRV_MINOR_OSS_MUSIC); | ||
42 | |||
43 | #ifdef SNDRV_SEQ_OSS_DEBUG | ||
44 | module_param(seq_oss_debug, int, 0644); | ||
45 | MODULE_PARM_DESC(seq_oss_debug, "debug option"); | ||
46 | int seq_oss_debug = 0; | ||
47 | #endif | ||
48 | |||
49 | |||
50 | /* | ||
51 | * prototypes | ||
52 | */ | ||
53 | static int register_device(void); | ||
54 | static void unregister_device(void); | ||
55 | static int register_proc(void); | ||
56 | static void unregister_proc(void); | ||
57 | |||
58 | static int odev_open(struct inode *inode, struct file *file); | ||
59 | static int odev_release(struct inode *inode, struct file *file); | ||
60 | static ssize_t odev_read(struct file *file, char __user *buf, size_t count, loff_t *offset); | ||
61 | static ssize_t odev_write(struct file *file, const char __user *buf, size_t count, loff_t *offset); | ||
62 | static long odev_ioctl(struct file *file, unsigned int cmd, unsigned long arg); | ||
63 | static unsigned int odev_poll(struct file *file, poll_table * wait); | ||
64 | #ifdef CONFIG_PROC_FS | ||
65 | static void info_read(snd_info_entry_t *entry, snd_info_buffer_t *buf); | ||
66 | #endif | ||
67 | |||
68 | |||
69 | /* | ||
70 | * module interface | ||
71 | */ | ||
72 | |||
73 | static 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 | |||
110 | static 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 | |||
118 | module_init(alsa_seq_oss_init) | ||
119 | module_exit(alsa_seq_oss_exit) | ||
120 | |||
121 | /* | ||
122 | * ALSA minor device interface | ||
123 | */ | ||
124 | |||
125 | static DECLARE_MUTEX(register_mutex); | ||
126 | |||
127 | static int | ||
128 | odev_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(®ister_mutex); | ||
138 | rc = snd_seq_oss_open(file, level); | ||
139 | up(®ister_mutex); | ||
140 | |||
141 | return rc; | ||
142 | } | ||
143 | |||
144 | static int | ||
145 | odev_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(®ister_mutex); | ||
155 | snd_seq_oss_release(dp); | ||
156 | up(®ister_mutex); | ||
157 | |||
158 | return 0; | ||
159 | } | ||
160 | |||
161 | static ssize_t | ||
162 | odev_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 | |||
171 | static ssize_t | ||
172 | odev_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 | |||
180 | static long | ||
181 | odev_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 | |||
195 | static unsigned int | ||
196 | odev_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 | |||
208 | static 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 | |||
220 | static snd_minor_t seq_oss_reg = { | ||
221 | .comment = "sequencer", | ||
222 | .f_ops = &seq_oss_f_ops, | ||
223 | }; | ||
224 | |||
225 | static int __init | ||
226 | register_device(void) | ||
227 | { | ||
228 | int rc; | ||
229 | |||
230 | down(®ister_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(®ister_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(®ister_mutex); | ||
246 | return rc; | ||
247 | } | ||
248 | debug_printk(("device registered\n")); | ||
249 | up(®ister_mutex); | ||
250 | return 0; | ||
251 | } | ||
252 | |||
253 | static void | ||
254 | unregister_device(void) | ||
255 | { | ||
256 | down(®ister_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(®ister_mutex); | ||
263 | } | ||
264 | |||
265 | /* | ||
266 | * /proc interface | ||
267 | */ | ||
268 | |||
269 | #ifdef CONFIG_PROC_FS | ||
270 | |||
271 | static snd_info_entry_t *info_entry; | ||
272 | |||
273 | static void | ||
274 | info_read(snd_info_entry_t *entry, snd_info_buffer_t *buf) | ||
275 | { | ||
276 | down(®ister_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(®ister_mutex); | ||
282 | } | ||
283 | |||
284 | #endif /* CONFIG_PROC_FS */ | ||
285 | |||
286 | static int __init | ||
287 | register_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 | |||
309 | static void | ||
310 | unregister_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 | |||
58 | typedef struct seq_oss_devinfo_t seq_oss_devinfo_t; | ||
59 | typedef struct seq_oss_writeq_t seq_oss_writeq_t; | ||
60 | typedef struct seq_oss_readq_t seq_oss_readq_t; | ||
61 | typedef struct seq_oss_timer_t seq_oss_timer_t; | ||
62 | typedef struct seq_oss_synthinfo_t seq_oss_synthinfo_t; | ||
63 | typedef struct seq_oss_synth_sysex_t seq_oss_synth_sysex_t; | ||
64 | typedef struct seq_oss_chinfo_t seq_oss_chinfo_t; | ||
65 | typedef unsigned int reltime_t; | ||
66 | typedef unsigned int abstime_t; | ||
67 | typedef union evrec_t evrec_t; | ||
68 | |||
69 | |||
70 | /* | ||
71 | * synthesizer channel information | ||
72 | */ | ||
73 | struct seq_oss_chinfo_t { | ||
74 | int note, vel; | ||
75 | }; | ||
76 | |||
77 | /* | ||
78 | * synthesizer information | ||
79 | */ | ||
80 | struct 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 | |||
95 | struct 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 */ | ||
131 | int snd_seq_oss_create_client(void); | ||
132 | int snd_seq_oss_delete_client(void); | ||
133 | |||
134 | /* device file interface */ | ||
135 | int snd_seq_oss_open(struct file *file, int level); | ||
136 | void snd_seq_oss_release(seq_oss_devinfo_t *dp); | ||
137 | int snd_seq_oss_ioctl(seq_oss_devinfo_t *dp, unsigned int cmd, unsigned long arg); | ||
138 | int snd_seq_oss_read(seq_oss_devinfo_t *dev, char __user *buf, int count); | ||
139 | int snd_seq_oss_write(seq_oss_devinfo_t *dp, const char __user *buf, int count, struct file *opt); | ||
140 | unsigned int snd_seq_oss_poll(seq_oss_devinfo_t *dp, struct file *file, poll_table * wait); | ||
141 | |||
142 | void snd_seq_oss_reset(seq_oss_devinfo_t *dp); | ||
143 | void snd_seq_oss_drain_write(seq_oss_devinfo_t *dp); | ||
144 | |||
145 | /* */ | ||
146 | void snd_seq_oss_process_queue(seq_oss_devinfo_t *dp, abstime_t time); | ||
147 | |||
148 | |||
149 | /* proc interface */ | ||
150 | void snd_seq_oss_system_info_read(snd_info_buffer_t *buf); | ||
151 | void snd_seq_oss_midi_info_read(snd_info_buffer_t *buf); | ||
152 | void snd_seq_oss_synth_info_read(snd_info_buffer_t *buf); | ||
153 | void 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 */ | ||
161 | inline static int | ||
162 | snd_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 */ | ||
168 | inline static int | ||
169 | snd_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 */ | ||
175 | inline static void | ||
176 | snd_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 */ | ||
187 | char *enabled_str(int bool); | ||
188 | |||
189 | |||
190 | /* for debug */ | ||
191 | #ifdef SNDRV_SEQ_OSS_DEBUG | ||
192 | extern 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 | */ | ||
34 | static int extended_event(seq_oss_devinfo_t *dp, evrec_t *q, snd_seq_event_t *ev); | ||
35 | static int chn_voice_event(seq_oss_devinfo_t *dp, evrec_t *event_rec, snd_seq_event_t *ev); | ||
36 | static int chn_common_event(seq_oss_devinfo_t *dp, evrec_t *event_rec, snd_seq_event_t *ev); | ||
37 | static int timing_event(seq_oss_devinfo_t *dp, evrec_t *event_rec, snd_seq_event_t *ev); | ||
38 | static int local_event(seq_oss_devinfo_t *dp, evrec_t *event_rec, snd_seq_event_t *ev); | ||
39 | static int old_event(seq_oss_devinfo_t *dp, evrec_t *q, snd_seq_event_t *ev); | ||
40 | static int note_on_event(seq_oss_devinfo_t *dp, int dev, int ch, int note, int vel, snd_seq_event_t *ev); | ||
41 | static int note_off_event(seq_oss_devinfo_t *dp, int dev, int ch, int note, int vel, snd_seq_event_t *ev); | ||
42 | static int set_note_event(seq_oss_devinfo_t *dp, int dev, int type, int ch, int note, int vel, snd_seq_event_t *ev); | ||
43 | static int set_control_event(seq_oss_devinfo_t *dp, int dev, int type, int ch, int param, int val, snd_seq_event_t *ev); | ||
44 | static 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 | |||
53 | int | ||
54 | snd_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 */ | ||
106 | static int | ||
107 | old_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 */ | ||
132 | static int | ||
133 | extended_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 */ | ||
186 | static int | ||
187 | chn_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 */ | ||
207 | static int | ||
208 | chn_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 */ | ||
234 | static int | ||
235 | timing_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 */ | ||
269 | static int | ||
270 | local_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 | */ | ||
285 | static int | ||
286 | note_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 | */ | ||
340 | static int | ||
341 | note_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 | */ | ||
371 | static int | ||
372 | set_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 | */ | ||
389 | static int | ||
390 | set_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 | */ | ||
407 | static int | ||
408 | set_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 | */ | ||
421 | int | ||
422 | snd_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) */ | ||
32 | typedef 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) */ | ||
40 | typedef 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) */ | ||
48 | typedef 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) */ | ||
56 | typedef 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) */ | ||
65 | typedef 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) */ | ||
75 | typedef 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) */ | ||
85 | typedef 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 */ | ||
92 | union 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 | |||
107 | int snd_seq_oss_process_event(seq_oss_devinfo_t *dp, evrec_t *q, snd_seq_event_t *ev); | ||
108 | int snd_seq_oss_process_timer_event(seq_oss_timer_t *rec, evrec_t *q); | ||
109 | int 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 | */ | ||
36 | static int maxqlen = SNDRV_SEQ_OSS_MAX_QLEN; | ||
37 | module_param(maxqlen, int, 0444); | ||
38 | MODULE_PARM_DESC(maxqlen, "maximum queue length"); | ||
39 | |||
40 | static int system_client = -1; /* ALSA sequencer client number */ | ||
41 | static int system_port = -1; | ||
42 | |||
43 | static int num_clients; | ||
44 | static seq_oss_devinfo_t *client_table[SNDRV_SEQ_OSS_MAX_CLIENTS]; | ||
45 | |||
46 | |||
47 | /* | ||
48 | * prototypes | ||
49 | */ | ||
50 | static int receive_announce(snd_seq_event_t *ev, int direct, void *private, int atomic, int hop); | ||
51 | static int translate_mode(struct file *file); | ||
52 | static int create_port(seq_oss_devinfo_t *dp); | ||
53 | static int delete_port(seq_oss_devinfo_t *dp); | ||
54 | static int alloc_seq_queue(seq_oss_devinfo_t *dp); | ||
55 | static int delete_seq_queue(int queue); | ||
56 | static 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 | */ | ||
64 | int __init | ||
65 | snd_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 | */ | ||
142 | static int | ||
143 | receive_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 | */ | ||
175 | int | ||
176 | snd_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 | */ | ||
190 | int | ||
191 | snd_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 | */ | ||
308 | static int | ||
309 | translate_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 | */ | ||
325 | static int | ||
326 | create_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 | */ | ||
360 | static int | ||
361 | delete_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 | */ | ||
373 | static int | ||
374 | alloc_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 | */ | ||
392 | static int | ||
393 | delete_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 | */ | ||
412 | static void | ||
413 | free_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 | */ | ||
433 | void | ||
434 | snd_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 | */ | ||
462 | void | ||
463 | snd_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 | */ | ||
479 | void | ||
480 | snd_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 | */ | ||
508 | char * | ||
509 | enabled_str(int bool) | ||
510 | { | ||
511 | return bool ? "enabled" : "disabled"; | ||
512 | } | ||
513 | |||
514 | static char * | ||
515 | filemode_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 | */ | ||
527 | void | ||
528 | snd_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 | |||
31 | static 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 | |||
44 | static 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 | |||
57 | static 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 | |||
73 | int | ||
74 | snd_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 | */ | ||
40 | struct 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 | */ | ||
56 | static int max_midi_devs; | ||
57 | static seq_oss_midi_t *midi_devs[SNDRV_SEQ_OSS_MAX_MIDI_DEVS]; | ||
58 | |||
59 | static DEFINE_SPINLOCK(register_lock); | ||
60 | |||
61 | /* | ||
62 | * prototypes | ||
63 | */ | ||
64 | static seq_oss_midi_t *get_mdev(int dev); | ||
65 | static seq_oss_midi_t *get_mididev(seq_oss_devinfo_t *dp, int dev); | ||
66 | static int send_synth_event(seq_oss_devinfo_t *dp, snd_seq_event_t *ev, int dev); | ||
67 | static 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 | */ | ||
73 | int __init | ||
74 | snd_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 | */ | ||
103 | static seq_oss_midi_t * | ||
104 | get_mdev(int dev) | ||
105 | { | ||
106 | seq_oss_midi_t *mdev; | ||
107 | unsigned long flags; | ||
108 | |||
109 | spin_lock_irqsave(®ister_lock, flags); | ||
110 | mdev = midi_devs[dev]; | ||
111 | if (mdev) | ||
112 | snd_use_lock_use(&mdev->use_lock); | ||
113 | spin_unlock_irqrestore(®ister_lock, flags); | ||
114 | return mdev; | ||
115 | } | ||
116 | |||
117 | /* | ||
118 | * look for the identical slot | ||
119 | */ | ||
120 | static seq_oss_midi_t * | ||
121 | find_slot(int client, int port) | ||
122 | { | ||
123 | int i; | ||
124 | seq_oss_midi_t *mdev; | ||
125 | unsigned long flags; | ||
126 | |||
127 | spin_lock_irqsave(®ister_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(®ister_lock, flags); | ||
134 | return mdev; | ||
135 | } | ||
136 | } | ||
137 | spin_unlock_irqrestore(®ister_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 | */ | ||
147 | int | ||
148 | snd_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(®ister_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(®ister_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(®ister_lock, flags); | ||
219 | |||
220 | return 0; | ||
221 | } | ||
222 | |||
223 | /* | ||
224 | * release the midi device if it was registered | ||
225 | */ | ||
226 | int | ||
227 | snd_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(®ister_lock, flags); | ||
235 | midi_devs[mdev->seq_device] = NULL; | ||
236 | spin_unlock_irqrestore(®ister_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(®ister_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(®ister_lock, flags); | ||
250 | return 0; | ||
251 | } | ||
252 | |||
253 | |||
254 | /* | ||
255 | * release the midi device if it was registered | ||
256 | */ | ||
257 | void | ||
258 | snd_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(®ister_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(®ister_lock, flags); | ||
275 | } | ||
276 | |||
277 | |||
278 | /* | ||
279 | * set up midi tables | ||
280 | */ | ||
281 | void | ||
282 | snd_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 | */ | ||
290 | void | ||
291 | snd_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 | */ | ||
303 | void | ||
304 | snd_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 | */ | ||
315 | static seq_oss_midi_t * | ||
316 | get_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 | */ | ||
327 | int | ||
328 | snd_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 | */ | ||
394 | int | ||
395 | snd_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 | */ | ||
432 | int | ||
433 | snd_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 | */ | ||
455 | void | ||
456 | snd_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 | */ | ||
503 | void | ||
504 | snd_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 | */ | ||
519 | int | ||
520 | snd_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 | */ | ||
547 | static int | ||
548 | send_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 | */ | ||
608 | static int | ||
609 | send_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 | */ | ||
636 | int | ||
637 | snd_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 | */ | ||
655 | int | ||
656 | snd_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 | */ | ||
674 | static char * | ||
675 | capmode_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 | |||
688 | void | ||
689 | snd_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 | |||
29 | typedef struct seq_oss_midi_t seq_oss_midi_t; | ||
30 | |||
31 | int snd_seq_oss_midi_lookup_ports(int client); | ||
32 | int snd_seq_oss_midi_check_new_port(snd_seq_port_info_t *pinfo); | ||
33 | int snd_seq_oss_midi_check_exit_port(int client, int port); | ||
34 | void snd_seq_oss_midi_clear_all(void); | ||
35 | |||
36 | void snd_seq_oss_midi_setup(seq_oss_devinfo_t *dp); | ||
37 | void snd_seq_oss_midi_cleanup(seq_oss_devinfo_t *dp); | ||
38 | |||
39 | int snd_seq_oss_midi_open(seq_oss_devinfo_t *dp, int dev, int file_mode); | ||
40 | void snd_seq_oss_midi_open_all(seq_oss_devinfo_t *dp, int file_mode); | ||
41 | int snd_seq_oss_midi_close(seq_oss_devinfo_t *dp, int dev); | ||
42 | void snd_seq_oss_midi_reset(seq_oss_devinfo_t *dp, int dev); | ||
43 | int snd_seq_oss_midi_putc(seq_oss_devinfo_t *dp, int dev, unsigned char c, snd_seq_event_t *ev); | ||
44 | int snd_seq_oss_midi_input(snd_seq_event_t *ev, int direct, void *private); | ||
45 | int snd_seq_oss_midi_filemode(seq_oss_devinfo_t *dp, int dev); | ||
46 | int snd_seq_oss_midi_make_info(seq_oss_devinfo_t *dp, int dev, struct midi_info *inf); | ||
47 | void 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 | */ | ||
44 | seq_oss_readq_t * | ||
45 | snd_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 | */ | ||
74 | void | ||
75 | snd_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 | */ | ||
86 | void | ||
87 | snd_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 | */ | ||
102 | int | ||
103 | snd_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 | */ | ||
125 | int | ||
126 | snd_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 | */ | ||
154 | int | ||
155 | snd_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 | */ | ||
166 | void | ||
167 | snd_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 | */ | ||
178 | void | ||
179 | snd_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 | */ | ||
191 | unsigned int | ||
192 | snd_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 | */ | ||
201 | int | ||
202 | snd_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 | */ | ||
228 | void | ||
229 | snd_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 | */ | ||
31 | struct 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 | |||
42 | seq_oss_readq_t *snd_seq_oss_readq_new(seq_oss_devinfo_t *dp, int maxlen); | ||
43 | void snd_seq_oss_readq_delete(seq_oss_readq_t *q); | ||
44 | void snd_seq_oss_readq_clear(seq_oss_readq_t *readq); | ||
45 | unsigned int snd_seq_oss_readq_poll(seq_oss_readq_t *readq, struct file *file, poll_table *wait); | ||
46 | int snd_seq_oss_readq_puts(seq_oss_readq_t *readq, int dev, unsigned char *data, int len); | ||
47 | int snd_seq_oss_readq_put_event(seq_oss_readq_t *readq, evrec_t *ev); | ||
48 | int snd_seq_oss_readq_put_timestamp(seq_oss_readq_t *readq, unsigned long curt, int seq_mode); | ||
49 | int snd_seq_oss_readq_pick(seq_oss_readq_t *q, evrec_t *rec); | ||
50 | void snd_seq_oss_readq_wait(seq_oss_readq_t *q); | ||
51 | void 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 | */ | ||
36 | static int insert_queue(seq_oss_devinfo_t *dp, evrec_t *rec, struct file *opt); | ||
37 | |||
38 | |||
39 | /* | ||
40 | * read interface | ||
41 | */ | ||
42 | |||
43 | int | ||
44 | snd_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 | |||
95 | int | ||
96 | snd_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 | */ | ||
163 | static int | ||
164 | insert_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 | |||
199 | unsigned int | ||
200 | snd_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 */ | ||
40 | struct seq_oss_synth_sysex_t { | ||
41 | int len; | ||
42 | int skip; | ||
43 | unsigned char buf[MAX_SYSEX_BUFLEN]; | ||
44 | }; | ||
45 | |||
46 | /* synth info */ | ||
47 | struct 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 | */ | ||
68 | static int max_synth_devs; | ||
69 | static seq_oss_synth_t *synth_devs[SNDRV_SEQ_OSS_MAX_SYNTH_DEVS]; | ||
70 | static 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 | |||
78 | static DEFINE_SPINLOCK(register_lock); | ||
79 | |||
80 | /* | ||
81 | * prototypes | ||
82 | */ | ||
83 | static seq_oss_synth_t *get_synthdev(seq_oss_devinfo_t *dp, int dev); | ||
84 | static void reset_channels(seq_oss_synthinfo_t *info); | ||
85 | |||
86 | /* | ||
87 | * global initialization | ||
88 | */ | ||
89 | void __init | ||
90 | snd_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 | */ | ||
98 | int | ||
99 | snd_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(®ister_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(®ister_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(®ister_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 | |||
150 | int | ||
151 | snd_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(®ister_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(®ister_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(®ister_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 | */ | ||
190 | static seq_oss_synth_t * | ||
191 | get_sdev(int dev) | ||
192 | { | ||
193 | seq_oss_synth_t *rec; | ||
194 | unsigned long flags; | ||
195 | |||
196 | spin_lock_irqsave(®ister_lock, flags); | ||
197 | rec = synth_devs[dev]; | ||
198 | if (rec) | ||
199 | snd_use_lock_use(&rec->use_lock); | ||
200 | spin_unlock_irqrestore(®ister_lock, flags); | ||
201 | return rec; | ||
202 | } | ||
203 | |||
204 | |||
205 | /* | ||
206 | * set up synth tables | ||
207 | */ | ||
208 | |||
209 | void | ||
210 | snd_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 | |||
265 | void | ||
266 | snd_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 | |||
299 | void | ||
300 | snd_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 | */ | ||
344 | static int | ||
345 | is_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 | */ | ||
357 | static seq_oss_synth_t * | ||
358 | get_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 | */ | ||
380 | static void | ||
381 | reset_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 | */ | ||
398 | void | ||
399 | snd_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 | */ | ||
454 | int | ||
455 | snd_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 | */ | ||
480 | int | ||
481 | snd_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 | */ | ||
498 | int | ||
499 | snd_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 | */ | ||
555 | int | ||
556 | snd_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 | */ | ||
569 | int | ||
570 | snd_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 | */ | ||
591 | int | ||
592 | snd_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 | */ | ||
605 | int | ||
606 | snd_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 | */ | ||
635 | void | ||
636 | snd_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 | |||
30 | typedef struct seq_oss_synth_t seq_oss_synth_t; | ||
31 | |||
32 | void snd_seq_oss_synth_init(void); | ||
33 | int snd_seq_oss_synth_register(snd_seq_device_t *dev); | ||
34 | int snd_seq_oss_synth_unregister(snd_seq_device_t *dev); | ||
35 | void snd_seq_oss_synth_setup(seq_oss_devinfo_t *dp); | ||
36 | void snd_seq_oss_synth_setup_midi(seq_oss_devinfo_t *dp); | ||
37 | void snd_seq_oss_synth_cleanup(seq_oss_devinfo_t *dp); | ||
38 | |||
39 | void snd_seq_oss_synth_reset(seq_oss_devinfo_t *dp, int dev); | ||
40 | int snd_seq_oss_synth_load_patch(seq_oss_devinfo_t *dp, int dev, int fmt, const char __user *buf, int p, int c); | ||
41 | int snd_seq_oss_synth_is_valid(seq_oss_devinfo_t *dp, int dev); | ||
42 | int snd_seq_oss_synth_sysex(seq_oss_devinfo_t *dp, int dev, unsigned char *buf, snd_seq_event_t *ev); | ||
43 | int snd_seq_oss_synth_addr(seq_oss_devinfo_t *dp, int dev, snd_seq_event_t *ev); | ||
44 | int snd_seq_oss_synth_ioctl(seq_oss_devinfo_t *dp, int dev, unsigned int cmd, unsigned long addr); | ||
45 | int snd_seq_oss_synth_raw_event(seq_oss_devinfo_t *dp, int dev, unsigned char *data, snd_seq_event_t *ev); | ||
46 | |||
47 | int 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 | */ | ||
36 | static void calc_alsa_tempo(seq_oss_timer_t *timer); | ||
37 | static 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 | */ | ||
44 | seq_oss_timer_t * | ||
45 | snd_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 | */ | ||
69 | void | ||
70 | snd_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 | */ | ||
84 | int | ||
85 | snd_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 | */ | ||
127 | static void | ||
128 | calc_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 | */ | ||
138 | static int | ||
139 | send_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 | */ | ||
158 | int | ||
159 | snd_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 | */ | ||
183 | int | ||
184 | snd_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 | */ | ||
197 | int | ||
198 | snd_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 | */ | ||
211 | int | ||
212 | snd_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 | */ | ||
229 | int | ||
230 | snd_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 | */ | ||
30 | struct 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 | |||
40 | seq_oss_timer_t *snd_seq_oss_timer_new(seq_oss_devinfo_t *dp); | ||
41 | void snd_seq_oss_timer_delete(seq_oss_timer_t *dp); | ||
42 | |||
43 | int snd_seq_oss_timer_start(seq_oss_timer_t *timer); | ||
44 | int snd_seq_oss_timer_stop(seq_oss_timer_t *timer); | ||
45 | int snd_seq_oss_timer_continue(seq_oss_timer_t *timer); | ||
46 | int 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 | |||
49 | int snd_seq_oss_timer_ioctl(seq_oss_timer_t *timer, unsigned int cmd, int __user *arg); | ||
50 | |||
51 | /* | ||
52 | * get current processed time | ||
53 | */ | ||
54 | static inline abstime_t | ||
55 | snd_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 | */ | ||
64 | static inline int | ||
65 | snd_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 | */ | ||
35 | seq_oss_writeq_t * | ||
36 | snd_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 | */ | ||
63 | void | ||
64 | snd_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 | */ | ||
74 | void | ||
75 | snd_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 | */ | ||
90 | int | ||
91 | snd_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 | */ | ||
130 | void | ||
131 | snd_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 | */ | ||
148 | int | ||
149 | snd_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 | */ | ||
161 | void | ||
162 | snd_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 | |||
28 | struct 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 | */ | ||
41 | seq_oss_writeq_t *snd_seq_oss_writeq_new(seq_oss_devinfo_t *dp, int maxlen); | ||
42 | void snd_seq_oss_writeq_delete(seq_oss_writeq_t *q); | ||
43 | void snd_seq_oss_writeq_clear(seq_oss_writeq_t *q); | ||
44 | int snd_seq_oss_writeq_sync(seq_oss_writeq_t *q); | ||
45 | void snd_seq_oss_writeq_wakeup(seq_oss_writeq_t *q, abstime_t time); | ||
46 | int snd_seq_oss_writeq_get_free_size(seq_oss_writeq_t *q); | ||
47 | void 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) | ||
39 | int seq_client_load[64] = {[0] = SNDRV_SEQ_CLIENT_DUMMY, [1 ... 63] = -1}; | ||
40 | #else | ||
41 | int seq_client_load[64] = {[0 ... 63] = -1}; | ||
42 | #endif | ||
43 | int seq_default_timer_class = SNDRV_TIMER_CLASS_GLOBAL; | ||
44 | int seq_default_timer_sclass = SNDRV_TIMER_SCLASS_NONE; | ||
45 | int seq_default_timer_card = -1; | ||
46 | int seq_default_timer_device = SNDRV_TIMER_GLOBAL_SYSTEM; | ||
47 | int seq_default_timer_subdevice = 0; | ||
48 | int seq_default_timer_resolution = 0; /* Hz */ | ||
49 | |||
50 | MODULE_AUTHOR("Frank van de Pol <fvdpol@coil.demon.nl>, Jaroslav Kysela <perex@suse.cz>"); | ||
51 | MODULE_DESCRIPTION("Advanced Linux Sound Architecture sequencer."); | ||
52 | MODULE_LICENSE("GPL"); | ||
53 | |||
54 | module_param_array(seq_client_load, int, NULL, 0444); | ||
55 | MODULE_PARM_DESC(seq_client_load, "The numbers of global (system) clients to load through kmod."); | ||
56 | module_param(seq_default_timer_class, int, 0644); | ||
57 | MODULE_PARM_DESC(seq_default_timer_class, "The default timer class."); | ||
58 | module_param(seq_default_timer_sclass, int, 0644); | ||
59 | MODULE_PARM_DESC(seq_default_timer_sclass, "The default timer slave class."); | ||
60 | module_param(seq_default_timer_card, int, 0644); | ||
61 | MODULE_PARM_DESC(seq_default_timer_card, "The default timer card number."); | ||
62 | module_param(seq_default_timer_device, int, 0644); | ||
63 | MODULE_PARM_DESC(seq_default_timer_device, "The default timer device number."); | ||
64 | module_param(seq_default_timer_subdevice, int, 0644); | ||
65 | MODULE_PARM_DESC(seq_default_timer_subdevice, "The default timer subdevice number."); | ||
66 | module_param(seq_default_timer_resolution, int, 0644); | ||
67 | MODULE_PARM_DESC(seq_default_timer_resolution, "The default timer resolution in Hz."); | ||
68 | |||
69 | /* | ||
70 | * INIT PART | ||
71 | */ | ||
72 | |||
73 | static 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 | |||
106 | static 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 | |||
124 | module_init(alsa_seq_init) | ||
125 | module_exit(alsa_seq_exit) | ||
126 | |||
127 | /* seq_clientmgr.c */ | ||
128 | EXPORT_SYMBOL(snd_seq_create_kernel_client); | ||
129 | EXPORT_SYMBOL(snd_seq_delete_kernel_client); | ||
130 | EXPORT_SYMBOL(snd_seq_kernel_client_enqueue); | ||
131 | EXPORT_SYMBOL(snd_seq_kernel_client_enqueue_blocking); | ||
132 | EXPORT_SYMBOL(snd_seq_kernel_client_dispatch); | ||
133 | EXPORT_SYMBOL(snd_seq_kernel_client_ctl); | ||
134 | EXPORT_SYMBOL(snd_seq_kernel_client_write_poll); | ||
135 | EXPORT_SYMBOL(snd_seq_set_queue_tempo); | ||
136 | /* seq_memory.c */ | ||
137 | EXPORT_SYMBOL(snd_seq_expand_var_event); | ||
138 | EXPORT_SYMBOL(snd_seq_dump_var_event); | ||
139 | /* seq_ports.c */ | ||
140 | EXPORT_SYMBOL(snd_seq_event_port_attach); | ||
141 | EXPORT_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);*/ | ||
146 | EXPORT_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 | |||
54 | static DEFINE_SPINLOCK(clients_lock); | ||
55 | static DECLARE_MUTEX(register_mutex); | ||
56 | |||
57 | /* | ||
58 | * client table | ||
59 | */ | ||
60 | static char clienttablock[SNDRV_SEQ_MAX_CLIENTS]; | ||
61 | static client_t *clienttab[SNDRV_SEQ_MAX_CLIENTS]; | ||
62 | static usage_t client_usage; | ||
63 | |||
64 | /* | ||
65 | * prototypes | ||
66 | */ | ||
67 | static int bounce_error_event(client_t *client, snd_seq_event_t *event, int err, int atomic, int hop); | ||
68 | static int snd_seq_deliver_single_event(client_t *client, snd_seq_event_t *event, int filter, int atomic, int hop); | ||
69 | |||
70 | /* | ||
71 | */ | ||
72 | |||
73 | static 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 | |||
80 | static inline void snd_leave_user(mm_segment_t fs) | ||
81 | { | ||
82 | set_fs(fs); | ||
83 | } | ||
84 | |||
85 | /* | ||
86 | */ | ||
87 | static 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 | |||
99 | static 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 */ | ||
105 | static 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 | |||
114 | extern int seq_client_load[]; | ||
115 | |||
116 | client_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 | |||
177 | static 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 | |||
184 | static void usage_free(usage_t * res, int num) | ||
185 | { | ||
186 | res->cur -= num; | ||
187 | } | ||
188 | |||
189 | /* initialise data structures */ | ||
190 | int __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 | |||
199 | static 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 | |||
244 | static 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 | |||
266 | static void seq_free_client(client_t * client) | ||
267 | { | ||
268 | down(®ister_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(®ister_mutex); | ||
283 | |||
284 | snd_seq_system_client_ev_client_exit(client->number); | ||
285 | } | ||
286 | |||
287 | |||
288 | |||
289 | /* -------------------------------------------------------- */ | ||
290 | |||
291 | /* create a user client */ | ||
292 | static 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(®ister_mutex)) | ||
299 | return -ERESTARTSYS; | ||
300 | client = seq_create_client1(-1, SNDRV_SEQ_DEFAULT_EVENTS); | ||
301 | if (client == NULL) { | ||
302 | up(®ister_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(®ister_mutex); | ||
323 | return -ENOMEM; | ||
324 | } | ||
325 | } | ||
326 | |||
327 | usage_alloc(&client_usage, 1); | ||
328 | client->type = USER_CLIENT; | ||
329 | up(®ister_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 */ | ||
345 | static 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 | */ | ||
367 | static 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 | */ | ||
450 | static 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 | */ | ||
461 | static 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 | */ | ||
494 | static 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 | */ | ||
532 | static 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 | */ | ||
560 | static 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 | */ | ||
619 | static 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 | */ | ||
671 | static 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 | */ | ||
705 | static 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 */ | ||
737 | static 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 | */ | ||
754 | static 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 | */ | ||
795 | int 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 | */ | ||
866 | static 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 | */ | ||
925 | static 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 | */ | ||
958 | static 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 | */ | ||
1044 | static 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() */ | ||
1076 | static 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() */ | ||
1096 | static 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() */ | ||
1133 | static 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 | |||
1147 | static 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() */ | ||
1170 | static 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 | */ | ||
1199 | static 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 | */ | ||
1249 | static 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 | */ | ||
1272 | static 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 | */ | ||
1304 | static 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 | |||
1329 | static 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 | */ | ||
1362 | int 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 | */ | ||
1379 | static 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 | */ | ||
1423 | static 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() */ | ||
1464 | static 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() */ | ||
1498 | static 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() */ | ||
1509 | static 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() */ | ||
1535 | static 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() */ | ||
1570 | static 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() */ | ||
1593 | static 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() */ | ||
1626 | static 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() */ | ||
1656 | int 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 | |||
1663 | static 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() */ | ||
1677 | static 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() */ | ||
1713 | static 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() */ | ||
1754 | static 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() */ | ||
1775 | static 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() */ | ||
1794 | static 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() */ | ||
1828 | static 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() */ | ||
1872 | static 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 | */ | ||
1901 | static 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 | */ | ||
1940 | static 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 | */ | ||
2009 | static 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 | */ | ||
2040 | static 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 | |||
2073 | static 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 | |||
2109 | static 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 | |||
2134 | static 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 */ | ||
2153 | int 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(®ister_mutex)) | ||
2169 | return -ERESTARTSYS; | ||
2170 | /* empty write queue as default */ | ||
2171 | client = seq_create_client1(client_index, 0); | ||
2172 | if (client == NULL) { | ||
2173 | up(®ister_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(®ister_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 */ | ||
2197 | int 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 | */ | ||
2216 | static 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 | */ | ||
2254 | int 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 | */ | ||
2265 | int 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 | */ | ||
2280 | int 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 | */ | ||
2313 | int 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) */ | ||
2330 | int 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 | */ | ||
2350 | static 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 | |||
2387 | static 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 */ | ||
2408 | void 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 | |||
2456 | static 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 | |||
2468 | static 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 | */ | ||
2478 | int __init snd_sequencer_device_init(void) | ||
2479 | { | ||
2480 | int err; | ||
2481 | |||
2482 | if (down_interruptible(®ister_mutex)) | ||
2483 | return -ERESTARTSYS; | ||
2484 | |||
2485 | if ((err = snd_register_device(SNDRV_DEVICE_TYPE_SEQUENCER, NULL, 0, &snd_seq_reg, "seq")) < 0) { | ||
2486 | up(®ister_mutex); | ||
2487 | return err; | ||
2488 | } | ||
2489 | |||
2490 | up(®ister_mutex); | ||
2491 | |||
2492 | return 0; | ||
2493 | } | ||
2494 | |||
2495 | |||
2496 | |||
2497 | /* | ||
2498 | * unregister sequencer device | ||
2499 | */ | ||
2500 | void __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 | |||
33 | struct _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 | |||
42 | struct _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 | |||
50 | struct _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 */ | ||
77 | typedef struct { | ||
78 | int cur; | ||
79 | int peak; | ||
80 | } usage_t; | ||
81 | |||
82 | |||
83 | extern int client_init_data(void); | ||
84 | extern int snd_sequencer_device_init(void); | ||
85 | extern void snd_sequencer_device_done(void); | ||
86 | |||
87 | /* get locked pointer to client */ | ||
88 | extern 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) */ | ||
94 | extern int snd_seq_dispatch_event(snd_seq_event_cell_t *cell, int atomic, int hop); | ||
95 | |||
96 | /* exported to other modules */ | ||
97 | extern int snd_seq_register_kernel_client(snd_seq_client_callback_t *callback, void *private_data); | ||
98 | extern int snd_seq_unregister_kernel_client(int client); | ||
99 | extern int snd_seq_kernel_client_enqueue(int client, snd_seq_event_t *ev, int atomic, int hop); | ||
100 | int snd_seq_kernel_client_enqueue_blocking(int client, snd_seq_event_t * ev, struct file *file, int atomic, int hop); | ||
101 | int snd_seq_kernel_client_write_poll(int clientid, struct file *file, poll_table *wait); | ||
102 | int 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 | |||
25 | struct 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 | |||
44 | static 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 | |||
82 | enum { | ||
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 | |||
90 | static 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 | |||
49 | MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>"); | ||
50 | MODULE_DESCRIPTION("ALSA sequencer device management"); | ||
51 | MODULE_LICENSE("GPL"); | ||
52 | |||
53 | /* | ||
54 | * driver list | ||
55 | */ | ||
56 | typedef 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 | |||
64 | struct 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 | |||
83 | static LIST_HEAD(opslist); | ||
84 | static int num_ops; | ||
85 | static DECLARE_MUTEX(ops_mutex); | ||
86 | static snd_info_entry_t *info_entry = NULL; | ||
87 | |||
88 | /* | ||
89 | * prototypes | ||
90 | */ | ||
91 | static int snd_seq_device_free(snd_seq_device_t *dev); | ||
92 | static int snd_seq_device_dev_free(snd_device_t *device); | ||
93 | static int snd_seq_device_dev_register(snd_device_t *device); | ||
94 | static int snd_seq_device_dev_disconnect(snd_device_t *device); | ||
95 | static int snd_seq_device_dev_unregister(snd_device_t *device); | ||
96 | |||
97 | static int init_device(snd_seq_device_t *dev, ops_list_t *ops); | ||
98 | static int free_device(snd_seq_device_t *dev, ops_list_t *ops); | ||
99 | static ops_list_t *find_driver(char *id, int create_if_empty); | ||
100 | static ops_list_t *create_driver(char *id); | ||
101 | static void unlock_driver(ops_list_t *ops); | ||
102 | static void remove_drivers(void); | ||
103 | |||
104 | /* | ||
105 | * show all drivers and their status | ||
106 | */ | ||
107 | |||
108 | static 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() */ | ||
131 | static int snd_seq_in_init; | ||
132 | void snd_seq_autoload_lock(void) | ||
133 | { | ||
134 | snd_seq_in_init++; | ||
135 | } | ||
136 | |||
137 | void snd_seq_autoload_unlock(void) | ||
138 | { | ||
139 | snd_seq_in_init--; | ||
140 | } | ||
141 | #endif | ||
142 | |||
143 | void 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 | */ | ||
181 | int 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 | */ | ||
238 | static 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 | |||
264 | static 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 | */ | ||
273 | static 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 | */ | ||
295 | static 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 | */ | ||
313 | static 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 | */ | ||
324 | int 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 | */ | ||
369 | static 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 | */ | ||
399 | int 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 | */ | ||
439 | static 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 | */ | ||
462 | static 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 | */ | ||
485 | static 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 | */ | ||
511 | static 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 | |||
530 | static 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 | |||
542 | static 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 | |||
557 | static 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 | |||
565 | module_init(alsa_seq_device_init) | ||
566 | module_exit(alsa_seq_device_exit) | ||
567 | |||
568 | EXPORT_SYMBOL(snd_seq_device_load_drivers); | ||
569 | EXPORT_SYMBOL(snd_seq_device_new); | ||
570 | EXPORT_SYMBOL(snd_seq_device_register_driver); | ||
571 | EXPORT_SYMBOL(snd_seq_device_unregister_driver); | ||
572 | #ifdef CONFIG_KMOD | ||
573 | EXPORT_SYMBOL(snd_seq_autoload_lock); | ||
574 | EXPORT_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 | |||
63 | MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>"); | ||
64 | MODULE_DESCRIPTION("ALSA sequencer MIDI-through client"); | ||
65 | MODULE_LICENSE("GPL"); | ||
66 | MODULE_ALIAS("snd-seq-client-" __stringify(SNDRV_SEQ_CLIENT_DUMMY)); | ||
67 | |||
68 | static int ports = 1; | ||
69 | static int duplex = 0; | ||
70 | |||
71 | module_param(ports, int, 0444); | ||
72 | MODULE_PARM_DESC(ports, "number of ports to be created"); | ||
73 | module_param(duplex, bool, 0444); | ||
74 | MODULE_PARM_DESC(duplex, "create DUPLEX ports"); | ||
75 | |||
76 | typedef struct snd_seq_dummy_port { | ||
77 | int client; | ||
78 | int port; | ||
79 | int duplex; | ||
80 | int connect; | ||
81 | } snd_seq_dummy_port_t; | ||
82 | |||
83 | static 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 | */ | ||
90 | static int | ||
91 | dummy_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 | */ | ||
118 | static int | ||
119 | dummy_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 | */ | ||
140 | static void | ||
141 | dummy_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 | */ | ||
152 | static snd_seq_dummy_port_t __init * | ||
153 | create_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 | */ | ||
195 | static int __init | ||
196 | register_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 | */ | ||
247 | static void __exit | ||
248 | delete_client(void) | ||
249 | { | ||
250 | if (my_client >= 0) | ||
251 | snd_seq_delete_kernel_client(my_client); | ||
252 | } | ||
253 | |||
254 | /* | ||
255 | * Init part | ||
256 | */ | ||
257 | |||
258 | static 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 | |||
267 | static void __exit alsa_seq_dummy_exit(void) | ||
268 | { | ||
269 | delete_client(); | ||
270 | } | ||
271 | |||
272 | module_init(alsa_seq_dummy_init) | ||
273 | module_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 */ | ||
32 | fifo_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 | |||
65 | void 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 | |||
91 | static snd_seq_event_cell_t *fifo_cell_out(fifo_t *f); | ||
92 | |||
93 | /* clear queue */ | ||
94 | void 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 */ | ||
113 | int 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 */ | ||
151 | static 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 */ | ||
170 | int 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 | |||
205 | void 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 */ | ||
220 | int 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 */ | ||
227 | int 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 | |||
30 | typedef 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) */ | ||
43 | extern fifo_t *snd_seq_fifo_new(int poolsize); | ||
44 | |||
45 | /* delete fifo (destructor) */ | ||
46 | extern void snd_seq_fifo_delete(fifo_t **f); | ||
47 | |||
48 | |||
49 | /* enqueue event to fifo */ | ||
50 | extern 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 */ | ||
57 | int 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 */ | ||
60 | extern void snd_seq_fifo_cell_putback(fifo_t *f, snd_seq_event_cell_t *cell); | ||
61 | |||
62 | /* clean up queue */ | ||
63 | extern void snd_seq_fifo_clear(fifo_t *f); | ||
64 | |||
65 | /* polling */ | ||
66 | extern int snd_seq_fifo_poll_wait(fifo_t *f, struct file *file, poll_table *wait); | ||
67 | |||
68 | /* resize pool in fifo */ | ||
69 | int 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 | |||
31 | static snd_info_entry_t *queues_entry; | ||
32 | static snd_info_entry_t *clients_entry; | ||
33 | static snd_info_entry_t *timer_entry; | ||
34 | |||
35 | |||
36 | static snd_info_entry_t * __init | ||
37 | create_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 */ | ||
56 | int __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 | |||
66 | int __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 | |||
27 | void snd_seq_info_clients_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer); | ||
28 | void snd_seq_info_timer_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer); | ||
29 | void snd_seq_info_queues_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer); | ||
30 | |||
31 | |||
32 | int snd_seq_info_init( void ); | ||
33 | int 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 | |||
29 | MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>"); | ||
30 | MODULE_DESCRIPTION("Advanced Linux Sound Architecture sequencer instrument library."); | ||
31 | MODULE_LICENSE("GPL"); | ||
32 | |||
33 | |||
34 | static 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 | |||
43 | static 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 | |||
52 | static 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 | |||
63 | static 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 | |||
76 | snd_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 | |||
90 | void 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 | |||
129 | static 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 | |||
164 | int 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 | |||
215 | static 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 | ||
227 | static 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 | |||
239 | static 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 | |||
268 | snd_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 | |||
301 | void 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 | |||
317 | static 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 | |||
327 | static 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 | |||
351 | static 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 | |||
368 | static 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 | |||
386 | static 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 | |||
394 | static 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 | |||
402 | static 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 | |||
410 | static 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 | |||
418 | static 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 | |||
492 | static 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 | |||
500 | static 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 | |||
572 | static 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 | |||
580 | static 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 | |||
588 | int 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 | |||
636 | static int __init alsa_seq_instr_init(void) | ||
637 | { | ||
638 | return 0; | ||
639 | } | ||
640 | |||
641 | static void __exit alsa_seq_instr_exit(void) | ||
642 | { | ||
643 | } | ||
644 | |||
645 | module_init(alsa_seq_instr_init) | ||
646 | module_exit(alsa_seq_instr_exit) | ||
647 | |||
648 | EXPORT_SYMBOL(snd_seq_instr_list_new); | ||
649 | EXPORT_SYMBOL(snd_seq_instr_list_free); | ||
650 | EXPORT_SYMBOL(snd_seq_instr_list_free_cond); | ||
651 | EXPORT_SYMBOL(snd_seq_instr_find); | ||
652 | EXPORT_SYMBOL(snd_seq_instr_free_use); | ||
653 | EXPORT_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 */ | ||
29 | void 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 | |||
8 | typedef 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 */ | ||
20 | void 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 | |||
25 | typedef 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 | |||
39 | inline static int snd_seq_pool_available(pool_t *pool) | ||
40 | { | ||
41 | return pool->total_elements - atomic_read(&pool->counter); | ||
42 | } | ||
43 | |||
44 | inline 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 | |||
75 | static 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 | |||
83 | int 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 | |||
130 | static 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 | |||
137 | static 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 | |||
145 | int 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 | |||
177 | static 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 | |||
184 | void 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 | */ | ||
218 | static 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 | */ | ||
283 | int 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 */ | ||
361 | int 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 */ | ||
369 | int 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 */ | ||
405 | int 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 */ | ||
450 | pool_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 */ | ||
476 | int 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 */ | ||
489 | int __init snd_sequencer_memory_init(void) | ||
490 | { | ||
491 | return 0; | ||
492 | } | ||
493 | |||
494 | /* release sequencer memory */ | ||
495 | void __exit snd_sequencer_memory_done(void) | ||
496 | { | ||
497 | } | ||
498 | |||
499 | |||
500 | /* exported to seq_clientmgr.c */ | ||
501 | void 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 | |||
27 | typedef struct pool pool_t; | ||
28 | |||
29 | /* container for sequencer event (internal use) */ | ||
30 | typedef 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 | |||
41 | struct 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 | |||
66 | extern void snd_seq_cell_free(snd_seq_event_cell_t* cell); | ||
67 | |||
68 | int 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 */ | ||
71 | static 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 */ | ||
77 | static inline int snd_seq_total_cells(pool_t *pool) | ||
78 | { | ||
79 | return pool ? pool->total_elements : 0; | ||
80 | } | ||
81 | |||
82 | /* init pool - allocate events */ | ||
83 | int snd_seq_pool_init(pool_t *pool); | ||
84 | |||
85 | /* done pool - free events */ | ||
86 | int snd_seq_pool_done(pool_t *pool); | ||
87 | |||
88 | /* create pool */ | ||
89 | pool_t *snd_seq_pool_new(int poolsize); | ||
90 | |||
91 | /* remove pool */ | ||
92 | int snd_seq_pool_delete(pool_t **pool); | ||
93 | |||
94 | /* init memory */ | ||
95 | int snd_sequencer_memory_init(void); | ||
96 | |||
97 | /* release event memory */ | ||
98 | void snd_sequencer_memory_done(void); | ||
99 | |||
100 | /* polling */ | ||
101 | int 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 | /* | ||
23 | Possible 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 | |||
43 | MODULE_AUTHOR("Frank van de Pol <fvdpol@coil.demon.nl>, Jaroslav Kysela <perex@suse.cz>"); | ||
44 | MODULE_DESCRIPTION("Advanced Linux Sound Architecture sequencer MIDI synth."); | ||
45 | MODULE_LICENSE("GPL"); | ||
46 | static int output_buffer_size = PAGE_SIZE; | ||
47 | module_param(output_buffer_size, int, 0644); | ||
48 | MODULE_PARM_DESC(output_buffer_size, "Output buffer size in bytes."); | ||
49 | static int input_buffer_size = PAGE_SIZE; | ||
50 | module_param(input_buffer_size, int, 0644); | ||
51 | MODULE_PARM_DESC(input_buffer_size, "Input buffer size in bytes."); | ||
52 | |||
53 | /* data for this midi synth driver */ | ||
54 | typedef 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 | |||
65 | typedef 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 | |||
72 | static seq_midisynth_client_t *synths[SNDRV_CARDS]; | ||
73 | static DECLARE_MUTEX(register_mutex); | ||
74 | |||
75 | /* handle rawmidi input event (MIDI v1.0 stream) */ | ||
76 | static 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 | |||
115 | static 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 | |||
131 | static 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 | |||
168 | static 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 */ | ||
182 | static 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(¶ms, 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, ¶ms)) < 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 */ | ||
210 | static 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 */ | ||
221 | static 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(¶ms, 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, ¶ms)) < 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 */ | ||
244 | static 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 */ | ||
257 | static 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 */ | ||
272 | static 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 */ | ||
287 | static int | ||
288 | snd_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(®ister_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(®ister_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(®ister_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(®ister_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(®ister_mutex); | ||
432 | return -ENOMEM; | ||
433 | } | ||
434 | |||
435 | /* release midi synth port */ | ||
436 | static int | ||
437 | snd_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(®ister_mutex); | ||
445 | client = synths[card->number]; | ||
446 | if (client == NULL || client->ports[device] == NULL) { | ||
447 | up(®ister_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(®ister_mutex); | ||
466 | return 0; | ||
467 | } | ||
468 | |||
469 | |||
470 | static 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 | |||
483 | static void __exit alsa_seq_midi_exit(void) | ||
484 | { | ||
485 | snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_MIDISYNTH); | ||
486 | } | ||
487 | |||
488 | module_init(alsa_seq_midi_init) | ||
489 | module_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 | |||
42 | MODULE_AUTHOR("Takashi Iwai / Steve Ratcliffe"); | ||
43 | MODULE_DESCRIPTION("Advanced Linux Sound Architecture sequencer MIDI emulation."); | ||
44 | MODULE_LICENSE("GPL"); | ||
45 | |||
46 | /* Prototypes for static functions */ | ||
47 | static void note_off(snd_midi_op_t *ops, void *drv, snd_midi_channel_t *chan, int note, int vel); | ||
48 | static 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); | ||
51 | static void rpn(snd_midi_op_t *ops, void *drv, snd_midi_channel_t *chan, snd_midi_channel_set_t *chset); | ||
52 | static void nrpn(snd_midi_op_t *ops, void *drv, snd_midi_channel_t *chan, snd_midi_channel_set_t *chset); | ||
53 | static void sysex(snd_midi_op_t *ops, void *private, unsigned char *sysex, int len, snd_midi_channel_set_t *chset); | ||
54 | static void all_sounds_off(snd_midi_op_t *ops, void *private, snd_midi_channel_t *chan); | ||
55 | static void all_notes_off(snd_midi_op_t *ops, void *private, snd_midi_channel_t *chan); | ||
56 | static void snd_midi_reset_controllers(snd_midi_channel_t *chan); | ||
57 | static 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 | */ | ||
74 | void | ||
75 | snd_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 | */ | ||
241 | static void | ||
242 | note_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 | */ | ||
262 | static void | ||
263 | do_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 | */ | ||
378 | void | ||
379 | snd_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 | */ | ||
409 | static void | ||
410 | rpn(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 | */ | ||
449 | static void | ||
450 | nrpn(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 | */ | ||
462 | static int | ||
463 | get_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 | */ | ||
477 | static void | ||
478 | sysex(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 | */ | ||
590 | static void | ||
591 | all_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 | */ | ||
608 | static void | ||
609 | all_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 | */ | ||
624 | static 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 | */ | ||
645 | static 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 | */ | ||
662 | static void | ||
663 | reset_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 | */ | ||
684 | snd_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 | */ | ||
700 | static 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 | */ | ||
712 | void 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 | |||
720 | static int __init alsa_seq_midi_emul_init(void) | ||
721 | { | ||
722 | return 0; | ||
723 | } | ||
724 | |||
725 | static void __exit alsa_seq_midi_emul_exit(void) | ||
726 | { | ||
727 | } | ||
728 | |||
729 | module_init(alsa_seq_midi_emul_init) | ||
730 | module_exit(alsa_seq_midi_emul_exit) | ||
731 | |||
732 | EXPORT_SYMBOL(snd_midi_process_event); | ||
733 | EXPORT_SYMBOL(snd_midi_channel_set_clear); | ||
734 | EXPORT_SYMBOL(snd_midi_channel_alloc_set); | ||
735 | EXPORT_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 | |||
31 | MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>, Jaroslav Kysela <perex@suse.cz>"); | ||
32 | MODULE_DESCRIPTION("MIDI byte <-> sequencer event coder"); | ||
33 | MODULE_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 */ | ||
45 | typedef void (*event_encode_t)(snd_midi_event_t *dev, snd_seq_event_t *ev); | ||
46 | typedef void (*event_decode_t)(snd_seq_event_t *ev, unsigned char *buf); | ||
47 | |||
48 | /* | ||
49 | * prototypes | ||
50 | */ | ||
51 | static void note_event(snd_midi_event_t *dev, snd_seq_event_t *ev); | ||
52 | static void one_param_ctrl_event(snd_midi_event_t *dev, snd_seq_event_t *ev); | ||
53 | static void pitchbend_ctrl_event(snd_midi_event_t *dev, snd_seq_event_t *ev); | ||
54 | static void two_param_ctrl_event(snd_midi_event_t *dev, snd_seq_event_t *ev); | ||
55 | static void one_param_event(snd_midi_event_t *dev, snd_seq_event_t *ev); | ||
56 | static void songpos_event(snd_midi_event_t *dev, snd_seq_event_t *ev); | ||
57 | static void note_decode(snd_seq_event_t *ev, unsigned char *buf); | ||
58 | static void one_param_decode(snd_seq_event_t *ev, unsigned char *buf); | ||
59 | static void pitchbend_decode(snd_seq_event_t *ev, unsigned char *buf); | ||
60 | static void two_param_decode(snd_seq_event_t *ev, unsigned char *buf); | ||
61 | static void songpos_decode(snd_seq_event_t *ev, unsigned char *buf); | ||
62 | |||
63 | /* | ||
64 | * event list | ||
65 | */ | ||
66 | static 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 | |||
100 | static int extra_decode_ctrl14(snd_midi_event_t *dev, unsigned char *buf, int len, snd_seq_event_t *ev); | ||
101 | static int extra_decode_xrpn(snd_midi_event_t *dev, unsigned char *buf, int count, snd_seq_event_t *ev); | ||
102 | |||
103 | static 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 | |||
116 | int 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 | |||
138 | void 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 | */ | ||
149 | inline static void reset_encode(snd_midi_event_t *dev) | ||
150 | { | ||
151 | dev->read = 0; | ||
152 | dev->qlen = 0; | ||
153 | dev->type = 0; | ||
154 | } | ||
155 | |||
156 | void 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 | |||
165 | void 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 | |||
174 | void 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 | |||
180 | void 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 | */ | ||
188 | int 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 | */ | ||
212 | long 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 | */ | ||
237 | int 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 */ | ||
302 | static 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 */ | ||
310 | static 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 */ | ||
317 | static 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 */ | ||
324 | static 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*/ | ||
332 | static 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 */ | ||
338 | static 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 | */ | ||
347 | long 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 */ | ||
403 | static 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 */ | ||
410 | static 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 */ | ||
416 | static 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 */ | ||
424 | static 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 */ | ||
431 | static 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 */ | ||
438 | static 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 */ | ||
475 | static 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 | |||
518 | EXPORT_SYMBOL(snd_midi_event_new); | ||
519 | EXPORT_SYMBOL(snd_midi_event_free); | ||
520 | EXPORT_SYMBOL(snd_midi_event_resize_buffer); | ||
521 | EXPORT_SYMBOL(snd_midi_event_init); | ||
522 | EXPORT_SYMBOL(snd_midi_event_reset_encode); | ||
523 | EXPORT_SYMBOL(snd_midi_event_reset_decode); | ||
524 | EXPORT_SYMBOL(snd_midi_event_no_status); | ||
525 | EXPORT_SYMBOL(snd_midi_event_encode); | ||
526 | EXPORT_SYMBOL(snd_midi_event_encode_byte); | ||
527 | EXPORT_SYMBOL(snd_midi_event_decode); | ||
528 | |||
529 | static int __init alsa_seq_midi_event_init(void) | ||
530 | { | ||
531 | return 0; | ||
532 | } | ||
533 | |||
534 | static void __exit alsa_seq_midi_event_exit(void) | ||
535 | { | ||
536 | } | ||
537 | |||
538 | module_init(alsa_seq_midi_event_init) | ||
539 | module_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 | |||
39 | NOTE: the current implementation of the port structure as a linked list is | ||
40 | not optimal for clients that have many ports. For sending messages to all | ||
41 | subscribers of a port we first need to find the address of the port | ||
42 | structure, which means we have to traverse the list. A direct access table | ||
43 | (array) would be better, but big preallocated arrays waste memory. | ||
44 | |||
45 | Possible actions: | ||
46 | |||
47 | 1) leave it this way, a client does normaly does not have more than a few | ||
48 | ports | ||
49 | |||
50 | 2) replace the linked list of ports by a array of pointers which is | ||
51 | dynamicly kmalloced. When a port is added or deleted we can simply allocate | ||
52 | a new array, copy the corresponding pointers, and delete the old one. We | ||
53 | then only need a pointer to this array, and an integer that tells us how | ||
54 | much elements are in array. | ||
55 | |||
56 | */ | ||
57 | |||
58 | /* return pointer to port structure - port is locked if found */ | ||
59 | client_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 */ | ||
83 | client_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 */ | ||
115 | static 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) */ | ||
128 | client_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 | /* */ | ||
180 | enum group_type_t { | ||
181 | SRC_LIST, DEST_LIST | ||
182 | }; | ||
183 | |||
184 | static int subscribe_port(client_t *client, client_port_t *port, port_subs_info_t *grp, snd_seq_port_subscribe_t *info, int send_ack); | ||
185 | static 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 | |||
188 | static 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 | */ | ||
207 | static 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 */ | ||
254 | static 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 */ | ||
276 | int 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 */ | ||
303 | int 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 */ | ||
336 | int 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 */ | ||
364 | int 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 | |||
413 | static 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 | |||
435 | static 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 */ | ||
456 | static 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 */ | ||
463 | static 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 */ | ||
478 | int 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 */ | ||
557 | int 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 */ | ||
600 | subscribers_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 */ | ||
625 | int 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 */ | ||
661 | int 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 | |||
43 | typedef 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 | |||
50 | typedef 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 | |||
60 | typedef 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 */ | ||
93 | client_port_t *snd_seq_port_use_ptr(client_t *client, int num); | ||
94 | |||
95 | /* search for next port - port is locked if found */ | ||
96 | client_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) */ | ||
102 | client_port_t *snd_seq_create_port(client_t *client, int port_index); | ||
103 | |||
104 | /* delete a port */ | ||
105 | int snd_seq_delete_port(client_t *client, int port); | ||
106 | |||
107 | /* delete all ports */ | ||
108 | int snd_seq_delete_all_ports(client_t *client); | ||
109 | |||
110 | /* set port info fields */ | ||
111 | int snd_seq_set_port_info(client_port_t *port, snd_seq_port_info_t *info); | ||
112 | |||
113 | /* get port info fields */ | ||
114 | int snd_seq_get_port_info(client_port_t *port, snd_seq_port_info_t *info); | ||
115 | |||
116 | /* add subscriber to subscription list */ | ||
117 | int 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 */ | ||
120 | int 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 */ | ||
123 | int snd_seq_port_subscribe(client_port_t *port, snd_seq_port_subscribe_t *info); | ||
124 | |||
125 | /* get matched subscriber */ | ||
126 | subscribers_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) */ | ||
58 | prioq_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) */ | ||
77 | void 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 */ | ||
104 | static 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 | */ | ||
120 | static 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 */ | ||
147 | int 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 */ | ||
218 | snd_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 */ | ||
246 | int 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 */ | ||
257 | snd_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 | |||
267 | static 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 */ | ||
289 | void 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 | |||
341 | static 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 */ | ||
397 | void 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 | |||
29 | typedef 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) */ | ||
38 | extern prioq_t *snd_seq_prioq_new(void); | ||
39 | |||
40 | /* delete prioq (destructor) */ | ||
41 | extern void snd_seq_prioq_delete(prioq_t **fifo); | ||
42 | |||
43 | /* enqueue cell to prioq */ | ||
44 | extern int snd_seq_prioq_cell_in(prioq_t *f, snd_seq_event_cell_t *cell); | ||
45 | |||
46 | /* dequeue cell from prioq */ | ||
47 | extern snd_seq_event_cell_t *snd_seq_prioq_cell_out(prioq_t *f); | ||
48 | |||
49 | /* return number of events available in prioq */ | ||
50 | extern int snd_seq_prioq_avail(prioq_t *f); | ||
51 | |||
52 | /* peek at cell at the head of the prioq */ | ||
53 | extern snd_seq_event_cell_t *snd_seq_prioq_cell_peek(prioq_t *f); | ||
54 | |||
55 | /* client left queue */ | ||
56 | extern void snd_seq_prioq_leave(prioq_t *f, int client, int timestamp); | ||
57 | |||
58 | /* Remove events */ | ||
59 | void 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 */ | ||
51 | static queue_t *queue_list[SNDRV_SEQ_MAX_QUEUES]; | ||
52 | static DEFINE_SPINLOCK(queue_list_lock); | ||
53 | /* number of queues allocated */ | ||
54 | static int num_queues; | ||
55 | |||
56 | int snd_seq_queue_get_cur_queues(void) | ||
57 | { | ||
58 | return num_queues; | ||
59 | } | ||
60 | |||
61 | /*----------------------------------------------------------------*/ | ||
62 | |||
63 | /* assign queue id and insert to list */ | ||
64 | static 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 | |||
83 | static 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) */ | ||
110 | static 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) */ | ||
145 | static 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 */ | ||
164 | int __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 */ | ||
174 | void __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 | */ | ||
188 | int 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 */ | ||
205 | int 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 */ | ||
221 | queue_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 */ | ||
237 | queue_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 | |||
255 | void 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 */ | ||
312 | int 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 | |||
364 | static 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 | */ | ||
372 | static 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 */ | ||
386 | static 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 */ | ||
396 | int 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 | */ | ||
416 | int 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 | */ | ||
443 | int 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 | */ | ||
464 | int 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 */ | ||
480 | int 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 | */ | ||
507 | int 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 | */ | ||
539 | int 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 | */ | ||
558 | void 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 | */ | ||
584 | void 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 */ | ||
615 | void 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 */ | ||
630 | void 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 | */ | ||
653 | static 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 | */ | ||
675 | void 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 | */ | ||
726 | int 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 */ | ||
752 | void 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 | |||
33 | struct _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 */ | ||
64 | int snd_seq_queue_get_cur_queues(void); | ||
65 | |||
66 | /* init queues structure */ | ||
67 | int snd_seq_queues_init(void); | ||
68 | |||
69 | /* delete queues */ | ||
70 | void snd_seq_queues_delete(void); | ||
71 | |||
72 | |||
73 | /* create new queue (constructor) */ | ||
74 | int snd_seq_queue_alloc(int client, int locked, unsigned int flags); | ||
75 | |||
76 | /* delete queue (destructor) */ | ||
77 | int snd_seq_queue_delete(int client, int queueid); | ||
78 | |||
79 | /* notification that client has left the system */ | ||
80 | void snd_seq_queue_client_termination(int client); | ||
81 | |||
82 | /* final stage */ | ||
83 | void snd_seq_queue_client_leave(int client); | ||
84 | |||
85 | /* enqueue a event received from one the clients */ | ||
86 | int snd_seq_enqueue_event(snd_seq_event_cell_t *cell, int atomic, int hop); | ||
87 | |||
88 | /* Remove events */ | ||
89 | void snd_seq_queue_client_leave_cells(int client); | ||
90 | void snd_seq_queue_remove_cells(int client, snd_seq_remove_events_t *info); | ||
91 | |||
92 | /* return pointer to queue structure for specified id */ | ||
93 | queue_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 */ | ||
98 | queue_t *snd_seq_queue_find_name(char *name); | ||
99 | |||
100 | /* check single queue and dispatch events */ | ||
101 | void snd_seq_check_queue(queue_t *q, int atomic, int hop); | ||
102 | |||
103 | /* access to queue's parameters */ | ||
104 | int snd_seq_queue_check_access(int queueid, int client); | ||
105 | int snd_seq_queue_timer_set_tempo(int queueid, int client, snd_seq_queue_tempo_t *info); | ||
106 | int snd_seq_queue_set_owner(int queueid, int client, int locked); | ||
107 | int snd_seq_queue_set_locked(int queueid, int client, int locked); | ||
108 | int snd_seq_queue_timer_open(int queueid); | ||
109 | int snd_seq_queue_timer_close(int queueid); | ||
110 | int snd_seq_queue_use(int queueid, int client, int use); | ||
111 | int snd_seq_queue_is_used(int queueid, int client); | ||
112 | |||
113 | int snd_seq_control_queue(snd_seq_event_t *ev, int atomic, int hop); | ||
114 | void 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 */ | ||
61 | static int sysclient = -1; | ||
62 | |||
63 | /* port id numbers for this client */ | ||
64 | static int announce_port = -1; | ||
65 | |||
66 | |||
67 | |||
68 | /* fill standard header data, source port & channel are filled in */ | ||
69 | static 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 */ | ||
93 | void 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 */ | ||
104 | int 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 */ | ||
115 | static 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 */ | ||
121 | int __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 */ | ||
181 | void __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 */ | ||
28 | void 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 | |||
37 | int snd_seq_system_notify(int client, int port, snd_seq_event_t *ev); | ||
38 | |||
39 | /* register our internal client */ | ||
40 | int snd_seq_system_client_init(void); | ||
41 | |||
42 | /* unregister our internal client */ | ||
43 | void 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 | |||
30 | extern int seq_default_timer_class; | ||
31 | extern int seq_default_timer_sclass; | ||
32 | extern int seq_default_timer_card; | ||
33 | extern int seq_default_timer_device; | ||
34 | extern int seq_default_timer_subdevice; | ||
35 | extern int seq_default_timer_resolution; | ||
36 | |||
37 | #define SKEW_BASE 0x10000 /* 16bit shift */ | ||
38 | |||
39 | void 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) */ | ||
58 | seq_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) */ | ||
79 | void 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 | |||
97 | void 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 | |||
116 | void 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 */ | ||
134 | static 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 */ | ||
175 | int 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 */ | ||
192 | int 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 */ | ||
215 | int 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 */ | ||
229 | int 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 */ | ||
243 | int 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 | |||
260 | int 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 | |||
300 | int 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 | |||
314 | int 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 | |||
325 | static 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 | |||
347 | int 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 | |||
362 | int 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. */ | ||
380 | snd_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) */ | ||
405 | snd_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 */ | ||
412 | void 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 | |||
27 | typedef 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 | |||
33 | typedef 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) */ | ||
62 | extern seq_timer_t *snd_seq_timer_new(void); | ||
63 | |||
64 | /* delete timer (destructor) */ | ||
65 | extern void snd_seq_timer_delete(seq_timer_t **tmr); | ||
66 | |||
67 | void snd_seq_timer_set_tick_resolution(seq_timer_tick_t *tick, int tempo, int ppq, int nticks); | ||
68 | |||
69 | /* */ | ||
70 | static 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 */ | ||
82 | static 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 | |||
88 | static 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 | |||
99 | static 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 */ | ||
110 | static 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 | |||
117 | static 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 */ | ||
124 | int snd_seq_timer_open(queue_t *q); | ||
125 | int snd_seq_timer_close(queue_t *q); | ||
126 | int snd_seq_timer_midi_open(queue_t *q); | ||
127 | int snd_seq_timer_midi_close(queue_t *q); | ||
128 | void snd_seq_timer_defaults(seq_timer_t *tmr); | ||
129 | void snd_seq_timer_reset(seq_timer_t *tmr); | ||
130 | int snd_seq_timer_stop(seq_timer_t *tmr); | ||
131 | int snd_seq_timer_start(seq_timer_t *tmr); | ||
132 | int snd_seq_timer_continue(seq_timer_t *tmr); | ||
133 | int snd_seq_timer_set_tempo(seq_timer_t *tmr, int tempo); | ||
134 | int snd_seq_timer_set_ppq(seq_timer_t *tmr, int ppq); | ||
135 | int snd_seq_timer_set_position_tick(seq_timer_t *tmr, snd_seq_tick_time_t position); | ||
136 | int snd_seq_timer_set_position_time(seq_timer_t *tmr, snd_seq_real_time_t position); | ||
137 | int snd_seq_timer_set_skew(seq_timer_t *tmr, unsigned int skew, unsigned int base); | ||
138 | snd_seq_real_time_t snd_seq_timer_get_cur_time(seq_timer_t *tmr); | ||
139 | snd_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 | |||
52 | MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>"); | ||
53 | MODULE_DESCRIPTION("Virtual Raw MIDI client on Sequencer"); | ||
54 | MODULE_LICENSE("GPL"); | ||
55 | |||
56 | /* | ||
57 | * initialize an event record | ||
58 | */ | ||
59 | static 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 | */ | ||
79 | static 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 */ | ||
114 | int 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 | */ | ||
125 | static 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 | */ | ||
139 | static 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 | */ | ||
153 | static 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 | */ | ||
200 | static 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 | */ | ||
229 | static 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 | */ | ||
255 | static 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 | */ | ||
268 | static 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 | */ | ||
280 | static 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 | */ | ||
294 | static 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 | */ | ||
308 | static 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 | */ | ||
322 | static 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 | |||
337 | static 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 | |||
343 | static 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 | */ | ||
352 | static 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 | */ | ||
428 | static 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 | */ | ||
439 | static 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 | */ | ||
466 | static 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 | */ | ||
478 | static 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 | */ | ||
486 | static 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 */ | ||
497 | int 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 | |||
538 | static int __init alsa_virmidi_init(void) | ||
539 | { | ||
540 | return 0; | ||
541 | } | ||
542 | |||
543 | static void __exit alsa_virmidi_exit(void) | ||
544 | { | ||
545 | } | ||
546 | |||
547 | module_init(alsa_virmidi_init) | ||
548 | module_exit(alsa_virmidi_exit) | ||
549 | |||
550 | EXPORT_SYMBOL(snd_virmidi_new); | ||
551 | EXPORT_SYMBOL(snd_virmidi_receive); | ||