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 |
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')
95 files changed, 43314 insertions, 0 deletions
diff --git a/sound/core/Kconfig b/sound/core/Kconfig new file mode 100644 index 000000000000..d1e800b9866d --- /dev/null +++ b/sound/core/Kconfig | |||
@@ -0,0 +1,133 @@ | |||
1 | # ALSA soundcard-configuration | ||
2 | config SND_TIMER | ||
3 | tristate | ||
4 | depends on SND | ||
5 | |||
6 | config SND_PCM | ||
7 | tristate | ||
8 | select SND_TIMER | ||
9 | depends on SND | ||
10 | |||
11 | config SND_HWDEP | ||
12 | tristate | ||
13 | depends on SND | ||
14 | |||
15 | config SND_RAWMIDI | ||
16 | tristate | ||
17 | depends on SND | ||
18 | |||
19 | config SND_SEQUENCER | ||
20 | tristate "Sequencer support" | ||
21 | depends on SND | ||
22 | select SND_TIMER | ||
23 | help | ||
24 | Say Y or M to enable MIDI sequencer and router support. This | ||
25 | feature allows routing and enqueueing of MIDI events. Events | ||
26 | can be processed at a given time. | ||
27 | |||
28 | Many programs require this feature, so you should enable it | ||
29 | unless you know what you're doing. | ||
30 | |||
31 | config SND_SEQ_DUMMY | ||
32 | tristate "Sequencer dummy client" | ||
33 | depends on SND_SEQUENCER | ||
34 | help | ||
35 | Say Y here to enable the dummy sequencer client. This client | ||
36 | is a simple MIDI-through client: all normal input events are | ||
37 | redirected to the output port immediately. | ||
38 | |||
39 | You don't need this unless you want to connect many MIDI | ||
40 | devices or applications together. | ||
41 | |||
42 | To compile this driver as a module, choose M here: the module | ||
43 | will be called snd-seq-dummy. | ||
44 | |||
45 | config SND_OSSEMUL | ||
46 | bool | ||
47 | depends on SND | ||
48 | |||
49 | config SND_MIXER_OSS | ||
50 | tristate "OSS Mixer API" | ||
51 | depends on SND | ||
52 | select SND_OSSEMUL | ||
53 | help | ||
54 | To enable OSS mixer API emulation (/dev/mixer*), say Y here | ||
55 | and read <file:Documentation/sound/alsa/OSS-Emulation.txt>. | ||
56 | |||
57 | Many programs still use the OSS API, so say Y. | ||
58 | |||
59 | To compile this driver as a module, choose M here: the module | ||
60 | will be called snd-mixer-oss. | ||
61 | |||
62 | config SND_PCM_OSS | ||
63 | tristate "OSS PCM (digital audio) API" | ||
64 | depends on SND | ||
65 | select SND_OSSEMUL | ||
66 | select SND_PCM | ||
67 | help | ||
68 | To enable OSS digital audio (PCM) emulation (/dev/dsp*), say Y | ||
69 | here and read <file:Documentation/sound/alsa/OSS-Emulation.txt>. | ||
70 | |||
71 | Many programs still use the OSS API, so say Y. | ||
72 | |||
73 | To compile this driver as a module, choose M here: the module | ||
74 | will be called snd-pcm-oss. | ||
75 | |||
76 | config SND_SEQUENCER_OSS | ||
77 | bool "OSS Sequencer API" | ||
78 | depends on SND && SND_SEQUENCER | ||
79 | select SND_OSSEMUL | ||
80 | help | ||
81 | Say Y here to enable OSS sequencer emulation (both | ||
82 | /dev/sequencer and /dev/music interfaces). | ||
83 | |||
84 | Many programs still use the OSS API, so say Y. | ||
85 | |||
86 | To compile this driver as a module, choose M here: the module | ||
87 | will be called snd-seq-oss. | ||
88 | |||
89 | config SND_RTCTIMER | ||
90 | tristate "RTC Timer support" | ||
91 | depends on SND && RTC | ||
92 | select SND_TIMER | ||
93 | help | ||
94 | Say Y here to enable RTC timer support for ALSA. ALSA uses | ||
95 | the RTC timer as a precise timing source and maps the RTC | ||
96 | timer to ALSA's timer interface. The ALSA sequencer code also | ||
97 | can use this timing source. | ||
98 | |||
99 | To compile this driver as a module, choose M here: the module | ||
100 | will be called snd-rtctimer. | ||
101 | |||
102 | config SND_VERBOSE_PRINTK | ||
103 | bool "Verbose printk" | ||
104 | depends on SND | ||
105 | help | ||
106 | Say Y here to enable verbose log messages. These messages | ||
107 | will help to identify source file and position containing | ||
108 | printed messages. | ||
109 | |||
110 | You don't need this unless you're debugging ALSA. | ||
111 | |||
112 | config SND_DEBUG | ||
113 | bool "Debug" | ||
114 | depends on SND | ||
115 | help | ||
116 | Say Y here to enable ALSA debug code. | ||
117 | |||
118 | config SND_DEBUG_MEMORY | ||
119 | bool "Debug memory" | ||
120 | depends on SND_DEBUG | ||
121 | help | ||
122 | Say Y here to enable debugging of memory allocations. | ||
123 | |||
124 | config SND_DEBUG_DETECT | ||
125 | bool "Debug detection" | ||
126 | depends on SND_DEBUG | ||
127 | help | ||
128 | Say Y here to enable extra-verbose log messages printed when | ||
129 | detecting devices. | ||
130 | |||
131 | config SND_GENERIC_PM | ||
132 | bool | ||
133 | depends on SND | ||
diff --git a/sound/core/Makefile b/sound/core/Makefile new file mode 100644 index 000000000000..764ac184b223 --- /dev/null +++ b/sound/core/Makefile | |||
@@ -0,0 +1,33 @@ | |||
1 | # | ||
2 | # Makefile for ALSA | ||
3 | # Copyright (c) 1999,2001 by Jaroslav Kysela <perex@suse.cz> | ||
4 | # | ||
5 | |||
6 | snd-objs := sound.o init.o memory.o info.o control.o misc.o \ | ||
7 | device.o wrappers.o | ||
8 | ifeq ($(CONFIG_ISA),y) | ||
9 | snd-objs += isadma.o | ||
10 | endif | ||
11 | ifeq ($(CONFIG_SND_OSSEMUL),y) | ||
12 | snd-objs += sound_oss.o info_oss.o | ||
13 | endif | ||
14 | |||
15 | snd-pcm-objs := pcm.o pcm_native.o pcm_lib.o pcm_timer.o pcm_misc.o \ | ||
16 | pcm_memory.o | ||
17 | |||
18 | snd-page-alloc-objs := memalloc.o sgbuf.o | ||
19 | |||
20 | snd-rawmidi-objs := rawmidi.o | ||
21 | snd-timer-objs := timer.o | ||
22 | snd-rtctimer-objs := rtctimer.o | ||
23 | snd-hwdep-objs := hwdep.o | ||
24 | |||
25 | obj-$(CONFIG_SND) += snd.o | ||
26 | obj-$(CONFIG_SND_HWDEP) += snd-hwdep.o | ||
27 | obj-$(CONFIG_SND_TIMER) += snd-timer.o | ||
28 | obj-$(CONFIG_SND_RTCTIMER) += snd-rtctimer.o | ||
29 | obj-$(CONFIG_SND_PCM) += snd-pcm.o snd-page-alloc.o | ||
30 | obj-$(CONFIG_SND_RAWMIDI) += snd-rawmidi.o | ||
31 | |||
32 | obj-$(CONFIG_SND_OSSEMUL) += oss/ | ||
33 | obj-$(CONFIG_SND_SEQUENCER) += seq/ | ||
diff --git a/sound/core/control.c b/sound/core/control.c new file mode 100644 index 000000000000..f4ea6bff1dd3 --- /dev/null +++ b/sound/core/control.c | |||
@@ -0,0 +1,1375 @@ | |||
1 | /* | ||
2 | * Routines for driver control interface | ||
3 | * Copyright (c) by Jaroslav Kysela <perex@suse.cz> | ||
4 | * | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #include <sound/driver.h> | ||
23 | #include <linux/threads.h> | ||
24 | #include <linux/interrupt.h> | ||
25 | #include <linux/smp_lock.h> | ||
26 | #include <linux/slab.h> | ||
27 | #include <linux/vmalloc.h> | ||
28 | #include <linux/time.h> | ||
29 | #include <sound/core.h> | ||
30 | #include <sound/minors.h> | ||
31 | #include <sound/info.h> | ||
32 | #include <sound/control.h> | ||
33 | |||
34 | /* max number of user-defined controls */ | ||
35 | #define MAX_USER_CONTROLS 32 | ||
36 | |||
37 | typedef struct _snd_kctl_ioctl { | ||
38 | struct list_head list; /* list of all ioctls */ | ||
39 | snd_kctl_ioctl_func_t fioctl; | ||
40 | } snd_kctl_ioctl_t; | ||
41 | |||
42 | #define snd_kctl_ioctl(n) list_entry(n, snd_kctl_ioctl_t, list) | ||
43 | |||
44 | static DECLARE_RWSEM(snd_ioctl_rwsem); | ||
45 | static LIST_HEAD(snd_control_ioctls); | ||
46 | #ifdef CONFIG_COMPAT | ||
47 | static LIST_HEAD(snd_control_compat_ioctls); | ||
48 | #endif | ||
49 | |||
50 | static int snd_ctl_open(struct inode *inode, struct file *file) | ||
51 | { | ||
52 | int cardnum = SNDRV_MINOR_CARD(iminor(inode)); | ||
53 | unsigned long flags; | ||
54 | snd_card_t *card; | ||
55 | snd_ctl_file_t *ctl; | ||
56 | int err; | ||
57 | |||
58 | card = snd_cards[cardnum]; | ||
59 | if (!card) { | ||
60 | err = -ENODEV; | ||
61 | goto __error1; | ||
62 | } | ||
63 | err = snd_card_file_add(card, file); | ||
64 | if (err < 0) { | ||
65 | err = -ENODEV; | ||
66 | goto __error1; | ||
67 | } | ||
68 | if (!try_module_get(card->module)) { | ||
69 | err = -EFAULT; | ||
70 | goto __error2; | ||
71 | } | ||
72 | ctl = kcalloc(1, sizeof(*ctl), GFP_KERNEL); | ||
73 | if (ctl == NULL) { | ||
74 | err = -ENOMEM; | ||
75 | goto __error; | ||
76 | } | ||
77 | INIT_LIST_HEAD(&ctl->events); | ||
78 | init_waitqueue_head(&ctl->change_sleep); | ||
79 | spin_lock_init(&ctl->read_lock); | ||
80 | ctl->card = card; | ||
81 | ctl->pid = current->pid; | ||
82 | file->private_data = ctl; | ||
83 | write_lock_irqsave(&card->ctl_files_rwlock, flags); | ||
84 | list_add_tail(&ctl->list, &card->ctl_files); | ||
85 | write_unlock_irqrestore(&card->ctl_files_rwlock, flags); | ||
86 | return 0; | ||
87 | |||
88 | __error: | ||
89 | module_put(card->module); | ||
90 | __error2: | ||
91 | snd_card_file_remove(card, file); | ||
92 | __error1: | ||
93 | return err; | ||
94 | } | ||
95 | |||
96 | static void snd_ctl_empty_read_queue(snd_ctl_file_t * ctl) | ||
97 | { | ||
98 | snd_kctl_event_t *cread; | ||
99 | |||
100 | spin_lock(&ctl->read_lock); | ||
101 | while (!list_empty(&ctl->events)) { | ||
102 | cread = snd_kctl_event(ctl->events.next); | ||
103 | list_del(&cread->list); | ||
104 | kfree(cread); | ||
105 | } | ||
106 | spin_unlock(&ctl->read_lock); | ||
107 | } | ||
108 | |||
109 | static int snd_ctl_release(struct inode *inode, struct file *file) | ||
110 | { | ||
111 | unsigned long flags; | ||
112 | struct list_head *list; | ||
113 | snd_card_t *card; | ||
114 | snd_ctl_file_t *ctl; | ||
115 | snd_kcontrol_t *control; | ||
116 | unsigned int idx; | ||
117 | |||
118 | ctl = file->private_data; | ||
119 | fasync_helper(-1, file, 0, &ctl->fasync); | ||
120 | file->private_data = NULL; | ||
121 | card = ctl->card; | ||
122 | write_lock_irqsave(&card->ctl_files_rwlock, flags); | ||
123 | list_del(&ctl->list); | ||
124 | write_unlock_irqrestore(&card->ctl_files_rwlock, flags); | ||
125 | down_write(&card->controls_rwsem); | ||
126 | list_for_each(list, &card->controls) { | ||
127 | control = snd_kcontrol(list); | ||
128 | for (idx = 0; idx < control->count; idx++) | ||
129 | if (control->vd[idx].owner == ctl) | ||
130 | control->vd[idx].owner = NULL; | ||
131 | } | ||
132 | up_write(&card->controls_rwsem); | ||
133 | snd_ctl_empty_read_queue(ctl); | ||
134 | kfree(ctl); | ||
135 | module_put(card->module); | ||
136 | snd_card_file_remove(card, file); | ||
137 | return 0; | ||
138 | } | ||
139 | |||
140 | void snd_ctl_notify(snd_card_t *card, unsigned int mask, snd_ctl_elem_id_t *id) | ||
141 | { | ||
142 | unsigned long flags; | ||
143 | struct list_head *flist; | ||
144 | snd_ctl_file_t *ctl; | ||
145 | snd_kctl_event_t *ev; | ||
146 | |||
147 | snd_runtime_check(card != NULL && id != NULL, return); | ||
148 | read_lock(&card->ctl_files_rwlock); | ||
149 | #if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE) | ||
150 | card->mixer_oss_change_count++; | ||
151 | #endif | ||
152 | list_for_each(flist, &card->ctl_files) { | ||
153 | struct list_head *elist; | ||
154 | ctl = snd_ctl_file(flist); | ||
155 | if (!ctl->subscribed) | ||
156 | continue; | ||
157 | spin_lock_irqsave(&ctl->read_lock, flags); | ||
158 | list_for_each(elist, &ctl->events) { | ||
159 | ev = snd_kctl_event(elist); | ||
160 | if (ev->id.numid == id->numid) { | ||
161 | ev->mask |= mask; | ||
162 | goto _found; | ||
163 | } | ||
164 | } | ||
165 | ev = kcalloc(1, sizeof(*ev), GFP_ATOMIC); | ||
166 | if (ev) { | ||
167 | ev->id = *id; | ||
168 | ev->mask = mask; | ||
169 | list_add_tail(&ev->list, &ctl->events); | ||
170 | } else { | ||
171 | snd_printk(KERN_ERR "No memory available to allocate event\n"); | ||
172 | } | ||
173 | _found: | ||
174 | wake_up(&ctl->change_sleep); | ||
175 | spin_unlock_irqrestore(&ctl->read_lock, flags); | ||
176 | kill_fasync(&ctl->fasync, SIGIO, POLL_IN); | ||
177 | } | ||
178 | read_unlock(&card->ctl_files_rwlock); | ||
179 | } | ||
180 | |||
181 | /** | ||
182 | * snd_ctl_new - create a control instance from the template | ||
183 | * @control: the control template | ||
184 | * @access: the default control access | ||
185 | * | ||
186 | * Allocates a new snd_kcontrol_t instance and copies the given template | ||
187 | * to the new instance. It does not copy volatile data (access). | ||
188 | * | ||
189 | * Returns the pointer of the new instance, or NULL on failure. | ||
190 | */ | ||
191 | snd_kcontrol_t *snd_ctl_new(snd_kcontrol_t * control, unsigned int access) | ||
192 | { | ||
193 | snd_kcontrol_t *kctl; | ||
194 | unsigned int idx; | ||
195 | |||
196 | snd_runtime_check(control != NULL, return NULL); | ||
197 | snd_runtime_check(control->count > 0, return NULL); | ||
198 | kctl = kcalloc(1, sizeof(*kctl) + sizeof(snd_kcontrol_volatile_t) * control->count, GFP_KERNEL); | ||
199 | if (kctl == NULL) | ||
200 | return NULL; | ||
201 | *kctl = *control; | ||
202 | for (idx = 0; idx < kctl->count; idx++) | ||
203 | kctl->vd[idx].access = access; | ||
204 | return kctl; | ||
205 | } | ||
206 | |||
207 | /** | ||
208 | * snd_ctl_new1 - create a control instance from the template | ||
209 | * @ncontrol: the initialization record | ||
210 | * @private_data: the private data to set | ||
211 | * | ||
212 | * Allocates a new snd_kcontrol_t instance and initialize from the given | ||
213 | * template. When the access field of ncontrol is 0, it's assumed as | ||
214 | * READWRITE access. When the count field is 0, it's assumes as one. | ||
215 | * | ||
216 | * Returns the pointer of the newly generated instance, or NULL on failure. | ||
217 | */ | ||
218 | snd_kcontrol_t *snd_ctl_new1(snd_kcontrol_new_t * ncontrol, void *private_data) | ||
219 | { | ||
220 | snd_kcontrol_t kctl; | ||
221 | unsigned int access; | ||
222 | |||
223 | snd_runtime_check(ncontrol != NULL, return NULL); | ||
224 | snd_assert(ncontrol->info != NULL, return NULL); | ||
225 | memset(&kctl, 0, sizeof(kctl)); | ||
226 | kctl.id.iface = ncontrol->iface; | ||
227 | kctl.id.device = ncontrol->device; | ||
228 | kctl.id.subdevice = ncontrol->subdevice; | ||
229 | if (ncontrol->name) | ||
230 | strlcpy(kctl.id.name, ncontrol->name, sizeof(kctl.id.name)); | ||
231 | kctl.id.index = ncontrol->index; | ||
232 | kctl.count = ncontrol->count ? ncontrol->count : 1; | ||
233 | access = ncontrol->access == 0 ? SNDRV_CTL_ELEM_ACCESS_READWRITE : | ||
234 | (ncontrol->access & (SNDRV_CTL_ELEM_ACCESS_READWRITE|SNDRV_CTL_ELEM_ACCESS_INACTIVE| | ||
235 | SNDRV_CTL_ELEM_ACCESS_DINDIRECT|SNDRV_CTL_ELEM_ACCESS_INDIRECT)); | ||
236 | kctl.info = ncontrol->info; | ||
237 | kctl.get = ncontrol->get; | ||
238 | kctl.put = ncontrol->put; | ||
239 | kctl.private_value = ncontrol->private_value; | ||
240 | kctl.private_data = private_data; | ||
241 | return snd_ctl_new(&kctl, access); | ||
242 | } | ||
243 | |||
244 | /** | ||
245 | * snd_ctl_free_one - release the control instance | ||
246 | * @kcontrol: the control instance | ||
247 | * | ||
248 | * Releases the control instance created via snd_ctl_new() | ||
249 | * or snd_ctl_new1(). | ||
250 | * Don't call this after the control was added to the card. | ||
251 | */ | ||
252 | void snd_ctl_free_one(snd_kcontrol_t * kcontrol) | ||
253 | { | ||
254 | if (kcontrol) { | ||
255 | if (kcontrol->private_free) | ||
256 | kcontrol->private_free(kcontrol); | ||
257 | kfree(kcontrol); | ||
258 | } | ||
259 | } | ||
260 | |||
261 | static unsigned int snd_ctl_hole_check(snd_card_t * card, | ||
262 | unsigned int count) | ||
263 | { | ||
264 | struct list_head *list; | ||
265 | snd_kcontrol_t *kctl; | ||
266 | |||
267 | list_for_each(list, &card->controls) { | ||
268 | kctl = snd_kcontrol(list); | ||
269 | if ((kctl->id.numid <= card->last_numid && | ||
270 | kctl->id.numid + kctl->count > card->last_numid) || | ||
271 | (kctl->id.numid <= card->last_numid + count - 1 && | ||
272 | kctl->id.numid + kctl->count > card->last_numid + count - 1)) | ||
273 | return card->last_numid = kctl->id.numid + kctl->count - 1; | ||
274 | } | ||
275 | return card->last_numid; | ||
276 | } | ||
277 | |||
278 | static int snd_ctl_find_hole(snd_card_t * card, unsigned int count) | ||
279 | { | ||
280 | unsigned int last_numid, iter = 100000; | ||
281 | |||
282 | last_numid = card->last_numid; | ||
283 | while (last_numid != snd_ctl_hole_check(card, count)) { | ||
284 | if (--iter == 0) { | ||
285 | /* this situation is very unlikely */ | ||
286 | snd_printk(KERN_ERR "unable to allocate new control numid\n"); | ||
287 | return -ENOMEM; | ||
288 | } | ||
289 | last_numid = card->last_numid; | ||
290 | } | ||
291 | return 0; | ||
292 | } | ||
293 | |||
294 | /** | ||
295 | * snd_ctl_add - add the control instance to the card | ||
296 | * @card: the card instance | ||
297 | * @kcontrol: the control instance to add | ||
298 | * | ||
299 | * Adds the control instance created via snd_ctl_new() or | ||
300 | * snd_ctl_new1() to the given card. Assigns also an unique | ||
301 | * numid used for fast search. | ||
302 | * | ||
303 | * Returns zero if successful, or a negative error code on failure. | ||
304 | * | ||
305 | * It frees automatically the control which cannot be added. | ||
306 | */ | ||
307 | int snd_ctl_add(snd_card_t * card, snd_kcontrol_t * kcontrol) | ||
308 | { | ||
309 | snd_ctl_elem_id_t id; | ||
310 | unsigned int idx; | ||
311 | |||
312 | snd_runtime_check(card != NULL && kcontrol != NULL, return -EINVAL); | ||
313 | snd_assert(kcontrol->info != NULL, return -EINVAL); | ||
314 | id = kcontrol->id; | ||
315 | down_write(&card->controls_rwsem); | ||
316 | if (snd_ctl_find_id(card, &id)) { | ||
317 | up_write(&card->controls_rwsem); | ||
318 | snd_ctl_free_one(kcontrol); | ||
319 | snd_printd(KERN_ERR "control %i:%i:%i:%s:%i is already present\n", | ||
320 | id.iface, | ||
321 | id.device, | ||
322 | id.subdevice, | ||
323 | id.name, | ||
324 | id.index); | ||
325 | return -EBUSY; | ||
326 | } | ||
327 | if (snd_ctl_find_hole(card, kcontrol->count) < 0) { | ||
328 | up_write(&card->controls_rwsem); | ||
329 | snd_ctl_free_one(kcontrol); | ||
330 | return -ENOMEM; | ||
331 | } | ||
332 | list_add_tail(&kcontrol->list, &card->controls); | ||
333 | card->controls_count += kcontrol->count; | ||
334 | kcontrol->id.numid = card->last_numid + 1; | ||
335 | card->last_numid += kcontrol->count; | ||
336 | up_write(&card->controls_rwsem); | ||
337 | for (idx = 0; idx < kcontrol->count; idx++, id.index++, id.numid++) | ||
338 | snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_ADD, &id); | ||
339 | return 0; | ||
340 | } | ||
341 | |||
342 | /** | ||
343 | * snd_ctl_remove - remove the control from the card and release it | ||
344 | * @card: the card instance | ||
345 | * @kcontrol: the control instance to remove | ||
346 | * | ||
347 | * Removes the control from the card and then releases the instance. | ||
348 | * You don't need to call snd_ctl_free_one(). You must be in | ||
349 | * the write lock - down_write(&card->controls_rwsem). | ||
350 | * | ||
351 | * Returns 0 if successful, or a negative error code on failure. | ||
352 | */ | ||
353 | int snd_ctl_remove(snd_card_t * card, snd_kcontrol_t * kcontrol) | ||
354 | { | ||
355 | snd_ctl_elem_id_t id; | ||
356 | unsigned int idx; | ||
357 | |||
358 | snd_runtime_check(card != NULL && kcontrol != NULL, return -EINVAL); | ||
359 | list_del(&kcontrol->list); | ||
360 | card->controls_count -= kcontrol->count; | ||
361 | id = kcontrol->id; | ||
362 | for (idx = 0; idx < kcontrol->count; idx++, id.index++, id.numid++) | ||
363 | snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_REMOVE, &id); | ||
364 | snd_ctl_free_one(kcontrol); | ||
365 | return 0; | ||
366 | } | ||
367 | |||
368 | /** | ||
369 | * snd_ctl_remove_id - remove the control of the given id and release it | ||
370 | * @card: the card instance | ||
371 | * @id: the control id to remove | ||
372 | * | ||
373 | * Finds the control instance with the given id, removes it from the | ||
374 | * card list and releases it. | ||
375 | * | ||
376 | * Returns 0 if successful, or a negative error code on failure. | ||
377 | */ | ||
378 | int snd_ctl_remove_id(snd_card_t * card, snd_ctl_elem_id_t *id) | ||
379 | { | ||
380 | snd_kcontrol_t *kctl; | ||
381 | int ret; | ||
382 | |||
383 | down_write(&card->controls_rwsem); | ||
384 | kctl = snd_ctl_find_id(card, id); | ||
385 | if (kctl == NULL) { | ||
386 | up_write(&card->controls_rwsem); | ||
387 | return -ENOENT; | ||
388 | } | ||
389 | ret = snd_ctl_remove(card, kctl); | ||
390 | up_write(&card->controls_rwsem); | ||
391 | return ret; | ||
392 | } | ||
393 | |||
394 | /** | ||
395 | * snd_ctl_remove_unlocked_id - remove the unlocked control of the given id and release it | ||
396 | * @file: active control handle | ||
397 | * @id: the control id to remove | ||
398 | * | ||
399 | * Finds the control instance with the given id, removes it from the | ||
400 | * card list and releases it. | ||
401 | * | ||
402 | * Returns 0 if successful, or a negative error code on failure. | ||
403 | */ | ||
404 | static int snd_ctl_remove_unlocked_id(snd_ctl_file_t * file, snd_ctl_elem_id_t *id) | ||
405 | { | ||
406 | snd_card_t *card = file->card; | ||
407 | snd_kcontrol_t *kctl; | ||
408 | int idx, ret; | ||
409 | |||
410 | down_write(&card->controls_rwsem); | ||
411 | kctl = snd_ctl_find_id(card, id); | ||
412 | if (kctl == NULL) { | ||
413 | up_write(&card->controls_rwsem); | ||
414 | return -ENOENT; | ||
415 | } | ||
416 | for (idx = 0; idx < kctl->count; idx++) | ||
417 | if (kctl->vd[idx].owner != NULL && kctl->vd[idx].owner != file) { | ||
418 | up_write(&card->controls_rwsem); | ||
419 | return -EBUSY; | ||
420 | } | ||
421 | ret = snd_ctl_remove(card, kctl); | ||
422 | up_write(&card->controls_rwsem); | ||
423 | return ret; | ||
424 | } | ||
425 | |||
426 | /** | ||
427 | * snd_ctl_rename_id - replace the id of a control on the card | ||
428 | * @card: the card instance | ||
429 | * @src_id: the old id | ||
430 | * @dst_id: the new id | ||
431 | * | ||
432 | * Finds the control with the old id from the card, and replaces the | ||
433 | * id with the new one. | ||
434 | * | ||
435 | * Returns zero if successful, or a negative error code on failure. | ||
436 | */ | ||
437 | int snd_ctl_rename_id(snd_card_t * card, snd_ctl_elem_id_t *src_id, snd_ctl_elem_id_t *dst_id) | ||
438 | { | ||
439 | snd_kcontrol_t *kctl; | ||
440 | |||
441 | down_write(&card->controls_rwsem); | ||
442 | kctl = snd_ctl_find_id(card, src_id); | ||
443 | if (kctl == NULL) { | ||
444 | up_write(&card->controls_rwsem); | ||
445 | return -ENOENT; | ||
446 | } | ||
447 | kctl->id = *dst_id; | ||
448 | kctl->id.numid = card->last_numid + 1; | ||
449 | card->last_numid += kctl->count; | ||
450 | up_write(&card->controls_rwsem); | ||
451 | return 0; | ||
452 | } | ||
453 | |||
454 | /** | ||
455 | * snd_ctl_find_numid - find the control instance with the given number-id | ||
456 | * @card: the card instance | ||
457 | * @numid: the number-id to search | ||
458 | * | ||
459 | * Finds the control instance with the given number-id from the card. | ||
460 | * | ||
461 | * Returns the pointer of the instance if found, or NULL if not. | ||
462 | * | ||
463 | * The caller must down card->controls_rwsem before calling this function | ||
464 | * (if the race condition can happen). | ||
465 | */ | ||
466 | snd_kcontrol_t *snd_ctl_find_numid(snd_card_t * card, unsigned int numid) | ||
467 | { | ||
468 | struct list_head *list; | ||
469 | snd_kcontrol_t *kctl; | ||
470 | |||
471 | snd_runtime_check(card != NULL && numid != 0, return NULL); | ||
472 | list_for_each(list, &card->controls) { | ||
473 | kctl = snd_kcontrol(list); | ||
474 | if (kctl->id.numid <= numid && kctl->id.numid + kctl->count > numid) | ||
475 | return kctl; | ||
476 | } | ||
477 | return NULL; | ||
478 | } | ||
479 | |||
480 | /** | ||
481 | * snd_ctl_find_id - find the control instance with the given id | ||
482 | * @card: the card instance | ||
483 | * @id: the id to search | ||
484 | * | ||
485 | * Finds the control instance with the given id from the card. | ||
486 | * | ||
487 | * Returns the pointer of the instance if found, or NULL if not. | ||
488 | * | ||
489 | * The caller must down card->controls_rwsem before calling this function | ||
490 | * (if the race condition can happen). | ||
491 | */ | ||
492 | snd_kcontrol_t *snd_ctl_find_id(snd_card_t * card, snd_ctl_elem_id_t *id) | ||
493 | { | ||
494 | struct list_head *list; | ||
495 | snd_kcontrol_t *kctl; | ||
496 | |||
497 | snd_runtime_check(card != NULL && id != NULL, return NULL); | ||
498 | if (id->numid != 0) | ||
499 | return snd_ctl_find_numid(card, id->numid); | ||
500 | list_for_each(list, &card->controls) { | ||
501 | kctl = snd_kcontrol(list); | ||
502 | if (kctl->id.iface != id->iface) | ||
503 | continue; | ||
504 | if (kctl->id.device != id->device) | ||
505 | continue; | ||
506 | if (kctl->id.subdevice != id->subdevice) | ||
507 | continue; | ||
508 | if (strncmp(kctl->id.name, id->name, sizeof(kctl->id.name))) | ||
509 | continue; | ||
510 | if (kctl->id.index > id->index) | ||
511 | continue; | ||
512 | if (kctl->id.index + kctl->count <= id->index) | ||
513 | continue; | ||
514 | return kctl; | ||
515 | } | ||
516 | return NULL; | ||
517 | } | ||
518 | |||
519 | static int snd_ctl_card_info(snd_card_t * card, snd_ctl_file_t * ctl, | ||
520 | unsigned int cmd, void __user *arg) | ||
521 | { | ||
522 | snd_ctl_card_info_t *info; | ||
523 | |||
524 | info = kcalloc(1, sizeof(*info), GFP_KERNEL); | ||
525 | if (! info) | ||
526 | return -ENOMEM; | ||
527 | down_read(&snd_ioctl_rwsem); | ||
528 | info->card = card->number; | ||
529 | strlcpy(info->id, card->id, sizeof(info->id)); | ||
530 | strlcpy(info->driver, card->driver, sizeof(info->driver)); | ||
531 | strlcpy(info->name, card->shortname, sizeof(info->name)); | ||
532 | strlcpy(info->longname, card->longname, sizeof(info->longname)); | ||
533 | strlcpy(info->mixername, card->mixername, sizeof(info->mixername)); | ||
534 | strlcpy(info->components, card->components, sizeof(info->components)); | ||
535 | up_read(&snd_ioctl_rwsem); | ||
536 | if (copy_to_user(arg, info, sizeof(snd_ctl_card_info_t))) { | ||
537 | kfree(info); | ||
538 | return -EFAULT; | ||
539 | } | ||
540 | kfree(info); | ||
541 | return 0; | ||
542 | } | ||
543 | |||
544 | static int snd_ctl_elem_list(snd_card_t *card, snd_ctl_elem_list_t __user *_list) | ||
545 | { | ||
546 | struct list_head *plist; | ||
547 | snd_ctl_elem_list_t list; | ||
548 | snd_kcontrol_t *kctl; | ||
549 | snd_ctl_elem_id_t *dst, *id; | ||
550 | unsigned int offset, space, first, jidx; | ||
551 | |||
552 | if (copy_from_user(&list, _list, sizeof(list))) | ||
553 | return -EFAULT; | ||
554 | offset = list.offset; | ||
555 | space = list.space; | ||
556 | first = 0; | ||
557 | /* try limit maximum space */ | ||
558 | if (space > 16384) | ||
559 | return -ENOMEM; | ||
560 | if (space > 0) { | ||
561 | /* allocate temporary buffer for atomic operation */ | ||
562 | dst = vmalloc(space * sizeof(snd_ctl_elem_id_t)); | ||
563 | if (dst == NULL) | ||
564 | return -ENOMEM; | ||
565 | down_read(&card->controls_rwsem); | ||
566 | list.count = card->controls_count; | ||
567 | plist = card->controls.next; | ||
568 | while (plist != &card->controls) { | ||
569 | if (offset == 0) | ||
570 | break; | ||
571 | kctl = snd_kcontrol(plist); | ||
572 | if (offset < kctl->count) | ||
573 | break; | ||
574 | offset -= kctl->count; | ||
575 | plist = plist->next; | ||
576 | } | ||
577 | list.used = 0; | ||
578 | id = dst; | ||
579 | while (space > 0 && plist != &card->controls) { | ||
580 | kctl = snd_kcontrol(plist); | ||
581 | for (jidx = offset; space > 0 && jidx < kctl->count; jidx++) { | ||
582 | snd_ctl_build_ioff(id, kctl, jidx); | ||
583 | id++; | ||
584 | space--; | ||
585 | list.used++; | ||
586 | } | ||
587 | plist = plist->next; | ||
588 | offset = 0; | ||
589 | } | ||
590 | up_read(&card->controls_rwsem); | ||
591 | if (list.used > 0 && copy_to_user(list.pids, dst, list.used * sizeof(snd_ctl_elem_id_t))) { | ||
592 | vfree(dst); | ||
593 | return -EFAULT; | ||
594 | } | ||
595 | vfree(dst); | ||
596 | } else { | ||
597 | down_read(&card->controls_rwsem); | ||
598 | list.count = card->controls_count; | ||
599 | up_read(&card->controls_rwsem); | ||
600 | } | ||
601 | if (copy_to_user(_list, &list, sizeof(list))) | ||
602 | return -EFAULT; | ||
603 | return 0; | ||
604 | } | ||
605 | |||
606 | static int snd_ctl_elem_info(snd_ctl_file_t *ctl, snd_ctl_elem_info_t *info) | ||
607 | { | ||
608 | snd_card_t *card = ctl->card; | ||
609 | snd_kcontrol_t *kctl; | ||
610 | snd_kcontrol_volatile_t *vd; | ||
611 | unsigned int index_offset; | ||
612 | int result; | ||
613 | |||
614 | down_read(&card->controls_rwsem); | ||
615 | kctl = snd_ctl_find_id(card, &info->id); | ||
616 | if (kctl == NULL) { | ||
617 | up_read(&card->controls_rwsem); | ||
618 | return -ENOENT; | ||
619 | } | ||
620 | #ifdef CONFIG_SND_DEBUG | ||
621 | info->access = 0; | ||
622 | #endif | ||
623 | result = kctl->info(kctl, info); | ||
624 | if (result >= 0) { | ||
625 | snd_assert(info->access == 0, ); | ||
626 | index_offset = snd_ctl_get_ioff(kctl, &info->id); | ||
627 | vd = &kctl->vd[index_offset]; | ||
628 | snd_ctl_build_ioff(&info->id, kctl, index_offset); | ||
629 | info->access = vd->access; | ||
630 | if (vd->owner) { | ||
631 | info->access |= SNDRV_CTL_ELEM_ACCESS_LOCK; | ||
632 | if (vd->owner == ctl) | ||
633 | info->access |= SNDRV_CTL_ELEM_ACCESS_OWNER; | ||
634 | info->owner = vd->owner_pid; | ||
635 | } else { | ||
636 | info->owner = -1; | ||
637 | } | ||
638 | } | ||
639 | up_read(&card->controls_rwsem); | ||
640 | return result; | ||
641 | } | ||
642 | |||
643 | static int snd_ctl_elem_info_user(snd_ctl_file_t *ctl, snd_ctl_elem_info_t __user *_info) | ||
644 | { | ||
645 | snd_ctl_elem_info_t info; | ||
646 | int result; | ||
647 | |||
648 | if (copy_from_user(&info, _info, sizeof(info))) | ||
649 | return -EFAULT; | ||
650 | result = snd_ctl_elem_info(ctl, &info); | ||
651 | if (result >= 0) | ||
652 | if (copy_to_user(_info, &info, sizeof(info))) | ||
653 | return -EFAULT; | ||
654 | return result; | ||
655 | } | ||
656 | |||
657 | int snd_ctl_elem_read(snd_card_t *card, snd_ctl_elem_value_t *control) | ||
658 | { | ||
659 | snd_kcontrol_t *kctl; | ||
660 | snd_kcontrol_volatile_t *vd; | ||
661 | unsigned int index_offset; | ||
662 | int result, indirect; | ||
663 | |||
664 | down_read(&card->controls_rwsem); | ||
665 | kctl = snd_ctl_find_id(card, &control->id); | ||
666 | if (kctl == NULL) { | ||
667 | result = -ENOENT; | ||
668 | } else { | ||
669 | index_offset = snd_ctl_get_ioff(kctl, &control->id); | ||
670 | vd = &kctl->vd[index_offset]; | ||
671 | indirect = vd->access & SNDRV_CTL_ELEM_ACCESS_INDIRECT ? 1 : 0; | ||
672 | if (control->indirect != indirect) { | ||
673 | result = -EACCES; | ||
674 | } else { | ||
675 | if ((vd->access & SNDRV_CTL_ELEM_ACCESS_READ) && kctl->get != NULL) { | ||
676 | snd_ctl_build_ioff(&control->id, kctl, index_offset); | ||
677 | result = kctl->get(kctl, control); | ||
678 | } else { | ||
679 | result = -EPERM; | ||
680 | } | ||
681 | } | ||
682 | } | ||
683 | up_read(&card->controls_rwsem); | ||
684 | return result; | ||
685 | } | ||
686 | |||
687 | static int snd_ctl_elem_read_user(snd_card_t *card, snd_ctl_elem_value_t __user *_control) | ||
688 | { | ||
689 | snd_ctl_elem_value_t *control; | ||
690 | int result; | ||
691 | |||
692 | control = kmalloc(sizeof(*control), GFP_KERNEL); | ||
693 | if (control == NULL) | ||
694 | return -ENOMEM; | ||
695 | if (copy_from_user(control, _control, sizeof(*control))) { | ||
696 | kfree(control); | ||
697 | return -EFAULT; | ||
698 | } | ||
699 | result = snd_ctl_elem_read(card, control); | ||
700 | if (result >= 0) | ||
701 | if (copy_to_user(_control, control, sizeof(*control))) | ||
702 | result = -EFAULT; | ||
703 | kfree(control); | ||
704 | return result; | ||
705 | } | ||
706 | |||
707 | int snd_ctl_elem_write(snd_card_t *card, snd_ctl_file_t *file, snd_ctl_elem_value_t *control) | ||
708 | { | ||
709 | snd_kcontrol_t *kctl; | ||
710 | snd_kcontrol_volatile_t *vd; | ||
711 | unsigned int index_offset; | ||
712 | int result, indirect; | ||
713 | |||
714 | down_read(&card->controls_rwsem); | ||
715 | kctl = snd_ctl_find_id(card, &control->id); | ||
716 | if (kctl == NULL) { | ||
717 | result = -ENOENT; | ||
718 | } else { | ||
719 | index_offset = snd_ctl_get_ioff(kctl, &control->id); | ||
720 | vd = &kctl->vd[index_offset]; | ||
721 | indirect = vd->access & SNDRV_CTL_ELEM_ACCESS_INDIRECT ? 1 : 0; | ||
722 | if (control->indirect != indirect) { | ||
723 | result = -EACCES; | ||
724 | } else { | ||
725 | if (!(vd->access & SNDRV_CTL_ELEM_ACCESS_WRITE) || | ||
726 | kctl->put == NULL || | ||
727 | (file && vd->owner != NULL && vd->owner != file)) { | ||
728 | result = -EPERM; | ||
729 | } else { | ||
730 | snd_ctl_build_ioff(&control->id, kctl, index_offset); | ||
731 | result = kctl->put(kctl, control); | ||
732 | } | ||
733 | if (result > 0) { | ||
734 | up_read(&card->controls_rwsem); | ||
735 | snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &control->id); | ||
736 | return 0; | ||
737 | } | ||
738 | } | ||
739 | } | ||
740 | up_read(&card->controls_rwsem); | ||
741 | return result; | ||
742 | } | ||
743 | |||
744 | static int snd_ctl_elem_write_user(snd_ctl_file_t *file, snd_ctl_elem_value_t __user *_control) | ||
745 | { | ||
746 | snd_ctl_elem_value_t *control; | ||
747 | int result; | ||
748 | |||
749 | control = kmalloc(sizeof(*control), GFP_KERNEL); | ||
750 | if (control == NULL) | ||
751 | return -ENOMEM; | ||
752 | if (copy_from_user(control, _control, sizeof(*control))) { | ||
753 | kfree(control); | ||
754 | return -EFAULT; | ||
755 | } | ||
756 | result = snd_ctl_elem_write(file->card, file, control); | ||
757 | if (result >= 0) | ||
758 | if (copy_to_user(_control, control, sizeof(*control))) | ||
759 | result = -EFAULT; | ||
760 | kfree(control); | ||
761 | return result; | ||
762 | } | ||
763 | |||
764 | static int snd_ctl_elem_lock(snd_ctl_file_t *file, snd_ctl_elem_id_t __user *_id) | ||
765 | { | ||
766 | snd_card_t *card = file->card; | ||
767 | snd_ctl_elem_id_t id; | ||
768 | snd_kcontrol_t *kctl; | ||
769 | snd_kcontrol_volatile_t *vd; | ||
770 | int result; | ||
771 | |||
772 | if (copy_from_user(&id, _id, sizeof(id))) | ||
773 | return -EFAULT; | ||
774 | down_write(&card->controls_rwsem); | ||
775 | kctl = snd_ctl_find_id(card, &id); | ||
776 | if (kctl == NULL) { | ||
777 | result = -ENOENT; | ||
778 | } else { | ||
779 | vd = &kctl->vd[snd_ctl_get_ioff(kctl, &id)]; | ||
780 | if (vd->owner != NULL) | ||
781 | result = -EBUSY; | ||
782 | else { | ||
783 | vd->owner = file; | ||
784 | vd->owner_pid = current->pid; | ||
785 | result = 0; | ||
786 | } | ||
787 | } | ||
788 | up_write(&card->controls_rwsem); | ||
789 | return result; | ||
790 | } | ||
791 | |||
792 | static int snd_ctl_elem_unlock(snd_ctl_file_t *file, snd_ctl_elem_id_t __user *_id) | ||
793 | { | ||
794 | snd_card_t *card = file->card; | ||
795 | snd_ctl_elem_id_t id; | ||
796 | snd_kcontrol_t *kctl; | ||
797 | snd_kcontrol_volatile_t *vd; | ||
798 | int result; | ||
799 | |||
800 | if (copy_from_user(&id, _id, sizeof(id))) | ||
801 | return -EFAULT; | ||
802 | down_write(&card->controls_rwsem); | ||
803 | kctl = snd_ctl_find_id(card, &id); | ||
804 | if (kctl == NULL) { | ||
805 | result = -ENOENT; | ||
806 | } else { | ||
807 | vd = &kctl->vd[snd_ctl_get_ioff(kctl, &id)]; | ||
808 | if (vd->owner == NULL) | ||
809 | result = -EINVAL; | ||
810 | else if (vd->owner != file) | ||
811 | result = -EPERM; | ||
812 | else { | ||
813 | vd->owner = NULL; | ||
814 | vd->owner_pid = 0; | ||
815 | result = 0; | ||
816 | } | ||
817 | } | ||
818 | up_write(&card->controls_rwsem); | ||
819 | return result; | ||
820 | } | ||
821 | |||
822 | struct user_element { | ||
823 | snd_ctl_elem_info_t info; | ||
824 | void *elem_data; /* element data */ | ||
825 | unsigned long elem_data_size; /* size of element data in bytes */ | ||
826 | void *priv_data; /* private data (like strings for enumerated type) */ | ||
827 | unsigned long priv_data_size; /* size of private data in bytes */ | ||
828 | }; | ||
829 | |||
830 | static int snd_ctl_elem_user_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) | ||
831 | { | ||
832 | struct user_element *ue = kcontrol->private_data; | ||
833 | |||
834 | *uinfo = ue->info; | ||
835 | return 0; | ||
836 | } | ||
837 | |||
838 | static int snd_ctl_elem_user_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
839 | { | ||
840 | struct user_element *ue = kcontrol->private_data; | ||
841 | |||
842 | memcpy(&ucontrol->value, ue->elem_data, ue->elem_data_size); | ||
843 | return 0; | ||
844 | } | ||
845 | |||
846 | static int snd_ctl_elem_user_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
847 | { | ||
848 | int change; | ||
849 | struct user_element *ue = kcontrol->private_data; | ||
850 | |||
851 | change = memcmp(&ucontrol->value, ue->elem_data, ue->elem_data_size) != 0; | ||
852 | if (change) | ||
853 | memcpy(ue->elem_data, &ucontrol->value, ue->elem_data_size); | ||
854 | return change; | ||
855 | } | ||
856 | |||
857 | static void snd_ctl_elem_user_free(snd_kcontrol_t * kcontrol) | ||
858 | { | ||
859 | kfree(kcontrol->private_data); | ||
860 | } | ||
861 | |||
862 | static int snd_ctl_elem_add(snd_ctl_file_t *file, snd_ctl_elem_info_t *info, int replace) | ||
863 | { | ||
864 | snd_card_t *card = file->card; | ||
865 | snd_kcontrol_t kctl, *_kctl; | ||
866 | unsigned int access; | ||
867 | long private_size; | ||
868 | struct user_element *ue; | ||
869 | int idx, err; | ||
870 | |||
871 | if (card->user_ctl_count >= MAX_USER_CONTROLS) | ||
872 | return -ENOMEM; | ||
873 | if (info->count > 1024) | ||
874 | return -EINVAL; | ||
875 | access = info->access == 0 ? SNDRV_CTL_ELEM_ACCESS_READWRITE : | ||
876 | (info->access & (SNDRV_CTL_ELEM_ACCESS_READWRITE|SNDRV_CTL_ELEM_ACCESS_INACTIVE)); | ||
877 | info->id.numid = 0; | ||
878 | memset(&kctl, 0, sizeof(kctl)); | ||
879 | down_write(&card->controls_rwsem); | ||
880 | _kctl = snd_ctl_find_id(card, &info->id); | ||
881 | err = 0; | ||
882 | if (_kctl) { | ||
883 | if (replace) | ||
884 | err = snd_ctl_remove(card, _kctl); | ||
885 | else | ||
886 | err = -EBUSY; | ||
887 | } else { | ||
888 | if (replace) | ||
889 | err = -ENOENT; | ||
890 | } | ||
891 | up_write(&card->controls_rwsem); | ||
892 | if (err < 0) | ||
893 | return err; | ||
894 | memcpy(&kctl.id, &info->id, sizeof(info->id)); | ||
895 | kctl.count = info->owner ? info->owner : 1; | ||
896 | access |= SNDRV_CTL_ELEM_ACCESS_USER; | ||
897 | kctl.info = snd_ctl_elem_user_info; | ||
898 | if (access & SNDRV_CTL_ELEM_ACCESS_READ) | ||
899 | kctl.get = snd_ctl_elem_user_get; | ||
900 | if (access & SNDRV_CTL_ELEM_ACCESS_WRITE) | ||
901 | kctl.put = snd_ctl_elem_user_put; | ||
902 | switch (info->type) { | ||
903 | case SNDRV_CTL_ELEM_TYPE_BOOLEAN: | ||
904 | private_size = sizeof(char); | ||
905 | if (info->count > 128) | ||
906 | return -EINVAL; | ||
907 | break; | ||
908 | case SNDRV_CTL_ELEM_TYPE_INTEGER: | ||
909 | private_size = sizeof(long); | ||
910 | if (info->count > 128) | ||
911 | return -EINVAL; | ||
912 | break; | ||
913 | case SNDRV_CTL_ELEM_TYPE_INTEGER64: | ||
914 | private_size = sizeof(long long); | ||
915 | if (info->count > 64) | ||
916 | return -EINVAL; | ||
917 | break; | ||
918 | case SNDRV_CTL_ELEM_TYPE_BYTES: | ||
919 | private_size = sizeof(unsigned char); | ||
920 | if (info->count > 512) | ||
921 | return -EINVAL; | ||
922 | break; | ||
923 | case SNDRV_CTL_ELEM_TYPE_IEC958: | ||
924 | private_size = sizeof(struct sndrv_aes_iec958); | ||
925 | if (info->count != 1) | ||
926 | return -EINVAL; | ||
927 | break; | ||
928 | default: | ||
929 | return -EINVAL; | ||
930 | } | ||
931 | private_size *= info->count; | ||
932 | ue = kcalloc(1, sizeof(struct user_element) + private_size, GFP_KERNEL); | ||
933 | if (ue == NULL) | ||
934 | return -ENOMEM; | ||
935 | ue->info = *info; | ||
936 | ue->elem_data = (char *)ue + sizeof(*ue); | ||
937 | ue->elem_data_size = private_size; | ||
938 | kctl.private_free = snd_ctl_elem_user_free; | ||
939 | _kctl = snd_ctl_new(&kctl, access); | ||
940 | if (_kctl == NULL) { | ||
941 | kfree(_kctl->private_data); | ||
942 | return -ENOMEM; | ||
943 | } | ||
944 | _kctl->private_data = ue; | ||
945 | for (idx = 0; idx < _kctl->count; idx++) | ||
946 | _kctl->vd[idx].owner = file; | ||
947 | err = snd_ctl_add(card, _kctl); | ||
948 | if (err < 0) { | ||
949 | snd_ctl_free_one(_kctl); | ||
950 | return err; | ||
951 | } | ||
952 | |||
953 | down_write(&card->controls_rwsem); | ||
954 | card->user_ctl_count++; | ||
955 | up_write(&card->controls_rwsem); | ||
956 | |||
957 | return 0; | ||
958 | } | ||
959 | |||
960 | static int snd_ctl_elem_add_user(snd_ctl_file_t *file, snd_ctl_elem_info_t __user *_info, int replace) | ||
961 | { | ||
962 | snd_ctl_elem_info_t info; | ||
963 | if (copy_from_user(&info, _info, sizeof(info))) | ||
964 | return -EFAULT; | ||
965 | return snd_ctl_elem_add(file, &info, replace); | ||
966 | } | ||
967 | |||
968 | static int snd_ctl_elem_remove(snd_ctl_file_t *file, snd_ctl_elem_id_t __user *_id) | ||
969 | { | ||
970 | snd_ctl_elem_id_t id; | ||
971 | int err; | ||
972 | |||
973 | if (copy_from_user(&id, _id, sizeof(id))) | ||
974 | return -EFAULT; | ||
975 | err = snd_ctl_remove_unlocked_id(file, &id); | ||
976 | if (! err) { | ||
977 | snd_card_t *card = file->card; | ||
978 | down_write(&card->controls_rwsem); | ||
979 | card->user_ctl_count--; | ||
980 | up_write(&card->controls_rwsem); | ||
981 | } | ||
982 | return err; | ||
983 | } | ||
984 | |||
985 | static int snd_ctl_subscribe_events(snd_ctl_file_t *file, int __user *ptr) | ||
986 | { | ||
987 | int subscribe; | ||
988 | if (get_user(subscribe, ptr)) | ||
989 | return -EFAULT; | ||
990 | if (subscribe < 0) { | ||
991 | subscribe = file->subscribed; | ||
992 | if (put_user(subscribe, ptr)) | ||
993 | return -EFAULT; | ||
994 | return 0; | ||
995 | } | ||
996 | if (subscribe) { | ||
997 | file->subscribed = 1; | ||
998 | return 0; | ||
999 | } else if (file->subscribed) { | ||
1000 | snd_ctl_empty_read_queue(file); | ||
1001 | file->subscribed = 0; | ||
1002 | } | ||
1003 | return 0; | ||
1004 | } | ||
1005 | |||
1006 | #ifdef CONFIG_PM | ||
1007 | /* | ||
1008 | * change the power state | ||
1009 | */ | ||
1010 | static int snd_ctl_set_power_state(snd_card_t *card, unsigned int power_state) | ||
1011 | { | ||
1012 | switch (power_state) { | ||
1013 | case SNDRV_CTL_POWER_D0: | ||
1014 | if (card->power_state != power_state) { | ||
1015 | card->pm_resume(card); | ||
1016 | snd_power_change_state(card, power_state); | ||
1017 | } | ||
1018 | break; | ||
1019 | case SNDRV_CTL_POWER_D3hot: | ||
1020 | if (card->power_state != power_state) { | ||
1021 | card->pm_suspend(card, PMSG_SUSPEND); | ||
1022 | snd_power_change_state(card, power_state); | ||
1023 | } | ||
1024 | break; | ||
1025 | case SNDRV_CTL_POWER_D1: | ||
1026 | case SNDRV_CTL_POWER_D2: | ||
1027 | case SNDRV_CTL_POWER_D3cold: | ||
1028 | /* not supported yet */ | ||
1029 | default: | ||
1030 | return -EINVAL; | ||
1031 | } | ||
1032 | return 0; | ||
1033 | } | ||
1034 | #endif | ||
1035 | |||
1036 | static long snd_ctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg) | ||
1037 | { | ||
1038 | snd_ctl_file_t *ctl; | ||
1039 | snd_card_t *card; | ||
1040 | struct list_head *list; | ||
1041 | snd_kctl_ioctl_t *p; | ||
1042 | void __user *argp = (void __user *)arg; | ||
1043 | int __user *ip = argp; | ||
1044 | int err; | ||
1045 | |||
1046 | ctl = file->private_data; | ||
1047 | card = ctl->card; | ||
1048 | snd_assert(card != NULL, return -ENXIO); | ||
1049 | switch (cmd) { | ||
1050 | case SNDRV_CTL_IOCTL_PVERSION: | ||
1051 | return put_user(SNDRV_CTL_VERSION, ip) ? -EFAULT : 0; | ||
1052 | case SNDRV_CTL_IOCTL_CARD_INFO: | ||
1053 | return snd_ctl_card_info(card, ctl, cmd, argp); | ||
1054 | case SNDRV_CTL_IOCTL_ELEM_LIST: | ||
1055 | return snd_ctl_elem_list(ctl->card, argp); | ||
1056 | case SNDRV_CTL_IOCTL_ELEM_INFO: | ||
1057 | return snd_ctl_elem_info_user(ctl, argp); | ||
1058 | case SNDRV_CTL_IOCTL_ELEM_READ: | ||
1059 | return snd_ctl_elem_read_user(ctl->card, argp); | ||
1060 | case SNDRV_CTL_IOCTL_ELEM_WRITE: | ||
1061 | return snd_ctl_elem_write_user(ctl, argp); | ||
1062 | case SNDRV_CTL_IOCTL_ELEM_LOCK: | ||
1063 | return snd_ctl_elem_lock(ctl, argp); | ||
1064 | case SNDRV_CTL_IOCTL_ELEM_UNLOCK: | ||
1065 | return snd_ctl_elem_unlock(ctl, argp); | ||
1066 | case SNDRV_CTL_IOCTL_ELEM_ADD: | ||
1067 | return snd_ctl_elem_add_user(ctl, argp, 0); | ||
1068 | case SNDRV_CTL_IOCTL_ELEM_REPLACE: | ||
1069 | return snd_ctl_elem_add_user(ctl, argp, 1); | ||
1070 | case SNDRV_CTL_IOCTL_ELEM_REMOVE: | ||
1071 | return snd_ctl_elem_remove(ctl, argp); | ||
1072 | case SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS: | ||
1073 | return snd_ctl_subscribe_events(ctl, ip); | ||
1074 | case SNDRV_CTL_IOCTL_POWER: | ||
1075 | if (get_user(err, ip)) | ||
1076 | return -EFAULT; | ||
1077 | if (!capable(CAP_SYS_ADMIN)) | ||
1078 | return -EPERM; | ||
1079 | #ifdef CONFIG_PM | ||
1080 | if (card->pm_suspend && card->pm_resume) { | ||
1081 | snd_power_lock(card); | ||
1082 | err = snd_ctl_set_power_state(card, err); | ||
1083 | snd_power_unlock(card); | ||
1084 | } else | ||
1085 | #endif | ||
1086 | err = -ENOPROTOOPT; | ||
1087 | return err; | ||
1088 | case SNDRV_CTL_IOCTL_POWER_STATE: | ||
1089 | #ifdef CONFIG_PM | ||
1090 | return put_user(card->power_state, ip) ? -EFAULT : 0; | ||
1091 | #else | ||
1092 | return put_user(SNDRV_CTL_POWER_D0, ip) ? -EFAULT : 0; | ||
1093 | #endif | ||
1094 | } | ||
1095 | down_read(&snd_ioctl_rwsem); | ||
1096 | list_for_each(list, &snd_control_ioctls) { | ||
1097 | p = list_entry(list, snd_kctl_ioctl_t, list); | ||
1098 | err = p->fioctl(card, ctl, cmd, arg); | ||
1099 | if (err != -ENOIOCTLCMD) { | ||
1100 | up_read(&snd_ioctl_rwsem); | ||
1101 | return err; | ||
1102 | } | ||
1103 | } | ||
1104 | up_read(&snd_ioctl_rwsem); | ||
1105 | snd_printd("unknown ioctl = 0x%x\n", cmd); | ||
1106 | return -ENOTTY; | ||
1107 | } | ||
1108 | |||
1109 | static ssize_t snd_ctl_read(struct file *file, char __user *buffer, size_t count, loff_t * offset) | ||
1110 | { | ||
1111 | snd_ctl_file_t *ctl; | ||
1112 | int err = 0; | ||
1113 | ssize_t result = 0; | ||
1114 | |||
1115 | ctl = file->private_data; | ||
1116 | snd_assert(ctl != NULL && ctl->card != NULL, return -ENXIO); | ||
1117 | if (!ctl->subscribed) | ||
1118 | return -EBADFD; | ||
1119 | if (count < sizeof(snd_ctl_event_t)) | ||
1120 | return -EINVAL; | ||
1121 | spin_lock_irq(&ctl->read_lock); | ||
1122 | while (count >= sizeof(snd_ctl_event_t)) { | ||
1123 | snd_ctl_event_t ev; | ||
1124 | snd_kctl_event_t *kev; | ||
1125 | while (list_empty(&ctl->events)) { | ||
1126 | wait_queue_t wait; | ||
1127 | if ((file->f_flags & O_NONBLOCK) != 0 || result > 0) { | ||
1128 | err = -EAGAIN; | ||
1129 | goto __end_lock; | ||
1130 | } | ||
1131 | init_waitqueue_entry(&wait, current); | ||
1132 | add_wait_queue(&ctl->change_sleep, &wait); | ||
1133 | set_current_state(TASK_INTERRUPTIBLE); | ||
1134 | spin_unlock_irq(&ctl->read_lock); | ||
1135 | schedule(); | ||
1136 | remove_wait_queue(&ctl->change_sleep, &wait); | ||
1137 | if (signal_pending(current)) | ||
1138 | return result > 0 ? result : -ERESTARTSYS; | ||
1139 | spin_lock_irq(&ctl->read_lock); | ||
1140 | } | ||
1141 | kev = snd_kctl_event(ctl->events.next); | ||
1142 | ev.type = SNDRV_CTL_EVENT_ELEM; | ||
1143 | ev.data.elem.mask = kev->mask; | ||
1144 | ev.data.elem.id = kev->id; | ||
1145 | list_del(&kev->list); | ||
1146 | spin_unlock_irq(&ctl->read_lock); | ||
1147 | kfree(kev); | ||
1148 | if (copy_to_user(buffer, &ev, sizeof(snd_ctl_event_t))) { | ||
1149 | err = -EFAULT; | ||
1150 | goto __end; | ||
1151 | } | ||
1152 | spin_lock_irq(&ctl->read_lock); | ||
1153 | buffer += sizeof(snd_ctl_event_t); | ||
1154 | count -= sizeof(snd_ctl_event_t); | ||
1155 | result += sizeof(snd_ctl_event_t); | ||
1156 | } | ||
1157 | __end_lock: | ||
1158 | spin_unlock_irq(&ctl->read_lock); | ||
1159 | __end: | ||
1160 | return result > 0 ? result : err; | ||
1161 | } | ||
1162 | |||
1163 | static unsigned int snd_ctl_poll(struct file *file, poll_table * wait) | ||
1164 | { | ||
1165 | unsigned int mask; | ||
1166 | snd_ctl_file_t *ctl; | ||
1167 | |||
1168 | ctl = file->private_data; | ||
1169 | if (!ctl->subscribed) | ||
1170 | return 0; | ||
1171 | poll_wait(file, &ctl->change_sleep, wait); | ||
1172 | |||
1173 | mask = 0; | ||
1174 | if (!list_empty(&ctl->events)) | ||
1175 | mask |= POLLIN | POLLRDNORM; | ||
1176 | |||
1177 | return mask; | ||
1178 | } | ||
1179 | |||
1180 | /* | ||
1181 | * register the device-specific control-ioctls. | ||
1182 | * called from each device manager like pcm.c, hwdep.c, etc. | ||
1183 | */ | ||
1184 | static int _snd_ctl_register_ioctl(snd_kctl_ioctl_func_t fcn, struct list_head *lists) | ||
1185 | { | ||
1186 | snd_kctl_ioctl_t *pn; | ||
1187 | |||
1188 | pn = kcalloc(1, sizeof(snd_kctl_ioctl_t), GFP_KERNEL); | ||
1189 | if (pn == NULL) | ||
1190 | return -ENOMEM; | ||
1191 | pn->fioctl = fcn; | ||
1192 | down_write(&snd_ioctl_rwsem); | ||
1193 | list_add_tail(&pn->list, lists); | ||
1194 | up_write(&snd_ioctl_rwsem); | ||
1195 | return 0; | ||
1196 | } | ||
1197 | |||
1198 | int snd_ctl_register_ioctl(snd_kctl_ioctl_func_t fcn) | ||
1199 | { | ||
1200 | return _snd_ctl_register_ioctl(fcn, &snd_control_ioctls); | ||
1201 | } | ||
1202 | |||
1203 | #ifdef CONFIG_COMPAT | ||
1204 | int snd_ctl_register_ioctl_compat(snd_kctl_ioctl_func_t fcn) | ||
1205 | { | ||
1206 | return _snd_ctl_register_ioctl(fcn, &snd_control_compat_ioctls); | ||
1207 | } | ||
1208 | #endif | ||
1209 | |||
1210 | /* | ||
1211 | * de-register the device-specific control-ioctls. | ||
1212 | */ | ||
1213 | static int _snd_ctl_unregister_ioctl(snd_kctl_ioctl_func_t fcn, struct list_head *lists) | ||
1214 | { | ||
1215 | struct list_head *list; | ||
1216 | snd_kctl_ioctl_t *p; | ||
1217 | |||
1218 | snd_runtime_check(fcn != NULL, return -EINVAL); | ||
1219 | down_write(&snd_ioctl_rwsem); | ||
1220 | list_for_each(list, lists) { | ||
1221 | p = list_entry(list, snd_kctl_ioctl_t, list); | ||
1222 | if (p->fioctl == fcn) { | ||
1223 | list_del(&p->list); | ||
1224 | up_write(&snd_ioctl_rwsem); | ||
1225 | kfree(p); | ||
1226 | return 0; | ||
1227 | } | ||
1228 | } | ||
1229 | up_write(&snd_ioctl_rwsem); | ||
1230 | snd_BUG(); | ||
1231 | return -EINVAL; | ||
1232 | } | ||
1233 | |||
1234 | int snd_ctl_unregister_ioctl(snd_kctl_ioctl_func_t fcn) | ||
1235 | { | ||
1236 | return _snd_ctl_unregister_ioctl(fcn, &snd_control_ioctls); | ||
1237 | } | ||
1238 | |||
1239 | #ifdef CONFIG_COMPAT | ||
1240 | int snd_ctl_unregister_ioctl_compat(snd_kctl_ioctl_func_t fcn) | ||
1241 | { | ||
1242 | return _snd_ctl_unregister_ioctl(fcn, &snd_control_compat_ioctls); | ||
1243 | } | ||
1244 | |||
1245 | #endif | ||
1246 | |||
1247 | static int snd_ctl_fasync(int fd, struct file * file, int on) | ||
1248 | { | ||
1249 | snd_ctl_file_t *ctl; | ||
1250 | int err; | ||
1251 | ctl = file->private_data; | ||
1252 | err = fasync_helper(fd, file, on, &ctl->fasync); | ||
1253 | if (err < 0) | ||
1254 | return err; | ||
1255 | return 0; | ||
1256 | } | ||
1257 | |||
1258 | /* | ||
1259 | * ioctl32 compat | ||
1260 | */ | ||
1261 | #ifdef CONFIG_COMPAT | ||
1262 | #include "control_compat.c" | ||
1263 | #else | ||
1264 | #define snd_ctl_ioctl_compat NULL | ||
1265 | #endif | ||
1266 | |||
1267 | /* | ||
1268 | * INIT PART | ||
1269 | */ | ||
1270 | |||
1271 | static struct file_operations snd_ctl_f_ops = | ||
1272 | { | ||
1273 | .owner = THIS_MODULE, | ||
1274 | .read = snd_ctl_read, | ||
1275 | .open = snd_ctl_open, | ||
1276 | .release = snd_ctl_release, | ||
1277 | .poll = snd_ctl_poll, | ||
1278 | .unlocked_ioctl = snd_ctl_ioctl, | ||
1279 | .compat_ioctl = snd_ctl_ioctl_compat, | ||
1280 | .fasync = snd_ctl_fasync, | ||
1281 | }; | ||
1282 | |||
1283 | static snd_minor_t snd_ctl_reg = | ||
1284 | { | ||
1285 | .comment = "ctl", | ||
1286 | .f_ops = &snd_ctl_f_ops, | ||
1287 | }; | ||
1288 | |||
1289 | /* | ||
1290 | * registration of the control device | ||
1291 | */ | ||
1292 | static int snd_ctl_dev_register(snd_device_t *device) | ||
1293 | { | ||
1294 | snd_card_t *card = device->device_data; | ||
1295 | int err, cardnum; | ||
1296 | char name[16]; | ||
1297 | |||
1298 | snd_assert(card != NULL, return -ENXIO); | ||
1299 | cardnum = card->number; | ||
1300 | snd_assert(cardnum >= 0 && cardnum < SNDRV_CARDS, return -ENXIO); | ||
1301 | sprintf(name, "controlC%i", cardnum); | ||
1302 | if ((err = snd_register_device(SNDRV_DEVICE_TYPE_CONTROL, | ||
1303 | card, 0, &snd_ctl_reg, name)) < 0) | ||
1304 | return err; | ||
1305 | return 0; | ||
1306 | } | ||
1307 | |||
1308 | /* | ||
1309 | * disconnection of the control device | ||
1310 | */ | ||
1311 | static int snd_ctl_dev_disconnect(snd_device_t *device) | ||
1312 | { | ||
1313 | snd_card_t *card = device->device_data; | ||
1314 | struct list_head *flist; | ||
1315 | snd_ctl_file_t *ctl; | ||
1316 | |||
1317 | down_read(&card->controls_rwsem); | ||
1318 | list_for_each(flist, &card->ctl_files) { | ||
1319 | ctl = snd_ctl_file(flist); | ||
1320 | wake_up(&ctl->change_sleep); | ||
1321 | kill_fasync(&ctl->fasync, SIGIO, POLL_ERR); | ||
1322 | } | ||
1323 | up_read(&card->controls_rwsem); | ||
1324 | return 0; | ||
1325 | } | ||
1326 | |||
1327 | /* | ||
1328 | * free all controls | ||
1329 | */ | ||
1330 | static int snd_ctl_dev_free(snd_device_t *device) | ||
1331 | { | ||
1332 | snd_card_t *card = device->device_data; | ||
1333 | snd_kcontrol_t *control; | ||
1334 | |||
1335 | down_write(&card->controls_rwsem); | ||
1336 | while (!list_empty(&card->controls)) { | ||
1337 | control = snd_kcontrol(card->controls.next); | ||
1338 | snd_ctl_remove(card, control); | ||
1339 | } | ||
1340 | up_write(&card->controls_rwsem); | ||
1341 | return 0; | ||
1342 | } | ||
1343 | |||
1344 | /* | ||
1345 | * de-registration of the control device | ||
1346 | */ | ||
1347 | static int snd_ctl_dev_unregister(snd_device_t *device) | ||
1348 | { | ||
1349 | snd_card_t *card = device->device_data; | ||
1350 | int err, cardnum; | ||
1351 | |||
1352 | snd_assert(card != NULL, return -ENXIO); | ||
1353 | cardnum = card->number; | ||
1354 | snd_assert(cardnum >= 0 && cardnum < SNDRV_CARDS, return -ENXIO); | ||
1355 | if ((err = snd_unregister_device(SNDRV_DEVICE_TYPE_CONTROL, card, 0)) < 0) | ||
1356 | return err; | ||
1357 | return snd_ctl_dev_free(device); | ||
1358 | } | ||
1359 | |||
1360 | /* | ||
1361 | * create control core: | ||
1362 | * called from init.c | ||
1363 | */ | ||
1364 | int snd_ctl_create(snd_card_t *card) | ||
1365 | { | ||
1366 | static snd_device_ops_t ops = { | ||
1367 | .dev_free = snd_ctl_dev_free, | ||
1368 | .dev_register = snd_ctl_dev_register, | ||
1369 | .dev_disconnect = snd_ctl_dev_disconnect, | ||
1370 | .dev_unregister = snd_ctl_dev_unregister | ||
1371 | }; | ||
1372 | |||
1373 | snd_assert(card != NULL, return -ENXIO); | ||
1374 | return snd_device_new(card, SNDRV_DEV_CONTROL, card, &ops); | ||
1375 | } | ||
diff --git a/sound/core/control_compat.c b/sound/core/control_compat.c new file mode 100644 index 000000000000..7fdabea4bfc8 --- /dev/null +++ b/sound/core/control_compat.c | |||
@@ -0,0 +1,412 @@ | |||
1 | /* | ||
2 | * compat ioctls for control API | ||
3 | * | ||
4 | * Copyright (c) by 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 | /* this file included from control.c */ | ||
22 | |||
23 | #include <linux/compat.h> | ||
24 | |||
25 | struct sndrv_ctl_elem_list32 { | ||
26 | u32 offset; | ||
27 | u32 space; | ||
28 | u32 used; | ||
29 | u32 count; | ||
30 | u32 pids; | ||
31 | unsigned char reserved[50]; | ||
32 | } /* don't set packed attribute here */; | ||
33 | |||
34 | static int snd_ctl_elem_list_compat(snd_card_t *card, struct sndrv_ctl_elem_list32 __user *data32) | ||
35 | { | ||
36 | struct sndrv_ctl_elem_list __user *data; | ||
37 | compat_caddr_t ptr; | ||
38 | int err; | ||
39 | |||
40 | data = compat_alloc_user_space(sizeof(*data)); | ||
41 | |||
42 | /* offset, space, used, count */ | ||
43 | if (copy_in_user(data, data32, 4 * sizeof(u32))) | ||
44 | return -EFAULT; | ||
45 | /* pids */ | ||
46 | if (get_user(ptr, &data32->pids) || | ||
47 | put_user(compat_ptr(ptr), &data->pids)) | ||
48 | return -EFAULT; | ||
49 | err = snd_ctl_elem_list(card, data); | ||
50 | if (err < 0) | ||
51 | return err; | ||
52 | /* copy the result */ | ||
53 | if (copy_in_user(data32, data, 4 * sizeof(u32))) | ||
54 | return -EFAULT; | ||
55 | return 0; | ||
56 | } | ||
57 | |||
58 | /* | ||
59 | * control element info | ||
60 | * it uses union, so the things are not easy.. | ||
61 | */ | ||
62 | |||
63 | struct sndrv_ctl_elem_info32 { | ||
64 | struct sndrv_ctl_elem_id id; // the size of struct is same | ||
65 | s32 type; | ||
66 | u32 access; | ||
67 | u32 count; | ||
68 | s32 owner; | ||
69 | union { | ||
70 | struct { | ||
71 | s32 min; | ||
72 | s32 max; | ||
73 | s32 step; | ||
74 | } integer; | ||
75 | struct { | ||
76 | u64 min; | ||
77 | u64 max; | ||
78 | u64 step; | ||
79 | } integer64; | ||
80 | struct { | ||
81 | u32 items; | ||
82 | u32 item; | ||
83 | char name[64]; | ||
84 | } enumerated; | ||
85 | unsigned char reserved[128]; | ||
86 | } value; | ||
87 | unsigned char reserved[64]; | ||
88 | } __attribute__((packed)); | ||
89 | |||
90 | static int snd_ctl_elem_info_compat(snd_ctl_file_t *ctl, struct sndrv_ctl_elem_info32 __user *data32) | ||
91 | { | ||
92 | struct sndrv_ctl_elem_info *data; | ||
93 | int err; | ||
94 | |||
95 | data = kcalloc(1, sizeof(*data), GFP_KERNEL); | ||
96 | if (! data) | ||
97 | return -ENOMEM; | ||
98 | |||
99 | err = -EFAULT; | ||
100 | /* copy id */ | ||
101 | if (copy_from_user(&data->id, &data32->id, sizeof(data->id))) | ||
102 | goto error; | ||
103 | /* we need to copy the item index. | ||
104 | * hope this doesn't break anything.. | ||
105 | */ | ||
106 | if (get_user(data->value.enumerated.item, &data32->value.enumerated.item)) | ||
107 | goto error; | ||
108 | err = snd_ctl_elem_info(ctl, data); | ||
109 | if (err < 0) | ||
110 | goto error; | ||
111 | /* restore info to 32bit */ | ||
112 | err = -EFAULT; | ||
113 | /* id, type, access, count */ | ||
114 | if (copy_to_user(&data32->id, &data->id, sizeof(data->id)) || | ||
115 | copy_to_user(&data32->type, &data->type, 3 * sizeof(u32))) | ||
116 | goto error; | ||
117 | if (put_user(data->owner, &data32->owner)) | ||
118 | goto error; | ||
119 | switch (data->type) { | ||
120 | case SNDRV_CTL_ELEM_TYPE_BOOLEAN: | ||
121 | case SNDRV_CTL_ELEM_TYPE_INTEGER: | ||
122 | if (put_user(data->value.integer.min, &data32->value.integer.min) || | ||
123 | put_user(data->value.integer.max, &data32->value.integer.max) || | ||
124 | put_user(data->value.integer.step, &data32->value.integer.step)) | ||
125 | goto error; | ||
126 | break; | ||
127 | case SNDRV_CTL_ELEM_TYPE_INTEGER64: | ||
128 | if (copy_to_user(&data32->value.integer64, | ||
129 | &data->value.integer64, | ||
130 | sizeof(data->value.integer64))) | ||
131 | goto error; | ||
132 | break; | ||
133 | case SNDRV_CTL_ELEM_TYPE_ENUMERATED: | ||
134 | if (copy_to_user(&data32->value.enumerated, | ||
135 | &data->value.enumerated, | ||
136 | sizeof(data->value.enumerated))) | ||
137 | goto error; | ||
138 | break; | ||
139 | default: | ||
140 | break; | ||
141 | } | ||
142 | err = 0; | ||
143 | error: | ||
144 | kfree(data); | ||
145 | return err; | ||
146 | } | ||
147 | |||
148 | /* read / write */ | ||
149 | struct sndrv_ctl_elem_value32 { | ||
150 | struct sndrv_ctl_elem_id id; | ||
151 | unsigned int indirect; /* bit-field causes misalignment */ | ||
152 | union { | ||
153 | s32 integer[128]; | ||
154 | unsigned char data[512]; | ||
155 | #ifndef CONFIG_X86_64 | ||
156 | s64 integer64[64]; | ||
157 | #endif | ||
158 | } value; | ||
159 | unsigned char reserved[128]; | ||
160 | }; | ||
161 | |||
162 | |||
163 | /* get the value type and count of the control */ | ||
164 | static int get_ctl_type(snd_card_t *card, snd_ctl_elem_id_t *id, int *countp) | ||
165 | { | ||
166 | snd_kcontrol_t *kctl; | ||
167 | snd_ctl_elem_info_t info; | ||
168 | int err; | ||
169 | |||
170 | down_read(&card->controls_rwsem); | ||
171 | kctl = snd_ctl_find_id(card, id); | ||
172 | if (! kctl) { | ||
173 | up_read(&card->controls_rwsem); | ||
174 | return -ENXIO; | ||
175 | } | ||
176 | info.id = *id; | ||
177 | err = kctl->info(kctl, &info); | ||
178 | up_read(&card->controls_rwsem); | ||
179 | if (err >= 0) { | ||
180 | err = info.type; | ||
181 | *countp = info.count; | ||
182 | } | ||
183 | return err; | ||
184 | } | ||
185 | |||
186 | static int get_elem_size(int type, int count) | ||
187 | { | ||
188 | switch (type) { | ||
189 | case SNDRV_CTL_ELEM_TYPE_INTEGER64: | ||
190 | return sizeof(s64) * count; | ||
191 | case SNDRV_CTL_ELEM_TYPE_ENUMERATED: | ||
192 | return sizeof(int) * count; | ||
193 | case SNDRV_CTL_ELEM_TYPE_BYTES: | ||
194 | return 512; | ||
195 | case SNDRV_CTL_ELEM_TYPE_IEC958: | ||
196 | return sizeof(struct sndrv_aes_iec958); | ||
197 | default: | ||
198 | return -1; | ||
199 | } | ||
200 | } | ||
201 | |||
202 | static int copy_ctl_value_from_user(snd_card_t *card, | ||
203 | struct sndrv_ctl_elem_value *data, | ||
204 | struct sndrv_ctl_elem_value32 __user *data32, | ||
205 | int *typep, int *countp) | ||
206 | { | ||
207 | int i, type, count, size; | ||
208 | unsigned int indirect; | ||
209 | |||
210 | if (copy_from_user(&data->id, &data32->id, sizeof(data->id))) | ||
211 | return -EFAULT; | ||
212 | if (get_user(indirect, &data32->indirect)) | ||
213 | return -EFAULT; | ||
214 | if (indirect) | ||
215 | return -EINVAL; | ||
216 | type = get_ctl_type(card, &data->id, &count); | ||
217 | if (type < 0) | ||
218 | return type; | ||
219 | |||
220 | if (type == SNDRV_CTL_ELEM_TYPE_BOOLEAN || | ||
221 | type == SNDRV_CTL_ELEM_TYPE_INTEGER) { | ||
222 | for (i = 0; i < count; i++) { | ||
223 | int val; | ||
224 | if (get_user(val, &data32->value.integer[i])) | ||
225 | return -EFAULT; | ||
226 | data->value.integer.value[i] = val; | ||
227 | } | ||
228 | } else { | ||
229 | size = get_elem_size(type, count); | ||
230 | if (size < 0) { | ||
231 | printk(KERN_ERR "snd_ioctl32_ctl_elem_value: unknown type %d\n", type); | ||
232 | return -EINVAL; | ||
233 | } | ||
234 | if (copy_from_user(data->value.bytes.data, | ||
235 | data32->value.data, size)) | ||
236 | return -EFAULT; | ||
237 | } | ||
238 | |||
239 | *typep = type; | ||
240 | *countp = count; | ||
241 | return 0; | ||
242 | } | ||
243 | |||
244 | /* restore the value to 32bit */ | ||
245 | static int copy_ctl_value_to_user(struct sndrv_ctl_elem_value32 __user *data32, | ||
246 | struct sndrv_ctl_elem_value *data, | ||
247 | int type, int count) | ||
248 | { | ||
249 | int i, size; | ||
250 | |||
251 | if (type == SNDRV_CTL_ELEM_TYPE_BOOLEAN || | ||
252 | type == SNDRV_CTL_ELEM_TYPE_INTEGER) { | ||
253 | for (i = 0; i < count; i++) { | ||
254 | int val; | ||
255 | val = data->value.integer.value[i]; | ||
256 | if (put_user(val, &data32->value.integer[i])) | ||
257 | return -EFAULT; | ||
258 | } | ||
259 | } else { | ||
260 | size = get_elem_size(type, count); | ||
261 | if (copy_to_user(data32->value.data, | ||
262 | data->value.bytes.data, size)) | ||
263 | return -EFAULT; | ||
264 | } | ||
265 | return 0; | ||
266 | } | ||
267 | |||
268 | static int snd_ctl_elem_read_user_compat(snd_card_t *card, | ||
269 | struct sndrv_ctl_elem_value32 __user *data32) | ||
270 | { | ||
271 | struct sndrv_ctl_elem_value *data; | ||
272 | int err, type, count; | ||
273 | |||
274 | data = kcalloc(1, sizeof(*data), GFP_KERNEL); | ||
275 | if (data == NULL) | ||
276 | return -ENOMEM; | ||
277 | |||
278 | if ((err = copy_ctl_value_from_user(card, data, data32, &type, &count)) < 0) | ||
279 | goto error; | ||
280 | if ((err = snd_ctl_elem_read(card, data)) < 0) | ||
281 | goto error; | ||
282 | err = copy_ctl_value_to_user(data32, data, type, count); | ||
283 | error: | ||
284 | kfree(data); | ||
285 | return err; | ||
286 | } | ||
287 | |||
288 | static int snd_ctl_elem_write_user_compat(snd_ctl_file_t *file, | ||
289 | struct sndrv_ctl_elem_value32 __user *data32) | ||
290 | { | ||
291 | struct sndrv_ctl_elem_value *data; | ||
292 | int err, type, count; | ||
293 | |||
294 | data = kcalloc(1, sizeof(*data), GFP_KERNEL); | ||
295 | if (data == NULL) | ||
296 | return -ENOMEM; | ||
297 | |||
298 | if ((err = copy_ctl_value_from_user(file->card, data, data32, &type, &count)) < 0) | ||
299 | goto error; | ||
300 | if ((err = snd_ctl_elem_write(file->card, file, data)) < 0) | ||
301 | goto error; | ||
302 | err = copy_ctl_value_to_user(data32, data, type, count); | ||
303 | error: | ||
304 | kfree(data); | ||
305 | return err; | ||
306 | } | ||
307 | |||
308 | /* add or replace a user control */ | ||
309 | static int snd_ctl_elem_add_compat(snd_ctl_file_t *file, | ||
310 | struct sndrv_ctl_elem_info32 __user *data32, | ||
311 | int replace) | ||
312 | { | ||
313 | struct sndrv_ctl_elem_info *data; | ||
314 | int err; | ||
315 | |||
316 | data = kcalloc(1, sizeof(*data), GFP_KERNEL); | ||
317 | if (! data) | ||
318 | return -ENOMEM; | ||
319 | |||
320 | err = -EFAULT; | ||
321 | /* id, type, access, count */ \ | ||
322 | if (copy_from_user(&data->id, &data32->id, sizeof(data->id)) || | ||
323 | copy_from_user(&data->type, &data32->type, 3 * sizeof(u32))) | ||
324 | goto error; | ||
325 | if (get_user(data->owner, &data32->owner) || | ||
326 | get_user(data->type, &data32->type)) | ||
327 | goto error; | ||
328 | switch (data->type) { | ||
329 | case SNDRV_CTL_ELEM_TYPE_BOOLEAN: | ||
330 | case SNDRV_CTL_ELEM_TYPE_INTEGER: | ||
331 | if (get_user(data->value.integer.min, &data32->value.integer.min) || | ||
332 | get_user(data->value.integer.max, &data32->value.integer.max) || | ||
333 | get_user(data->value.integer.step, &data32->value.integer.step)) | ||
334 | goto error; | ||
335 | break; | ||
336 | case SNDRV_CTL_ELEM_TYPE_INTEGER64: | ||
337 | if (copy_from_user(&data->value.integer64, | ||
338 | &data32->value.integer64, | ||
339 | sizeof(data->value.integer64))) | ||
340 | goto error; | ||
341 | break; | ||
342 | case SNDRV_CTL_ELEM_TYPE_ENUMERATED: | ||
343 | if (copy_from_user(&data->value.enumerated, | ||
344 | &data32->value.enumerated, | ||
345 | sizeof(data->value.enumerated))) | ||
346 | goto error; | ||
347 | break; | ||
348 | default: | ||
349 | break; | ||
350 | } | ||
351 | err = snd_ctl_elem_add(file, data, replace); | ||
352 | error: | ||
353 | kfree(data); | ||
354 | return err; | ||
355 | } | ||
356 | |||
357 | enum { | ||
358 | SNDRV_CTL_IOCTL_ELEM_LIST32 = _IOWR('U', 0x10, struct sndrv_ctl_elem_list32), | ||
359 | SNDRV_CTL_IOCTL_ELEM_INFO32 = _IOWR('U', 0x11, struct sndrv_ctl_elem_info32), | ||
360 | SNDRV_CTL_IOCTL_ELEM_READ32 = _IOWR('U', 0x12, struct sndrv_ctl_elem_value32), | ||
361 | SNDRV_CTL_IOCTL_ELEM_WRITE32 = _IOWR('U', 0x13, struct sndrv_ctl_elem_value32), | ||
362 | SNDRV_CTL_IOCTL_ELEM_ADD32 = _IOWR('U', 0x17, struct sndrv_ctl_elem_info32), | ||
363 | SNDRV_CTL_IOCTL_ELEM_REPLACE32 = _IOWR('U', 0x18, struct sndrv_ctl_elem_info32), | ||
364 | }; | ||
365 | |||
366 | static inline long snd_ctl_ioctl_compat(struct file *file, unsigned int cmd, unsigned long arg) | ||
367 | { | ||
368 | snd_ctl_file_t *ctl; | ||
369 | struct list_head *list; | ||
370 | void __user *argp = compat_ptr(arg); | ||
371 | int err; | ||
372 | |||
373 | ctl = file->private_data; | ||
374 | snd_assert(ctl && ctl->card, return -ENXIO); | ||
375 | |||
376 | switch (cmd) { | ||
377 | case SNDRV_CTL_IOCTL_PVERSION: | ||
378 | case SNDRV_CTL_IOCTL_CARD_INFO: | ||
379 | case SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS: | ||
380 | case SNDRV_CTL_IOCTL_POWER: | ||
381 | case SNDRV_CTL_IOCTL_POWER_STATE: | ||
382 | case SNDRV_CTL_IOCTL_ELEM_LOCK: | ||
383 | case SNDRV_CTL_IOCTL_ELEM_UNLOCK: | ||
384 | return snd_ctl_ioctl(file, cmd, (unsigned long)argp); | ||
385 | case SNDRV_CTL_IOCTL_ELEM_LIST32: | ||
386 | return snd_ctl_elem_list_compat(ctl->card, argp); | ||
387 | case SNDRV_CTL_IOCTL_ELEM_INFO32: | ||
388 | return snd_ctl_elem_info_compat(ctl, argp); | ||
389 | case SNDRV_CTL_IOCTL_ELEM_READ32: | ||
390 | return snd_ctl_elem_read_user_compat(ctl->card, argp); | ||
391 | case SNDRV_CTL_IOCTL_ELEM_WRITE32: | ||
392 | return snd_ctl_elem_write_user_compat(ctl, argp); | ||
393 | case SNDRV_CTL_IOCTL_ELEM_ADD32: | ||
394 | return snd_ctl_elem_add_compat(ctl, argp, 0); | ||
395 | case SNDRV_CTL_IOCTL_ELEM_REPLACE32: | ||
396 | return snd_ctl_elem_add_compat(ctl, argp, 1); | ||
397 | } | ||
398 | |||
399 | down_read(&snd_ioctl_rwsem); | ||
400 | list_for_each(list, &snd_control_compat_ioctls) { | ||
401 | snd_kctl_ioctl_t *p = list_entry(list, snd_kctl_ioctl_t, list); | ||
402 | if (p->fioctl) { | ||
403 | err = p->fioctl(ctl->card, ctl, cmd, arg); | ||
404 | if (err != -ENOIOCTLCMD) { | ||
405 | up_read(&snd_ioctl_rwsem); | ||
406 | return err; | ||
407 | } | ||
408 | } | ||
409 | } | ||
410 | up_read(&snd_ioctl_rwsem); | ||
411 | return -ENOIOCTLCMD; | ||
412 | } | ||
diff --git a/sound/core/device.c b/sound/core/device.c new file mode 100644 index 000000000000..18c71f913d2a --- /dev/null +++ b/sound/core/device.c | |||
@@ -0,0 +1,240 @@ | |||
1 | /* | ||
2 | * Device management routines | ||
3 | * Copyright (c) by Jaroslav Kysela <perex@suse.cz> | ||
4 | * | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #include <sound/driver.h> | ||
23 | #include <linux/slab.h> | ||
24 | #include <linux/time.h> | ||
25 | #include <linux/errno.h> | ||
26 | #include <sound/core.h> | ||
27 | |||
28 | /** | ||
29 | * snd_device_new - create an ALSA device component | ||
30 | * @card: the card instance | ||
31 | * @type: the device type, SNDRV_DEV_TYPE_XXX | ||
32 | * @device_data: the data pointer of this device | ||
33 | * @ops: the operator table | ||
34 | * | ||
35 | * Creates a new device component for the given data pointer. | ||
36 | * The device will be assigned to the card and managed together | ||
37 | * by the card. | ||
38 | * | ||
39 | * The data pointer plays a role as the identifier, too, so the | ||
40 | * pointer address must be unique and unchanged. | ||
41 | * | ||
42 | * Returns zero if successful, or a negative error code on failure. | ||
43 | */ | ||
44 | int snd_device_new(snd_card_t *card, snd_device_type_t type, | ||
45 | void *device_data, snd_device_ops_t *ops) | ||
46 | { | ||
47 | snd_device_t *dev; | ||
48 | |||
49 | snd_assert(card != NULL && device_data != NULL && ops != NULL, return -ENXIO); | ||
50 | dev = kcalloc(1, sizeof(*dev), GFP_KERNEL); | ||
51 | if (dev == NULL) | ||
52 | return -ENOMEM; | ||
53 | dev->card = card; | ||
54 | dev->type = type; | ||
55 | dev->state = SNDRV_DEV_BUILD; | ||
56 | dev->device_data = device_data; | ||
57 | dev->ops = ops; | ||
58 | list_add(&dev->list, &card->devices); /* add to the head of list */ | ||
59 | return 0; | ||
60 | } | ||
61 | |||
62 | /** | ||
63 | * snd_device_free - release the device from the card | ||
64 | * @card: the card instance | ||
65 | * @device_data: the data pointer to release | ||
66 | * | ||
67 | * Removes the device from the list on the card and invokes the | ||
68 | * callback, dev_unregister or dev_free, corresponding to the state. | ||
69 | * Then release the device. | ||
70 | * | ||
71 | * Returns zero if successful, or a negative error code on failure or if the | ||
72 | * device not found. | ||
73 | */ | ||
74 | int snd_device_free(snd_card_t *card, void *device_data) | ||
75 | { | ||
76 | struct list_head *list; | ||
77 | snd_device_t *dev; | ||
78 | |||
79 | snd_assert(card != NULL, return -ENXIO); | ||
80 | snd_assert(device_data != NULL, return -ENXIO); | ||
81 | list_for_each(list, &card->devices) { | ||
82 | dev = snd_device(list); | ||
83 | if (dev->device_data != device_data) | ||
84 | continue; | ||
85 | /* unlink */ | ||
86 | list_del(&dev->list); | ||
87 | if ((dev->state == SNDRV_DEV_REGISTERED || dev->state == SNDRV_DEV_DISCONNECTED) && | ||
88 | dev->ops->dev_unregister) { | ||
89 | if (dev->ops->dev_unregister(dev)) | ||
90 | snd_printk(KERN_ERR "device unregister failure\n"); | ||
91 | } else { | ||
92 | if (dev->ops->dev_free) { | ||
93 | if (dev->ops->dev_free(dev)) | ||
94 | snd_printk(KERN_ERR "device free failure\n"); | ||
95 | } | ||
96 | } | ||
97 | kfree(dev); | ||
98 | return 0; | ||
99 | } | ||
100 | snd_printd("device free %p (from %p), not found\n", device_data, __builtin_return_address(0)); | ||
101 | return -ENXIO; | ||
102 | } | ||
103 | |||
104 | /** | ||
105 | * snd_device_free - disconnect the device | ||
106 | * @card: the card instance | ||
107 | * @device_data: the data pointer to disconnect | ||
108 | * | ||
109 | * Turns the device into the disconnection state, invoking | ||
110 | * dev_disconnect callback, if the device was already registered. | ||
111 | * | ||
112 | * Usually called from snd_card_disconnect(). | ||
113 | * | ||
114 | * Returns zero if successful, or a negative error code on failure or if the | ||
115 | * device not found. | ||
116 | */ | ||
117 | int snd_device_disconnect(snd_card_t *card, void *device_data) | ||
118 | { | ||
119 | struct list_head *list; | ||
120 | snd_device_t *dev; | ||
121 | |||
122 | snd_assert(card != NULL, return -ENXIO); | ||
123 | snd_assert(device_data != NULL, return -ENXIO); | ||
124 | list_for_each(list, &card->devices) { | ||
125 | dev = snd_device(list); | ||
126 | if (dev->device_data != device_data) | ||
127 | continue; | ||
128 | if (dev->state == SNDRV_DEV_REGISTERED && dev->ops->dev_disconnect) { | ||
129 | if (dev->ops->dev_disconnect(dev)) | ||
130 | snd_printk(KERN_ERR "device disconnect failure\n"); | ||
131 | dev->state = SNDRV_DEV_DISCONNECTED; | ||
132 | } | ||
133 | return 0; | ||
134 | } | ||
135 | snd_printd("device disconnect %p (from %p), not found\n", device_data, __builtin_return_address(0)); | ||
136 | return -ENXIO; | ||
137 | } | ||
138 | |||
139 | /** | ||
140 | * snd_device_register - register the device | ||
141 | * @card: the card instance | ||
142 | * @device_data: the data pointer to register | ||
143 | * | ||
144 | * Registers the device which was already created via | ||
145 | * snd_device_new(). Usually this is called from snd_card_register(), | ||
146 | * but it can be called later if any new devices are created after | ||
147 | * invocation of snd_card_register(). | ||
148 | * | ||
149 | * Returns zero if successful, or a negative error code on failure or if the | ||
150 | * device not found. | ||
151 | */ | ||
152 | int snd_device_register(snd_card_t *card, void *device_data) | ||
153 | { | ||
154 | struct list_head *list; | ||
155 | snd_device_t *dev; | ||
156 | int err; | ||
157 | |||
158 | snd_assert(card != NULL && device_data != NULL, return -ENXIO); | ||
159 | list_for_each(list, &card->devices) { | ||
160 | dev = snd_device(list); | ||
161 | if (dev->device_data != device_data) | ||
162 | continue; | ||
163 | if (dev->state == SNDRV_DEV_BUILD && dev->ops->dev_register) { | ||
164 | if ((err = dev->ops->dev_register(dev)) < 0) | ||
165 | return err; | ||
166 | dev->state = SNDRV_DEV_REGISTERED; | ||
167 | return 0; | ||
168 | } | ||
169 | return -EBUSY; | ||
170 | } | ||
171 | snd_BUG(); | ||
172 | return -ENXIO; | ||
173 | } | ||
174 | |||
175 | /* | ||
176 | * register all the devices on the card. | ||
177 | * called from init.c | ||
178 | */ | ||
179 | int snd_device_register_all(snd_card_t *card) | ||
180 | { | ||
181 | struct list_head *list; | ||
182 | snd_device_t *dev; | ||
183 | int err; | ||
184 | |||
185 | snd_assert(card != NULL, return -ENXIO); | ||
186 | list_for_each(list, &card->devices) { | ||
187 | dev = snd_device(list); | ||
188 | if (dev->state == SNDRV_DEV_BUILD && dev->ops->dev_register) { | ||
189 | if ((err = dev->ops->dev_register(dev)) < 0) | ||
190 | return err; | ||
191 | dev->state = SNDRV_DEV_REGISTERED; | ||
192 | } | ||
193 | } | ||
194 | return 0; | ||
195 | } | ||
196 | |||
197 | /* | ||
198 | * disconnect all the devices on the card. | ||
199 | * called from init.c | ||
200 | */ | ||
201 | int snd_device_disconnect_all(snd_card_t *card) | ||
202 | { | ||
203 | snd_device_t *dev; | ||
204 | struct list_head *list; | ||
205 | int err = 0; | ||
206 | |||
207 | snd_assert(card != NULL, return -ENXIO); | ||
208 | list_for_each(list, &card->devices) { | ||
209 | dev = snd_device(list); | ||
210 | if (snd_device_disconnect(card, dev->device_data) < 0) | ||
211 | err = -ENXIO; | ||
212 | } | ||
213 | return err; | ||
214 | } | ||
215 | |||
216 | /* | ||
217 | * release all the devices on the card. | ||
218 | * called from init.c | ||
219 | */ | ||
220 | int snd_device_free_all(snd_card_t *card, snd_device_cmd_t cmd) | ||
221 | { | ||
222 | snd_device_t *dev; | ||
223 | struct list_head *list; | ||
224 | int err; | ||
225 | unsigned int range_low, range_high; | ||
226 | |||
227 | snd_assert(card != NULL, return -ENXIO); | ||
228 | range_low = cmd * SNDRV_DEV_TYPE_RANGE_SIZE; | ||
229 | range_high = range_low + SNDRV_DEV_TYPE_RANGE_SIZE - 1; | ||
230 | __again: | ||
231 | list_for_each(list, &card->devices) { | ||
232 | dev = snd_device(list); | ||
233 | if (dev->type >= range_low && dev->type <= range_high) { | ||
234 | if ((err = snd_device_free(card, dev->device_data)) < 0) | ||
235 | return err; | ||
236 | goto __again; | ||
237 | } | ||
238 | } | ||
239 | return 0; | ||
240 | } | ||
diff --git a/sound/core/hwdep.c b/sound/core/hwdep.c new file mode 100644 index 000000000000..997dd41c584e --- /dev/null +++ b/sound/core/hwdep.c | |||
@@ -0,0 +1,524 @@ | |||
1 | /* | ||
2 | * Hardware dependent layer | ||
3 | * Copyright (c) by Jaroslav Kysela <perex@suse.cz> | ||
4 | * | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #include <sound/driver.h> | ||
23 | #include <linux/major.h> | ||
24 | #include <linux/init.h> | ||
25 | #include <linux/smp_lock.h> | ||
26 | #include <linux/slab.h> | ||
27 | #include <linux/time.h> | ||
28 | #include <sound/core.h> | ||
29 | #include <sound/control.h> | ||
30 | #include <sound/minors.h> | ||
31 | #include <sound/hwdep.h> | ||
32 | #include <sound/info.h> | ||
33 | |||
34 | MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>"); | ||
35 | MODULE_DESCRIPTION("Hardware dependent layer"); | ||
36 | MODULE_LICENSE("GPL"); | ||
37 | |||
38 | static snd_hwdep_t *snd_hwdep_devices[SNDRV_CARDS * SNDRV_MINOR_HWDEPS]; | ||
39 | |||
40 | static DECLARE_MUTEX(register_mutex); | ||
41 | |||
42 | static int snd_hwdep_free(snd_hwdep_t *hwdep); | ||
43 | static int snd_hwdep_dev_free(snd_device_t *device); | ||
44 | static int snd_hwdep_dev_register(snd_device_t *device); | ||
45 | static int snd_hwdep_dev_unregister(snd_device_t *device); | ||
46 | |||
47 | /* | ||
48 | |||
49 | */ | ||
50 | |||
51 | static loff_t snd_hwdep_llseek(struct file * file, loff_t offset, int orig) | ||
52 | { | ||
53 | snd_hwdep_t *hw = file->private_data; | ||
54 | if (hw->ops.llseek) | ||
55 | return hw->ops.llseek(hw, file, offset, orig); | ||
56 | return -ENXIO; | ||
57 | } | ||
58 | |||
59 | static ssize_t snd_hwdep_read(struct file * file, char __user *buf, size_t count, loff_t *offset) | ||
60 | { | ||
61 | snd_hwdep_t *hw = file->private_data; | ||
62 | if (hw->ops.read) | ||
63 | return hw->ops.read(hw, buf, count, offset); | ||
64 | return -ENXIO; | ||
65 | } | ||
66 | |||
67 | static ssize_t snd_hwdep_write(struct file * file, const char __user *buf, size_t count, loff_t *offset) | ||
68 | { | ||
69 | snd_hwdep_t *hw = file->private_data; | ||
70 | if (hw->ops.write) | ||
71 | return hw->ops.write(hw, buf, count, offset); | ||
72 | return -ENXIO; | ||
73 | } | ||
74 | |||
75 | static int snd_hwdep_open(struct inode *inode, struct file * file) | ||
76 | { | ||
77 | int major = imajor(inode); | ||
78 | int cardnum; | ||
79 | int device; | ||
80 | snd_hwdep_t *hw; | ||
81 | int err; | ||
82 | wait_queue_t wait; | ||
83 | |||
84 | switch (major) { | ||
85 | case CONFIG_SND_MAJOR: | ||
86 | cardnum = SNDRV_MINOR_CARD(iminor(inode)); | ||
87 | device = SNDRV_MINOR_DEVICE(iminor(inode)) - SNDRV_MINOR_HWDEP; | ||
88 | break; | ||
89 | #ifdef CONFIG_SND_OSSEMUL | ||
90 | case SOUND_MAJOR: | ||
91 | cardnum = SNDRV_MINOR_OSS_CARD(iminor(inode)); | ||
92 | device = 0; | ||
93 | break; | ||
94 | #endif | ||
95 | default: | ||
96 | return -ENXIO; | ||
97 | } | ||
98 | cardnum %= SNDRV_CARDS; | ||
99 | device %= SNDRV_MINOR_HWDEPS; | ||
100 | hw = snd_hwdep_devices[(cardnum * SNDRV_MINOR_HWDEPS) + device]; | ||
101 | if (hw == NULL) | ||
102 | return -ENODEV; | ||
103 | |||
104 | if (!hw->ops.open) | ||
105 | return -ENXIO; | ||
106 | #ifdef CONFIG_SND_OSSEMUL | ||
107 | if (major == SOUND_MAJOR && hw->oss_type < 0) | ||
108 | return -ENXIO; | ||
109 | #endif | ||
110 | |||
111 | if (!try_module_get(hw->card->module)) | ||
112 | return -EFAULT; | ||
113 | |||
114 | init_waitqueue_entry(&wait, current); | ||
115 | add_wait_queue(&hw->open_wait, &wait); | ||
116 | down(&hw->open_mutex); | ||
117 | while (1) { | ||
118 | if (hw->exclusive && hw->used > 0) { | ||
119 | err = -EBUSY; | ||
120 | break; | ||
121 | } | ||
122 | err = hw->ops.open(hw, file); | ||
123 | if (err >= 0) | ||
124 | break; | ||
125 | if (err == -EAGAIN) { | ||
126 | if (file->f_flags & O_NONBLOCK) { | ||
127 | err = -EBUSY; | ||
128 | break; | ||
129 | } | ||
130 | } else | ||
131 | break; | ||
132 | set_current_state(TASK_INTERRUPTIBLE); | ||
133 | up(&hw->open_mutex); | ||
134 | schedule(); | ||
135 | down(&hw->open_mutex); | ||
136 | if (signal_pending(current)) { | ||
137 | err = -ERESTARTSYS; | ||
138 | break; | ||
139 | } | ||
140 | } | ||
141 | remove_wait_queue(&hw->open_wait, &wait); | ||
142 | if (err >= 0) { | ||
143 | err = snd_card_file_add(hw->card, file); | ||
144 | if (err >= 0) { | ||
145 | file->private_data = hw; | ||
146 | hw->used++; | ||
147 | } else { | ||
148 | if (hw->ops.release) | ||
149 | hw->ops.release(hw, file); | ||
150 | } | ||
151 | } | ||
152 | up(&hw->open_mutex); | ||
153 | if (err < 0) | ||
154 | module_put(hw->card->module); | ||
155 | return err; | ||
156 | } | ||
157 | |||
158 | static int snd_hwdep_release(struct inode *inode, struct file * file) | ||
159 | { | ||
160 | int err = -ENXIO; | ||
161 | snd_hwdep_t *hw = file->private_data; | ||
162 | down(&hw->open_mutex); | ||
163 | if (hw->ops.release) { | ||
164 | err = hw->ops.release(hw, file); | ||
165 | wake_up(&hw->open_wait); | ||
166 | } | ||
167 | if (hw->used > 0) | ||
168 | hw->used--; | ||
169 | snd_card_file_remove(hw->card, file); | ||
170 | up(&hw->open_mutex); | ||
171 | module_put(hw->card->module); | ||
172 | return err; | ||
173 | } | ||
174 | |||
175 | static unsigned int snd_hwdep_poll(struct file * file, poll_table * wait) | ||
176 | { | ||
177 | snd_hwdep_t *hw = file->private_data; | ||
178 | if (hw->ops.poll) | ||
179 | return hw->ops.poll(hw, file, wait); | ||
180 | return 0; | ||
181 | } | ||
182 | |||
183 | static int snd_hwdep_info(snd_hwdep_t *hw, snd_hwdep_info_t __user *_info) | ||
184 | { | ||
185 | snd_hwdep_info_t info; | ||
186 | |||
187 | memset(&info, 0, sizeof(info)); | ||
188 | info.card = hw->card->number; | ||
189 | strlcpy(info.id, hw->id, sizeof(info.id)); | ||
190 | strlcpy(info.name, hw->name, sizeof(info.name)); | ||
191 | info.iface = hw->iface; | ||
192 | if (copy_to_user(_info, &info, sizeof(info))) | ||
193 | return -EFAULT; | ||
194 | return 0; | ||
195 | } | ||
196 | |||
197 | static int snd_hwdep_dsp_status(snd_hwdep_t *hw, snd_hwdep_dsp_status_t __user *_info) | ||
198 | { | ||
199 | snd_hwdep_dsp_status_t info; | ||
200 | int err; | ||
201 | |||
202 | if (! hw->ops.dsp_status) | ||
203 | return -ENXIO; | ||
204 | memset(&info, 0, sizeof(info)); | ||
205 | info.dsp_loaded = hw->dsp_loaded; | ||
206 | if ((err = hw->ops.dsp_status(hw, &info)) < 0) | ||
207 | return err; | ||
208 | if (copy_to_user(_info, &info, sizeof(info))) | ||
209 | return -EFAULT; | ||
210 | return 0; | ||
211 | } | ||
212 | |||
213 | static int snd_hwdep_dsp_load(snd_hwdep_t *hw, snd_hwdep_dsp_image_t __user *_info) | ||
214 | { | ||
215 | snd_hwdep_dsp_image_t info; | ||
216 | int err; | ||
217 | |||
218 | if (! hw->ops.dsp_load) | ||
219 | return -ENXIO; | ||
220 | memset(&info, 0, sizeof(info)); | ||
221 | if (copy_from_user(&info, _info, sizeof(info))) | ||
222 | return -EFAULT; | ||
223 | /* check whether the dsp was already loaded */ | ||
224 | if (hw->dsp_loaded & (1 << info.index)) | ||
225 | return -EBUSY; | ||
226 | if (!access_ok(VERIFY_READ, info.image, info.length)) | ||
227 | return -EFAULT; | ||
228 | err = hw->ops.dsp_load(hw, &info); | ||
229 | if (err < 0) | ||
230 | return err; | ||
231 | hw->dsp_loaded |= (1 << info.index); | ||
232 | return 0; | ||
233 | } | ||
234 | |||
235 | static long snd_hwdep_ioctl(struct file * file, unsigned int cmd, unsigned long arg) | ||
236 | { | ||
237 | snd_hwdep_t *hw = file->private_data; | ||
238 | void __user *argp = (void __user *)arg; | ||
239 | switch (cmd) { | ||
240 | case SNDRV_HWDEP_IOCTL_PVERSION: | ||
241 | return put_user(SNDRV_HWDEP_VERSION, (int __user *)argp); | ||
242 | case SNDRV_HWDEP_IOCTL_INFO: | ||
243 | return snd_hwdep_info(hw, argp); | ||
244 | case SNDRV_HWDEP_IOCTL_DSP_STATUS: | ||
245 | return snd_hwdep_dsp_status(hw, argp); | ||
246 | case SNDRV_HWDEP_IOCTL_DSP_LOAD: | ||
247 | return snd_hwdep_dsp_load(hw, argp); | ||
248 | } | ||
249 | if (hw->ops.ioctl) | ||
250 | return hw->ops.ioctl(hw, file, cmd, arg); | ||
251 | return -ENOTTY; | ||
252 | } | ||
253 | |||
254 | static int snd_hwdep_mmap(struct file * file, struct vm_area_struct * vma) | ||
255 | { | ||
256 | snd_hwdep_t *hw = file->private_data; | ||
257 | if (hw->ops.mmap) | ||
258 | return hw->ops.mmap(hw, file, vma); | ||
259 | return -ENXIO; | ||
260 | } | ||
261 | |||
262 | static int snd_hwdep_control_ioctl(snd_card_t * card, snd_ctl_file_t * control, | ||
263 | unsigned int cmd, unsigned long arg) | ||
264 | { | ||
265 | unsigned int tmp; | ||
266 | |||
267 | tmp = card->number * SNDRV_MINOR_HWDEPS; | ||
268 | switch (cmd) { | ||
269 | case SNDRV_CTL_IOCTL_HWDEP_NEXT_DEVICE: | ||
270 | { | ||
271 | int device; | ||
272 | |||
273 | if (get_user(device, (int __user *)arg)) | ||
274 | return -EFAULT; | ||
275 | device = device < 0 ? 0 : device + 1; | ||
276 | while (device < SNDRV_MINOR_HWDEPS) { | ||
277 | if (snd_hwdep_devices[tmp + device]) | ||
278 | break; | ||
279 | device++; | ||
280 | } | ||
281 | if (device >= SNDRV_MINOR_HWDEPS) | ||
282 | device = -1; | ||
283 | if (put_user(device, (int __user *)arg)) | ||
284 | return -EFAULT; | ||
285 | return 0; | ||
286 | } | ||
287 | case SNDRV_CTL_IOCTL_HWDEP_INFO: | ||
288 | { | ||
289 | snd_hwdep_info_t __user *info = (snd_hwdep_info_t __user *)arg; | ||
290 | int device; | ||
291 | snd_hwdep_t *hwdep; | ||
292 | |||
293 | if (get_user(device, &info->device)) | ||
294 | return -EFAULT; | ||
295 | if (device < 0 || device >= SNDRV_MINOR_HWDEPS) | ||
296 | return -ENXIO; | ||
297 | hwdep = snd_hwdep_devices[tmp + device]; | ||
298 | if (hwdep == NULL) | ||
299 | return -ENXIO; | ||
300 | return snd_hwdep_info(hwdep, info); | ||
301 | } | ||
302 | } | ||
303 | return -ENOIOCTLCMD; | ||
304 | } | ||
305 | |||
306 | #ifdef CONFIG_COMPAT | ||
307 | #include "hwdep_compat.c" | ||
308 | #else | ||
309 | #define snd_hwdep_ioctl_compat NULL | ||
310 | #endif | ||
311 | |||
312 | /* | ||
313 | |||
314 | */ | ||
315 | |||
316 | static struct file_operations snd_hwdep_f_ops = | ||
317 | { | ||
318 | .owner = THIS_MODULE, | ||
319 | .llseek = snd_hwdep_llseek, | ||
320 | .read = snd_hwdep_read, | ||
321 | .write = snd_hwdep_write, | ||
322 | .open = snd_hwdep_open, | ||
323 | .release = snd_hwdep_release, | ||
324 | .poll = snd_hwdep_poll, | ||
325 | .unlocked_ioctl = snd_hwdep_ioctl, | ||
326 | .compat_ioctl = snd_hwdep_ioctl_compat, | ||
327 | .mmap = snd_hwdep_mmap, | ||
328 | }; | ||
329 | |||
330 | static snd_minor_t snd_hwdep_reg = | ||
331 | { | ||
332 | .comment = "hardware dependent", | ||
333 | .f_ops = &snd_hwdep_f_ops, | ||
334 | }; | ||
335 | |||
336 | /** | ||
337 | * snd_hwdep_new - create a new hwdep instance | ||
338 | * @card: the card instance | ||
339 | * @id: the id string | ||
340 | * @device: the device index (zero-based) | ||
341 | * @rhwdep: the pointer to store the new hwdep instance | ||
342 | * | ||
343 | * Creates a new hwdep instance with the given index on the card. | ||
344 | * The callbacks (hwdep->ops) must be set on the returned instance | ||
345 | * after this call manually by the caller. | ||
346 | * | ||
347 | * Returns zero if successful, or a negative error code on failure. | ||
348 | */ | ||
349 | int snd_hwdep_new(snd_card_t * card, char *id, int device, snd_hwdep_t ** rhwdep) | ||
350 | { | ||
351 | snd_hwdep_t *hwdep; | ||
352 | int err; | ||
353 | static snd_device_ops_t ops = { | ||
354 | .dev_free = snd_hwdep_dev_free, | ||
355 | .dev_register = snd_hwdep_dev_register, | ||
356 | .dev_unregister = snd_hwdep_dev_unregister | ||
357 | }; | ||
358 | |||
359 | snd_assert(rhwdep != NULL, return -EINVAL); | ||
360 | *rhwdep = NULL; | ||
361 | snd_assert(card != NULL, return -ENXIO); | ||
362 | hwdep = kcalloc(1, sizeof(*hwdep), GFP_KERNEL); | ||
363 | if (hwdep == NULL) | ||
364 | return -ENOMEM; | ||
365 | hwdep->card = card; | ||
366 | hwdep->device = device; | ||
367 | if (id) { | ||
368 | strlcpy(hwdep->id, id, sizeof(hwdep->id)); | ||
369 | } | ||
370 | #ifdef CONFIG_SND_OSSEMUL | ||
371 | hwdep->oss_type = -1; | ||
372 | #endif | ||
373 | if ((err = snd_device_new(card, SNDRV_DEV_HWDEP, hwdep, &ops)) < 0) { | ||
374 | snd_hwdep_free(hwdep); | ||
375 | return err; | ||
376 | } | ||
377 | init_waitqueue_head(&hwdep->open_wait); | ||
378 | init_MUTEX(&hwdep->open_mutex); | ||
379 | *rhwdep = hwdep; | ||
380 | return 0; | ||
381 | } | ||
382 | |||
383 | static int snd_hwdep_free(snd_hwdep_t *hwdep) | ||
384 | { | ||
385 | snd_assert(hwdep != NULL, return -ENXIO); | ||
386 | if (hwdep->private_free) | ||
387 | hwdep->private_free(hwdep); | ||
388 | kfree(hwdep); | ||
389 | return 0; | ||
390 | } | ||
391 | |||
392 | static int snd_hwdep_dev_free(snd_device_t *device) | ||
393 | { | ||
394 | snd_hwdep_t *hwdep = device->device_data; | ||
395 | return snd_hwdep_free(hwdep); | ||
396 | } | ||
397 | |||
398 | static int snd_hwdep_dev_register(snd_device_t *device) | ||
399 | { | ||
400 | snd_hwdep_t *hwdep = device->device_data; | ||
401 | int idx, err; | ||
402 | char name[32]; | ||
403 | |||
404 | down(®ister_mutex); | ||
405 | idx = (hwdep->card->number * SNDRV_MINOR_HWDEPS) + hwdep->device; | ||
406 | if (snd_hwdep_devices[idx]) { | ||
407 | up(®ister_mutex); | ||
408 | return -EBUSY; | ||
409 | } | ||
410 | snd_hwdep_devices[idx] = hwdep; | ||
411 | sprintf(name, "hwC%iD%i", hwdep->card->number, hwdep->device); | ||
412 | if ((err = snd_register_device(SNDRV_DEVICE_TYPE_HWDEP, | ||
413 | hwdep->card, hwdep->device, | ||
414 | &snd_hwdep_reg, name)) < 0) { | ||
415 | snd_printk(KERN_ERR "unable to register hardware dependent device %i:%i\n", | ||
416 | hwdep->card->number, hwdep->device); | ||
417 | snd_hwdep_devices[idx] = NULL; | ||
418 | up(®ister_mutex); | ||
419 | return err; | ||
420 | } | ||
421 | #ifdef CONFIG_SND_OSSEMUL | ||
422 | hwdep->ossreg = 0; | ||
423 | if (hwdep->oss_type >= 0) { | ||
424 | if ((hwdep->oss_type == SNDRV_OSS_DEVICE_TYPE_DMFM) && (hwdep->device != 0)) { | ||
425 | snd_printk (KERN_WARNING "only hwdep device 0 can be registered as OSS direct FM device!\n"); | ||
426 | } else { | ||
427 | if (snd_register_oss_device(hwdep->oss_type, | ||
428 | hwdep->card, hwdep->device, | ||
429 | &snd_hwdep_reg, hwdep->oss_dev) < 0) { | ||
430 | snd_printk(KERN_ERR "unable to register OSS compatibility device %i:%i\n", | ||
431 | hwdep->card->number, hwdep->device); | ||
432 | } else | ||
433 | hwdep->ossreg = 1; | ||
434 | } | ||
435 | } | ||
436 | #endif | ||
437 | up(®ister_mutex); | ||
438 | return 0; | ||
439 | } | ||
440 | |||
441 | static int snd_hwdep_dev_unregister(snd_device_t *device) | ||
442 | { | ||
443 | snd_hwdep_t *hwdep = device->device_data; | ||
444 | int idx; | ||
445 | |||
446 | snd_assert(hwdep != NULL, return -ENXIO); | ||
447 | down(®ister_mutex); | ||
448 | idx = (hwdep->card->number * SNDRV_MINOR_HWDEPS) + hwdep->device; | ||
449 | if (snd_hwdep_devices[idx] != hwdep) { | ||
450 | up(®ister_mutex); | ||
451 | return -EINVAL; | ||
452 | } | ||
453 | #ifdef CONFIG_SND_OSSEMUL | ||
454 | if (hwdep->ossreg) | ||
455 | snd_unregister_oss_device(hwdep->oss_type, hwdep->card, hwdep->device); | ||
456 | #endif | ||
457 | snd_unregister_device(SNDRV_DEVICE_TYPE_HWDEP, hwdep->card, hwdep->device); | ||
458 | snd_hwdep_devices[idx] = NULL; | ||
459 | up(®ister_mutex); | ||
460 | return snd_hwdep_free(hwdep); | ||
461 | } | ||
462 | |||
463 | /* | ||
464 | * Info interface | ||
465 | */ | ||
466 | |||
467 | static void snd_hwdep_proc_read(snd_info_entry_t *entry, | ||
468 | snd_info_buffer_t * buffer) | ||
469 | { | ||
470 | int idx; | ||
471 | snd_hwdep_t *hwdep; | ||
472 | |||
473 | down(®ister_mutex); | ||
474 | for (idx = 0; idx < SNDRV_CARDS * SNDRV_MINOR_HWDEPS; idx++) { | ||
475 | hwdep = snd_hwdep_devices[idx]; | ||
476 | if (hwdep == NULL) | ||
477 | continue; | ||
478 | snd_iprintf(buffer, "%02i-%02i: %s\n", | ||
479 | idx / SNDRV_MINOR_HWDEPS, | ||
480 | idx % SNDRV_MINOR_HWDEPS, | ||
481 | hwdep->name); | ||
482 | } | ||
483 | up(®ister_mutex); | ||
484 | } | ||
485 | |||
486 | /* | ||
487 | * ENTRY functions | ||
488 | */ | ||
489 | |||
490 | static snd_info_entry_t *snd_hwdep_proc_entry = NULL; | ||
491 | |||
492 | static int __init alsa_hwdep_init(void) | ||
493 | { | ||
494 | snd_info_entry_t *entry; | ||
495 | |||
496 | memset(snd_hwdep_devices, 0, sizeof(snd_hwdep_devices)); | ||
497 | if ((entry = snd_info_create_module_entry(THIS_MODULE, "hwdep", NULL)) != NULL) { | ||
498 | entry->c.text.read_size = 512; | ||
499 | entry->c.text.read = snd_hwdep_proc_read; | ||
500 | if (snd_info_register(entry) < 0) { | ||
501 | snd_info_free_entry(entry); | ||
502 | entry = NULL; | ||
503 | } | ||
504 | } | ||
505 | snd_hwdep_proc_entry = entry; | ||
506 | snd_ctl_register_ioctl(snd_hwdep_control_ioctl); | ||
507 | snd_ctl_register_ioctl_compat(snd_hwdep_control_ioctl); | ||
508 | return 0; | ||
509 | } | ||
510 | |||
511 | static void __exit alsa_hwdep_exit(void) | ||
512 | { | ||
513 | snd_ctl_unregister_ioctl(snd_hwdep_control_ioctl); | ||
514 | snd_ctl_unregister_ioctl_compat(snd_hwdep_control_ioctl); | ||
515 | if (snd_hwdep_proc_entry) { | ||
516 | snd_info_unregister(snd_hwdep_proc_entry); | ||
517 | snd_hwdep_proc_entry = NULL; | ||
518 | } | ||
519 | } | ||
520 | |||
521 | module_init(alsa_hwdep_init) | ||
522 | module_exit(alsa_hwdep_exit) | ||
523 | |||
524 | EXPORT_SYMBOL(snd_hwdep_new); | ||
diff --git a/sound/core/hwdep_compat.c b/sound/core/hwdep_compat.c new file mode 100644 index 000000000000..6866f423d4b9 --- /dev/null +++ b/sound/core/hwdep_compat.c | |||
@@ -0,0 +1,77 @@ | |||
1 | /* | ||
2 | * 32bit -> 64bit ioctl wrapper for hwdep 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 is included from hwdep.c */ | ||
22 | |||
23 | #include <linux/compat.h> | ||
24 | |||
25 | struct sndrv_hwdep_dsp_image32 { | ||
26 | u32 index; | ||
27 | unsigned char name[64]; | ||
28 | u32 image; /* pointer */ | ||
29 | u32 length; | ||
30 | u32 driver_data; | ||
31 | } /* don't set packed attribute here */; | ||
32 | |||
33 | static int snd_hwdep_dsp_load_compat(snd_hwdep_t *hw, | ||
34 | struct sndrv_hwdep_dsp_image32 __user *src) | ||
35 | { | ||
36 | struct sndrv_hwdep_dsp_image *dst; | ||
37 | compat_caddr_t ptr; | ||
38 | u32 val; | ||
39 | |||
40 | dst = compat_alloc_user_space(sizeof(*dst)); | ||
41 | |||
42 | /* index and name */ | ||
43 | if (copy_in_user(dst, src, 4 + 64)) | ||
44 | return -EFAULT; | ||
45 | if (get_user(ptr, &src->image) || | ||
46 | put_user(compat_ptr(ptr), &dst->image)) | ||
47 | return -EFAULT; | ||
48 | if (get_user(val, &src->length) || | ||
49 | put_user(val, &dst->length)) | ||
50 | return -EFAULT; | ||
51 | if (get_user(val, &src->driver_data) || | ||
52 | put_user(val, &dst->driver_data)) | ||
53 | return -EFAULT; | ||
54 | |||
55 | return snd_hwdep_dsp_load(hw, dst); | ||
56 | } | ||
57 | |||
58 | enum { | ||
59 | SNDRV_HWDEP_IOCTL_DSP_LOAD32 = _IOW('H', 0x03, struct sndrv_hwdep_dsp_image32) | ||
60 | }; | ||
61 | |||
62 | static long snd_hwdep_ioctl_compat(struct file * file, unsigned int cmd, unsigned long arg) | ||
63 | { | ||
64 | snd_hwdep_t *hw = file->private_data; | ||
65 | void __user *argp = compat_ptr(arg); | ||
66 | switch (cmd) { | ||
67 | case SNDRV_HWDEP_IOCTL_PVERSION: | ||
68 | case SNDRV_HWDEP_IOCTL_INFO: | ||
69 | case SNDRV_HWDEP_IOCTL_DSP_STATUS: | ||
70 | return snd_hwdep_ioctl(file, cmd, (unsigned long)argp); | ||
71 | case SNDRV_HWDEP_IOCTL_DSP_LOAD32: | ||
72 | return snd_hwdep_dsp_load_compat(hw, argp); | ||
73 | } | ||
74 | if (hw->ops.ioctl_compat) | ||
75 | return hw->ops.ioctl_compat(hw, file, cmd, arg); | ||
76 | return -ENOIOCTLCMD; | ||
77 | } | ||
diff --git a/sound/core/info.c b/sound/core/info.c new file mode 100644 index 000000000000..31faffe01cb0 --- /dev/null +++ b/sound/core/info.c | |||
@@ -0,0 +1,989 @@ | |||
1 | /* | ||
2 | * Information interface for ALSA driver | ||
3 | * Copyright (c) by Jaroslav Kysela <perex@suse.cz> | ||
4 | * | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #include <sound/driver.h> | ||
23 | #include <linux/init.h> | ||
24 | #include <linux/vmalloc.h> | ||
25 | #include <linux/time.h> | ||
26 | #include <linux/smp_lock.h> | ||
27 | #include <sound/core.h> | ||
28 | #include <sound/minors.h> | ||
29 | #include <sound/info.h> | ||
30 | #include <sound/version.h> | ||
31 | #include <linux/proc_fs.h> | ||
32 | #include <linux/devfs_fs_kernel.h> | ||
33 | #include <stdarg.h> | ||
34 | |||
35 | /* | ||
36 | * | ||
37 | */ | ||
38 | |||
39 | int snd_info_check_reserved_words(const char *str) | ||
40 | { | ||
41 | static char *reserved[] = | ||
42 | { | ||
43 | "version", | ||
44 | "meminfo", | ||
45 | "memdebug", | ||
46 | "detect", | ||
47 | "devices", | ||
48 | "oss", | ||
49 | "cards", | ||
50 | "timers", | ||
51 | "synth", | ||
52 | "pcm", | ||
53 | "seq", | ||
54 | NULL | ||
55 | }; | ||
56 | char **xstr = reserved; | ||
57 | |||
58 | while (*xstr) { | ||
59 | if (!strcmp(*xstr, str)) | ||
60 | return 0; | ||
61 | xstr++; | ||
62 | } | ||
63 | if (!strncmp(str, "card", 4)) | ||
64 | return 0; | ||
65 | return 1; | ||
66 | } | ||
67 | |||
68 | #ifdef CONFIG_PROC_FS | ||
69 | |||
70 | static DECLARE_MUTEX(info_mutex); | ||
71 | |||
72 | typedef struct _snd_info_private_data { | ||
73 | snd_info_buffer_t *rbuffer; | ||
74 | snd_info_buffer_t *wbuffer; | ||
75 | snd_info_entry_t *entry; | ||
76 | void *file_private_data; | ||
77 | } snd_info_private_data_t; | ||
78 | |||
79 | static int snd_info_version_init(void); | ||
80 | static int snd_info_version_done(void); | ||
81 | |||
82 | |||
83 | /** | ||
84 | * snd_iprintf - printf on the procfs buffer | ||
85 | * @buffer: the procfs buffer | ||
86 | * @fmt: the printf format | ||
87 | * | ||
88 | * Outputs the string on the procfs buffer just like printf(). | ||
89 | * | ||
90 | * Returns the size of output string. | ||
91 | */ | ||
92 | int snd_iprintf(snd_info_buffer_t * buffer, char *fmt,...) | ||
93 | { | ||
94 | va_list args; | ||
95 | int len, res; | ||
96 | |||
97 | if (buffer->stop || buffer->error) | ||
98 | return 0; | ||
99 | len = buffer->len - buffer->size; | ||
100 | va_start(args, fmt); | ||
101 | res = vsnprintf(buffer->curr, len, fmt, args); | ||
102 | va_end(args); | ||
103 | if (res >= len) { | ||
104 | buffer->stop = 1; | ||
105 | return 0; | ||
106 | } | ||
107 | buffer->curr += res; | ||
108 | buffer->size += res; | ||
109 | return res; | ||
110 | } | ||
111 | |||
112 | /* | ||
113 | |||
114 | */ | ||
115 | |||
116 | static struct proc_dir_entry *snd_proc_root = NULL; | ||
117 | snd_info_entry_t *snd_seq_root = NULL; | ||
118 | #ifdef CONFIG_SND_OSSEMUL | ||
119 | snd_info_entry_t *snd_oss_root = NULL; | ||
120 | #endif | ||
121 | |||
122 | static inline void snd_info_entry_prepare(struct proc_dir_entry *de) | ||
123 | { | ||
124 | de->owner = THIS_MODULE; | ||
125 | } | ||
126 | |||
127 | static void snd_remove_proc_entry(struct proc_dir_entry *parent, | ||
128 | struct proc_dir_entry *de) | ||
129 | { | ||
130 | if (de) | ||
131 | remove_proc_entry(de->name, parent); | ||
132 | } | ||
133 | |||
134 | static loff_t snd_info_entry_llseek(struct file *file, loff_t offset, int orig) | ||
135 | { | ||
136 | snd_info_private_data_t *data; | ||
137 | struct snd_info_entry *entry; | ||
138 | loff_t ret; | ||
139 | |||
140 | data = file->private_data; | ||
141 | entry = data->entry; | ||
142 | lock_kernel(); | ||
143 | switch (entry->content) { | ||
144 | case SNDRV_INFO_CONTENT_TEXT: | ||
145 | switch (orig) { | ||
146 | case 0: /* SEEK_SET */ | ||
147 | file->f_pos = offset; | ||
148 | ret = file->f_pos; | ||
149 | goto out; | ||
150 | case 1: /* SEEK_CUR */ | ||
151 | file->f_pos += offset; | ||
152 | ret = file->f_pos; | ||
153 | goto out; | ||
154 | case 2: /* SEEK_END */ | ||
155 | default: | ||
156 | ret = -EINVAL; | ||
157 | goto out; | ||
158 | } | ||
159 | break; | ||
160 | case SNDRV_INFO_CONTENT_DATA: | ||
161 | if (entry->c.ops->llseek) { | ||
162 | ret = entry->c.ops->llseek(entry, | ||
163 | data->file_private_data, | ||
164 | file, offset, orig); | ||
165 | goto out; | ||
166 | } | ||
167 | break; | ||
168 | } | ||
169 | ret = -ENXIO; | ||
170 | out: | ||
171 | unlock_kernel(); | ||
172 | return ret; | ||
173 | } | ||
174 | |||
175 | static ssize_t snd_info_entry_read(struct file *file, char __user *buffer, | ||
176 | size_t count, loff_t * offset) | ||
177 | { | ||
178 | snd_info_private_data_t *data; | ||
179 | struct snd_info_entry *entry; | ||
180 | snd_info_buffer_t *buf; | ||
181 | size_t size = 0; | ||
182 | loff_t pos; | ||
183 | |||
184 | data = file->private_data; | ||
185 | snd_assert(data != NULL, return -ENXIO); | ||
186 | pos = *offset; | ||
187 | if (pos < 0 || (long) pos != pos || (ssize_t) count < 0) | ||
188 | return -EIO; | ||
189 | if ((unsigned long) pos + (unsigned long) count < (unsigned long) pos) | ||
190 | return -EIO; | ||
191 | entry = data->entry; | ||
192 | switch (entry->content) { | ||
193 | case SNDRV_INFO_CONTENT_TEXT: | ||
194 | buf = data->rbuffer; | ||
195 | if (buf == NULL) | ||
196 | return -EIO; | ||
197 | if (pos >= buf->size) | ||
198 | return 0; | ||
199 | size = buf->size - pos; | ||
200 | size = min(count, size); | ||
201 | if (copy_to_user(buffer, buf->buffer + pos, size)) | ||
202 | return -EFAULT; | ||
203 | break; | ||
204 | case SNDRV_INFO_CONTENT_DATA: | ||
205 | if (entry->c.ops->read) | ||
206 | size = entry->c.ops->read(entry, | ||
207 | data->file_private_data, | ||
208 | file, buffer, count, pos); | ||
209 | break; | ||
210 | } | ||
211 | if ((ssize_t) size > 0) | ||
212 | *offset = pos + size; | ||
213 | return size; | ||
214 | } | ||
215 | |||
216 | static ssize_t snd_info_entry_write(struct file *file, const char __user *buffer, | ||
217 | size_t count, loff_t * offset) | ||
218 | { | ||
219 | snd_info_private_data_t *data; | ||
220 | struct snd_info_entry *entry; | ||
221 | snd_info_buffer_t *buf; | ||
222 | size_t size = 0; | ||
223 | loff_t pos; | ||
224 | |||
225 | data = file->private_data; | ||
226 | snd_assert(data != NULL, return -ENXIO); | ||
227 | entry = data->entry; | ||
228 | pos = *offset; | ||
229 | if (pos < 0 || (long) pos != pos || (ssize_t) count < 0) | ||
230 | return -EIO; | ||
231 | if ((unsigned long) pos + (unsigned long) count < (unsigned long) pos) | ||
232 | return -EIO; | ||
233 | switch (entry->content) { | ||
234 | case SNDRV_INFO_CONTENT_TEXT: | ||
235 | buf = data->wbuffer; | ||
236 | if (buf == NULL) | ||
237 | return -EIO; | ||
238 | if (pos >= buf->len) | ||
239 | return -ENOMEM; | ||
240 | size = buf->len - pos; | ||
241 | size = min(count, size); | ||
242 | if (copy_from_user(buf->buffer + pos, buffer, size)) | ||
243 | return -EFAULT; | ||
244 | if ((long)buf->size < pos + size) | ||
245 | buf->size = pos + size; | ||
246 | break; | ||
247 | case SNDRV_INFO_CONTENT_DATA: | ||
248 | if (entry->c.ops->write) | ||
249 | size = entry->c.ops->write(entry, | ||
250 | data->file_private_data, | ||
251 | file, buffer, count, pos); | ||
252 | break; | ||
253 | } | ||
254 | if ((ssize_t) size > 0) | ||
255 | *offset = pos + size; | ||
256 | return size; | ||
257 | } | ||
258 | |||
259 | static int snd_info_entry_open(struct inode *inode, struct file *file) | ||
260 | { | ||
261 | snd_info_entry_t *entry; | ||
262 | snd_info_private_data_t *data; | ||
263 | snd_info_buffer_t *buffer; | ||
264 | struct proc_dir_entry *p; | ||
265 | int mode, err; | ||
266 | |||
267 | down(&info_mutex); | ||
268 | p = PDE(inode); | ||
269 | entry = p == NULL ? NULL : (snd_info_entry_t *)p->data; | ||
270 | if (entry == NULL || entry->disconnected) { | ||
271 | up(&info_mutex); | ||
272 | return -ENODEV; | ||
273 | } | ||
274 | if (!try_module_get(entry->module)) { | ||
275 | err = -EFAULT; | ||
276 | goto __error1; | ||
277 | } | ||
278 | mode = file->f_flags & O_ACCMODE; | ||
279 | if (mode == O_RDONLY || mode == O_RDWR) { | ||
280 | if ((entry->content == SNDRV_INFO_CONTENT_TEXT && | ||
281 | !entry->c.text.read_size) || | ||
282 | (entry->content == SNDRV_INFO_CONTENT_DATA && | ||
283 | entry->c.ops->read == NULL)) { | ||
284 | err = -ENODEV; | ||
285 | goto __error; | ||
286 | } | ||
287 | } | ||
288 | if (mode == O_WRONLY || mode == O_RDWR) { | ||
289 | if ((entry->content == SNDRV_INFO_CONTENT_TEXT && | ||
290 | !entry->c.text.write_size) || | ||
291 | (entry->content == SNDRV_INFO_CONTENT_DATA && | ||
292 | entry->c.ops->write == NULL)) { | ||
293 | err = -ENODEV; | ||
294 | goto __error; | ||
295 | } | ||
296 | } | ||
297 | data = kcalloc(1, sizeof(*data), GFP_KERNEL); | ||
298 | if (data == NULL) { | ||
299 | err = -ENOMEM; | ||
300 | goto __error; | ||
301 | } | ||
302 | data->entry = entry; | ||
303 | switch (entry->content) { | ||
304 | case SNDRV_INFO_CONTENT_TEXT: | ||
305 | if (mode == O_RDONLY || mode == O_RDWR) { | ||
306 | buffer = kcalloc(1, sizeof(*buffer), GFP_KERNEL); | ||
307 | if (buffer == NULL) { | ||
308 | kfree(data); | ||
309 | err = -ENOMEM; | ||
310 | goto __error; | ||
311 | } | ||
312 | buffer->len = (entry->c.text.read_size + | ||
313 | (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1); | ||
314 | buffer->buffer = vmalloc(buffer->len); | ||
315 | if (buffer->buffer == NULL) { | ||
316 | kfree(buffer); | ||
317 | kfree(data); | ||
318 | err = -ENOMEM; | ||
319 | goto __error; | ||
320 | } | ||
321 | buffer->curr = buffer->buffer; | ||
322 | data->rbuffer = buffer; | ||
323 | } | ||
324 | if (mode == O_WRONLY || mode == O_RDWR) { | ||
325 | buffer = kcalloc(1, sizeof(*buffer), GFP_KERNEL); | ||
326 | if (buffer == NULL) { | ||
327 | if (mode == O_RDWR) { | ||
328 | vfree(data->rbuffer->buffer); | ||
329 | kfree(data->rbuffer); | ||
330 | } | ||
331 | kfree(data); | ||
332 | err = -ENOMEM; | ||
333 | goto __error; | ||
334 | } | ||
335 | buffer->len = (entry->c.text.write_size + | ||
336 | (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1); | ||
337 | buffer->buffer = vmalloc(buffer->len); | ||
338 | if (buffer->buffer == NULL) { | ||
339 | if (mode == O_RDWR) { | ||
340 | vfree(data->rbuffer->buffer); | ||
341 | kfree(data->rbuffer); | ||
342 | } | ||
343 | kfree(buffer); | ||
344 | kfree(data); | ||
345 | err = -ENOMEM; | ||
346 | goto __error; | ||
347 | } | ||
348 | buffer->curr = buffer->buffer; | ||
349 | data->wbuffer = buffer; | ||
350 | } | ||
351 | break; | ||
352 | case SNDRV_INFO_CONTENT_DATA: /* data */ | ||
353 | if (entry->c.ops->open) { | ||
354 | if ((err = entry->c.ops->open(entry, mode, | ||
355 | &data->file_private_data)) < 0) { | ||
356 | kfree(data); | ||
357 | goto __error; | ||
358 | } | ||
359 | } | ||
360 | break; | ||
361 | } | ||
362 | file->private_data = data; | ||
363 | up(&info_mutex); | ||
364 | if (entry->content == SNDRV_INFO_CONTENT_TEXT && | ||
365 | (mode == O_RDONLY || mode == O_RDWR)) { | ||
366 | if (entry->c.text.read) { | ||
367 | down(&entry->access); | ||
368 | entry->c.text.read(entry, data->rbuffer); | ||
369 | up(&entry->access); | ||
370 | } | ||
371 | } | ||
372 | return 0; | ||
373 | |||
374 | __error: | ||
375 | module_put(entry->module); | ||
376 | __error1: | ||
377 | up(&info_mutex); | ||
378 | return err; | ||
379 | } | ||
380 | |||
381 | static int snd_info_entry_release(struct inode *inode, struct file *file) | ||
382 | { | ||
383 | snd_info_entry_t *entry; | ||
384 | snd_info_private_data_t *data; | ||
385 | int mode; | ||
386 | |||
387 | mode = file->f_flags & O_ACCMODE; | ||
388 | data = file->private_data; | ||
389 | entry = data->entry; | ||
390 | switch (entry->content) { | ||
391 | case SNDRV_INFO_CONTENT_TEXT: | ||
392 | if (mode == O_RDONLY || mode == O_RDWR) { | ||
393 | vfree(data->rbuffer->buffer); | ||
394 | kfree(data->rbuffer); | ||
395 | } | ||
396 | if (mode == O_WRONLY || mode == O_RDWR) { | ||
397 | if (entry->c.text.write) { | ||
398 | entry->c.text.write(entry, data->wbuffer); | ||
399 | if (data->wbuffer->error) { | ||
400 | snd_printk(KERN_WARNING "data write error to %s (%i)\n", | ||
401 | entry->name, | ||
402 | data->wbuffer->error); | ||
403 | } | ||
404 | } | ||
405 | vfree(data->wbuffer->buffer); | ||
406 | kfree(data->wbuffer); | ||
407 | } | ||
408 | break; | ||
409 | case SNDRV_INFO_CONTENT_DATA: | ||
410 | if (entry->c.ops->release) | ||
411 | entry->c.ops->release(entry, mode, | ||
412 | data->file_private_data); | ||
413 | break; | ||
414 | } | ||
415 | module_put(entry->module); | ||
416 | kfree(data); | ||
417 | return 0; | ||
418 | } | ||
419 | |||
420 | static unsigned int snd_info_entry_poll(struct file *file, poll_table * wait) | ||
421 | { | ||
422 | snd_info_private_data_t *data; | ||
423 | struct snd_info_entry *entry; | ||
424 | unsigned int mask; | ||
425 | |||
426 | data = file->private_data; | ||
427 | if (data == NULL) | ||
428 | return 0; | ||
429 | entry = data->entry; | ||
430 | mask = 0; | ||
431 | switch (entry->content) { | ||
432 | case SNDRV_INFO_CONTENT_DATA: | ||
433 | if (entry->c.ops->poll) | ||
434 | return entry->c.ops->poll(entry, | ||
435 | data->file_private_data, | ||
436 | file, wait); | ||
437 | if (entry->c.ops->read) | ||
438 | mask |= POLLIN | POLLRDNORM; | ||
439 | if (entry->c.ops->write) | ||
440 | mask |= POLLOUT | POLLWRNORM; | ||
441 | break; | ||
442 | } | ||
443 | return mask; | ||
444 | } | ||
445 | |||
446 | static inline int _snd_info_entry_ioctl(struct inode *inode, struct file *file, | ||
447 | unsigned int cmd, unsigned long arg) | ||
448 | { | ||
449 | snd_info_private_data_t *data; | ||
450 | struct snd_info_entry *entry; | ||
451 | |||
452 | data = file->private_data; | ||
453 | if (data == NULL) | ||
454 | return 0; | ||
455 | entry = data->entry; | ||
456 | switch (entry->content) { | ||
457 | case SNDRV_INFO_CONTENT_DATA: | ||
458 | if (entry->c.ops->ioctl) | ||
459 | return entry->c.ops->ioctl(entry, | ||
460 | data->file_private_data, | ||
461 | file, cmd, arg); | ||
462 | break; | ||
463 | } | ||
464 | return -ENOTTY; | ||
465 | } | ||
466 | |||
467 | /* FIXME: need to unlock BKL to allow preemption */ | ||
468 | static int snd_info_entry_ioctl(struct inode *inode, struct file *file, | ||
469 | unsigned int cmd, unsigned long arg) | ||
470 | { | ||
471 | int err; | ||
472 | unlock_kernel(); | ||
473 | err = _snd_info_entry_ioctl(inode, file, cmd, arg); | ||
474 | lock_kernel(); | ||
475 | return err; | ||
476 | } | ||
477 | |||
478 | static int snd_info_entry_mmap(struct file *file, struct vm_area_struct *vma) | ||
479 | { | ||
480 | struct inode *inode = file->f_dentry->d_inode; | ||
481 | snd_info_private_data_t *data; | ||
482 | struct snd_info_entry *entry; | ||
483 | |||
484 | data = file->private_data; | ||
485 | if (data == NULL) | ||
486 | return 0; | ||
487 | entry = data->entry; | ||
488 | switch (entry->content) { | ||
489 | case SNDRV_INFO_CONTENT_DATA: | ||
490 | if (entry->c.ops->mmap) | ||
491 | return entry->c.ops->mmap(entry, | ||
492 | data->file_private_data, | ||
493 | inode, file, vma); | ||
494 | break; | ||
495 | } | ||
496 | return -ENXIO; | ||
497 | } | ||
498 | |||
499 | static struct file_operations snd_info_entry_operations = | ||
500 | { | ||
501 | .owner = THIS_MODULE, | ||
502 | .llseek = snd_info_entry_llseek, | ||
503 | .read = snd_info_entry_read, | ||
504 | .write = snd_info_entry_write, | ||
505 | .poll = snd_info_entry_poll, | ||
506 | .ioctl = snd_info_entry_ioctl, | ||
507 | .mmap = snd_info_entry_mmap, | ||
508 | .open = snd_info_entry_open, | ||
509 | .release = snd_info_entry_release, | ||
510 | }; | ||
511 | |||
512 | /** | ||
513 | * snd_create_proc_entry - create a procfs entry | ||
514 | * @name: the name of the proc file | ||
515 | * @mode: the file permission bits, S_Ixxx | ||
516 | * @parent: the parent proc-directory entry | ||
517 | * | ||
518 | * Creates a new proc file entry with the given name and permission | ||
519 | * on the given directory. | ||
520 | * | ||
521 | * Returns the pointer of new instance or NULL on failure. | ||
522 | */ | ||
523 | static struct proc_dir_entry *snd_create_proc_entry(const char *name, mode_t mode, | ||
524 | struct proc_dir_entry *parent) | ||
525 | { | ||
526 | struct proc_dir_entry *p; | ||
527 | p = create_proc_entry(name, mode, parent); | ||
528 | if (p) | ||
529 | snd_info_entry_prepare(p); | ||
530 | return p; | ||
531 | } | ||
532 | |||
533 | int __init snd_info_init(void) | ||
534 | { | ||
535 | struct proc_dir_entry *p; | ||
536 | |||
537 | p = snd_create_proc_entry("asound", S_IFDIR | S_IRUGO | S_IXUGO, &proc_root); | ||
538 | if (p == NULL) | ||
539 | return -ENOMEM; | ||
540 | snd_proc_root = p; | ||
541 | #ifdef CONFIG_SND_OSSEMUL | ||
542 | { | ||
543 | snd_info_entry_t *entry; | ||
544 | if ((entry = snd_info_create_module_entry(THIS_MODULE, "oss", NULL)) == NULL) | ||
545 | return -ENOMEM; | ||
546 | entry->mode = S_IFDIR | S_IRUGO | S_IXUGO; | ||
547 | if (snd_info_register(entry) < 0) { | ||
548 | snd_info_free_entry(entry); | ||
549 | return -ENOMEM; | ||
550 | } | ||
551 | snd_oss_root = entry; | ||
552 | } | ||
553 | #endif | ||
554 | #if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE) | ||
555 | { | ||
556 | snd_info_entry_t *entry; | ||
557 | if ((entry = snd_info_create_module_entry(THIS_MODULE, "seq", NULL)) == NULL) | ||
558 | return -ENOMEM; | ||
559 | entry->mode = S_IFDIR | S_IRUGO | S_IXUGO; | ||
560 | if (snd_info_register(entry) < 0) { | ||
561 | snd_info_free_entry(entry); | ||
562 | return -ENOMEM; | ||
563 | } | ||
564 | snd_seq_root = entry; | ||
565 | } | ||
566 | #endif | ||
567 | snd_info_version_init(); | ||
568 | snd_memory_info_init(); | ||
569 | snd_minor_info_init(); | ||
570 | snd_minor_info_oss_init(); | ||
571 | snd_card_info_init(); | ||
572 | return 0; | ||
573 | } | ||
574 | |||
575 | int __exit snd_info_done(void) | ||
576 | { | ||
577 | snd_card_info_done(); | ||
578 | snd_minor_info_oss_done(); | ||
579 | snd_minor_info_done(); | ||
580 | snd_memory_info_done(); | ||
581 | snd_info_version_done(); | ||
582 | if (snd_proc_root) { | ||
583 | #if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE) | ||
584 | if (snd_seq_root) | ||
585 | snd_info_unregister(snd_seq_root); | ||
586 | #endif | ||
587 | #ifdef CONFIG_SND_OSSEMUL | ||
588 | if (snd_oss_root) | ||
589 | snd_info_unregister(snd_oss_root); | ||
590 | #endif | ||
591 | snd_remove_proc_entry(&proc_root, snd_proc_root); | ||
592 | } | ||
593 | return 0; | ||
594 | } | ||
595 | |||
596 | /* | ||
597 | |||
598 | */ | ||
599 | |||
600 | |||
601 | /* | ||
602 | * create a card proc file | ||
603 | * called from init.c | ||
604 | */ | ||
605 | int snd_info_card_create(snd_card_t * card) | ||
606 | { | ||
607 | char str[8]; | ||
608 | snd_info_entry_t *entry; | ||
609 | |||
610 | snd_assert(card != NULL, return -ENXIO); | ||
611 | |||
612 | sprintf(str, "card%i", card->number); | ||
613 | if ((entry = snd_info_create_module_entry(card->module, str, NULL)) == NULL) | ||
614 | return -ENOMEM; | ||
615 | entry->mode = S_IFDIR | S_IRUGO | S_IXUGO; | ||
616 | if (snd_info_register(entry) < 0) { | ||
617 | snd_info_free_entry(entry); | ||
618 | return -ENOMEM; | ||
619 | } | ||
620 | card->proc_root = entry; | ||
621 | return 0; | ||
622 | } | ||
623 | |||
624 | /* | ||
625 | * register the card proc file | ||
626 | * called from init.c | ||
627 | */ | ||
628 | int snd_info_card_register(snd_card_t * card) | ||
629 | { | ||
630 | struct proc_dir_entry *p; | ||
631 | |||
632 | snd_assert(card != NULL, return -ENXIO); | ||
633 | |||
634 | if (!strcmp(card->id, card->proc_root->name)) | ||
635 | return 0; | ||
636 | |||
637 | p = proc_symlink(card->id, snd_proc_root, card->proc_root->name); | ||
638 | if (p == NULL) | ||
639 | return -ENOMEM; | ||
640 | card->proc_root_link = p; | ||
641 | return 0; | ||
642 | } | ||
643 | |||
644 | /* | ||
645 | * de-register the card proc file | ||
646 | * called from init.c | ||
647 | */ | ||
648 | int snd_info_card_free(snd_card_t * card) | ||
649 | { | ||
650 | snd_assert(card != NULL, return -ENXIO); | ||
651 | if (card->proc_root_link) { | ||
652 | snd_remove_proc_entry(snd_proc_root, card->proc_root_link); | ||
653 | card->proc_root_link = NULL; | ||
654 | } | ||
655 | if (card->proc_root) { | ||
656 | snd_info_unregister(card->proc_root); | ||
657 | card->proc_root = NULL; | ||
658 | } | ||
659 | return 0; | ||
660 | } | ||
661 | |||
662 | |||
663 | /** | ||
664 | * snd_info_get_line - read one line from the procfs buffer | ||
665 | * @buffer: the procfs buffer | ||
666 | * @line: the buffer to store | ||
667 | * @len: the max. buffer size - 1 | ||
668 | * | ||
669 | * Reads one line from the buffer and stores the string. | ||
670 | * | ||
671 | * Returns zero if successful, or 1 if error or EOF. | ||
672 | */ | ||
673 | int snd_info_get_line(snd_info_buffer_t * buffer, char *line, int len) | ||
674 | { | ||
675 | int c = -1; | ||
676 | |||
677 | if (len <= 0 || buffer->stop || buffer->error) | ||
678 | return 1; | ||
679 | while (--len > 0) { | ||
680 | c = *buffer->curr++; | ||
681 | if (c == '\n') { | ||
682 | if ((buffer->curr - buffer->buffer) >= (long)buffer->size) { | ||
683 | buffer->stop = 1; | ||
684 | } | ||
685 | break; | ||
686 | } | ||
687 | *line++ = c; | ||
688 | if ((buffer->curr - buffer->buffer) >= (long)buffer->size) { | ||
689 | buffer->stop = 1; | ||
690 | break; | ||
691 | } | ||
692 | } | ||
693 | while (c != '\n' && !buffer->stop) { | ||
694 | c = *buffer->curr++; | ||
695 | if ((buffer->curr - buffer->buffer) >= (long)buffer->size) { | ||
696 | buffer->stop = 1; | ||
697 | } | ||
698 | } | ||
699 | *line = '\0'; | ||
700 | return 0; | ||
701 | } | ||
702 | |||
703 | /** | ||
704 | * snd_info_get_line - parse a string token | ||
705 | * @dest: the buffer to store the string token | ||
706 | * @src: the original string | ||
707 | * @len: the max. length of token - 1 | ||
708 | * | ||
709 | * Parses the original string and copy a token to the given | ||
710 | * string buffer. | ||
711 | * | ||
712 | * Returns the updated pointer of the original string so that | ||
713 | * it can be used for the next call. | ||
714 | */ | ||
715 | char *snd_info_get_str(char *dest, char *src, int len) | ||
716 | { | ||
717 | int c; | ||
718 | |||
719 | while (*src == ' ' || *src == '\t') | ||
720 | src++; | ||
721 | if (*src == '"' || *src == '\'') { | ||
722 | c = *src++; | ||
723 | while (--len > 0 && *src && *src != c) { | ||
724 | *dest++ = *src++; | ||
725 | } | ||
726 | if (*src == c) | ||
727 | src++; | ||
728 | } else { | ||
729 | while (--len > 0 && *src && *src != ' ' && *src != '\t') { | ||
730 | *dest++ = *src++; | ||
731 | } | ||
732 | } | ||
733 | *dest = 0; | ||
734 | while (*src == ' ' || *src == '\t') | ||
735 | src++; | ||
736 | return src; | ||
737 | } | ||
738 | |||
739 | /** | ||
740 | * snd_info_create_entry - create an info entry | ||
741 | * @name: the proc file name | ||
742 | * | ||
743 | * Creates an info entry with the given file name and initializes as | ||
744 | * the default state. | ||
745 | * | ||
746 | * Usually called from other functions such as | ||
747 | * snd_info_create_card_entry(). | ||
748 | * | ||
749 | * Returns the pointer of the new instance, or NULL on failure. | ||
750 | */ | ||
751 | static snd_info_entry_t *snd_info_create_entry(const char *name) | ||
752 | { | ||
753 | snd_info_entry_t *entry; | ||
754 | entry = kcalloc(1, sizeof(*entry), GFP_KERNEL); | ||
755 | if (entry == NULL) | ||
756 | return NULL; | ||
757 | entry->name = snd_kmalloc_strdup(name, GFP_KERNEL); | ||
758 | if (entry->name == NULL) { | ||
759 | kfree(entry); | ||
760 | return NULL; | ||
761 | } | ||
762 | entry->mode = S_IFREG | S_IRUGO; | ||
763 | entry->content = SNDRV_INFO_CONTENT_TEXT; | ||
764 | init_MUTEX(&entry->access); | ||
765 | return entry; | ||
766 | } | ||
767 | |||
768 | /** | ||
769 | * snd_info_create_module_entry - create an info entry for the given module | ||
770 | * @module: the module pointer | ||
771 | * @name: the file name | ||
772 | * @parent: the parent directory | ||
773 | * | ||
774 | * Creates a new info entry and assigns it to the given module. | ||
775 | * | ||
776 | * Returns the pointer of the new instance, or NULL on failure. | ||
777 | */ | ||
778 | snd_info_entry_t *snd_info_create_module_entry(struct module * module, | ||
779 | const char *name, | ||
780 | snd_info_entry_t *parent) | ||
781 | { | ||
782 | snd_info_entry_t *entry = snd_info_create_entry(name); | ||
783 | if (entry) { | ||
784 | entry->module = module; | ||
785 | entry->parent = parent; | ||
786 | } | ||
787 | return entry; | ||
788 | } | ||
789 | |||
790 | /** | ||
791 | * snd_info_create_card_entry - create an info entry for the given card | ||
792 | * @card: the card instance | ||
793 | * @name: the file name | ||
794 | * @parent: the parent directory | ||
795 | * | ||
796 | * Creates a new info entry and assigns it to the given card. | ||
797 | * | ||
798 | * Returns the pointer of the new instance, or NULL on failure. | ||
799 | */ | ||
800 | snd_info_entry_t *snd_info_create_card_entry(snd_card_t * card, | ||
801 | const char *name, | ||
802 | snd_info_entry_t * parent) | ||
803 | { | ||
804 | snd_info_entry_t *entry = snd_info_create_entry(name); | ||
805 | if (entry) { | ||
806 | entry->module = card->module; | ||
807 | entry->card = card; | ||
808 | entry->parent = parent; | ||
809 | } | ||
810 | return entry; | ||
811 | } | ||
812 | |||
813 | static int snd_info_dev_free_entry(snd_device_t *device) | ||
814 | { | ||
815 | snd_info_entry_t *entry = device->device_data; | ||
816 | snd_info_free_entry(entry); | ||
817 | return 0; | ||
818 | } | ||
819 | |||
820 | static int snd_info_dev_register_entry(snd_device_t *device) | ||
821 | { | ||
822 | snd_info_entry_t *entry = device->device_data; | ||
823 | return snd_info_register(entry); | ||
824 | } | ||
825 | |||
826 | static int snd_info_dev_disconnect_entry(snd_device_t *device) | ||
827 | { | ||
828 | snd_info_entry_t *entry = device->device_data; | ||
829 | entry->disconnected = 1; | ||
830 | return 0; | ||
831 | } | ||
832 | |||
833 | static int snd_info_dev_unregister_entry(snd_device_t *device) | ||
834 | { | ||
835 | snd_info_entry_t *entry = device->device_data; | ||
836 | return snd_info_unregister(entry); | ||
837 | } | ||
838 | |||
839 | /** | ||
840 | * snd_card_proc_new - create an info entry for the given card | ||
841 | * @card: the card instance | ||
842 | * @name: the file name | ||
843 | * @entryp: the pointer to store the new info entry | ||
844 | * | ||
845 | * Creates a new info entry and assigns it to the given card. | ||
846 | * Unlike snd_info_create_card_entry(), this function registers the | ||
847 | * info entry as an ALSA device component, so that it can be | ||
848 | * unregistered/released without explicit call. | ||
849 | * Also, you don't have to register this entry via snd_info_register(), | ||
850 | * since this will be registered by snd_card_register() automatically. | ||
851 | * | ||
852 | * The parent is assumed as card->proc_root. | ||
853 | * | ||
854 | * For releasing this entry, use snd_device_free() instead of | ||
855 | * snd_info_free_entry(). | ||
856 | * | ||
857 | * Returns zero if successful, or a negative error code on failure. | ||
858 | */ | ||
859 | int snd_card_proc_new(snd_card_t *card, const char *name, | ||
860 | snd_info_entry_t **entryp) | ||
861 | { | ||
862 | static snd_device_ops_t ops = { | ||
863 | .dev_free = snd_info_dev_free_entry, | ||
864 | .dev_register = snd_info_dev_register_entry, | ||
865 | .dev_disconnect = snd_info_dev_disconnect_entry, | ||
866 | .dev_unregister = snd_info_dev_unregister_entry | ||
867 | }; | ||
868 | snd_info_entry_t *entry; | ||
869 | int err; | ||
870 | |||
871 | entry = snd_info_create_card_entry(card, name, card->proc_root); | ||
872 | if (! entry) | ||
873 | return -ENOMEM; | ||
874 | if ((err = snd_device_new(card, SNDRV_DEV_INFO, entry, &ops)) < 0) { | ||
875 | snd_info_free_entry(entry); | ||
876 | return err; | ||
877 | } | ||
878 | if (entryp) | ||
879 | *entryp = entry; | ||
880 | return 0; | ||
881 | } | ||
882 | |||
883 | /** | ||
884 | * snd_info_free_entry - release the info entry | ||
885 | * @entry: the info entry | ||
886 | * | ||
887 | * Releases the info entry. Don't call this after registered. | ||
888 | */ | ||
889 | void snd_info_free_entry(snd_info_entry_t * entry) | ||
890 | { | ||
891 | if (entry == NULL) | ||
892 | return; | ||
893 | kfree(entry->name); | ||
894 | if (entry->private_free) | ||
895 | entry->private_free(entry); | ||
896 | kfree(entry); | ||
897 | } | ||
898 | |||
899 | /** | ||
900 | * snd_info_register - register the info entry | ||
901 | * @entry: the info entry | ||
902 | * | ||
903 | * Registers the proc info entry. | ||
904 | * | ||
905 | * Returns zero if successful, or a negative error code on failure. | ||
906 | */ | ||
907 | int snd_info_register(snd_info_entry_t * entry) | ||
908 | { | ||
909 | struct proc_dir_entry *root, *p = NULL; | ||
910 | |||
911 | snd_assert(entry != NULL, return -ENXIO); | ||
912 | root = entry->parent == NULL ? snd_proc_root : entry->parent->p; | ||
913 | down(&info_mutex); | ||
914 | p = snd_create_proc_entry(entry->name, entry->mode, root); | ||
915 | if (!p) { | ||
916 | up(&info_mutex); | ||
917 | return -ENOMEM; | ||
918 | } | ||
919 | p->owner = entry->module; | ||
920 | if (!S_ISDIR(entry->mode)) | ||
921 | p->proc_fops = &snd_info_entry_operations; | ||
922 | p->size = entry->size; | ||
923 | p->data = entry; | ||
924 | entry->p = p; | ||
925 | up(&info_mutex); | ||
926 | return 0; | ||
927 | } | ||
928 | |||
929 | /** | ||
930 | * snd_info_unregister - de-register the info entry | ||
931 | * @entry: the info entry | ||
932 | * | ||
933 | * De-registers the info entry and releases the instance. | ||
934 | * | ||
935 | * Returns zero if successful, or a negative error code on failure. | ||
936 | */ | ||
937 | int snd_info_unregister(snd_info_entry_t * entry) | ||
938 | { | ||
939 | struct proc_dir_entry *root; | ||
940 | |||
941 | snd_assert(entry != NULL && entry->p != NULL, return -ENXIO); | ||
942 | root = entry->parent == NULL ? snd_proc_root : entry->parent->p; | ||
943 | snd_assert(root, return -ENXIO); | ||
944 | down(&info_mutex); | ||
945 | snd_remove_proc_entry(root, entry->p); | ||
946 | up(&info_mutex); | ||
947 | snd_info_free_entry(entry); | ||
948 | return 0; | ||
949 | } | ||
950 | |||
951 | /* | ||
952 | |||
953 | */ | ||
954 | |||
955 | static snd_info_entry_t *snd_info_version_entry = NULL; | ||
956 | |||
957 | static void snd_info_version_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer) | ||
958 | { | ||
959 | snd_iprintf(buffer, | ||
960 | "Advanced Linux Sound Architecture Driver Version " | ||
961 | CONFIG_SND_VERSION CONFIG_SND_DATE ".\n" | ||
962 | ); | ||
963 | } | ||
964 | |||
965 | static int __init snd_info_version_init(void) | ||
966 | { | ||
967 | snd_info_entry_t *entry; | ||
968 | |||
969 | entry = snd_info_create_module_entry(THIS_MODULE, "version", NULL); | ||
970 | if (entry == NULL) | ||
971 | return -ENOMEM; | ||
972 | entry->c.text.read_size = 256; | ||
973 | entry->c.text.read = snd_info_version_read; | ||
974 | if (snd_info_register(entry) < 0) { | ||
975 | snd_info_free_entry(entry); | ||
976 | return -ENOMEM; | ||
977 | } | ||
978 | snd_info_version_entry = entry; | ||
979 | return 0; | ||
980 | } | ||
981 | |||
982 | static int __exit snd_info_version_done(void) | ||
983 | { | ||
984 | if (snd_info_version_entry) | ||
985 | snd_info_unregister(snd_info_version_entry); | ||
986 | return 0; | ||
987 | } | ||
988 | |||
989 | #endif /* CONFIG_PROC_FS */ | ||
diff --git a/sound/core/info_oss.c b/sound/core/info_oss.c new file mode 100644 index 000000000000..f9e4ce443454 --- /dev/null +++ b/sound/core/info_oss.c | |||
@@ -0,0 +1,137 @@ | |||
1 | /* | ||
2 | * Information interface for ALSA driver | ||
3 | * Copyright (c) by Jaroslav Kysela <perex@suse.cz> | ||
4 | * | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #include <sound/driver.h> | ||
23 | #include <linux/slab.h> | ||
24 | #include <linux/time.h> | ||
25 | #include <sound/core.h> | ||
26 | #include <sound/minors.h> | ||
27 | #include <sound/info.h> | ||
28 | #include <sound/version.h> | ||
29 | #include <linux/utsname.h> | ||
30 | |||
31 | #if defined(CONFIG_SND_OSSEMUL) && defined(CONFIG_PROC_FS) | ||
32 | |||
33 | /* | ||
34 | * OSS compatible part | ||
35 | */ | ||
36 | |||
37 | static DECLARE_MUTEX(strings); | ||
38 | static char *snd_sndstat_strings[SNDRV_CARDS][SNDRV_OSS_INFO_DEV_COUNT]; | ||
39 | static snd_info_entry_t *snd_sndstat_proc_entry; | ||
40 | |||
41 | int snd_oss_info_register(int dev, int num, char *string) | ||
42 | { | ||
43 | char *x; | ||
44 | |||
45 | snd_assert(dev >= 0 && dev < SNDRV_OSS_INFO_DEV_COUNT, return -ENXIO); | ||
46 | snd_assert(num >= 0 && num < SNDRV_CARDS, return -ENXIO); | ||
47 | down(&strings); | ||
48 | if (string == NULL) { | ||
49 | if ((x = snd_sndstat_strings[num][dev]) != NULL) { | ||
50 | kfree(x); | ||
51 | x = NULL; | ||
52 | } | ||
53 | } else { | ||
54 | x = snd_kmalloc_strdup(string, GFP_KERNEL); | ||
55 | if (x == NULL) { | ||
56 | up(&strings); | ||
57 | return -ENOMEM; | ||
58 | } | ||
59 | } | ||
60 | snd_sndstat_strings[num][dev] = x; | ||
61 | up(&strings); | ||
62 | return 0; | ||
63 | } | ||
64 | |||
65 | extern void snd_card_info_read_oss(snd_info_buffer_t * buffer); | ||
66 | |||
67 | static int snd_sndstat_show_strings(snd_info_buffer_t * buf, char *id, int dev) | ||
68 | { | ||
69 | int idx, ok = -1; | ||
70 | char *str; | ||
71 | |||
72 | snd_iprintf(buf, "\n%s:", id); | ||
73 | down(&strings); | ||
74 | for (idx = 0; idx < SNDRV_CARDS; idx++) { | ||
75 | str = snd_sndstat_strings[idx][dev]; | ||
76 | if (str) { | ||
77 | if (ok < 0) { | ||
78 | snd_iprintf(buf, "\n"); | ||
79 | ok++; | ||
80 | } | ||
81 | snd_iprintf(buf, "%i: %s\n", idx, str); | ||
82 | } | ||
83 | } | ||
84 | up(&strings); | ||
85 | if (ok < 0) | ||
86 | snd_iprintf(buf, " NOT ENABLED IN CONFIG\n"); | ||
87 | return ok; | ||
88 | } | ||
89 | |||
90 | static void snd_sndstat_proc_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer) | ||
91 | { | ||
92 | snd_iprintf(buffer, "Sound Driver:3.8.1a-980706 (ALSA v" CONFIG_SND_VERSION " emulation code)\n"); | ||
93 | snd_iprintf(buffer, "Kernel: %s %s %s %s %s\n", | ||
94 | system_utsname.sysname, | ||
95 | system_utsname.nodename, | ||
96 | system_utsname.release, | ||
97 | system_utsname.version, | ||
98 | system_utsname.machine); | ||
99 | snd_iprintf(buffer, "Config options: 0\n"); | ||
100 | snd_iprintf(buffer, "\nInstalled drivers: \n"); | ||
101 | snd_iprintf(buffer, "Type 10: ALSA emulation\n"); | ||
102 | snd_iprintf(buffer, "\nCard config: \n"); | ||
103 | snd_card_info_read_oss(buffer); | ||
104 | snd_sndstat_show_strings(buffer, "Audio devices", SNDRV_OSS_INFO_DEV_AUDIO); | ||
105 | snd_sndstat_show_strings(buffer, "Synth devices", SNDRV_OSS_INFO_DEV_SYNTH); | ||
106 | snd_sndstat_show_strings(buffer, "Midi devices", SNDRV_OSS_INFO_DEV_MIDI); | ||
107 | snd_sndstat_show_strings(buffer, "Timers", SNDRV_OSS_INFO_DEV_TIMERS); | ||
108 | snd_sndstat_show_strings(buffer, "Mixers", SNDRV_OSS_INFO_DEV_MIXERS); | ||
109 | } | ||
110 | |||
111 | int snd_info_minor_register(void) | ||
112 | { | ||
113 | snd_info_entry_t *entry; | ||
114 | |||
115 | memset(snd_sndstat_strings, 0, sizeof(snd_sndstat_strings)); | ||
116 | if ((entry = snd_info_create_module_entry(THIS_MODULE, "sndstat", snd_oss_root)) != NULL) { | ||
117 | entry->c.text.read_size = 2048; | ||
118 | entry->c.text.read = snd_sndstat_proc_read; | ||
119 | if (snd_info_register(entry) < 0) { | ||
120 | snd_info_free_entry(entry); | ||
121 | entry = NULL; | ||
122 | } | ||
123 | } | ||
124 | snd_sndstat_proc_entry = entry; | ||
125 | return 0; | ||
126 | } | ||
127 | |||
128 | int snd_info_minor_unregister(void) | ||
129 | { | ||
130 | if (snd_sndstat_proc_entry) { | ||
131 | snd_info_unregister(snd_sndstat_proc_entry); | ||
132 | snd_sndstat_proc_entry = NULL; | ||
133 | } | ||
134 | return 0; | ||
135 | } | ||
136 | |||
137 | #endif /* CONFIG_SND_OSSEMUL */ | ||
diff --git a/sound/core/init.c b/sound/core/init.c new file mode 100644 index 000000000000..3f1fa8eabb72 --- /dev/null +++ b/sound/core/init.c | |||
@@ -0,0 +1,887 @@ | |||
1 | /* | ||
2 | * Initialization routines | ||
3 | * Copyright (c) by Jaroslav Kysela <perex@suse.cz> | ||
4 | * | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #include <sound/driver.h> | ||
23 | #include <linux/init.h> | ||
24 | #include <linux/sched.h> | ||
25 | #include <linux/file.h> | ||
26 | #include <linux/slab.h> | ||
27 | #include <linux/time.h> | ||
28 | #include <linux/ctype.h> | ||
29 | #include <linux/pci.h> | ||
30 | #include <linux/pm.h> | ||
31 | #include <sound/core.h> | ||
32 | #include <sound/control.h> | ||
33 | #include <sound/info.h> | ||
34 | |||
35 | struct snd_shutdown_f_ops { | ||
36 | struct file_operations f_ops; | ||
37 | struct snd_shutdown_f_ops *next; | ||
38 | }; | ||
39 | |||
40 | unsigned int snd_cards_lock = 0; /* locked for registering/using */ | ||
41 | snd_card_t *snd_cards[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = NULL}; | ||
42 | DEFINE_RWLOCK(snd_card_rwlock); | ||
43 | |||
44 | #if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE) | ||
45 | int (*snd_mixer_oss_notify_callback)(snd_card_t *card, int free_flag); | ||
46 | #endif | ||
47 | |||
48 | static void snd_card_id_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer) | ||
49 | { | ||
50 | snd_iprintf(buffer, "%s\n", entry->card->id); | ||
51 | } | ||
52 | |||
53 | static void snd_card_free_thread(void * __card); | ||
54 | |||
55 | /** | ||
56 | * snd_card_new - create and initialize a soundcard structure | ||
57 | * @idx: card index (address) [0 ... (SNDRV_CARDS-1)] | ||
58 | * @xid: card identification (ASCII string) | ||
59 | * @module: top level module for locking | ||
60 | * @extra_size: allocate this extra size after the main soundcard structure | ||
61 | * | ||
62 | * Creates and initializes a soundcard structure. | ||
63 | * | ||
64 | * Returns kmallocated snd_card_t structure. Creates the ALSA control interface | ||
65 | * (which is blocked until snd_card_register function is called). | ||
66 | */ | ||
67 | snd_card_t *snd_card_new(int idx, const char *xid, | ||
68 | struct module *module, int extra_size) | ||
69 | { | ||
70 | snd_card_t *card; | ||
71 | int err; | ||
72 | |||
73 | if (extra_size < 0) | ||
74 | extra_size = 0; | ||
75 | card = kcalloc(1, sizeof(*card) + extra_size, GFP_KERNEL); | ||
76 | if (card == NULL) | ||
77 | return NULL; | ||
78 | if (xid) { | ||
79 | if (!snd_info_check_reserved_words(xid)) | ||
80 | goto __error; | ||
81 | strlcpy(card->id, xid, sizeof(card->id)); | ||
82 | } | ||
83 | err = 0; | ||
84 | write_lock(&snd_card_rwlock); | ||
85 | if (idx < 0) { | ||
86 | int idx2; | ||
87 | for (idx2 = 0; idx2 < SNDRV_CARDS; idx2++) | ||
88 | if (~snd_cards_lock & idx & 1<<idx2) { | ||
89 | idx = idx2; | ||
90 | if (idx >= snd_ecards_limit) | ||
91 | snd_ecards_limit = idx + 1; | ||
92 | break; | ||
93 | } | ||
94 | } else if (idx < snd_ecards_limit) { | ||
95 | if (snd_cards_lock & (1 << idx)) | ||
96 | err = -ENODEV; /* invalid */ | ||
97 | } else if (idx < SNDRV_CARDS) | ||
98 | snd_ecards_limit = idx + 1; /* increase the limit */ | ||
99 | else | ||
100 | err = -ENODEV; | ||
101 | if (idx < 0 || err < 0) { | ||
102 | write_unlock(&snd_card_rwlock); | ||
103 | snd_printk(KERN_ERR "cannot find the slot for index %d (range 0-%i)\n", idx, snd_ecards_limit - 1); | ||
104 | goto __error; | ||
105 | } | ||
106 | snd_cards_lock |= 1 << idx; /* lock it */ | ||
107 | write_unlock(&snd_card_rwlock); | ||
108 | card->number = idx; | ||
109 | card->module = module; | ||
110 | INIT_LIST_HEAD(&card->devices); | ||
111 | init_rwsem(&card->controls_rwsem); | ||
112 | rwlock_init(&card->ctl_files_rwlock); | ||
113 | INIT_LIST_HEAD(&card->controls); | ||
114 | INIT_LIST_HEAD(&card->ctl_files); | ||
115 | spin_lock_init(&card->files_lock); | ||
116 | init_waitqueue_head(&card->shutdown_sleep); | ||
117 | INIT_WORK(&card->free_workq, snd_card_free_thread, card); | ||
118 | #ifdef CONFIG_PM | ||
119 | init_MUTEX(&card->power_lock); | ||
120 | init_waitqueue_head(&card->power_sleep); | ||
121 | #endif | ||
122 | /* the control interface cannot be accessed from the user space until */ | ||
123 | /* snd_cards_bitmask and snd_cards are set with snd_card_register */ | ||
124 | if ((err = snd_ctl_create(card)) < 0) { | ||
125 | snd_printd("unable to register control minors\n"); | ||
126 | goto __error; | ||
127 | } | ||
128 | if ((err = snd_info_card_create(card)) < 0) { | ||
129 | snd_printd("unable to create card info\n"); | ||
130 | goto __error_ctl; | ||
131 | } | ||
132 | if (extra_size > 0) | ||
133 | card->private_data = (char *)card + sizeof(snd_card_t); | ||
134 | return card; | ||
135 | |||
136 | __error_ctl: | ||
137 | snd_device_free_all(card, SNDRV_DEV_CMD_PRE); | ||
138 | __error: | ||
139 | kfree(card); | ||
140 | return NULL; | ||
141 | } | ||
142 | |||
143 | static unsigned int snd_disconnect_poll(struct file * file, poll_table * wait) | ||
144 | { | ||
145 | return POLLERR | POLLNVAL; | ||
146 | } | ||
147 | |||
148 | /** | ||
149 | * snd_card_disconnect - disconnect all APIs from the file-operations (user space) | ||
150 | * @card: soundcard structure | ||
151 | * | ||
152 | * Disconnects all APIs from the file-operations (user space). | ||
153 | * | ||
154 | * Returns zero, otherwise a negative error code. | ||
155 | * | ||
156 | * Note: The current implementation replaces all active file->f_op with special | ||
157 | * dummy file operations (they do nothing except release). | ||
158 | */ | ||
159 | int snd_card_disconnect(snd_card_t * card) | ||
160 | { | ||
161 | struct snd_monitor_file *mfile; | ||
162 | struct file *file; | ||
163 | struct snd_shutdown_f_ops *s_f_ops; | ||
164 | struct file_operations *f_ops, *old_f_ops; | ||
165 | int err; | ||
166 | |||
167 | spin_lock(&card->files_lock); | ||
168 | if (card->shutdown) { | ||
169 | spin_unlock(&card->files_lock); | ||
170 | return 0; | ||
171 | } | ||
172 | card->shutdown = 1; | ||
173 | spin_unlock(&card->files_lock); | ||
174 | |||
175 | /* phase 1: disable fops (user space) operations for ALSA API */ | ||
176 | write_lock(&snd_card_rwlock); | ||
177 | snd_cards[card->number] = NULL; | ||
178 | write_unlock(&snd_card_rwlock); | ||
179 | |||
180 | /* phase 2: replace file->f_op with special dummy operations */ | ||
181 | |||
182 | spin_lock(&card->files_lock); | ||
183 | mfile = card->files; | ||
184 | while (mfile) { | ||
185 | file = mfile->file; | ||
186 | |||
187 | /* it's critical part, use endless loop */ | ||
188 | /* we have no room to fail */ | ||
189 | s_f_ops = kmalloc(sizeof(struct snd_shutdown_f_ops), GFP_ATOMIC); | ||
190 | if (s_f_ops == NULL) | ||
191 | panic("Atomic allocation failed for snd_shutdown_f_ops!"); | ||
192 | |||
193 | f_ops = &s_f_ops->f_ops; | ||
194 | |||
195 | memset(f_ops, 0, sizeof(*f_ops)); | ||
196 | f_ops->owner = file->f_op->owner; | ||
197 | f_ops->release = file->f_op->release; | ||
198 | f_ops->poll = snd_disconnect_poll; | ||
199 | |||
200 | s_f_ops->next = card->s_f_ops; | ||
201 | card->s_f_ops = s_f_ops; | ||
202 | |||
203 | f_ops = fops_get(f_ops); | ||
204 | |||
205 | old_f_ops = file->f_op; | ||
206 | file->f_op = f_ops; /* must be atomic */ | ||
207 | fops_put(old_f_ops); | ||
208 | |||
209 | mfile = mfile->next; | ||
210 | } | ||
211 | spin_unlock(&card->files_lock); | ||
212 | |||
213 | /* phase 3: notify all connected devices about disconnection */ | ||
214 | /* at this point, they cannot respond to any calls except release() */ | ||
215 | |||
216 | #if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE) | ||
217 | if (snd_mixer_oss_notify_callback) | ||
218 | snd_mixer_oss_notify_callback(card, SND_MIXER_OSS_NOTIFY_DISCONNECT); | ||
219 | #endif | ||
220 | |||
221 | /* notify all devices that we are disconnected */ | ||
222 | err = snd_device_disconnect_all(card); | ||
223 | if (err < 0) | ||
224 | snd_printk(KERN_ERR "not all devices for card %i can be disconnected\n", card->number); | ||
225 | |||
226 | return 0; | ||
227 | } | ||
228 | |||
229 | #if defined(CONFIG_PM) && defined(CONFIG_SND_GENERIC_PM) | ||
230 | static void snd_generic_device_unregister(struct snd_generic_device *dev); | ||
231 | #endif | ||
232 | |||
233 | /** | ||
234 | * snd_card_free - frees given soundcard structure | ||
235 | * @card: soundcard structure | ||
236 | * | ||
237 | * This function releases the soundcard structure and the all assigned | ||
238 | * devices automatically. That is, you don't have to release the devices | ||
239 | * by yourself. | ||
240 | * | ||
241 | * Returns zero. Frees all associated devices and frees the control | ||
242 | * interface associated to given soundcard. | ||
243 | */ | ||
244 | int snd_card_free(snd_card_t * card) | ||
245 | { | ||
246 | struct snd_shutdown_f_ops *s_f_ops; | ||
247 | |||
248 | if (card == NULL) | ||
249 | return -EINVAL; | ||
250 | write_lock(&snd_card_rwlock); | ||
251 | snd_cards[card->number] = NULL; | ||
252 | write_unlock(&snd_card_rwlock); | ||
253 | |||
254 | #ifdef CONFIG_PM | ||
255 | wake_up(&card->power_sleep); | ||
256 | #ifdef CONFIG_SND_GENERIC_PM | ||
257 | if (card->pm_dev) { | ||
258 | snd_generic_device_unregister(card->pm_dev); | ||
259 | card->pm_dev = NULL; | ||
260 | } | ||
261 | #endif | ||
262 | #endif | ||
263 | |||
264 | /* wait, until all devices are ready for the free operation */ | ||
265 | wait_event(card->shutdown_sleep, card->files == NULL); | ||
266 | |||
267 | #if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE) | ||
268 | if (snd_mixer_oss_notify_callback) | ||
269 | snd_mixer_oss_notify_callback(card, SND_MIXER_OSS_NOTIFY_FREE); | ||
270 | #endif | ||
271 | if (snd_device_free_all(card, SNDRV_DEV_CMD_PRE) < 0) { | ||
272 | snd_printk(KERN_ERR "unable to free all devices (pre)\n"); | ||
273 | /* Fatal, but this situation should never occur */ | ||
274 | } | ||
275 | if (snd_device_free_all(card, SNDRV_DEV_CMD_NORMAL) < 0) { | ||
276 | snd_printk(KERN_ERR "unable to free all devices (normal)\n"); | ||
277 | /* Fatal, but this situation should never occur */ | ||
278 | } | ||
279 | if (snd_device_free_all(card, SNDRV_DEV_CMD_POST) < 0) { | ||
280 | snd_printk(KERN_ERR "unable to free all devices (post)\n"); | ||
281 | /* Fatal, but this situation should never occur */ | ||
282 | } | ||
283 | if (card->private_free) | ||
284 | card->private_free(card); | ||
285 | if (card->proc_id) | ||
286 | snd_info_unregister(card->proc_id); | ||
287 | if (snd_info_card_free(card) < 0) { | ||
288 | snd_printk(KERN_WARNING "unable to free card info\n"); | ||
289 | /* Not fatal error */ | ||
290 | } | ||
291 | while (card->s_f_ops) { | ||
292 | s_f_ops = card->s_f_ops; | ||
293 | card->s_f_ops = s_f_ops->next; | ||
294 | kfree(s_f_ops); | ||
295 | } | ||
296 | write_lock(&snd_card_rwlock); | ||
297 | snd_cards_lock &= ~(1 << card->number); | ||
298 | write_unlock(&snd_card_rwlock); | ||
299 | kfree(card); | ||
300 | return 0; | ||
301 | } | ||
302 | |||
303 | static void snd_card_free_thread(void * __card) | ||
304 | { | ||
305 | snd_card_t *card = __card; | ||
306 | struct module * module = card->module; | ||
307 | |||
308 | if (!try_module_get(module)) { | ||
309 | snd_printk(KERN_ERR "unable to lock toplevel module for card %i in free thread\n", card->number); | ||
310 | module = NULL; | ||
311 | } | ||
312 | |||
313 | snd_card_free(card); | ||
314 | |||
315 | module_put(module); | ||
316 | } | ||
317 | |||
318 | /** | ||
319 | * snd_card_free_in_thread - call snd_card_free() in thread | ||
320 | * @card: soundcard structure | ||
321 | * | ||
322 | * This function schedules the call of snd_card_free() function in a | ||
323 | * work queue. When all devices are released (non-busy), the work | ||
324 | * is woken up and calls snd_card_free(). | ||
325 | * | ||
326 | * When a card can be disconnected at any time by hotplug service, | ||
327 | * this function should be used in disconnect (or detach) callback | ||
328 | * instead of calling snd_card_free() directly. | ||
329 | * | ||
330 | * Returns - zero otherwise a negative error code if the start of thread failed. | ||
331 | */ | ||
332 | int snd_card_free_in_thread(snd_card_t * card) | ||
333 | { | ||
334 | if (card->files == NULL) { | ||
335 | snd_card_free(card); | ||
336 | return 0; | ||
337 | } | ||
338 | |||
339 | if (schedule_work(&card->free_workq)) | ||
340 | return 0; | ||
341 | |||
342 | snd_printk(KERN_ERR "schedule_work() failed in snd_card_free_in_thread for card %i\n", card->number); | ||
343 | /* try to free the structure immediately */ | ||
344 | snd_card_free(card); | ||
345 | return -EFAULT; | ||
346 | } | ||
347 | |||
348 | static void choose_default_id(snd_card_t * card) | ||
349 | { | ||
350 | int i, len, idx_flag = 0, loops = 8; | ||
351 | char *id, *spos; | ||
352 | |||
353 | id = spos = card->shortname; | ||
354 | while (*id != '\0') { | ||
355 | if (*id == ' ') | ||
356 | spos = id + 1; | ||
357 | id++; | ||
358 | } | ||
359 | id = card->id; | ||
360 | while (*spos != '\0' && !isalnum(*spos)) | ||
361 | spos++; | ||
362 | if (isdigit(*spos)) | ||
363 | *id++ = isalpha(card->shortname[0]) ? card->shortname[0] : 'D'; | ||
364 | while (*spos != '\0' && (size_t)(id - card->id) < sizeof(card->id) - 1) { | ||
365 | if (isalnum(*spos)) | ||
366 | *id++ = *spos; | ||
367 | spos++; | ||
368 | } | ||
369 | *id = '\0'; | ||
370 | |||
371 | id = card->id; | ||
372 | |||
373 | if (*id == '\0') | ||
374 | strcpy(id, "default"); | ||
375 | |||
376 | while (1) { | ||
377 | if (loops-- == 0) { | ||
378 | snd_printk(KERN_ERR "unable to choose default card id (%s)\n", id); | ||
379 | strcpy(card->id, card->proc_root->name); | ||
380 | return; | ||
381 | } | ||
382 | if (!snd_info_check_reserved_words(id)) | ||
383 | goto __change; | ||
384 | for (i = 0; i < snd_ecards_limit; i++) { | ||
385 | if (snd_cards[i] && !strcmp(snd_cards[i]->id, id)) | ||
386 | goto __change; | ||
387 | } | ||
388 | break; | ||
389 | |||
390 | __change: | ||
391 | len = strlen(id); | ||
392 | if (idx_flag) | ||
393 | id[len-1]++; | ||
394 | else if ((size_t)len <= sizeof(card->id) - 3) { | ||
395 | strcat(id, "_1"); | ||
396 | idx_flag++; | ||
397 | } else { | ||
398 | spos = id + len - 2; | ||
399 | if ((size_t)len <= sizeof(card->id) - 2) | ||
400 | spos++; | ||
401 | *spos++ = '_'; | ||
402 | *spos++ = '1'; | ||
403 | *spos++ = '\0'; | ||
404 | idx_flag++; | ||
405 | } | ||
406 | } | ||
407 | } | ||
408 | |||
409 | /** | ||
410 | * snd_card_register - register the soundcard | ||
411 | * @card: soundcard structure | ||
412 | * | ||
413 | * This function registers all the devices assigned to the soundcard. | ||
414 | * Until calling this, the ALSA control interface is blocked from the | ||
415 | * external accesses. Thus, you should call this function at the end | ||
416 | * of the initialization of the card. | ||
417 | * | ||
418 | * Returns zero otherwise a negative error code if the registrain failed. | ||
419 | */ | ||
420 | int snd_card_register(snd_card_t * card) | ||
421 | { | ||
422 | int err; | ||
423 | snd_info_entry_t *entry; | ||
424 | |||
425 | snd_runtime_check(card != NULL, return -EINVAL); | ||
426 | if ((err = snd_device_register_all(card)) < 0) | ||
427 | return err; | ||
428 | write_lock(&snd_card_rwlock); | ||
429 | if (snd_cards[card->number]) { | ||
430 | /* already registered */ | ||
431 | write_unlock(&snd_card_rwlock); | ||
432 | return 0; | ||
433 | } | ||
434 | if (card->id[0] == '\0') | ||
435 | choose_default_id(card); | ||
436 | snd_cards[card->number] = card; | ||
437 | write_unlock(&snd_card_rwlock); | ||
438 | if ((err = snd_info_card_register(card)) < 0) { | ||
439 | snd_printd("unable to create card info\n"); | ||
440 | goto __skip_info; | ||
441 | } | ||
442 | if ((entry = snd_info_create_card_entry(card, "id", card->proc_root)) == NULL) { | ||
443 | snd_printd("unable to create card entry\n"); | ||
444 | goto __skip_info; | ||
445 | } | ||
446 | entry->c.text.read_size = PAGE_SIZE; | ||
447 | entry->c.text.read = snd_card_id_read; | ||
448 | if (snd_info_register(entry) < 0) { | ||
449 | snd_info_free_entry(entry); | ||
450 | entry = NULL; | ||
451 | } | ||
452 | card->proc_id = entry; | ||
453 | __skip_info: | ||
454 | #if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE) | ||
455 | if (snd_mixer_oss_notify_callback) | ||
456 | snd_mixer_oss_notify_callback(card, SND_MIXER_OSS_NOTIFY_REGISTER); | ||
457 | #endif | ||
458 | return 0; | ||
459 | } | ||
460 | |||
461 | static snd_info_entry_t *snd_card_info_entry = NULL; | ||
462 | |||
463 | static void snd_card_info_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer) | ||
464 | { | ||
465 | int idx, count; | ||
466 | snd_card_t *card; | ||
467 | |||
468 | for (idx = count = 0; idx < SNDRV_CARDS; idx++) { | ||
469 | read_lock(&snd_card_rwlock); | ||
470 | if ((card = snd_cards[idx]) != NULL) { | ||
471 | count++; | ||
472 | snd_iprintf(buffer, "%i [%-15s]: %s - %s\n", | ||
473 | idx, | ||
474 | card->id, | ||
475 | card->driver, | ||
476 | card->shortname); | ||
477 | snd_iprintf(buffer, " %s\n", | ||
478 | card->longname); | ||
479 | } | ||
480 | read_unlock(&snd_card_rwlock); | ||
481 | } | ||
482 | if (!count) | ||
483 | snd_iprintf(buffer, "--- no soundcards ---\n"); | ||
484 | } | ||
485 | |||
486 | #if defined(CONFIG_SND_OSSEMUL) && defined(CONFIG_PROC_FS) | ||
487 | |||
488 | void snd_card_info_read_oss(snd_info_buffer_t * buffer) | ||
489 | { | ||
490 | int idx, count; | ||
491 | snd_card_t *card; | ||
492 | |||
493 | for (idx = count = 0; idx < SNDRV_CARDS; idx++) { | ||
494 | read_lock(&snd_card_rwlock); | ||
495 | if ((card = snd_cards[idx]) != NULL) { | ||
496 | count++; | ||
497 | snd_iprintf(buffer, "%s\n", card->longname); | ||
498 | } | ||
499 | read_unlock(&snd_card_rwlock); | ||
500 | } | ||
501 | if (!count) { | ||
502 | snd_iprintf(buffer, "--- no soundcards ---\n"); | ||
503 | } | ||
504 | } | ||
505 | |||
506 | #endif | ||
507 | |||
508 | #ifdef MODULE | ||
509 | static snd_info_entry_t *snd_card_module_info_entry; | ||
510 | static void snd_card_module_info_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer) | ||
511 | { | ||
512 | int idx; | ||
513 | snd_card_t *card; | ||
514 | |||
515 | for (idx = 0; idx < SNDRV_CARDS; idx++) { | ||
516 | read_lock(&snd_card_rwlock); | ||
517 | if ((card = snd_cards[idx]) != NULL) | ||
518 | snd_iprintf(buffer, "%i %s\n", idx, card->module->name); | ||
519 | read_unlock(&snd_card_rwlock); | ||
520 | } | ||
521 | } | ||
522 | #endif | ||
523 | |||
524 | int __init snd_card_info_init(void) | ||
525 | { | ||
526 | snd_info_entry_t *entry; | ||
527 | |||
528 | entry = snd_info_create_module_entry(THIS_MODULE, "cards", NULL); | ||
529 | snd_runtime_check(entry != NULL, return -ENOMEM); | ||
530 | entry->c.text.read_size = PAGE_SIZE; | ||
531 | entry->c.text.read = snd_card_info_read; | ||
532 | if (snd_info_register(entry) < 0) { | ||
533 | snd_info_free_entry(entry); | ||
534 | return -ENOMEM; | ||
535 | } | ||
536 | snd_card_info_entry = entry; | ||
537 | |||
538 | #ifdef MODULE | ||
539 | entry = snd_info_create_module_entry(THIS_MODULE, "modules", NULL); | ||
540 | if (entry) { | ||
541 | entry->c.text.read_size = PAGE_SIZE; | ||
542 | entry->c.text.read = snd_card_module_info_read; | ||
543 | if (snd_info_register(entry) < 0) | ||
544 | snd_info_free_entry(entry); | ||
545 | else | ||
546 | snd_card_module_info_entry = entry; | ||
547 | } | ||
548 | #endif | ||
549 | |||
550 | return 0; | ||
551 | } | ||
552 | |||
553 | int __exit snd_card_info_done(void) | ||
554 | { | ||
555 | if (snd_card_info_entry) | ||
556 | snd_info_unregister(snd_card_info_entry); | ||
557 | #ifdef MODULE | ||
558 | if (snd_card_module_info_entry) | ||
559 | snd_info_unregister(snd_card_module_info_entry); | ||
560 | #endif | ||
561 | return 0; | ||
562 | } | ||
563 | |||
564 | /** | ||
565 | * snd_component_add - add a component string | ||
566 | * @card: soundcard structure | ||
567 | * @component: the component id string | ||
568 | * | ||
569 | * This function adds the component id string to the supported list. | ||
570 | * The component can be referred from the alsa-lib. | ||
571 | * | ||
572 | * Returns zero otherwise a negative error code. | ||
573 | */ | ||
574 | |||
575 | int snd_component_add(snd_card_t *card, const char *component) | ||
576 | { | ||
577 | char *ptr; | ||
578 | int len = strlen(component); | ||
579 | |||
580 | ptr = strstr(card->components, component); | ||
581 | if (ptr != NULL) { | ||
582 | if (ptr[len] == '\0' || ptr[len] == ' ') /* already there */ | ||
583 | return 1; | ||
584 | } | ||
585 | if (strlen(card->components) + 1 + len + 1 > sizeof(card->components)) { | ||
586 | snd_BUG(); | ||
587 | return -ENOMEM; | ||
588 | } | ||
589 | if (card->components[0] != '\0') | ||
590 | strcat(card->components, " "); | ||
591 | strcat(card->components, component); | ||
592 | return 0; | ||
593 | } | ||
594 | |||
595 | /** | ||
596 | * snd_card_file_add - add the file to the file list of the card | ||
597 | * @card: soundcard structure | ||
598 | * @file: file pointer | ||
599 | * | ||
600 | * This function adds the file to the file linked-list of the card. | ||
601 | * This linked-list is used to keep tracking the connection state, | ||
602 | * and to avoid the release of busy resources by hotplug. | ||
603 | * | ||
604 | * Returns zero or a negative error code. | ||
605 | */ | ||
606 | int snd_card_file_add(snd_card_t *card, struct file *file) | ||
607 | { | ||
608 | struct snd_monitor_file *mfile; | ||
609 | |||
610 | mfile = kmalloc(sizeof(*mfile), GFP_KERNEL); | ||
611 | if (mfile == NULL) | ||
612 | return -ENOMEM; | ||
613 | mfile->file = file; | ||
614 | mfile->next = NULL; | ||
615 | spin_lock(&card->files_lock); | ||
616 | if (card->shutdown) { | ||
617 | spin_unlock(&card->files_lock); | ||
618 | kfree(mfile); | ||
619 | return -ENODEV; | ||
620 | } | ||
621 | mfile->next = card->files; | ||
622 | card->files = mfile; | ||
623 | spin_unlock(&card->files_lock); | ||
624 | return 0; | ||
625 | } | ||
626 | |||
627 | /** | ||
628 | * snd_card_file_remove - remove the file from the file list | ||
629 | * @card: soundcard structure | ||
630 | * @file: file pointer | ||
631 | * | ||
632 | * This function removes the file formerly added to the card via | ||
633 | * snd_card_file_add() function. | ||
634 | * If all files are removed and the release of the card is | ||
635 | * scheduled, it will wake up the the thread to call snd_card_free() | ||
636 | * (see snd_card_free_in_thread() function). | ||
637 | * | ||
638 | * Returns zero or a negative error code. | ||
639 | */ | ||
640 | int snd_card_file_remove(snd_card_t *card, struct file *file) | ||
641 | { | ||
642 | struct snd_monitor_file *mfile, *pfile = NULL; | ||
643 | |||
644 | spin_lock(&card->files_lock); | ||
645 | mfile = card->files; | ||
646 | while (mfile) { | ||
647 | if (mfile->file == file) { | ||
648 | if (pfile) | ||
649 | pfile->next = mfile->next; | ||
650 | else | ||
651 | card->files = mfile->next; | ||
652 | break; | ||
653 | } | ||
654 | pfile = mfile; | ||
655 | mfile = mfile->next; | ||
656 | } | ||
657 | spin_unlock(&card->files_lock); | ||
658 | if (card->files == NULL) | ||
659 | wake_up(&card->shutdown_sleep); | ||
660 | if (!mfile) { | ||
661 | snd_printk(KERN_ERR "ALSA card file remove problem (%p)\n", file); | ||
662 | return -ENOENT; | ||
663 | } | ||
664 | kfree(mfile); | ||
665 | return 0; | ||
666 | } | ||
667 | |||
668 | #ifdef CONFIG_PM | ||
669 | /** | ||
670 | * snd_power_wait - wait until the power-state is changed. | ||
671 | * @card: soundcard structure | ||
672 | * @power_state: expected power state | ||
673 | * @file: file structure for the O_NONBLOCK check (optional) | ||
674 | * | ||
675 | * Waits until the power-state is changed. | ||
676 | * | ||
677 | * Note: the power lock must be active before call. | ||
678 | */ | ||
679 | int snd_power_wait(snd_card_t *card, unsigned int power_state, struct file *file) | ||
680 | { | ||
681 | wait_queue_t wait; | ||
682 | int result = 0; | ||
683 | |||
684 | /* fastpath */ | ||
685 | if (snd_power_get_state(card) == power_state) | ||
686 | return 0; | ||
687 | init_waitqueue_entry(&wait, current); | ||
688 | add_wait_queue(&card->power_sleep, &wait); | ||
689 | while (1) { | ||
690 | if (card->shutdown) { | ||
691 | result = -ENODEV; | ||
692 | break; | ||
693 | } | ||
694 | if (snd_power_get_state(card) == power_state) | ||
695 | break; | ||
696 | #if 0 /* block all devices */ | ||
697 | if (file && (file->f_flags & O_NONBLOCK)) { | ||
698 | result = -EAGAIN; | ||
699 | break; | ||
700 | } | ||
701 | #endif | ||
702 | set_current_state(TASK_UNINTERRUPTIBLE); | ||
703 | snd_power_unlock(card); | ||
704 | schedule_timeout(30 * HZ); | ||
705 | snd_power_lock(card); | ||
706 | } | ||
707 | remove_wait_queue(&card->power_sleep, &wait); | ||
708 | return result; | ||
709 | } | ||
710 | |||
711 | /** | ||
712 | * snd_card_set_pm_callback - set the PCI power-management callbacks | ||
713 | * @card: soundcard structure | ||
714 | * @suspend: suspend callback function | ||
715 | * @resume: resume callback function | ||
716 | * @private_data: private data to pass to the callback functions | ||
717 | * | ||
718 | * Sets the power-management callback functions of the card. | ||
719 | * These callbacks are called from ALSA's common PCI suspend/resume | ||
720 | * handler and from the control API. | ||
721 | */ | ||
722 | int snd_card_set_pm_callback(snd_card_t *card, | ||
723 | int (*suspend)(snd_card_t *, pm_message_t), | ||
724 | int (*resume)(snd_card_t *), | ||
725 | void *private_data) | ||
726 | { | ||
727 | card->pm_suspend = suspend; | ||
728 | card->pm_resume = resume; | ||
729 | card->pm_private_data = private_data; | ||
730 | return 0; | ||
731 | } | ||
732 | |||
733 | #ifdef CONFIG_SND_GENERIC_PM | ||
734 | /* | ||
735 | * use platform_device for generic power-management without a proper bus | ||
736 | * (e.g. ISA) | ||
737 | */ | ||
738 | struct snd_generic_device { | ||
739 | struct platform_device pdev; | ||
740 | snd_card_t *card; | ||
741 | }; | ||
742 | |||
743 | #define get_snd_generic_card(dev) container_of(to_platform_device(dev), struct snd_generic_device, pdev)->card | ||
744 | |||
745 | #define SND_GENERIC_NAME "snd_generic_pm" | ||
746 | |||
747 | static int snd_generic_suspend(struct device *dev, u32 state, u32 level); | ||
748 | static int snd_generic_resume(struct device *dev, u32 level); | ||
749 | |||
750 | static struct device_driver snd_generic_driver = { | ||
751 | .name = SND_GENERIC_NAME, | ||
752 | .bus = &platform_bus_type, | ||
753 | .suspend = snd_generic_suspend, | ||
754 | .resume = snd_generic_resume, | ||
755 | }; | ||
756 | |||
757 | static int generic_driver_registered; | ||
758 | |||
759 | static void generic_driver_unregister(void) | ||
760 | { | ||
761 | if (generic_driver_registered) { | ||
762 | generic_driver_registered--; | ||
763 | if (! generic_driver_registered) | ||
764 | driver_unregister(&snd_generic_driver); | ||
765 | } | ||
766 | } | ||
767 | |||
768 | static struct snd_generic_device *snd_generic_device_register(snd_card_t *card) | ||
769 | { | ||
770 | struct snd_generic_device *dev; | ||
771 | |||
772 | if (! generic_driver_registered) { | ||
773 | if (driver_register(&snd_generic_driver) < 0) | ||
774 | return NULL; | ||
775 | } | ||
776 | generic_driver_registered++; | ||
777 | |||
778 | dev = kcalloc(1, sizeof(*dev), GFP_KERNEL); | ||
779 | if (! dev) { | ||
780 | generic_driver_unregister(); | ||
781 | return NULL; | ||
782 | } | ||
783 | |||
784 | dev->pdev.name = SND_GENERIC_NAME; | ||
785 | dev->pdev.id = card->number; | ||
786 | dev->card = card; | ||
787 | if (platform_device_register(&dev->pdev) < 0) { | ||
788 | kfree(dev); | ||
789 | generic_driver_unregister(); | ||
790 | return NULL; | ||
791 | } | ||
792 | return dev; | ||
793 | } | ||
794 | |||
795 | static void snd_generic_device_unregister(struct snd_generic_device *dev) | ||
796 | { | ||
797 | platform_device_unregister(&dev->pdev); | ||
798 | kfree(dev); | ||
799 | generic_driver_unregister(); | ||
800 | } | ||
801 | |||
802 | /* suspend/resume callbacks for snd_generic platform device */ | ||
803 | static int snd_generic_suspend(struct device *dev, u32 state, u32 level) | ||
804 | { | ||
805 | snd_card_t *card; | ||
806 | |||
807 | if (level != SUSPEND_DISABLE) | ||
808 | return 0; | ||
809 | |||
810 | card = get_snd_generic_card(dev); | ||
811 | if (card->power_state == SNDRV_CTL_POWER_D3hot) | ||
812 | return 0; | ||
813 | card->pm_suspend(card, PMSG_SUSPEND); | ||
814 | snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); | ||
815 | return 0; | ||
816 | } | ||
817 | |||
818 | static int snd_generic_resume(struct device *dev, u32 level) | ||
819 | { | ||
820 | snd_card_t *card; | ||
821 | |||
822 | if (level != RESUME_ENABLE) | ||
823 | return 0; | ||
824 | |||
825 | card = get_snd_generic_card(dev); | ||
826 | if (card->power_state == SNDRV_CTL_POWER_D0) | ||
827 | return 0; | ||
828 | card->pm_resume(card); | ||
829 | snd_power_change_state(card, SNDRV_CTL_POWER_D0); | ||
830 | return 0; | ||
831 | } | ||
832 | |||
833 | /** | ||
834 | * snd_card_set_generic_pm_callback - set the generic power-management callbacks | ||
835 | * @card: soundcard structure | ||
836 | * @suspend: suspend callback function | ||
837 | * @resume: resume callback function | ||
838 | * @private_data: private data to pass to the callback functions | ||
839 | * | ||
840 | * Registers the power-management and sets the lowlevel callbacks for | ||
841 | * the given card. These callbacks are called from the ALSA's common | ||
842 | * PM handler and from the control API. | ||
843 | */ | ||
844 | int snd_card_set_generic_pm_callback(snd_card_t *card, | ||
845 | int (*suspend)(snd_card_t *, pm_message_t), | ||
846 | int (*resume)(snd_card_t *), | ||
847 | void *private_data) | ||
848 | { | ||
849 | card->pm_dev = snd_generic_device_register(card); | ||
850 | if (! card->pm_dev) | ||
851 | return -ENOMEM; | ||
852 | snd_card_set_pm_callback(card, suspend, resume, private_data); | ||
853 | return 0; | ||
854 | } | ||
855 | #endif /* CONFIG_SND_GENERIC_PM */ | ||
856 | |||
857 | #ifdef CONFIG_PCI | ||
858 | int snd_card_pci_suspend(struct pci_dev *dev, pm_message_t state) | ||
859 | { | ||
860 | snd_card_t *card = pci_get_drvdata(dev); | ||
861 | int err; | ||
862 | if (! card || ! card->pm_suspend) | ||
863 | return 0; | ||
864 | if (card->power_state == SNDRV_CTL_POWER_D3hot) | ||
865 | return 0; | ||
866 | err = card->pm_suspend(card, PMSG_SUSPEND); | ||
867 | pci_save_state(dev); | ||
868 | snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); | ||
869 | return err; | ||
870 | } | ||
871 | |||
872 | int snd_card_pci_resume(struct pci_dev *dev) | ||
873 | { | ||
874 | snd_card_t *card = pci_get_drvdata(dev); | ||
875 | if (! card || ! card->pm_resume) | ||
876 | return 0; | ||
877 | if (card->power_state == SNDRV_CTL_POWER_D0) | ||
878 | return 0; | ||
879 | /* restore the PCI config space */ | ||
880 | pci_restore_state(dev); | ||
881 | card->pm_resume(card); | ||
882 | snd_power_change_state(card, SNDRV_CTL_POWER_D0); | ||
883 | return 0; | ||
884 | } | ||
885 | #endif | ||
886 | |||
887 | #endif /* CONFIG_PM */ | ||
diff --git a/sound/core/isadma.c b/sound/core/isadma.c new file mode 100644 index 000000000000..1a378951da5b --- /dev/null +++ b/sound/core/isadma.c | |||
@@ -0,0 +1,103 @@ | |||
1 | /* | ||
2 | * ISA DMA support functions | ||
3 | * Copyright (c) by Jaroslav Kysela <perex@suse.cz> | ||
4 | * | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | /* | ||
23 | * Defining following add some delay. Maybe this helps for some broken | ||
24 | * ISA DMA controllers. | ||
25 | */ | ||
26 | |||
27 | #undef HAVE_REALLY_SLOW_DMA_CONTROLLER | ||
28 | |||
29 | #include <sound/driver.h> | ||
30 | #include <sound/core.h> | ||
31 | #include <asm/dma.h> | ||
32 | |||
33 | /** | ||
34 | * snd_dma_program - program an ISA DMA transfer | ||
35 | * @dma: the dma number | ||
36 | * @addr: the physical address of the buffer | ||
37 | * @size: the DMA transfer size | ||
38 | * @mode: the DMA transfer mode, DMA_MODE_XXX | ||
39 | * | ||
40 | * Programs an ISA DMA transfer for the given buffer. | ||
41 | */ | ||
42 | void snd_dma_program(unsigned long dma, | ||
43 | unsigned long addr, unsigned int size, | ||
44 | unsigned short mode) | ||
45 | { | ||
46 | unsigned long flags; | ||
47 | |||
48 | flags = claim_dma_lock(); | ||
49 | disable_dma(dma); | ||
50 | clear_dma_ff(dma); | ||
51 | set_dma_mode(dma, mode); | ||
52 | set_dma_addr(dma, addr); | ||
53 | set_dma_count(dma, size); | ||
54 | if (!(mode & DMA_MODE_NO_ENABLE)) | ||
55 | enable_dma(dma); | ||
56 | release_dma_lock(flags); | ||
57 | } | ||
58 | |||
59 | /** | ||
60 | * snd_dma_disable - stop the ISA DMA transfer | ||
61 | * @dma: the dma number | ||
62 | * | ||
63 | * Stops the ISA DMA transfer. | ||
64 | */ | ||
65 | void snd_dma_disable(unsigned long dma) | ||
66 | { | ||
67 | unsigned long flags; | ||
68 | |||
69 | flags = claim_dma_lock(); | ||
70 | clear_dma_ff(dma); | ||
71 | disable_dma(dma); | ||
72 | release_dma_lock(flags); | ||
73 | } | ||
74 | |||
75 | /** | ||
76 | * snd_dma_pointer - return the current pointer to DMA transfer buffer in bytes | ||
77 | * @dma: the dma number | ||
78 | * @size: the dma transfer size | ||
79 | * | ||
80 | * Returns the current pointer in DMA tranfer buffer in bytes | ||
81 | */ | ||
82 | unsigned int snd_dma_pointer(unsigned long dma, unsigned int size) | ||
83 | { | ||
84 | unsigned long flags; | ||
85 | unsigned int result; | ||
86 | |||
87 | flags = claim_dma_lock(); | ||
88 | clear_dma_ff(dma); | ||
89 | if (!isa_dma_bridge_buggy) | ||
90 | disable_dma(dma); | ||
91 | result = get_dma_residue(dma); | ||
92 | if (!isa_dma_bridge_buggy) | ||
93 | enable_dma(dma); | ||
94 | release_dma_lock(flags); | ||
95 | #ifdef CONFIG_SND_DEBUG | ||
96 | if (result > size) | ||
97 | snd_printk(KERN_ERR "pointer (0x%x) for DMA #%ld is greater than transfer size (0x%x)\n", result, dma, size); | ||
98 | #endif | ||
99 | if (result >= size || result == 0) | ||
100 | return 0; | ||
101 | else | ||
102 | return size - result; | ||
103 | } | ||
diff --git a/sound/core/memalloc.c b/sound/core/memalloc.c new file mode 100644 index 000000000000..344a83fd7c2e --- /dev/null +++ b/sound/core/memalloc.c | |||
@@ -0,0 +1,663 @@ | |||
1 | /* | ||
2 | * Copyright (c) by Jaroslav Kysela <perex@suse.cz> | ||
3 | * Takashi Iwai <tiwai@suse.de> | ||
4 | * | ||
5 | * Generic memory allocators | ||
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 <linux/config.h> | ||
25 | #include <linux/module.h> | ||
26 | #include <linux/proc_fs.h> | ||
27 | #include <linux/init.h> | ||
28 | #include <linux/pci.h> | ||
29 | #include <linux/slab.h> | ||
30 | #include <linux/mm.h> | ||
31 | #include <linux/dma-mapping.h> | ||
32 | #include <linux/moduleparam.h> | ||
33 | #include <asm/semaphore.h> | ||
34 | #include <sound/memalloc.h> | ||
35 | #ifdef CONFIG_SBUS | ||
36 | #include <asm/sbus.h> | ||
37 | #endif | ||
38 | |||
39 | |||
40 | MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>, Jaroslav Kysela <perex@suse.cz>"); | ||
41 | MODULE_DESCRIPTION("Memory allocator for ALSA system."); | ||
42 | MODULE_LICENSE("GPL"); | ||
43 | |||
44 | |||
45 | #ifndef SNDRV_CARDS | ||
46 | #define SNDRV_CARDS 8 | ||
47 | #endif | ||
48 | |||
49 | /* FIXME: so far only some PCI devices have the preallocation table */ | ||
50 | #ifdef CONFIG_PCI | ||
51 | static int enable[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = 1}; | ||
52 | module_param_array(enable, bool, NULL, 0444); | ||
53 | MODULE_PARM_DESC(enable, "Enable cards to allocate buffers."); | ||
54 | #endif | ||
55 | |||
56 | /* | ||
57 | */ | ||
58 | |||
59 | void *snd_malloc_sgbuf_pages(struct device *device, | ||
60 | size_t size, struct snd_dma_buffer *dmab, | ||
61 | size_t *res_size); | ||
62 | int snd_free_sgbuf_pages(struct snd_dma_buffer *dmab); | ||
63 | |||
64 | /* | ||
65 | */ | ||
66 | |||
67 | static DECLARE_MUTEX(list_mutex); | ||
68 | static LIST_HEAD(mem_list_head); | ||
69 | |||
70 | /* buffer preservation list */ | ||
71 | struct snd_mem_list { | ||
72 | struct snd_dma_buffer buffer; | ||
73 | unsigned int id; | ||
74 | struct list_head list; | ||
75 | }; | ||
76 | |||
77 | /* id for pre-allocated buffers */ | ||
78 | #define SNDRV_DMA_DEVICE_UNUSED (unsigned int)-1 | ||
79 | |||
80 | #ifdef CONFIG_SND_DEBUG | ||
81 | #define __ASTRING__(x) #x | ||
82 | #define snd_assert(expr, args...) do {\ | ||
83 | if (!(expr)) {\ | ||
84 | printk(KERN_ERR "snd-malloc: BUG? (%s) (called from %p)\n", __ASTRING__(expr), __builtin_return_address(0));\ | ||
85 | args;\ | ||
86 | }\ | ||
87 | } while (0) | ||
88 | #else | ||
89 | #define snd_assert(expr, args...) /**/ | ||
90 | #endif | ||
91 | |||
92 | /* | ||
93 | * Hacks | ||
94 | */ | ||
95 | |||
96 | #if defined(__i386__) || defined(__ppc__) || defined(__x86_64__) | ||
97 | /* | ||
98 | * A hack to allocate large buffers via dma_alloc_coherent() | ||
99 | * | ||
100 | * since dma_alloc_coherent always tries GFP_DMA when the requested | ||
101 | * pci memory region is below 32bit, it happens quite often that even | ||
102 | * 2 order of pages cannot be allocated. | ||
103 | * | ||
104 | * so in the following, we allocate at first without dma_mask, so that | ||
105 | * allocation will be done without GFP_DMA. if the area doesn't match | ||
106 | * with the requested region, then realloate with the original dma_mask | ||
107 | * again. | ||
108 | * | ||
109 | * Really, we want to move this type of thing into dma_alloc_coherent() | ||
110 | * so dma_mask doesn't have to be messed with. | ||
111 | */ | ||
112 | |||
113 | static void *snd_dma_hack_alloc_coherent(struct device *dev, size_t size, | ||
114 | dma_addr_t *dma_handle, int flags) | ||
115 | { | ||
116 | void *ret; | ||
117 | u64 dma_mask, coherent_dma_mask; | ||
118 | |||
119 | if (dev == NULL || !dev->dma_mask) | ||
120 | return dma_alloc_coherent(dev, size, dma_handle, flags); | ||
121 | dma_mask = *dev->dma_mask; | ||
122 | coherent_dma_mask = dev->coherent_dma_mask; | ||
123 | *dev->dma_mask = 0xffffffff; /* do without masking */ | ||
124 | dev->coherent_dma_mask = 0xffffffff; /* do without masking */ | ||
125 | ret = dma_alloc_coherent(dev, size, dma_handle, flags); | ||
126 | *dev->dma_mask = dma_mask; /* restore */ | ||
127 | dev->coherent_dma_mask = coherent_dma_mask; /* restore */ | ||
128 | if (ret) { | ||
129 | /* obtained address is out of range? */ | ||
130 | if (((unsigned long)*dma_handle + size - 1) & ~dma_mask) { | ||
131 | /* reallocate with the proper mask */ | ||
132 | dma_free_coherent(dev, size, ret, *dma_handle); | ||
133 | ret = dma_alloc_coherent(dev, size, dma_handle, flags); | ||
134 | } | ||
135 | } else { | ||
136 | /* wish to success now with the proper mask... */ | ||
137 | if (dma_mask != 0xffffffffUL) { | ||
138 | /* allocation with GFP_ATOMIC to avoid the long stall */ | ||
139 | flags &= ~GFP_KERNEL; | ||
140 | flags |= GFP_ATOMIC; | ||
141 | ret = dma_alloc_coherent(dev, size, dma_handle, flags); | ||
142 | } | ||
143 | } | ||
144 | return ret; | ||
145 | } | ||
146 | |||
147 | /* redefine dma_alloc_coherent for some architectures */ | ||
148 | #undef dma_alloc_coherent | ||
149 | #define dma_alloc_coherent snd_dma_hack_alloc_coherent | ||
150 | |||
151 | #endif /* arch */ | ||
152 | |||
153 | #if ! defined(__arm__) | ||
154 | #define NEED_RESERVE_PAGES | ||
155 | #endif | ||
156 | |||
157 | /* | ||
158 | * | ||
159 | * Generic memory allocators | ||
160 | * | ||
161 | */ | ||
162 | |||
163 | static long snd_allocated_pages; /* holding the number of allocated pages */ | ||
164 | |||
165 | static inline void inc_snd_pages(int order) | ||
166 | { | ||
167 | snd_allocated_pages += 1 << order; | ||
168 | } | ||
169 | |||
170 | static inline void dec_snd_pages(int order) | ||
171 | { | ||
172 | snd_allocated_pages -= 1 << order; | ||
173 | } | ||
174 | |||
175 | static void mark_pages(struct page *page, int order) | ||
176 | { | ||
177 | struct page *last_page = page + (1 << order); | ||
178 | while (page < last_page) | ||
179 | SetPageReserved(page++); | ||
180 | } | ||
181 | |||
182 | static void unmark_pages(struct page *page, int order) | ||
183 | { | ||
184 | struct page *last_page = page + (1 << order); | ||
185 | while (page < last_page) | ||
186 | ClearPageReserved(page++); | ||
187 | } | ||
188 | |||
189 | /** | ||
190 | * snd_malloc_pages - allocate pages with the given size | ||
191 | * @size: the size to allocate in bytes | ||
192 | * @gfp_flags: the allocation conditions, GFP_XXX | ||
193 | * | ||
194 | * Allocates the physically contiguous pages with the given size. | ||
195 | * | ||
196 | * Returns the pointer of the buffer, or NULL if no enoguh memory. | ||
197 | */ | ||
198 | void *snd_malloc_pages(size_t size, unsigned int gfp_flags) | ||
199 | { | ||
200 | int pg; | ||
201 | void *res; | ||
202 | |||
203 | snd_assert(size > 0, return NULL); | ||
204 | snd_assert(gfp_flags != 0, return NULL); | ||
205 | pg = get_order(size); | ||
206 | if ((res = (void *) __get_free_pages(gfp_flags, pg)) != NULL) { | ||
207 | mark_pages(virt_to_page(res), pg); | ||
208 | inc_snd_pages(pg); | ||
209 | } | ||
210 | return res; | ||
211 | } | ||
212 | |||
213 | /** | ||
214 | * snd_free_pages - release the pages | ||
215 | * @ptr: the buffer pointer to release | ||
216 | * @size: the allocated buffer size | ||
217 | * | ||
218 | * Releases the buffer allocated via snd_malloc_pages(). | ||
219 | */ | ||
220 | void snd_free_pages(void *ptr, size_t size) | ||
221 | { | ||
222 | int pg; | ||
223 | |||
224 | if (ptr == NULL) | ||
225 | return; | ||
226 | pg = get_order(size); | ||
227 | dec_snd_pages(pg); | ||
228 | unmark_pages(virt_to_page(ptr), pg); | ||
229 | free_pages((unsigned long) ptr, pg); | ||
230 | } | ||
231 | |||
232 | /* | ||
233 | * | ||
234 | * Bus-specific memory allocators | ||
235 | * | ||
236 | */ | ||
237 | |||
238 | /* allocate the coherent DMA pages */ | ||
239 | static void *snd_malloc_dev_pages(struct device *dev, size_t size, dma_addr_t *dma) | ||
240 | { | ||
241 | int pg; | ||
242 | void *res; | ||
243 | unsigned int gfp_flags; | ||
244 | |||
245 | snd_assert(size > 0, return NULL); | ||
246 | snd_assert(dma != NULL, return NULL); | ||
247 | pg = get_order(size); | ||
248 | gfp_flags = GFP_KERNEL | ||
249 | | __GFP_NORETRY /* don't trigger OOM-killer */ | ||
250 | | __GFP_NOWARN; /* no stack trace print - this call is non-critical */ | ||
251 | res = dma_alloc_coherent(dev, PAGE_SIZE << pg, dma, gfp_flags); | ||
252 | if (res != NULL) { | ||
253 | #ifdef NEED_RESERVE_PAGES | ||
254 | mark_pages(virt_to_page(res), pg); /* should be dma_to_page() */ | ||
255 | #endif | ||
256 | inc_snd_pages(pg); | ||
257 | } | ||
258 | |||
259 | return res; | ||
260 | } | ||
261 | |||
262 | /* free the coherent DMA pages */ | ||
263 | static void snd_free_dev_pages(struct device *dev, size_t size, void *ptr, | ||
264 | dma_addr_t dma) | ||
265 | { | ||
266 | int pg; | ||
267 | |||
268 | if (ptr == NULL) | ||
269 | return; | ||
270 | pg = get_order(size); | ||
271 | dec_snd_pages(pg); | ||
272 | #ifdef NEED_RESERVE_PAGES | ||
273 | unmark_pages(virt_to_page(ptr), pg); /* should be dma_to_page() */ | ||
274 | #endif | ||
275 | dma_free_coherent(dev, PAGE_SIZE << pg, ptr, dma); | ||
276 | } | ||
277 | |||
278 | #ifdef CONFIG_SBUS | ||
279 | |||
280 | static void *snd_malloc_sbus_pages(struct device *dev, size_t size, | ||
281 | dma_addr_t *dma_addr) | ||
282 | { | ||
283 | struct sbus_dev *sdev = (struct sbus_dev *)dev; | ||
284 | int pg; | ||
285 | void *res; | ||
286 | |||
287 | snd_assert(size > 0, return NULL); | ||
288 | snd_assert(dma_addr != NULL, return NULL); | ||
289 | pg = get_order(size); | ||
290 | res = sbus_alloc_consistent(sdev, PAGE_SIZE * (1 << pg), dma_addr); | ||
291 | if (res != NULL) | ||
292 | inc_snd_pages(pg); | ||
293 | return res; | ||
294 | } | ||
295 | |||
296 | static void snd_free_sbus_pages(struct device *dev, size_t size, | ||
297 | void *ptr, dma_addr_t dma_addr) | ||
298 | { | ||
299 | struct sbus_dev *sdev = (struct sbus_dev *)dev; | ||
300 | int pg; | ||
301 | |||
302 | if (ptr == NULL) | ||
303 | return; | ||
304 | pg = get_order(size); | ||
305 | dec_snd_pages(pg); | ||
306 | sbus_free_consistent(sdev, PAGE_SIZE * (1 << pg), ptr, dma_addr); | ||
307 | } | ||
308 | |||
309 | #endif /* CONFIG_SBUS */ | ||
310 | |||
311 | /* | ||
312 | * | ||
313 | * ALSA generic memory management | ||
314 | * | ||
315 | */ | ||
316 | |||
317 | |||
318 | /** | ||
319 | * snd_dma_alloc_pages - allocate the buffer area according to the given type | ||
320 | * @type: the DMA buffer type | ||
321 | * @device: the device pointer | ||
322 | * @size: the buffer size to allocate | ||
323 | * @dmab: buffer allocation record to store the allocated data | ||
324 | * | ||
325 | * Calls the memory-allocator function for the corresponding | ||
326 | * buffer type. | ||
327 | * | ||
328 | * Returns zero if the buffer with the given size is allocated successfuly, | ||
329 | * other a negative value at error. | ||
330 | */ | ||
331 | int snd_dma_alloc_pages(int type, struct device *device, size_t size, | ||
332 | struct snd_dma_buffer *dmab) | ||
333 | { | ||
334 | snd_assert(size > 0, return -ENXIO); | ||
335 | snd_assert(dmab != NULL, return -ENXIO); | ||
336 | |||
337 | dmab->dev.type = type; | ||
338 | dmab->dev.dev = device; | ||
339 | dmab->bytes = 0; | ||
340 | switch (type) { | ||
341 | case SNDRV_DMA_TYPE_CONTINUOUS: | ||
342 | dmab->area = snd_malloc_pages(size, (unsigned long)device); | ||
343 | dmab->addr = 0; | ||
344 | break; | ||
345 | #ifdef CONFIG_SBUS | ||
346 | case SNDRV_DMA_TYPE_SBUS: | ||
347 | dmab->area = snd_malloc_sbus_pages(device, size, &dmab->addr); | ||
348 | break; | ||
349 | #endif | ||
350 | case SNDRV_DMA_TYPE_DEV: | ||
351 | dmab->area = snd_malloc_dev_pages(device, size, &dmab->addr); | ||
352 | break; | ||
353 | case SNDRV_DMA_TYPE_DEV_SG: | ||
354 | snd_malloc_sgbuf_pages(device, size, dmab, NULL); | ||
355 | break; | ||
356 | default: | ||
357 | printk(KERN_ERR "snd-malloc: invalid device type %d\n", type); | ||
358 | dmab->area = NULL; | ||
359 | dmab->addr = 0; | ||
360 | return -ENXIO; | ||
361 | } | ||
362 | if (! dmab->area) | ||
363 | return -ENOMEM; | ||
364 | dmab->bytes = size; | ||
365 | return 0; | ||
366 | } | ||
367 | |||
368 | /** | ||
369 | * snd_dma_alloc_pages_fallback - allocate the buffer area according to the given type with fallback | ||
370 | * @type: the DMA buffer type | ||
371 | * @device: the device pointer | ||
372 | * @size: the buffer size to allocate | ||
373 | * @dmab: buffer allocation record to store the allocated data | ||
374 | * | ||
375 | * Calls the memory-allocator function for the corresponding | ||
376 | * buffer type. When no space is left, this function reduces the size and | ||
377 | * tries to allocate again. The size actually allocated is stored in | ||
378 | * res_size argument. | ||
379 | * | ||
380 | * Returns zero if the buffer with the given size is allocated successfuly, | ||
381 | * other a negative value at error. | ||
382 | */ | ||
383 | int snd_dma_alloc_pages_fallback(int type, struct device *device, size_t size, | ||
384 | struct snd_dma_buffer *dmab) | ||
385 | { | ||
386 | int err; | ||
387 | |||
388 | snd_assert(size > 0, return -ENXIO); | ||
389 | snd_assert(dmab != NULL, return -ENXIO); | ||
390 | |||
391 | while ((err = snd_dma_alloc_pages(type, device, size, dmab)) < 0) { | ||
392 | if (err != -ENOMEM) | ||
393 | return err; | ||
394 | size >>= 1; | ||
395 | if (size <= PAGE_SIZE) | ||
396 | return -ENOMEM; | ||
397 | } | ||
398 | if (! dmab->area) | ||
399 | return -ENOMEM; | ||
400 | return 0; | ||
401 | } | ||
402 | |||
403 | |||
404 | /** | ||
405 | * snd_dma_free_pages - release the allocated buffer | ||
406 | * @dmab: the buffer allocation record to release | ||
407 | * | ||
408 | * Releases the allocated buffer via snd_dma_alloc_pages(). | ||
409 | */ | ||
410 | void snd_dma_free_pages(struct snd_dma_buffer *dmab) | ||
411 | { | ||
412 | switch (dmab->dev.type) { | ||
413 | case SNDRV_DMA_TYPE_CONTINUOUS: | ||
414 | snd_free_pages(dmab->area, dmab->bytes); | ||
415 | break; | ||
416 | #ifdef CONFIG_SBUS | ||
417 | case SNDRV_DMA_TYPE_SBUS: | ||
418 | snd_free_sbus_pages(dmab->dev.dev, dmab->bytes, dmab->area, dmab->addr); | ||
419 | break; | ||
420 | #endif | ||
421 | case SNDRV_DMA_TYPE_DEV: | ||
422 | snd_free_dev_pages(dmab->dev.dev, dmab->bytes, dmab->area, dmab->addr); | ||
423 | break; | ||
424 | case SNDRV_DMA_TYPE_DEV_SG: | ||
425 | snd_free_sgbuf_pages(dmab); | ||
426 | break; | ||
427 | default: | ||
428 | printk(KERN_ERR "snd-malloc: invalid device type %d\n", dmab->dev.type); | ||
429 | } | ||
430 | } | ||
431 | |||
432 | |||
433 | /** | ||
434 | * snd_dma_get_reserved - get the reserved buffer for the given device | ||
435 | * @dmab: the buffer allocation record to store | ||
436 | * @id: the buffer id | ||
437 | * | ||
438 | * Looks for the reserved-buffer list and re-uses if the same buffer | ||
439 | * is found in the list. When the buffer is found, it's removed from the free list. | ||
440 | * | ||
441 | * Returns the size of buffer if the buffer is found, or zero if not found. | ||
442 | */ | ||
443 | size_t snd_dma_get_reserved_buf(struct snd_dma_buffer *dmab, unsigned int id) | ||
444 | { | ||
445 | struct list_head *p; | ||
446 | struct snd_mem_list *mem; | ||
447 | |||
448 | snd_assert(dmab, return 0); | ||
449 | |||
450 | down(&list_mutex); | ||
451 | list_for_each(p, &mem_list_head) { | ||
452 | mem = list_entry(p, struct snd_mem_list, list); | ||
453 | if (mem->id == id && | ||
454 | ! memcmp(&mem->buffer.dev, &dmab->dev, sizeof(dmab->dev))) { | ||
455 | list_del(p); | ||
456 | *dmab = mem->buffer; | ||
457 | kfree(mem); | ||
458 | up(&list_mutex); | ||
459 | return dmab->bytes; | ||
460 | } | ||
461 | } | ||
462 | up(&list_mutex); | ||
463 | return 0; | ||
464 | } | ||
465 | |||
466 | /** | ||
467 | * snd_dma_reserve_buf - reserve the buffer | ||
468 | * @dmab: the buffer to reserve | ||
469 | * @id: the buffer id | ||
470 | * | ||
471 | * Reserves the given buffer as a reserved buffer. | ||
472 | * | ||
473 | * Returns zero if successful, or a negative code at error. | ||
474 | */ | ||
475 | int snd_dma_reserve_buf(struct snd_dma_buffer *dmab, unsigned int id) | ||
476 | { | ||
477 | struct snd_mem_list *mem; | ||
478 | |||
479 | snd_assert(dmab, return -EINVAL); | ||
480 | mem = kmalloc(sizeof(*mem), GFP_KERNEL); | ||
481 | if (! mem) | ||
482 | return -ENOMEM; | ||
483 | down(&list_mutex); | ||
484 | mem->buffer = *dmab; | ||
485 | mem->id = id; | ||
486 | list_add_tail(&mem->list, &mem_list_head); | ||
487 | up(&list_mutex); | ||
488 | return 0; | ||
489 | } | ||
490 | |||
491 | /* | ||
492 | * purge all reserved buffers | ||
493 | */ | ||
494 | static void free_all_reserved_pages(void) | ||
495 | { | ||
496 | struct list_head *p; | ||
497 | struct snd_mem_list *mem; | ||
498 | |||
499 | down(&list_mutex); | ||
500 | while (! list_empty(&mem_list_head)) { | ||
501 | p = mem_list_head.next; | ||
502 | mem = list_entry(p, struct snd_mem_list, list); | ||
503 | list_del(p); | ||
504 | snd_dma_free_pages(&mem->buffer); | ||
505 | kfree(mem); | ||
506 | } | ||
507 | up(&list_mutex); | ||
508 | } | ||
509 | |||
510 | |||
511 | |||
512 | /* | ||
513 | * allocation of buffers for pre-defined devices | ||
514 | */ | ||
515 | |||
516 | #ifdef CONFIG_PCI | ||
517 | /* FIXME: for pci only - other bus? */ | ||
518 | struct prealloc_dev { | ||
519 | unsigned short vendor; | ||
520 | unsigned short device; | ||
521 | unsigned long dma_mask; | ||
522 | unsigned int size; | ||
523 | unsigned int buffers; | ||
524 | }; | ||
525 | |||
526 | #define HAMMERFALL_BUFFER_SIZE (16*1024*4*(26+1)+0x10000) | ||
527 | |||
528 | static struct prealloc_dev prealloc_devices[] __initdata = { | ||
529 | { | ||
530 | /* hammerfall */ | ||
531 | .vendor = 0x10ee, | ||
532 | .device = 0x3fc4, | ||
533 | .dma_mask = 0xffffffff, | ||
534 | .size = HAMMERFALL_BUFFER_SIZE, | ||
535 | .buffers = 2 | ||
536 | }, | ||
537 | { | ||
538 | /* HDSP */ | ||
539 | .vendor = 0x10ee, | ||
540 | .device = 0x3fc5, | ||
541 | .dma_mask = 0xffffffff, | ||
542 | .size = HAMMERFALL_BUFFER_SIZE, | ||
543 | .buffers = 2 | ||
544 | }, | ||
545 | { }, /* terminator */ | ||
546 | }; | ||
547 | |||
548 | static void __init preallocate_cards(void) | ||
549 | { | ||
550 | struct pci_dev *pci = NULL; | ||
551 | int card; | ||
552 | |||
553 | card = 0; | ||
554 | |||
555 | while ((pci = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, pci)) != NULL) { | ||
556 | struct prealloc_dev *dev; | ||
557 | unsigned int i; | ||
558 | if (card >= SNDRV_CARDS) | ||
559 | break; | ||
560 | for (dev = prealloc_devices; dev->vendor; dev++) { | ||
561 | if (dev->vendor == pci->vendor && dev->device == pci->device) | ||
562 | break; | ||
563 | } | ||
564 | if (! dev->vendor) | ||
565 | continue; | ||
566 | if (! enable[card++]) { | ||
567 | printk(KERN_DEBUG "snd-page-alloc: skipping card %d, device %04x:%04x\n", card, pci->vendor, pci->device); | ||
568 | continue; | ||
569 | } | ||
570 | |||
571 | if (pci_set_dma_mask(pci, dev->dma_mask) < 0 || | ||
572 | pci_set_consistent_dma_mask(pci, dev->dma_mask) < 0) { | ||
573 | printk(KERN_ERR "snd-page-alloc: cannot set DMA mask %lx for pci %04x:%04x\n", dev->dma_mask, dev->vendor, dev->device); | ||
574 | continue; | ||
575 | } | ||
576 | for (i = 0; i < dev->buffers; i++) { | ||
577 | struct snd_dma_buffer dmab; | ||
578 | memset(&dmab, 0, sizeof(dmab)); | ||
579 | if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci), | ||
580 | dev->size, &dmab) < 0) | ||
581 | printk(KERN_WARNING "snd-page-alloc: cannot allocate buffer pages (size = %d)\n", dev->size); | ||
582 | else | ||
583 | snd_dma_reserve_buf(&dmab, snd_dma_pci_buf_id(pci)); | ||
584 | } | ||
585 | } | ||
586 | } | ||
587 | #else | ||
588 | #define preallocate_cards() /* NOP */ | ||
589 | #endif | ||
590 | |||
591 | |||
592 | #ifdef CONFIG_PROC_FS | ||
593 | /* | ||
594 | * proc file interface | ||
595 | */ | ||
596 | static int snd_mem_proc_read(char *page, char **start, off_t off, | ||
597 | int count, int *eof, void *data) | ||
598 | { | ||
599 | int len = 0; | ||
600 | long pages = snd_allocated_pages >> (PAGE_SHIFT-12); | ||
601 | struct list_head *p; | ||
602 | struct snd_mem_list *mem; | ||
603 | int devno; | ||
604 | static char *types[] = { "UNKNOWN", "CONT", "DEV", "DEV-SG", "SBUS" }; | ||
605 | |||
606 | down(&list_mutex); | ||
607 | len += snprintf(page + len, count - len, | ||
608 | "pages : %li bytes (%li pages per %likB)\n", | ||
609 | pages * PAGE_SIZE, pages, PAGE_SIZE / 1024); | ||
610 | devno = 0; | ||
611 | list_for_each(p, &mem_list_head) { | ||
612 | mem = list_entry(p, struct snd_mem_list, list); | ||
613 | devno++; | ||
614 | len += snprintf(page + len, count - len, | ||
615 | "buffer %d : ID %08x : type %s\n", | ||
616 | devno, mem->id, types[mem->buffer.dev.type]); | ||
617 | len += snprintf(page + len, count - len, | ||
618 | " addr = 0x%lx, size = %d bytes\n", | ||
619 | (unsigned long)mem->buffer.addr, (int)mem->buffer.bytes); | ||
620 | } | ||
621 | up(&list_mutex); | ||
622 | return len; | ||
623 | } | ||
624 | #endif /* CONFIG_PROC_FS */ | ||
625 | |||
626 | /* | ||
627 | * module entry | ||
628 | */ | ||
629 | |||
630 | static int __init snd_mem_init(void) | ||
631 | { | ||
632 | #ifdef CONFIG_PROC_FS | ||
633 | create_proc_read_entry("driver/snd-page-alloc", 0, NULL, snd_mem_proc_read, NULL); | ||
634 | #endif | ||
635 | preallocate_cards(); | ||
636 | return 0; | ||
637 | } | ||
638 | |||
639 | static void __exit snd_mem_exit(void) | ||
640 | { | ||
641 | remove_proc_entry("driver/snd-page-alloc", NULL); | ||
642 | free_all_reserved_pages(); | ||
643 | if (snd_allocated_pages > 0) | ||
644 | printk(KERN_ERR "snd-malloc: Memory leak? pages not freed = %li\n", snd_allocated_pages); | ||
645 | } | ||
646 | |||
647 | |||
648 | module_init(snd_mem_init) | ||
649 | module_exit(snd_mem_exit) | ||
650 | |||
651 | |||
652 | /* | ||
653 | * exports | ||
654 | */ | ||
655 | EXPORT_SYMBOL(snd_dma_alloc_pages); | ||
656 | EXPORT_SYMBOL(snd_dma_alloc_pages_fallback); | ||
657 | EXPORT_SYMBOL(snd_dma_free_pages); | ||
658 | |||
659 | EXPORT_SYMBOL(snd_dma_get_reserved_buf); | ||
660 | EXPORT_SYMBOL(snd_dma_reserve_buf); | ||
661 | |||
662 | EXPORT_SYMBOL(snd_malloc_pages); | ||
663 | EXPORT_SYMBOL(snd_free_pages); | ||
diff --git a/sound/core/memory.c b/sound/core/memory.c new file mode 100644 index 000000000000..20860fec9364 --- /dev/null +++ b/sound/core/memory.c | |||
@@ -0,0 +1,306 @@ | |||
1 | /* | ||
2 | * Copyright (c) by Jaroslav Kysela <perex@suse.cz> | ||
3 | * | ||
4 | * Memory allocation helpers. | ||
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 <asm/io.h> | ||
25 | #include <asm/uaccess.h> | ||
26 | #include <linux/init.h> | ||
27 | #include <linux/slab.h> | ||
28 | #include <linux/time.h> | ||
29 | #include <linux/pci.h> | ||
30 | #include <sound/core.h> | ||
31 | #include <sound/info.h> | ||
32 | |||
33 | /* | ||
34 | * memory allocation helpers and debug routines | ||
35 | */ | ||
36 | |||
37 | #ifdef CONFIG_SND_DEBUG_MEMORY | ||
38 | |||
39 | struct snd_alloc_track { | ||
40 | unsigned long magic; | ||
41 | void *caller; | ||
42 | size_t size; | ||
43 | struct list_head list; | ||
44 | long data[0]; | ||
45 | }; | ||
46 | |||
47 | #define snd_alloc_track_entry(obj) (struct snd_alloc_track *)((char*)obj - (unsigned long)((struct snd_alloc_track *)0)->data) | ||
48 | |||
49 | static long snd_alloc_kmalloc; | ||
50 | static long snd_alloc_vmalloc; | ||
51 | static LIST_HEAD(snd_alloc_kmalloc_list); | ||
52 | static LIST_HEAD(snd_alloc_vmalloc_list); | ||
53 | static DEFINE_SPINLOCK(snd_alloc_kmalloc_lock); | ||
54 | static DEFINE_SPINLOCK(snd_alloc_vmalloc_lock); | ||
55 | #define KMALLOC_MAGIC 0x87654321 | ||
56 | #define VMALLOC_MAGIC 0x87654320 | ||
57 | static snd_info_entry_t *snd_memory_info_entry; | ||
58 | |||
59 | void snd_memory_init(void) | ||
60 | { | ||
61 | snd_alloc_kmalloc = 0; | ||
62 | snd_alloc_vmalloc = 0; | ||
63 | } | ||
64 | |||
65 | void snd_memory_done(void) | ||
66 | { | ||
67 | struct list_head *head; | ||
68 | struct snd_alloc_track *t; | ||
69 | |||
70 | if (snd_alloc_kmalloc > 0) | ||
71 | snd_printk(KERN_ERR "Not freed snd_alloc_kmalloc = %li\n", snd_alloc_kmalloc); | ||
72 | if (snd_alloc_vmalloc > 0) | ||
73 | snd_printk(KERN_ERR "Not freed snd_alloc_vmalloc = %li\n", snd_alloc_vmalloc); | ||
74 | list_for_each_prev(head, &snd_alloc_kmalloc_list) { | ||
75 | t = list_entry(head, struct snd_alloc_track, list); | ||
76 | if (t->magic != KMALLOC_MAGIC) { | ||
77 | snd_printk(KERN_ERR "Corrupted kmalloc\n"); | ||
78 | break; | ||
79 | } | ||
80 | snd_printk(KERN_ERR "kmalloc(%ld) from %p not freed\n", (long) t->size, t->caller); | ||
81 | } | ||
82 | list_for_each_prev(head, &snd_alloc_vmalloc_list) { | ||
83 | t = list_entry(head, struct snd_alloc_track, list); | ||
84 | if (t->magic != VMALLOC_MAGIC) { | ||
85 | snd_printk(KERN_ERR "Corrupted vmalloc\n"); | ||
86 | break; | ||
87 | } | ||
88 | snd_printk(KERN_ERR "vmalloc(%ld) from %p not freed\n", (long) t->size, t->caller); | ||
89 | } | ||
90 | } | ||
91 | |||
92 | static void *__snd_kmalloc(size_t size, int flags, void *caller) | ||
93 | { | ||
94 | unsigned long cpu_flags; | ||
95 | struct snd_alloc_track *t; | ||
96 | void *ptr; | ||
97 | |||
98 | ptr = snd_wrapper_kmalloc(size + sizeof(struct snd_alloc_track), flags); | ||
99 | if (ptr != NULL) { | ||
100 | t = (struct snd_alloc_track *)ptr; | ||
101 | t->magic = KMALLOC_MAGIC; | ||
102 | t->caller = caller; | ||
103 | spin_lock_irqsave(&snd_alloc_kmalloc_lock, cpu_flags); | ||
104 | list_add_tail(&t->list, &snd_alloc_kmalloc_list); | ||
105 | spin_unlock_irqrestore(&snd_alloc_kmalloc_lock, cpu_flags); | ||
106 | t->size = size; | ||
107 | snd_alloc_kmalloc += size; | ||
108 | ptr = t->data; | ||
109 | } | ||
110 | return ptr; | ||
111 | } | ||
112 | |||
113 | #define _snd_kmalloc(size, flags) __snd_kmalloc((size), (flags), __builtin_return_address(0)); | ||
114 | void *snd_hidden_kmalloc(size_t size, int flags) | ||
115 | { | ||
116 | return _snd_kmalloc(size, flags); | ||
117 | } | ||
118 | |||
119 | void *snd_hidden_kcalloc(size_t n, size_t size, int flags) | ||
120 | { | ||
121 | void *ret = NULL; | ||
122 | if (n != 0 && size > INT_MAX / n) | ||
123 | return ret; | ||
124 | ret = _snd_kmalloc(n * size, flags); | ||
125 | if (ret) | ||
126 | memset(ret, 0, n * size); | ||
127 | return ret; | ||
128 | } | ||
129 | |||
130 | void snd_hidden_kfree(const void *obj) | ||
131 | { | ||
132 | unsigned long flags; | ||
133 | struct snd_alloc_track *t; | ||
134 | if (obj == NULL) | ||
135 | return; | ||
136 | t = snd_alloc_track_entry(obj); | ||
137 | if (t->magic != KMALLOC_MAGIC) { | ||
138 | snd_printk(KERN_WARNING "bad kfree (called from %p)\n", __builtin_return_address(0)); | ||
139 | return; | ||
140 | } | ||
141 | spin_lock_irqsave(&snd_alloc_kmalloc_lock, flags); | ||
142 | list_del(&t->list); | ||
143 | spin_unlock_irqrestore(&snd_alloc_kmalloc_lock, flags); | ||
144 | t->magic = 0; | ||
145 | snd_alloc_kmalloc -= t->size; | ||
146 | obj = t; | ||
147 | snd_wrapper_kfree(obj); | ||
148 | } | ||
149 | |||
150 | void *snd_hidden_vmalloc(unsigned long size) | ||
151 | { | ||
152 | void *ptr; | ||
153 | ptr = snd_wrapper_vmalloc(size + sizeof(struct snd_alloc_track)); | ||
154 | if (ptr) { | ||
155 | struct snd_alloc_track *t = (struct snd_alloc_track *)ptr; | ||
156 | t->magic = VMALLOC_MAGIC; | ||
157 | t->caller = __builtin_return_address(0); | ||
158 | spin_lock(&snd_alloc_vmalloc_lock); | ||
159 | list_add_tail(&t->list, &snd_alloc_vmalloc_list); | ||
160 | spin_unlock(&snd_alloc_vmalloc_lock); | ||
161 | t->size = size; | ||
162 | snd_alloc_vmalloc += size; | ||
163 | ptr = t->data; | ||
164 | } | ||
165 | return ptr; | ||
166 | } | ||
167 | |||
168 | void snd_hidden_vfree(void *obj) | ||
169 | { | ||
170 | struct snd_alloc_track *t; | ||
171 | if (obj == NULL) | ||
172 | return; | ||
173 | t = snd_alloc_track_entry(obj); | ||
174 | if (t->magic != VMALLOC_MAGIC) { | ||
175 | snd_printk(KERN_ERR "bad vfree (called from %p)\n", __builtin_return_address(0)); | ||
176 | return; | ||
177 | } | ||
178 | spin_lock(&snd_alloc_vmalloc_lock); | ||
179 | list_del(&t->list); | ||
180 | spin_unlock(&snd_alloc_vmalloc_lock); | ||
181 | t->magic = 0; | ||
182 | snd_alloc_vmalloc -= t->size; | ||
183 | obj = t; | ||
184 | snd_wrapper_vfree(obj); | ||
185 | } | ||
186 | |||
187 | static void snd_memory_info_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer) | ||
188 | { | ||
189 | snd_iprintf(buffer, "kmalloc: %li bytes\n", snd_alloc_kmalloc); | ||
190 | snd_iprintf(buffer, "vmalloc: %li bytes\n", snd_alloc_vmalloc); | ||
191 | } | ||
192 | |||
193 | int __init snd_memory_info_init(void) | ||
194 | { | ||
195 | snd_info_entry_t *entry; | ||
196 | |||
197 | entry = snd_info_create_module_entry(THIS_MODULE, "meminfo", NULL); | ||
198 | if (entry) { | ||
199 | entry->c.text.read_size = 256; | ||
200 | entry->c.text.read = snd_memory_info_read; | ||
201 | if (snd_info_register(entry) < 0) { | ||
202 | snd_info_free_entry(entry); | ||
203 | entry = NULL; | ||
204 | } | ||
205 | } | ||
206 | snd_memory_info_entry = entry; | ||
207 | return 0; | ||
208 | } | ||
209 | |||
210 | int __exit snd_memory_info_done(void) | ||
211 | { | ||
212 | if (snd_memory_info_entry) | ||
213 | snd_info_unregister(snd_memory_info_entry); | ||
214 | return 0; | ||
215 | } | ||
216 | |||
217 | #else | ||
218 | |||
219 | #define _snd_kmalloc kmalloc | ||
220 | |||
221 | #endif /* CONFIG_SND_DEBUG_MEMORY */ | ||
222 | |||
223 | /** | ||
224 | * snd_kmalloc_strdup - copy the string | ||
225 | * @string: the original string | ||
226 | * @flags: allocation conditions, GFP_XXX | ||
227 | * | ||
228 | * Allocates a memory chunk via kmalloc() and copies the string to it. | ||
229 | * | ||
230 | * Returns the pointer, or NULL if no enoguh memory. | ||
231 | */ | ||
232 | char *snd_kmalloc_strdup(const char *string, int flags) | ||
233 | { | ||
234 | size_t len; | ||
235 | char *ptr; | ||
236 | |||
237 | if (!string) | ||
238 | return NULL; | ||
239 | len = strlen(string) + 1; | ||
240 | ptr = _snd_kmalloc(len, flags); | ||
241 | if (ptr) | ||
242 | memcpy(ptr, string, len); | ||
243 | return ptr; | ||
244 | } | ||
245 | |||
246 | /** | ||
247 | * copy_to_user_fromio - copy data from mmio-space to user-space | ||
248 | * @dst: the destination pointer on user-space | ||
249 | * @src: the source pointer on mmio | ||
250 | * @count: the data size to copy in bytes | ||
251 | * | ||
252 | * Copies the data from mmio-space to user-space. | ||
253 | * | ||
254 | * Returns zero if successful, or non-zero on failure. | ||
255 | */ | ||
256 | int copy_to_user_fromio(void __user *dst, const volatile void __iomem *src, size_t count) | ||
257 | { | ||
258 | #if defined(__i386__) || defined(CONFIG_SPARC32) | ||
259 | return copy_to_user(dst, (const void*)src, count) ? -EFAULT : 0; | ||
260 | #else | ||
261 | char buf[256]; | ||
262 | while (count) { | ||
263 | size_t c = count; | ||
264 | if (c > sizeof(buf)) | ||
265 | c = sizeof(buf); | ||
266 | memcpy_fromio(buf, (void __iomem *)src, c); | ||
267 | if (copy_to_user(dst, buf, c)) | ||
268 | return -EFAULT; | ||
269 | count -= c; | ||
270 | dst += c; | ||
271 | src += c; | ||
272 | } | ||
273 | return 0; | ||
274 | #endif | ||
275 | } | ||
276 | |||
277 | /** | ||
278 | * copy_from_user_toio - copy data from user-space to mmio-space | ||
279 | * @dst: the destination pointer on mmio-space | ||
280 | * @src: the source pointer on user-space | ||
281 | * @count: the data size to copy in bytes | ||
282 | * | ||
283 | * Copies the data from user-space to mmio-space. | ||
284 | * | ||
285 | * Returns zero if successful, or non-zero on failure. | ||
286 | */ | ||
287 | int copy_from_user_toio(volatile void __iomem *dst, const void __user *src, size_t count) | ||
288 | { | ||
289 | #if defined(__i386__) || defined(CONFIG_SPARC32) | ||
290 | return copy_from_user((void*)dst, src, count) ? -EFAULT : 0; | ||
291 | #else | ||
292 | char buf[256]; | ||
293 | while (count) { | ||
294 | size_t c = count; | ||
295 | if (c > sizeof(buf)) | ||
296 | c = sizeof(buf); | ||
297 | if (copy_from_user(buf, src, c)) | ||
298 | return -EFAULT; | ||
299 | memcpy_toio(dst, buf, c); | ||
300 | count -= c; | ||
301 | dst += c; | ||
302 | src += c; | ||
303 | } | ||
304 | return 0; | ||
305 | #endif | ||
306 | } | ||
diff --git a/sound/core/misc.c b/sound/core/misc.c new file mode 100644 index 000000000000..1a81fe4df218 --- /dev/null +++ b/sound/core/misc.c | |||
@@ -0,0 +1,76 @@ | |||
1 | /* | ||
2 | * Misc and compatibility things | ||
3 | * Copyright (c) by Jaroslav Kysela <perex@suse.cz> | ||
4 | * | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #include <sound/driver.h> | ||
23 | #include <linux/init.h> | ||
24 | #include <linux/sched.h> | ||
25 | #include <linux/time.h> | ||
26 | #include <sound/core.h> | ||
27 | |||
28 | int snd_task_name(struct task_struct *task, char *name, size_t size) | ||
29 | { | ||
30 | unsigned int idx; | ||
31 | |||
32 | snd_assert(task != NULL && name != NULL && size >= 2, return -EINVAL); | ||
33 | for (idx = 0; idx < sizeof(task->comm) && idx + 1 < size; idx++) | ||
34 | name[idx] = task->comm[idx]; | ||
35 | name[idx] = '\0'; | ||
36 | return 0; | ||
37 | } | ||
38 | |||
39 | #ifdef CONFIG_SND_VERBOSE_PRINTK | ||
40 | void snd_verbose_printk(const char *file, int line, const char *format, ...) | ||
41 | { | ||
42 | va_list args; | ||
43 | |||
44 | if (format[0] == '<' && format[1] >= '0' && format[1] <= '9' && format[2] == '>') { | ||
45 | char tmp[] = "<0>"; | ||
46 | tmp[1] = format[1]; | ||
47 | printk("%sALSA %s:%d: ", tmp, file, line); | ||
48 | format += 3; | ||
49 | } else { | ||
50 | printk("ALSA %s:%d: ", file, line); | ||
51 | } | ||
52 | va_start(args, format); | ||
53 | vprintk(format, args); | ||
54 | va_end(args); | ||
55 | } | ||
56 | #endif | ||
57 | |||
58 | #if defined(CONFIG_SND_DEBUG) && defined(CONFIG_SND_VERBOSE_PRINTK) | ||
59 | void snd_verbose_printd(const char *file, int line, const char *format, ...) | ||
60 | { | ||
61 | va_list args; | ||
62 | |||
63 | if (format[0] == '<' && format[1] >= '0' && format[1] <= '9' && format[2] == '>') { | ||
64 | char tmp[] = "<0>"; | ||
65 | tmp[1] = format[1]; | ||
66 | printk("%sALSA %s:%d: ", tmp, file, line); | ||
67 | format += 3; | ||
68 | } else { | ||
69 | printk(KERN_DEBUG "ALSA %s:%d: ", file, line); | ||
70 | } | ||
71 | va_start(args, format); | ||
72 | vprintk(format, args); | ||
73 | va_end(args); | ||
74 | |||
75 | } | ||
76 | #endif | ||
diff --git a/sound/core/oss/Makefile b/sound/core/oss/Makefile new file mode 100644 index 000000000000..e6d5a045ba27 --- /dev/null +++ b/sound/core/oss/Makefile | |||
@@ -0,0 +1,12 @@ | |||
1 | # | ||
2 | # Makefile for ALSA | ||
3 | # Copyright (c) 1999 by Jaroslav Kysela <perex@suse.cz> | ||
4 | # | ||
5 | |||
6 | snd-mixer-oss-objs := mixer_oss.o | ||
7 | |||
8 | snd-pcm-oss-objs := pcm_oss.o pcm_plugin.o \ | ||
9 | io.o copy.o linear.o mulaw.o route.o rate.o | ||
10 | |||
11 | obj-$(CONFIG_SND_MIXER_OSS) += snd-mixer-oss.o | ||
12 | obj-$(CONFIG_SND_PCM_OSS) += snd-pcm-oss.o | ||
diff --git a/sound/core/oss/copy.c b/sound/core/oss/copy.c new file mode 100644 index 000000000000..edecbe7417bd --- /dev/null +++ b/sound/core/oss/copy.c | |||
@@ -0,0 +1,87 @@ | |||
1 | /* | ||
2 | * Linear conversion Plug-In | ||
3 | * Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org> | ||
4 | * | ||
5 | * | ||
6 | * This library is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU Library General Public License as | ||
8 | * published by the Free Software Foundation; either version 2 of | ||
9 | * the License, or (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU Library General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU Library General Public | ||
17 | * License along with this library; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #include <sound/driver.h> | ||
23 | #include <linux/time.h> | ||
24 | #include <sound/core.h> | ||
25 | #include <sound/pcm.h> | ||
26 | #include "pcm_plugin.h" | ||
27 | |||
28 | static snd_pcm_sframes_t copy_transfer(snd_pcm_plugin_t *plugin, | ||
29 | const snd_pcm_plugin_channel_t *src_channels, | ||
30 | snd_pcm_plugin_channel_t *dst_channels, | ||
31 | snd_pcm_uframes_t frames) | ||
32 | { | ||
33 | unsigned int channel; | ||
34 | unsigned int nchannels; | ||
35 | |||
36 | snd_assert(plugin != NULL && src_channels != NULL && dst_channels != NULL, return -ENXIO); | ||
37 | if (frames == 0) | ||
38 | return 0; | ||
39 | nchannels = plugin->src_format.channels; | ||
40 | for (channel = 0; channel < nchannels; channel++) { | ||
41 | snd_assert(src_channels->area.first % 8 == 0 && | ||
42 | src_channels->area.step % 8 == 0, | ||
43 | return -ENXIO); | ||
44 | snd_assert(dst_channels->area.first % 8 == 0 && | ||
45 | dst_channels->area.step % 8 == 0, | ||
46 | return -ENXIO); | ||
47 | if (!src_channels->enabled) { | ||
48 | if (dst_channels->wanted) | ||
49 | snd_pcm_area_silence(&dst_channels->area, 0, frames, plugin->dst_format.format); | ||
50 | dst_channels->enabled = 0; | ||
51 | continue; | ||
52 | } | ||
53 | dst_channels->enabled = 1; | ||
54 | snd_pcm_area_copy(&src_channels->area, 0, &dst_channels->area, 0, frames, plugin->src_format.format); | ||
55 | src_channels++; | ||
56 | dst_channels++; | ||
57 | } | ||
58 | return frames; | ||
59 | } | ||
60 | |||
61 | int snd_pcm_plugin_build_copy(snd_pcm_plug_t *plug, | ||
62 | snd_pcm_plugin_format_t *src_format, | ||
63 | snd_pcm_plugin_format_t *dst_format, | ||
64 | snd_pcm_plugin_t **r_plugin) | ||
65 | { | ||
66 | int err; | ||
67 | snd_pcm_plugin_t *plugin; | ||
68 | int width; | ||
69 | |||
70 | snd_assert(r_plugin != NULL, return -ENXIO); | ||
71 | *r_plugin = NULL; | ||
72 | |||
73 | snd_assert(src_format->format == dst_format->format, return -ENXIO); | ||
74 | snd_assert(src_format->rate == dst_format->rate, return -ENXIO); | ||
75 | snd_assert(src_format->channels == dst_format->channels, return -ENXIO); | ||
76 | |||
77 | width = snd_pcm_format_physical_width(src_format->format); | ||
78 | snd_assert(width > 0, return -ENXIO); | ||
79 | |||
80 | err = snd_pcm_plugin_build(plug, "copy", src_format, dst_format, | ||
81 | 0, &plugin); | ||
82 | if (err < 0) | ||
83 | return err; | ||
84 | plugin->transfer = copy_transfer; | ||
85 | *r_plugin = plugin; | ||
86 | return 0; | ||
87 | } | ||
diff --git a/sound/core/oss/io.c b/sound/core/oss/io.c new file mode 100644 index 000000000000..bb1c99a5b734 --- /dev/null +++ b/sound/core/oss/io.c | |||
@@ -0,0 +1,134 @@ | |||
1 | /* | ||
2 | * PCM I/O Plug-In Interface | ||
3 | * Copyright (c) 1999 by Jaroslav Kysela <perex@suse.cz> | ||
4 | * | ||
5 | * | ||
6 | * This library is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU Library General Public License as | ||
8 | * published by the Free Software Foundation; either version 2 of | ||
9 | * the License, or (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU Library General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU Library General Public | ||
17 | * License along with this library; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #include <sound/driver.h> | ||
23 | #include <linux/time.h> | ||
24 | #include <sound/core.h> | ||
25 | #include <sound/pcm.h> | ||
26 | #include <sound/pcm_params.h> | ||
27 | #include "pcm_plugin.h" | ||
28 | |||
29 | #define pcm_write(plug,buf,count) snd_pcm_oss_write3(plug,buf,count,1) | ||
30 | #define pcm_writev(plug,vec,count) snd_pcm_oss_writev3(plug,vec,count,1) | ||
31 | #define pcm_read(plug,buf,count) snd_pcm_oss_read3(plug,buf,count,1) | ||
32 | #define pcm_readv(plug,vec,count) snd_pcm_oss_readv3(plug,vec,count,1) | ||
33 | |||
34 | /* | ||
35 | * Basic io plugin | ||
36 | */ | ||
37 | |||
38 | static snd_pcm_sframes_t io_playback_transfer(snd_pcm_plugin_t *plugin, | ||
39 | const snd_pcm_plugin_channel_t *src_channels, | ||
40 | snd_pcm_plugin_channel_t *dst_channels ATTRIBUTE_UNUSED, | ||
41 | snd_pcm_uframes_t frames) | ||
42 | { | ||
43 | snd_assert(plugin != NULL, return -ENXIO); | ||
44 | snd_assert(src_channels != NULL, return -ENXIO); | ||
45 | if (plugin->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED) { | ||
46 | return pcm_write(plugin->plug, src_channels->area.addr, frames); | ||
47 | } else { | ||
48 | int channel, channels = plugin->dst_format.channels; | ||
49 | void **bufs = (void**)plugin->extra_data; | ||
50 | snd_assert(bufs != NULL, return -ENXIO); | ||
51 | for (channel = 0; channel < channels; channel++) { | ||
52 | if (src_channels[channel].enabled) | ||
53 | bufs[channel] = src_channels[channel].area.addr; | ||
54 | else | ||
55 | bufs[channel] = NULL; | ||
56 | } | ||
57 | return pcm_writev(plugin->plug, bufs, frames); | ||
58 | } | ||
59 | } | ||
60 | |||
61 | static snd_pcm_sframes_t io_capture_transfer(snd_pcm_plugin_t *plugin, | ||
62 | const snd_pcm_plugin_channel_t *src_channels ATTRIBUTE_UNUSED, | ||
63 | snd_pcm_plugin_channel_t *dst_channels, | ||
64 | snd_pcm_uframes_t frames) | ||
65 | { | ||
66 | snd_assert(plugin != NULL, return -ENXIO); | ||
67 | snd_assert(dst_channels != NULL, return -ENXIO); | ||
68 | if (plugin->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED) { | ||
69 | return pcm_read(plugin->plug, dst_channels->area.addr, frames); | ||
70 | } else { | ||
71 | int channel, channels = plugin->dst_format.channels; | ||
72 | void **bufs = (void**)plugin->extra_data; | ||
73 | snd_assert(bufs != NULL, return -ENXIO); | ||
74 | for (channel = 0; channel < channels; channel++) { | ||
75 | if (dst_channels[channel].enabled) | ||
76 | bufs[channel] = dst_channels[channel].area.addr; | ||
77 | else | ||
78 | bufs[channel] = NULL; | ||
79 | } | ||
80 | return pcm_readv(plugin->plug, bufs, frames); | ||
81 | } | ||
82 | return 0; | ||
83 | } | ||
84 | |||
85 | static snd_pcm_sframes_t io_src_channels(snd_pcm_plugin_t *plugin, | ||
86 | snd_pcm_uframes_t frames, | ||
87 | snd_pcm_plugin_channel_t **channels) | ||
88 | { | ||
89 | int err; | ||
90 | unsigned int channel; | ||
91 | snd_pcm_plugin_channel_t *v; | ||
92 | err = snd_pcm_plugin_client_channels(plugin, frames, &v); | ||
93 | if (err < 0) | ||
94 | return err; | ||
95 | *channels = v; | ||
96 | if (plugin->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED) { | ||
97 | for (channel = 0; channel < plugin->src_format.channels; ++channel, ++v) | ||
98 | v->wanted = 1; | ||
99 | } | ||
100 | return frames; | ||
101 | } | ||
102 | |||
103 | int snd_pcm_plugin_build_io(snd_pcm_plug_t *plug, | ||
104 | snd_pcm_hw_params_t *params, | ||
105 | snd_pcm_plugin_t **r_plugin) | ||
106 | { | ||
107 | int err; | ||
108 | snd_pcm_plugin_format_t format; | ||
109 | snd_pcm_plugin_t *plugin; | ||
110 | |||
111 | snd_assert(r_plugin != NULL, return -ENXIO); | ||
112 | *r_plugin = NULL; | ||
113 | snd_assert(plug != NULL && params != NULL, return -ENXIO); | ||
114 | format.format = params_format(params); | ||
115 | format.rate = params_rate(params); | ||
116 | format.channels = params_channels(params); | ||
117 | err = snd_pcm_plugin_build(plug, "I/O io", | ||
118 | &format, &format, | ||
119 | sizeof(void *) * format.channels, | ||
120 | &plugin); | ||
121 | if (err < 0) | ||
122 | return err; | ||
123 | plugin->access = params_access(params); | ||
124 | if (snd_pcm_plug_stream(plug) == SNDRV_PCM_STREAM_PLAYBACK) { | ||
125 | plugin->transfer = io_playback_transfer; | ||
126 | if (plugin->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED) | ||
127 | plugin->client_channels = io_src_channels; | ||
128 | } else { | ||
129 | plugin->transfer = io_capture_transfer; | ||
130 | } | ||
131 | |||
132 | *r_plugin = plugin; | ||
133 | return 0; | ||
134 | } | ||
diff --git a/sound/core/oss/linear.c b/sound/core/oss/linear.c new file mode 100644 index 000000000000..12ed27a57b27 --- /dev/null +++ b/sound/core/oss/linear.c | |||
@@ -0,0 +1,158 @@ | |||
1 | /* | ||
2 | * Linear conversion Plug-In | ||
3 | * Copyright (c) 1999 by Jaroslav Kysela <perex@suse.cz>, | ||
4 | * Abramo Bagnara <abramo@alsa-project.org> | ||
5 | * | ||
6 | * | ||
7 | * This library is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU Library General Public License as | ||
9 | * published by the Free Software Foundation; either version 2 of | ||
10 | * the License, or (at your option) any later version. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | * GNU Library General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU Library General Public | ||
18 | * License along with this library; if not, write to the Free Software | ||
19 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
20 | * | ||
21 | */ | ||
22 | |||
23 | #include <sound/driver.h> | ||
24 | #include <linux/time.h> | ||
25 | #include <sound/core.h> | ||
26 | #include <sound/pcm.h> | ||
27 | #include "pcm_plugin.h" | ||
28 | |||
29 | /* | ||
30 | * Basic linear conversion plugin | ||
31 | */ | ||
32 | |||
33 | typedef struct linear_private_data { | ||
34 | int conv; | ||
35 | } linear_t; | ||
36 | |||
37 | static void convert(snd_pcm_plugin_t *plugin, | ||
38 | const snd_pcm_plugin_channel_t *src_channels, | ||
39 | snd_pcm_plugin_channel_t *dst_channels, | ||
40 | snd_pcm_uframes_t frames) | ||
41 | { | ||
42 | #define CONV_LABELS | ||
43 | #include "plugin_ops.h" | ||
44 | #undef CONV_LABELS | ||
45 | linear_t *data = (linear_t *)plugin->extra_data; | ||
46 | void *conv = conv_labels[data->conv]; | ||
47 | int channel; | ||
48 | int nchannels = plugin->src_format.channels; | ||
49 | for (channel = 0; channel < nchannels; ++channel) { | ||
50 | char *src; | ||
51 | char *dst; | ||
52 | int src_step, dst_step; | ||
53 | snd_pcm_uframes_t frames1; | ||
54 | if (!src_channels[channel].enabled) { | ||
55 | if (dst_channels[channel].wanted) | ||
56 | snd_pcm_area_silence(&dst_channels[channel].area, 0, frames, plugin->dst_format.format); | ||
57 | dst_channels[channel].enabled = 0; | ||
58 | continue; | ||
59 | } | ||
60 | dst_channels[channel].enabled = 1; | ||
61 | src = src_channels[channel].area.addr + src_channels[channel].area.first / 8; | ||
62 | dst = dst_channels[channel].area.addr + dst_channels[channel].area.first / 8; | ||
63 | src_step = src_channels[channel].area.step / 8; | ||
64 | dst_step = dst_channels[channel].area.step / 8; | ||
65 | frames1 = frames; | ||
66 | while (frames1-- > 0) { | ||
67 | goto *conv; | ||
68 | #define CONV_END after | ||
69 | #include "plugin_ops.h" | ||
70 | #undef CONV_END | ||
71 | after: | ||
72 | src += src_step; | ||
73 | dst += dst_step; | ||
74 | } | ||
75 | } | ||
76 | } | ||
77 | |||
78 | static snd_pcm_sframes_t linear_transfer(snd_pcm_plugin_t *plugin, | ||
79 | const snd_pcm_plugin_channel_t *src_channels, | ||
80 | snd_pcm_plugin_channel_t *dst_channels, | ||
81 | snd_pcm_uframes_t frames) | ||
82 | { | ||
83 | linear_t *data; | ||
84 | |||
85 | snd_assert(plugin != NULL && src_channels != NULL && dst_channels != NULL, return -ENXIO); | ||
86 | data = (linear_t *)plugin->extra_data; | ||
87 | if (frames == 0) | ||
88 | return 0; | ||
89 | #ifdef CONFIG_SND_DEBUG | ||
90 | { | ||
91 | unsigned int channel; | ||
92 | for (channel = 0; channel < plugin->src_format.channels; channel++) { | ||
93 | snd_assert(src_channels[channel].area.first % 8 == 0 && | ||
94 | src_channels[channel].area.step % 8 == 0, | ||
95 | return -ENXIO); | ||
96 | snd_assert(dst_channels[channel].area.first % 8 == 0 && | ||
97 | dst_channels[channel].area.step % 8 == 0, | ||
98 | return -ENXIO); | ||
99 | } | ||
100 | } | ||
101 | #endif | ||
102 | convert(plugin, src_channels, dst_channels, frames); | ||
103 | return frames; | ||
104 | } | ||
105 | |||
106 | int conv_index(int src_format, int dst_format) | ||
107 | { | ||
108 | int src_endian, dst_endian, sign, src_width, dst_width; | ||
109 | |||
110 | sign = (snd_pcm_format_signed(src_format) != | ||
111 | snd_pcm_format_signed(dst_format)); | ||
112 | #ifdef SNDRV_LITTLE_ENDIAN | ||
113 | src_endian = snd_pcm_format_big_endian(src_format); | ||
114 | dst_endian = snd_pcm_format_big_endian(dst_format); | ||
115 | #else | ||
116 | src_endian = snd_pcm_format_little_endian(src_format); | ||
117 | dst_endian = snd_pcm_format_little_endian(dst_format); | ||
118 | #endif | ||
119 | |||
120 | if (src_endian < 0) | ||
121 | src_endian = 0; | ||
122 | if (dst_endian < 0) | ||
123 | dst_endian = 0; | ||
124 | |||
125 | src_width = snd_pcm_format_width(src_format) / 8 - 1; | ||
126 | dst_width = snd_pcm_format_width(dst_format) / 8 - 1; | ||
127 | |||
128 | return src_width * 32 + src_endian * 16 + sign * 8 + dst_width * 2 + dst_endian; | ||
129 | } | ||
130 | |||
131 | int snd_pcm_plugin_build_linear(snd_pcm_plug_t *plug, | ||
132 | snd_pcm_plugin_format_t *src_format, | ||
133 | snd_pcm_plugin_format_t *dst_format, | ||
134 | snd_pcm_plugin_t **r_plugin) | ||
135 | { | ||
136 | int err; | ||
137 | struct linear_private_data *data; | ||
138 | snd_pcm_plugin_t *plugin; | ||
139 | |||
140 | snd_assert(r_plugin != NULL, return -ENXIO); | ||
141 | *r_plugin = NULL; | ||
142 | |||
143 | snd_assert(src_format->rate == dst_format->rate, return -ENXIO); | ||
144 | snd_assert(src_format->channels == dst_format->channels, return -ENXIO); | ||
145 | snd_assert(snd_pcm_format_linear(src_format->format) && | ||
146 | snd_pcm_format_linear(dst_format->format), return -ENXIO); | ||
147 | |||
148 | err = snd_pcm_plugin_build(plug, "linear format conversion", | ||
149 | src_format, dst_format, | ||
150 | sizeof(linear_t), &plugin); | ||
151 | if (err < 0) | ||
152 | return err; | ||
153 | data = (linear_t *)plugin->extra_data; | ||
154 | data->conv = conv_index(src_format->format, dst_format->format); | ||
155 | plugin->transfer = linear_transfer; | ||
156 | *r_plugin = plugin; | ||
157 | return 0; | ||
158 | } | ||
diff --git a/sound/core/oss/mixer_oss.c b/sound/core/oss/mixer_oss.c new file mode 100644 index 000000000000..98ed9a9f0da6 --- /dev/null +++ b/sound/core/oss/mixer_oss.c | |||
@@ -0,0 +1,1340 @@ | |||
1 | /* | ||
2 | * OSS emulation layer for the mixer interface | ||
3 | * Copyright (c) by Jaroslav Kysela <perex@suse.cz> | ||
4 | * | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #include <sound/driver.h> | ||
23 | #include <linux/init.h> | ||
24 | #include <linux/smp_lock.h> | ||
25 | #include <linux/slab.h> | ||
26 | #include <linux/time.h> | ||
27 | #include <sound/core.h> | ||
28 | #include <sound/minors.h> | ||
29 | #include <sound/control.h> | ||
30 | #include <sound/info.h> | ||
31 | #include <sound/mixer_oss.h> | ||
32 | #include <linux/soundcard.h> | ||
33 | |||
34 | #define OSS_ALSAEMULVER _SIOR ('M', 249, int) | ||
35 | |||
36 | MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>"); | ||
37 | MODULE_DESCRIPTION("Mixer OSS emulation for ALSA."); | ||
38 | MODULE_LICENSE("GPL"); | ||
39 | MODULE_ALIAS_SNDRV_MINOR(SNDRV_MINOR_OSS_MIXER); | ||
40 | |||
41 | static int snd_mixer_oss_open(struct inode *inode, struct file *file) | ||
42 | { | ||
43 | int cardnum = SNDRV_MINOR_OSS_CARD(iminor(inode)); | ||
44 | snd_card_t *card; | ||
45 | snd_mixer_oss_file_t *fmixer; | ||
46 | int err; | ||
47 | |||
48 | if ((card = snd_cards[cardnum]) == NULL) | ||
49 | return -ENODEV; | ||
50 | if (card->mixer_oss == NULL) | ||
51 | return -ENODEV; | ||
52 | err = snd_card_file_add(card, file); | ||
53 | if (err < 0) | ||
54 | return err; | ||
55 | fmixer = kcalloc(1, sizeof(*fmixer), GFP_KERNEL); | ||
56 | if (fmixer == NULL) { | ||
57 | snd_card_file_remove(card, file); | ||
58 | return -ENOMEM; | ||
59 | } | ||
60 | fmixer->card = card; | ||
61 | fmixer->mixer = card->mixer_oss; | ||
62 | file->private_data = fmixer; | ||
63 | if (!try_module_get(card->module)) { | ||
64 | kfree(fmixer); | ||
65 | snd_card_file_remove(card, file); | ||
66 | return -EFAULT; | ||
67 | } | ||
68 | return 0; | ||
69 | } | ||
70 | |||
71 | static int snd_mixer_oss_release(struct inode *inode, struct file *file) | ||
72 | { | ||
73 | snd_mixer_oss_file_t *fmixer; | ||
74 | |||
75 | if (file->private_data) { | ||
76 | fmixer = (snd_mixer_oss_file_t *) file->private_data; | ||
77 | module_put(fmixer->card->module); | ||
78 | snd_card_file_remove(fmixer->card, file); | ||
79 | kfree(fmixer); | ||
80 | } | ||
81 | return 0; | ||
82 | } | ||
83 | |||
84 | static int snd_mixer_oss_info(snd_mixer_oss_file_t *fmixer, | ||
85 | mixer_info __user *_info) | ||
86 | { | ||
87 | snd_card_t *card = fmixer->card; | ||
88 | snd_mixer_oss_t *mixer = fmixer->mixer; | ||
89 | struct mixer_info info; | ||
90 | |||
91 | memset(&info, 0, sizeof(info)); | ||
92 | strlcpy(info.id, mixer && mixer->id[0] ? mixer->id : card->driver, sizeof(info.id)); | ||
93 | strlcpy(info.name, mixer && mixer->name[0] ? mixer->name : card->mixername, sizeof(info.name)); | ||
94 | info.modify_counter = card->mixer_oss_change_count; | ||
95 | if (copy_to_user(_info, &info, sizeof(info))) | ||
96 | return -EFAULT; | ||
97 | return 0; | ||
98 | } | ||
99 | |||
100 | static int snd_mixer_oss_info_obsolete(snd_mixer_oss_file_t *fmixer, | ||
101 | _old_mixer_info __user *_info) | ||
102 | { | ||
103 | snd_card_t *card = fmixer->card; | ||
104 | snd_mixer_oss_t *mixer = fmixer->mixer; | ||
105 | _old_mixer_info info; | ||
106 | |||
107 | memset(&info, 0, sizeof(info)); | ||
108 | strlcpy(info.id, mixer && mixer->id[0] ? mixer->id : card->driver, sizeof(info.id)); | ||
109 | strlcpy(info.name, mixer && mixer->name[0] ? mixer->name : card->mixername, sizeof(info.name)); | ||
110 | if (copy_to_user(_info, &info, sizeof(info))) | ||
111 | return -EFAULT; | ||
112 | return 0; | ||
113 | } | ||
114 | |||
115 | static int snd_mixer_oss_caps(snd_mixer_oss_file_t *fmixer) | ||
116 | { | ||
117 | snd_mixer_oss_t *mixer = fmixer->mixer; | ||
118 | int result = 0; | ||
119 | |||
120 | if (mixer == NULL) | ||
121 | return -EIO; | ||
122 | if (mixer->get_recsrc && mixer->put_recsrc) | ||
123 | result |= SOUND_CAP_EXCL_INPUT; | ||
124 | return result; | ||
125 | } | ||
126 | |||
127 | static int snd_mixer_oss_devmask(snd_mixer_oss_file_t *fmixer) | ||
128 | { | ||
129 | snd_mixer_oss_t *mixer = fmixer->mixer; | ||
130 | snd_mixer_oss_slot_t *pslot; | ||
131 | int result = 0, chn; | ||
132 | |||
133 | if (mixer == NULL) | ||
134 | return -EIO; | ||
135 | for (chn = 0; chn < 31; chn++) { | ||
136 | pslot = &mixer->slots[chn]; | ||
137 | if (pslot->put_volume || pslot->put_recsrc) | ||
138 | result |= 1 << chn; | ||
139 | } | ||
140 | return result; | ||
141 | } | ||
142 | |||
143 | static int snd_mixer_oss_stereodevs(snd_mixer_oss_file_t *fmixer) | ||
144 | { | ||
145 | snd_mixer_oss_t *mixer = fmixer->mixer; | ||
146 | snd_mixer_oss_slot_t *pslot; | ||
147 | int result = 0, chn; | ||
148 | |||
149 | if (mixer == NULL) | ||
150 | return -EIO; | ||
151 | for (chn = 0; chn < 31; chn++) { | ||
152 | pslot = &mixer->slots[chn]; | ||
153 | if (pslot->put_volume && pslot->stereo) | ||
154 | result |= 1 << chn; | ||
155 | } | ||
156 | return result; | ||
157 | } | ||
158 | |||
159 | static int snd_mixer_oss_recmask(snd_mixer_oss_file_t *fmixer) | ||
160 | { | ||
161 | snd_mixer_oss_t *mixer = fmixer->mixer; | ||
162 | int result = 0; | ||
163 | |||
164 | if (mixer == NULL) | ||
165 | return -EIO; | ||
166 | if (mixer->put_recsrc && mixer->get_recsrc) { /* exclusive */ | ||
167 | result = mixer->mask_recsrc; | ||
168 | } else { | ||
169 | snd_mixer_oss_slot_t *pslot; | ||
170 | int chn; | ||
171 | for (chn = 0; chn < 31; chn++) { | ||
172 | pslot = &mixer->slots[chn]; | ||
173 | if (pslot->put_recsrc) | ||
174 | result |= 1 << chn; | ||
175 | } | ||
176 | } | ||
177 | return result; | ||
178 | } | ||
179 | |||
180 | static int snd_mixer_oss_get_recsrc(snd_mixer_oss_file_t *fmixer) | ||
181 | { | ||
182 | snd_mixer_oss_t *mixer = fmixer->mixer; | ||
183 | int result = 0; | ||
184 | |||
185 | if (mixer == NULL) | ||
186 | return -EIO; | ||
187 | if (mixer->put_recsrc && mixer->get_recsrc) { /* exclusive */ | ||
188 | int err; | ||
189 | if ((err = mixer->get_recsrc(fmixer, &result)) < 0) | ||
190 | return err; | ||
191 | result = 1 << result; | ||
192 | } else { | ||
193 | snd_mixer_oss_slot_t *pslot; | ||
194 | int chn; | ||
195 | for (chn = 0; chn < 31; chn++) { | ||
196 | pslot = &mixer->slots[chn]; | ||
197 | if (pslot->get_recsrc) { | ||
198 | int active = 0; | ||
199 | pslot->get_recsrc(fmixer, pslot, &active); | ||
200 | if (active) | ||
201 | result |= 1 << chn; | ||
202 | } | ||
203 | } | ||
204 | } | ||
205 | return mixer->oss_recsrc = result; | ||
206 | } | ||
207 | |||
208 | static int snd_mixer_oss_set_recsrc(snd_mixer_oss_file_t *fmixer, int recsrc) | ||
209 | { | ||
210 | snd_mixer_oss_t *mixer = fmixer->mixer; | ||
211 | snd_mixer_oss_slot_t *pslot; | ||
212 | int chn, active; | ||
213 | int result = 0; | ||
214 | |||
215 | if (mixer == NULL) | ||
216 | return -EIO; | ||
217 | if (mixer->get_recsrc && mixer->put_recsrc) { /* exclusive input */ | ||
218 | if (recsrc & ~mixer->oss_recsrc) | ||
219 | recsrc &= ~mixer->oss_recsrc; | ||
220 | mixer->put_recsrc(fmixer, ffz(~recsrc)); | ||
221 | mixer->get_recsrc(fmixer, &result); | ||
222 | result = 1 << result; | ||
223 | } | ||
224 | for (chn = 0; chn < 31; chn++) { | ||
225 | pslot = &mixer->slots[chn]; | ||
226 | if (pslot->put_recsrc) { | ||
227 | active = (recsrc & (1 << chn)) ? 1 : 0; | ||
228 | pslot->put_recsrc(fmixer, pslot, active); | ||
229 | } | ||
230 | } | ||
231 | if (! result) { | ||
232 | for (chn = 0; chn < 31; chn++) { | ||
233 | pslot = &mixer->slots[chn]; | ||
234 | if (pslot->get_recsrc) { | ||
235 | active = 0; | ||
236 | pslot->get_recsrc(fmixer, pslot, &active); | ||
237 | if (active) | ||
238 | result |= 1 << chn; | ||
239 | } | ||
240 | } | ||
241 | } | ||
242 | return result; | ||
243 | } | ||
244 | |||
245 | static int snd_mixer_oss_get_volume(snd_mixer_oss_file_t *fmixer, int slot) | ||
246 | { | ||
247 | snd_mixer_oss_t *mixer = fmixer->mixer; | ||
248 | snd_mixer_oss_slot_t *pslot; | ||
249 | int result = 0, left, right; | ||
250 | |||
251 | if (mixer == NULL || slot > 30) | ||
252 | return -EIO; | ||
253 | pslot = &mixer->slots[slot]; | ||
254 | left = pslot->volume[0]; | ||
255 | right = pslot->volume[1]; | ||
256 | if (pslot->get_volume) | ||
257 | result = pslot->get_volume(fmixer, pslot, &left, &right); | ||
258 | if (!pslot->stereo) | ||
259 | right = left; | ||
260 | snd_assert(left >= 0 && left <= 100, return -EIO); | ||
261 | snd_assert(right >= 0 && right <= 100, return -EIO); | ||
262 | if (result >= 0) { | ||
263 | pslot->volume[0] = left; | ||
264 | pslot->volume[1] = right; | ||
265 | result = (left & 0xff) | ((right & 0xff) << 8); | ||
266 | } | ||
267 | return result; | ||
268 | } | ||
269 | |||
270 | static int snd_mixer_oss_set_volume(snd_mixer_oss_file_t *fmixer, | ||
271 | int slot, int volume) | ||
272 | { | ||
273 | snd_mixer_oss_t *mixer = fmixer->mixer; | ||
274 | snd_mixer_oss_slot_t *pslot; | ||
275 | int result = 0, left = volume & 0xff, right = (volume >> 8) & 0xff; | ||
276 | |||
277 | if (mixer == NULL || slot > 30) | ||
278 | return -EIO; | ||
279 | pslot = &mixer->slots[slot]; | ||
280 | if (left > 100) | ||
281 | left = 100; | ||
282 | if (right > 100) | ||
283 | right = 100; | ||
284 | if (!pslot->stereo) | ||
285 | right = left; | ||
286 | if (pslot->put_volume) | ||
287 | result = pslot->put_volume(fmixer, pslot, left, right); | ||
288 | if (result < 0) | ||
289 | return result; | ||
290 | pslot->volume[0] = left; | ||
291 | pslot->volume[1] = right; | ||
292 | return (left & 0xff) | ((right & 0xff) << 8); | ||
293 | } | ||
294 | |||
295 | static int snd_mixer_oss_ioctl1(snd_mixer_oss_file_t *fmixer, unsigned int cmd, unsigned long arg) | ||
296 | { | ||
297 | void __user *argp = (void __user *)arg; | ||
298 | int __user *p = argp; | ||
299 | int tmp; | ||
300 | |||
301 | snd_assert(fmixer != NULL, return -ENXIO); | ||
302 | if (((cmd >> 8) & 0xff) == 'M') { | ||
303 | switch (cmd) { | ||
304 | case SOUND_MIXER_INFO: | ||
305 | return snd_mixer_oss_info(fmixer, argp); | ||
306 | case SOUND_OLD_MIXER_INFO: | ||
307 | return snd_mixer_oss_info_obsolete(fmixer, argp); | ||
308 | case SOUND_MIXER_WRITE_RECSRC: | ||
309 | if (get_user(tmp, p)) | ||
310 | return -EFAULT; | ||
311 | tmp = snd_mixer_oss_set_recsrc(fmixer, tmp); | ||
312 | if (tmp < 0) | ||
313 | return tmp; | ||
314 | return put_user(tmp, p); | ||
315 | case OSS_GETVERSION: | ||
316 | return put_user(SNDRV_OSS_VERSION, p); | ||
317 | case OSS_ALSAEMULVER: | ||
318 | return put_user(1, p); | ||
319 | case SOUND_MIXER_READ_DEVMASK: | ||
320 | tmp = snd_mixer_oss_devmask(fmixer); | ||
321 | if (tmp < 0) | ||
322 | return tmp; | ||
323 | return put_user(tmp, p); | ||
324 | case SOUND_MIXER_READ_STEREODEVS: | ||
325 | tmp = snd_mixer_oss_stereodevs(fmixer); | ||
326 | if (tmp < 0) | ||
327 | return tmp; | ||
328 | return put_user(tmp, p); | ||
329 | case SOUND_MIXER_READ_RECMASK: | ||
330 | tmp = snd_mixer_oss_recmask(fmixer); | ||
331 | if (tmp < 0) | ||
332 | return tmp; | ||
333 | return put_user(tmp, p); | ||
334 | case SOUND_MIXER_READ_CAPS: | ||
335 | tmp = snd_mixer_oss_caps(fmixer); | ||
336 | if (tmp < 0) | ||
337 | return tmp; | ||
338 | return put_user(tmp, p); | ||
339 | case SOUND_MIXER_READ_RECSRC: | ||
340 | tmp = snd_mixer_oss_get_recsrc(fmixer); | ||
341 | if (tmp < 0) | ||
342 | return tmp; | ||
343 | return put_user(tmp, p); | ||
344 | } | ||
345 | } | ||
346 | if (cmd & SIOC_IN) { | ||
347 | if (get_user(tmp, p)) | ||
348 | return -EFAULT; | ||
349 | tmp = snd_mixer_oss_set_volume(fmixer, cmd & 0xff, tmp); | ||
350 | if (tmp < 0) | ||
351 | return tmp; | ||
352 | return put_user(tmp, p); | ||
353 | } else if (cmd & SIOC_OUT) { | ||
354 | tmp = snd_mixer_oss_get_volume(fmixer, cmd & 0xff); | ||
355 | if (tmp < 0) | ||
356 | return tmp; | ||
357 | return put_user(tmp, p); | ||
358 | } | ||
359 | return -ENXIO; | ||
360 | } | ||
361 | |||
362 | static long snd_mixer_oss_ioctl(struct file *file, unsigned int cmd, unsigned long arg) | ||
363 | { | ||
364 | return snd_mixer_oss_ioctl1((snd_mixer_oss_file_t *) file->private_data, cmd, arg); | ||
365 | } | ||
366 | |||
367 | int snd_mixer_oss_ioctl_card(snd_card_t *card, unsigned int cmd, unsigned long arg) | ||
368 | { | ||
369 | snd_mixer_oss_file_t fmixer; | ||
370 | |||
371 | snd_assert(card != NULL, return -ENXIO); | ||
372 | if (card->mixer_oss == NULL) | ||
373 | return -ENXIO; | ||
374 | memset(&fmixer, 0, sizeof(fmixer)); | ||
375 | fmixer.card = card; | ||
376 | fmixer.mixer = card->mixer_oss; | ||
377 | return snd_mixer_oss_ioctl1(&fmixer, cmd, arg); | ||
378 | } | ||
379 | |||
380 | #ifdef CONFIG_COMPAT | ||
381 | /* all compatible */ | ||
382 | #define snd_mixer_oss_ioctl_compat snd_mixer_oss_ioctl | ||
383 | #else | ||
384 | #define snd_mixer_oss_ioctl_compat NULL | ||
385 | #endif | ||
386 | |||
387 | /* | ||
388 | * REGISTRATION PART | ||
389 | */ | ||
390 | |||
391 | static struct file_operations snd_mixer_oss_f_ops = | ||
392 | { | ||
393 | .owner = THIS_MODULE, | ||
394 | .open = snd_mixer_oss_open, | ||
395 | .release = snd_mixer_oss_release, | ||
396 | .unlocked_ioctl = snd_mixer_oss_ioctl, | ||
397 | .compat_ioctl = snd_mixer_oss_ioctl_compat, | ||
398 | }; | ||
399 | |||
400 | static snd_minor_t snd_mixer_oss_reg = | ||
401 | { | ||
402 | .comment = "mixer", | ||
403 | .f_ops = &snd_mixer_oss_f_ops, | ||
404 | }; | ||
405 | |||
406 | /* | ||
407 | * utilities | ||
408 | */ | ||
409 | |||
410 | static long snd_mixer_oss_conv(long val, long omin, long omax, long nmin, long nmax) | ||
411 | { | ||
412 | long orange = omax - omin, nrange = nmax - nmin; | ||
413 | |||
414 | if (orange == 0) | ||
415 | return 0; | ||
416 | return ((nrange * (val - omin)) + (orange / 2)) / orange + nmin; | ||
417 | } | ||
418 | |||
419 | /* convert from alsa native to oss values (0-100) */ | ||
420 | static long snd_mixer_oss_conv1(long val, long min, long max, int *old) | ||
421 | { | ||
422 | if (val == snd_mixer_oss_conv(*old, 0, 100, min, max)) | ||
423 | return *old; | ||
424 | return snd_mixer_oss_conv(val, min, max, 0, 100); | ||
425 | } | ||
426 | |||
427 | /* convert from oss to alsa native values */ | ||
428 | static long snd_mixer_oss_conv2(long val, long min, long max) | ||
429 | { | ||
430 | return snd_mixer_oss_conv(val, 0, 100, min, max); | ||
431 | } | ||
432 | |||
433 | #if 0 | ||
434 | static void snd_mixer_oss_recsrce_set(snd_card_t *card, int slot) | ||
435 | { | ||
436 | snd_mixer_oss_t *mixer = card->mixer_oss; | ||
437 | if (mixer) | ||
438 | mixer->mask_recsrc |= 1 << slot; | ||
439 | } | ||
440 | |||
441 | static int snd_mixer_oss_recsrce_get(snd_card_t *card, int slot) | ||
442 | { | ||
443 | snd_mixer_oss_t *mixer = card->mixer_oss; | ||
444 | if (mixer && (mixer->mask_recsrc & (1 << slot))) | ||
445 | return 1; | ||
446 | return 0; | ||
447 | } | ||
448 | #endif | ||
449 | |||
450 | #define SNDRV_MIXER_OSS_SIGNATURE 0x65999250 | ||
451 | |||
452 | #define SNDRV_MIXER_OSS_ITEM_GLOBAL 0 | ||
453 | #define SNDRV_MIXER_OSS_ITEM_GSWITCH 1 | ||
454 | #define SNDRV_MIXER_OSS_ITEM_GROUTE 2 | ||
455 | #define SNDRV_MIXER_OSS_ITEM_GVOLUME 3 | ||
456 | #define SNDRV_MIXER_OSS_ITEM_PSWITCH 4 | ||
457 | #define SNDRV_MIXER_OSS_ITEM_PROUTE 5 | ||
458 | #define SNDRV_MIXER_OSS_ITEM_PVOLUME 6 | ||
459 | #define SNDRV_MIXER_OSS_ITEM_CSWITCH 7 | ||
460 | #define SNDRV_MIXER_OSS_ITEM_CROUTE 8 | ||
461 | #define SNDRV_MIXER_OSS_ITEM_CVOLUME 9 | ||
462 | #define SNDRV_MIXER_OSS_ITEM_CAPTURE 10 | ||
463 | |||
464 | #define SNDRV_MIXER_OSS_ITEM_COUNT 11 | ||
465 | |||
466 | #define SNDRV_MIXER_OSS_PRESENT_GLOBAL (1<<0) | ||
467 | #define SNDRV_MIXER_OSS_PRESENT_GSWITCH (1<<1) | ||
468 | #define SNDRV_MIXER_OSS_PRESENT_GROUTE (1<<2) | ||
469 | #define SNDRV_MIXER_OSS_PRESENT_GVOLUME (1<<3) | ||
470 | #define SNDRV_MIXER_OSS_PRESENT_PSWITCH (1<<4) | ||
471 | #define SNDRV_MIXER_OSS_PRESENT_PROUTE (1<<5) | ||
472 | #define SNDRV_MIXER_OSS_PRESENT_PVOLUME (1<<6) | ||
473 | #define SNDRV_MIXER_OSS_PRESENT_CSWITCH (1<<7) | ||
474 | #define SNDRV_MIXER_OSS_PRESENT_CROUTE (1<<8) | ||
475 | #define SNDRV_MIXER_OSS_PRESENT_CVOLUME (1<<9) | ||
476 | #define SNDRV_MIXER_OSS_PRESENT_CAPTURE (1<<10) | ||
477 | |||
478 | struct slot { | ||
479 | unsigned int signature; | ||
480 | unsigned int present; | ||
481 | unsigned int channels; | ||
482 | unsigned int numid[SNDRV_MIXER_OSS_ITEM_COUNT]; | ||
483 | unsigned int capture_item; | ||
484 | struct snd_mixer_oss_assign_table *assigned; | ||
485 | unsigned int allocated: 1; | ||
486 | }; | ||
487 | |||
488 | #define ID_UNKNOWN ((unsigned int)-1) | ||
489 | |||
490 | static snd_kcontrol_t *snd_mixer_oss_test_id(snd_mixer_oss_t *mixer, const char *name, int index) | ||
491 | { | ||
492 | snd_card_t * card = mixer->card; | ||
493 | snd_ctl_elem_id_t id; | ||
494 | |||
495 | memset(&id, 0, sizeof(id)); | ||
496 | id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; | ||
497 | strcpy(id.name, name); | ||
498 | id.index = index; | ||
499 | return snd_ctl_find_id(card, &id); | ||
500 | } | ||
501 | |||
502 | static void snd_mixer_oss_get_volume1_vol(snd_mixer_oss_file_t *fmixer, | ||
503 | snd_mixer_oss_slot_t *pslot, | ||
504 | unsigned int numid, | ||
505 | int *left, int *right) | ||
506 | { | ||
507 | snd_ctl_elem_info_t *uinfo; | ||
508 | snd_ctl_elem_value_t *uctl; | ||
509 | snd_kcontrol_t *kctl; | ||
510 | snd_card_t *card = fmixer->card; | ||
511 | |||
512 | if (numid == ID_UNKNOWN) | ||
513 | return; | ||
514 | down_read(&card->controls_rwsem); | ||
515 | if ((kctl = snd_ctl_find_numid(card, numid)) == NULL) { | ||
516 | up_read(&card->controls_rwsem); | ||
517 | return; | ||
518 | } | ||
519 | uinfo = kcalloc(1, sizeof(*uinfo), GFP_KERNEL); | ||
520 | uctl = kcalloc(1, sizeof(*uctl), GFP_KERNEL); | ||
521 | if (uinfo == NULL || uctl == NULL) | ||
522 | goto __unalloc; | ||
523 | snd_runtime_check(!kctl->info(kctl, uinfo), goto __unalloc); | ||
524 | snd_runtime_check(!kctl->get(kctl, uctl), goto __unalloc); | ||
525 | snd_runtime_check(uinfo->type != SNDRV_CTL_ELEM_TYPE_BOOLEAN || uinfo->value.integer.min != 0 || uinfo->value.integer.max != 1, goto __unalloc); | ||
526 | *left = snd_mixer_oss_conv1(uctl->value.integer.value[0], uinfo->value.integer.min, uinfo->value.integer.max, &pslot->volume[0]); | ||
527 | if (uinfo->count > 1) | ||
528 | *right = snd_mixer_oss_conv1(uctl->value.integer.value[1], uinfo->value.integer.min, uinfo->value.integer.max, &pslot->volume[1]); | ||
529 | __unalloc: | ||
530 | up_read(&card->controls_rwsem); | ||
531 | kfree(uctl); | ||
532 | kfree(uinfo); | ||
533 | } | ||
534 | |||
535 | static void snd_mixer_oss_get_volume1_sw(snd_mixer_oss_file_t *fmixer, | ||
536 | snd_mixer_oss_slot_t *pslot, | ||
537 | unsigned int numid, | ||
538 | int *left, int *right, | ||
539 | int route) | ||
540 | { | ||
541 | snd_ctl_elem_info_t *uinfo; | ||
542 | snd_ctl_elem_value_t *uctl; | ||
543 | snd_kcontrol_t *kctl; | ||
544 | snd_card_t *card = fmixer->card; | ||
545 | |||
546 | if (numid == ID_UNKNOWN) | ||
547 | return; | ||
548 | down_read(&card->controls_rwsem); | ||
549 | if ((kctl = snd_ctl_find_numid(card, numid)) == NULL) { | ||
550 | up_read(&card->controls_rwsem); | ||
551 | return; | ||
552 | } | ||
553 | uinfo = kcalloc(1, sizeof(*uinfo), GFP_KERNEL); | ||
554 | uctl = kcalloc(1, sizeof(*uctl), GFP_KERNEL); | ||
555 | if (uinfo == NULL || uctl == NULL) | ||
556 | goto __unalloc; | ||
557 | snd_runtime_check(!kctl->info(kctl, uinfo), goto __unalloc); | ||
558 | snd_runtime_check(!kctl->get(kctl, uctl), goto __unalloc); | ||
559 | if (!uctl->value.integer.value[0]) { | ||
560 | *left = 0; | ||
561 | if (uinfo->count == 1) | ||
562 | *right = 0; | ||
563 | } | ||
564 | if (uinfo->count > 1 && !uctl->value.integer.value[route ? 3 : 1]) | ||
565 | *right = 0; | ||
566 | __unalloc: | ||
567 | up_read(&card->controls_rwsem); | ||
568 | kfree(uctl); | ||
569 | kfree(uinfo); | ||
570 | } | ||
571 | |||
572 | static int snd_mixer_oss_get_volume1(snd_mixer_oss_file_t *fmixer, | ||
573 | snd_mixer_oss_slot_t *pslot, | ||
574 | int *left, int *right) | ||
575 | { | ||
576 | struct slot *slot = (struct slot *)pslot->private_data; | ||
577 | |||
578 | *left = *right = 100; | ||
579 | if (slot->present & SNDRV_MIXER_OSS_PRESENT_PVOLUME) { | ||
580 | snd_mixer_oss_get_volume1_vol(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_PVOLUME], left, right); | ||
581 | } else if (slot->present & SNDRV_MIXER_OSS_PRESENT_GVOLUME) { | ||
582 | snd_mixer_oss_get_volume1_vol(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_GVOLUME], left, right); | ||
583 | } else if (slot->present & SNDRV_MIXER_OSS_PRESENT_GLOBAL) { | ||
584 | snd_mixer_oss_get_volume1_vol(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_GLOBAL], left, right); | ||
585 | } | ||
586 | if (slot->present & SNDRV_MIXER_OSS_PRESENT_PSWITCH) { | ||
587 | snd_mixer_oss_get_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_PSWITCH], left, right, 0); | ||
588 | } else if (slot->present & SNDRV_MIXER_OSS_PRESENT_GSWITCH) { | ||
589 | snd_mixer_oss_get_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_GSWITCH], left, right, 0); | ||
590 | } else if (slot->present & SNDRV_MIXER_OSS_PRESENT_PROUTE) { | ||
591 | snd_mixer_oss_get_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_PROUTE], left, right, 1); | ||
592 | } else if (slot->present & SNDRV_MIXER_OSS_PRESENT_GROUTE) { | ||
593 | snd_mixer_oss_get_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_GROUTE], left, right, 1); | ||
594 | } | ||
595 | return 0; | ||
596 | } | ||
597 | |||
598 | static void snd_mixer_oss_put_volume1_vol(snd_mixer_oss_file_t *fmixer, | ||
599 | snd_mixer_oss_slot_t *pslot, | ||
600 | unsigned int numid, | ||
601 | int left, int right) | ||
602 | { | ||
603 | snd_ctl_elem_info_t *uinfo; | ||
604 | snd_ctl_elem_value_t *uctl; | ||
605 | snd_kcontrol_t *kctl; | ||
606 | snd_card_t *card = fmixer->card; | ||
607 | int res; | ||
608 | |||
609 | if (numid == ID_UNKNOWN) | ||
610 | return; | ||
611 | down_read(&card->controls_rwsem); | ||
612 | if ((kctl = snd_ctl_find_numid(card, numid)) == NULL) | ||
613 | return; | ||
614 | uinfo = kcalloc(1, sizeof(*uinfo), GFP_KERNEL); | ||
615 | uctl = kcalloc(1, sizeof(*uctl), GFP_KERNEL); | ||
616 | if (uinfo == NULL || uctl == NULL) | ||
617 | goto __unalloc; | ||
618 | snd_runtime_check(!kctl->info(kctl, uinfo), goto __unalloc); | ||
619 | snd_runtime_check(uinfo->type != SNDRV_CTL_ELEM_TYPE_BOOLEAN || uinfo->value.integer.min != 0 || uinfo->value.integer.max != 1, goto __unalloc); | ||
620 | uctl->value.integer.value[0] = snd_mixer_oss_conv2(left, uinfo->value.integer.min, uinfo->value.integer.max); | ||
621 | if (uinfo->count > 1) | ||
622 | uctl->value.integer.value[1] = snd_mixer_oss_conv2(right, uinfo->value.integer.min, uinfo->value.integer.max); | ||
623 | snd_runtime_check((res = kctl->put(kctl, uctl)) >= 0, goto __unalloc); | ||
624 | if (res > 0) | ||
625 | snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id); | ||
626 | __unalloc: | ||
627 | up_read(&card->controls_rwsem); | ||
628 | kfree(uctl); | ||
629 | kfree(uinfo); | ||
630 | } | ||
631 | |||
632 | static void snd_mixer_oss_put_volume1_sw(snd_mixer_oss_file_t *fmixer, | ||
633 | snd_mixer_oss_slot_t *pslot, | ||
634 | unsigned int numid, | ||
635 | int left, int right, | ||
636 | int route) | ||
637 | { | ||
638 | snd_ctl_elem_info_t *uinfo; | ||
639 | snd_ctl_elem_value_t *uctl; | ||
640 | snd_kcontrol_t *kctl; | ||
641 | snd_card_t *card = fmixer->card; | ||
642 | int res; | ||
643 | |||
644 | if (numid == ID_UNKNOWN) | ||
645 | return; | ||
646 | down_read(&card->controls_rwsem); | ||
647 | if ((kctl = snd_ctl_find_numid(card, numid)) == NULL) { | ||
648 | up_read(&fmixer->card->controls_rwsem); | ||
649 | return; | ||
650 | } | ||
651 | uinfo = kcalloc(1, sizeof(*uinfo), GFP_KERNEL); | ||
652 | uctl = kcalloc(1, sizeof(*uctl), GFP_KERNEL); | ||
653 | if (uinfo == NULL || uctl == NULL) | ||
654 | goto __unalloc; | ||
655 | snd_runtime_check(!kctl->info(kctl, uinfo), goto __unalloc); | ||
656 | if (uinfo->count > 1) { | ||
657 | uctl->value.integer.value[0] = left > 0 ? 1 : 0; | ||
658 | uctl->value.integer.value[route ? 3 : 1] = right > 0 ? 1 : 0; | ||
659 | if (route) { | ||
660 | uctl->value.integer.value[1] = | ||
661 | uctl->value.integer.value[2] = 0; | ||
662 | } | ||
663 | } else { | ||
664 | uctl->value.integer.value[0] = (left > 0 || right > 0) ? 1 : 0; | ||
665 | } | ||
666 | snd_runtime_check((res = kctl->put(kctl, uctl)) >= 0, goto __unalloc); | ||
667 | if (res > 0) | ||
668 | snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id); | ||
669 | __unalloc: | ||
670 | up_read(&card->controls_rwsem); | ||
671 | kfree(uctl); | ||
672 | kfree(uinfo); | ||
673 | } | ||
674 | |||
675 | static int snd_mixer_oss_put_volume1(snd_mixer_oss_file_t *fmixer, | ||
676 | snd_mixer_oss_slot_t *pslot, | ||
677 | int left, int right) | ||
678 | { | ||
679 | struct slot *slot = (struct slot *)pslot->private_data; | ||
680 | |||
681 | if (slot->present & SNDRV_MIXER_OSS_PRESENT_PVOLUME) { | ||
682 | snd_mixer_oss_put_volume1_vol(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_PVOLUME], left, right); | ||
683 | if (slot->present & SNDRV_MIXER_OSS_PRESENT_CVOLUME) | ||
684 | snd_mixer_oss_put_volume1_vol(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_CVOLUME], left, right); | ||
685 | } else if (slot->present & SNDRV_MIXER_OSS_PRESENT_GVOLUME) { | ||
686 | snd_mixer_oss_put_volume1_vol(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_GVOLUME], left, right); | ||
687 | } else if (slot->present & SNDRV_MIXER_OSS_PRESENT_GLOBAL) { | ||
688 | snd_mixer_oss_put_volume1_vol(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_GLOBAL], left, right); | ||
689 | } | ||
690 | if (left || right) { | ||
691 | if (slot->present & SNDRV_MIXER_OSS_PRESENT_PSWITCH) | ||
692 | snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_PSWITCH], left, right, 0); | ||
693 | if (slot->present & SNDRV_MIXER_OSS_PRESENT_GSWITCH) | ||
694 | snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_GSWITCH], left, right, 0); | ||
695 | if (slot->present & SNDRV_MIXER_OSS_PRESENT_PROUTE) | ||
696 | snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_PROUTE], left, right, 1); | ||
697 | if (slot->present & SNDRV_MIXER_OSS_PRESENT_GROUTE) | ||
698 | snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_GROUTE], left, right, 1); | ||
699 | } else { | ||
700 | if (slot->present & SNDRV_MIXER_OSS_PRESENT_PSWITCH) { | ||
701 | snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_PSWITCH], left, right, 0); | ||
702 | } else if (slot->present & SNDRV_MIXER_OSS_PRESENT_GSWITCH) { | ||
703 | snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_GSWITCH], left, right, 0); | ||
704 | } else if (slot->present & SNDRV_MIXER_OSS_PRESENT_PROUTE) { | ||
705 | snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_PROUTE], left, right, 1); | ||
706 | } else if (slot->present & SNDRV_MIXER_OSS_PRESENT_GROUTE) { | ||
707 | snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_GROUTE], left, right, 1); | ||
708 | } | ||
709 | } | ||
710 | return 0; | ||
711 | } | ||
712 | |||
713 | static int snd_mixer_oss_get_recsrc1_sw(snd_mixer_oss_file_t *fmixer, | ||
714 | snd_mixer_oss_slot_t *pslot, | ||
715 | int *active) | ||
716 | { | ||
717 | struct slot *slot = (struct slot *)pslot->private_data; | ||
718 | int left, right; | ||
719 | |||
720 | left = right = 1; | ||
721 | snd_mixer_oss_get_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_CSWITCH], &left, &right, 0); | ||
722 | *active = (left || right) ? 1 : 0; | ||
723 | return 0; | ||
724 | } | ||
725 | |||
726 | static int snd_mixer_oss_get_recsrc1_route(snd_mixer_oss_file_t *fmixer, | ||
727 | snd_mixer_oss_slot_t *pslot, | ||
728 | int *active) | ||
729 | { | ||
730 | struct slot *slot = (struct slot *)pslot->private_data; | ||
731 | int left, right; | ||
732 | |||
733 | left = right = 1; | ||
734 | snd_mixer_oss_get_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_CROUTE], &left, &right, 1); | ||
735 | *active = (left || right) ? 1 : 0; | ||
736 | return 0; | ||
737 | } | ||
738 | |||
739 | static int snd_mixer_oss_put_recsrc1_sw(snd_mixer_oss_file_t *fmixer, | ||
740 | snd_mixer_oss_slot_t *pslot, | ||
741 | int active) | ||
742 | { | ||
743 | struct slot *slot = (struct slot *)pslot->private_data; | ||
744 | |||
745 | snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_CSWITCH], active, active, 0); | ||
746 | return 0; | ||
747 | } | ||
748 | |||
749 | static int snd_mixer_oss_put_recsrc1_route(snd_mixer_oss_file_t *fmixer, | ||
750 | snd_mixer_oss_slot_t *pslot, | ||
751 | int active) | ||
752 | { | ||
753 | struct slot *slot = (struct slot *)pslot->private_data; | ||
754 | |||
755 | snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_CROUTE], active, active, 1); | ||
756 | return 0; | ||
757 | } | ||
758 | |||
759 | static int snd_mixer_oss_get_recsrc2(snd_mixer_oss_file_t *fmixer, unsigned int *active_index) | ||
760 | { | ||
761 | snd_card_t *card = fmixer->card; | ||
762 | snd_mixer_oss_t *mixer = fmixer->mixer; | ||
763 | snd_kcontrol_t *kctl; | ||
764 | snd_mixer_oss_slot_t *pslot; | ||
765 | struct slot *slot; | ||
766 | snd_ctl_elem_info_t *uinfo; | ||
767 | snd_ctl_elem_value_t *uctl; | ||
768 | int err, idx; | ||
769 | |||
770 | uinfo = kcalloc(1, sizeof(*uinfo), GFP_KERNEL); | ||
771 | uctl = kcalloc(1, sizeof(*uctl), GFP_KERNEL); | ||
772 | if (uinfo == NULL || uctl == NULL) { | ||
773 | err = -ENOMEM; | ||
774 | goto __unlock; | ||
775 | } | ||
776 | down_read(&card->controls_rwsem); | ||
777 | kctl = snd_mixer_oss_test_id(mixer, "Capture Source", 0); | ||
778 | snd_runtime_check(kctl != NULL, err = -ENOENT; goto __unlock); | ||
779 | snd_runtime_check(!(err = kctl->info(kctl, uinfo)), goto __unlock); | ||
780 | snd_runtime_check(!(err = kctl->get(kctl, uctl)), goto __unlock); | ||
781 | for (idx = 0; idx < 32; idx++) { | ||
782 | if (!(mixer->mask_recsrc & (1 << idx))) | ||
783 | continue; | ||
784 | pslot = &mixer->slots[idx]; | ||
785 | slot = (struct slot *)pslot->private_data; | ||
786 | if (slot->signature != SNDRV_MIXER_OSS_SIGNATURE) | ||
787 | continue; | ||
788 | if (!(slot->present & SNDRV_MIXER_OSS_PRESENT_CAPTURE)) | ||
789 | continue; | ||
790 | if (slot->capture_item == uctl->value.enumerated.item[0]) { | ||
791 | *active_index = idx; | ||
792 | break; | ||
793 | } | ||
794 | } | ||
795 | err = 0; | ||
796 | __unlock: | ||
797 | up_read(&card->controls_rwsem); | ||
798 | kfree(uctl); | ||
799 | kfree(uinfo); | ||
800 | return err; | ||
801 | } | ||
802 | |||
803 | static int snd_mixer_oss_put_recsrc2(snd_mixer_oss_file_t *fmixer, unsigned int active_index) | ||
804 | { | ||
805 | snd_card_t *card = fmixer->card; | ||
806 | snd_mixer_oss_t *mixer = fmixer->mixer; | ||
807 | snd_kcontrol_t *kctl; | ||
808 | snd_mixer_oss_slot_t *pslot; | ||
809 | struct slot *slot = NULL; | ||
810 | snd_ctl_elem_info_t *uinfo; | ||
811 | snd_ctl_elem_value_t *uctl; | ||
812 | int err; | ||
813 | unsigned int idx; | ||
814 | |||
815 | uinfo = kcalloc(1, sizeof(*uinfo), GFP_KERNEL); | ||
816 | uctl = kcalloc(1, sizeof(*uctl), GFP_KERNEL); | ||
817 | if (uinfo == NULL || uctl == NULL) { | ||
818 | err = -ENOMEM; | ||
819 | goto __unlock; | ||
820 | } | ||
821 | down_read(&card->controls_rwsem); | ||
822 | kctl = snd_mixer_oss_test_id(mixer, "Capture Source", 0); | ||
823 | snd_runtime_check(kctl != NULL, err = -ENOENT; goto __unlock); | ||
824 | snd_runtime_check(!(err = kctl->info(kctl, uinfo)), goto __unlock); | ||
825 | for (idx = 0; idx < 32; idx++) { | ||
826 | if (!(mixer->mask_recsrc & (1 << idx))) | ||
827 | continue; | ||
828 | pslot = &mixer->slots[idx]; | ||
829 | slot = (struct slot *)pslot->private_data; | ||
830 | if (slot->signature != SNDRV_MIXER_OSS_SIGNATURE) | ||
831 | continue; | ||
832 | if (!(slot->present & SNDRV_MIXER_OSS_PRESENT_CAPTURE)) | ||
833 | continue; | ||
834 | if (idx == active_index) | ||
835 | break; | ||
836 | slot = NULL; | ||
837 | } | ||
838 | snd_runtime_check(slot != NULL, goto __unlock); | ||
839 | for (idx = 0; idx < uinfo->count; idx++) | ||
840 | uctl->value.enumerated.item[idx] = slot->capture_item; | ||
841 | snd_runtime_check((err = kctl->put(kctl, uctl)) >= 0, ); | ||
842 | if (err > 0) | ||
843 | snd_ctl_notify(fmixer->card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id); | ||
844 | err = 0; | ||
845 | __unlock: | ||
846 | up_read(&card->controls_rwsem); | ||
847 | kfree(uctl); | ||
848 | kfree(uinfo); | ||
849 | return err; | ||
850 | } | ||
851 | |||
852 | struct snd_mixer_oss_assign_table { | ||
853 | int oss_id; | ||
854 | const char *name; | ||
855 | int index; | ||
856 | }; | ||
857 | |||
858 | static int snd_mixer_oss_build_test(snd_mixer_oss_t *mixer, struct slot *slot, const char *name, int index, int item) | ||
859 | { | ||
860 | snd_ctl_elem_info_t *info; | ||
861 | snd_kcontrol_t *kcontrol; | ||
862 | snd_card_t *card = mixer->card; | ||
863 | int err; | ||
864 | |||
865 | down_read(&card->controls_rwsem); | ||
866 | kcontrol = snd_mixer_oss_test_id(mixer, name, index); | ||
867 | if (kcontrol == NULL) { | ||
868 | up_read(&card->controls_rwsem); | ||
869 | return 0; | ||
870 | } | ||
871 | info = kmalloc(sizeof(*info), GFP_KERNEL); | ||
872 | if (! info) { | ||
873 | up_read(&card->controls_rwsem); | ||
874 | return -ENOMEM; | ||
875 | } | ||
876 | if ((err = kcontrol->info(kcontrol, info)) < 0) { | ||
877 | up_read(&card->controls_rwsem); | ||
878 | kfree(info); | ||
879 | return err; | ||
880 | } | ||
881 | slot->numid[item] = kcontrol->id.numid; | ||
882 | up_read(&card->controls_rwsem); | ||
883 | if (info->count > slot->channels) | ||
884 | slot->channels = info->count; | ||
885 | slot->present |= 1 << item; | ||
886 | kfree(info); | ||
887 | return 0; | ||
888 | } | ||
889 | |||
890 | static void snd_mixer_oss_slot_free(snd_mixer_oss_slot_t *chn) | ||
891 | { | ||
892 | struct slot *p = (struct slot *)chn->private_data; | ||
893 | if (p) { | ||
894 | if (p->allocated && p->assigned) { | ||
895 | kfree(p->assigned->name); | ||
896 | kfree(p->assigned); | ||
897 | } | ||
898 | kfree(p); | ||
899 | } | ||
900 | } | ||
901 | |||
902 | static void mixer_slot_clear(snd_mixer_oss_slot_t *rslot) | ||
903 | { | ||
904 | int idx = rslot->number; /* remember this */ | ||
905 | if (rslot->private_free) | ||
906 | rslot->private_free(rslot); | ||
907 | memset(rslot, 0, sizeof(*rslot)); | ||
908 | rslot->number = idx; | ||
909 | } | ||
910 | |||
911 | /* | ||
912 | * build an OSS mixer element. | ||
913 | * ptr_allocated means the entry is dynamically allocated (change via proc file). | ||
914 | * when replace_old = 1, the old entry is replaced with the new one. | ||
915 | */ | ||
916 | static int snd_mixer_oss_build_input(snd_mixer_oss_t *mixer, struct snd_mixer_oss_assign_table *ptr, int ptr_allocated, int replace_old) | ||
917 | { | ||
918 | struct slot slot; | ||
919 | struct slot *pslot; | ||
920 | snd_kcontrol_t *kctl; | ||
921 | snd_mixer_oss_slot_t *rslot; | ||
922 | char str[64]; | ||
923 | |||
924 | /* check if already assigned */ | ||
925 | if (mixer->slots[ptr->oss_id].get_volume && ! replace_old) | ||
926 | return 0; | ||
927 | |||
928 | memset(&slot, 0, sizeof(slot)); | ||
929 | memset(slot.numid, 0xff, sizeof(slot.numid)); /* ID_UNKNOWN */ | ||
930 | if (snd_mixer_oss_build_test(mixer, &slot, ptr->name, ptr->index, | ||
931 | SNDRV_MIXER_OSS_ITEM_GLOBAL)) | ||
932 | return 0; | ||
933 | sprintf(str, "%s Switch", ptr->name); | ||
934 | if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index, | ||
935 | SNDRV_MIXER_OSS_ITEM_GSWITCH)) | ||
936 | return 0; | ||
937 | sprintf(str, "%s Route", ptr->name); | ||
938 | if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index, | ||
939 | SNDRV_MIXER_OSS_ITEM_GROUTE)) | ||
940 | return 0; | ||
941 | sprintf(str, "%s Volume", ptr->name); | ||
942 | if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index, | ||
943 | SNDRV_MIXER_OSS_ITEM_GVOLUME)) | ||
944 | return 0; | ||
945 | sprintf(str, "%s Playback Switch", ptr->name); | ||
946 | if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index, | ||
947 | SNDRV_MIXER_OSS_ITEM_PSWITCH)) | ||
948 | return 0; | ||
949 | sprintf(str, "%s Playback Route", ptr->name); | ||
950 | if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index, | ||
951 | SNDRV_MIXER_OSS_ITEM_PROUTE)) | ||
952 | return 0; | ||
953 | sprintf(str, "%s Playback Volume", ptr->name); | ||
954 | if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index, | ||
955 | SNDRV_MIXER_OSS_ITEM_PVOLUME)) | ||
956 | return 0; | ||
957 | sprintf(str, "%s Capture Switch", ptr->name); | ||
958 | if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index, | ||
959 | SNDRV_MIXER_OSS_ITEM_CSWITCH)) | ||
960 | return 0; | ||
961 | sprintf(str, "%s Capture Route", ptr->name); | ||
962 | if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index, | ||
963 | SNDRV_MIXER_OSS_ITEM_CROUTE)) | ||
964 | return 0; | ||
965 | sprintf(str, "%s Capture Volume", ptr->name); | ||
966 | if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index, | ||
967 | SNDRV_MIXER_OSS_ITEM_CVOLUME)) | ||
968 | return 0; | ||
969 | down_read(&mixer->card->controls_rwsem); | ||
970 | if (ptr->index == 0 && (kctl = snd_mixer_oss_test_id(mixer, "Capture Source", 0)) != NULL) { | ||
971 | snd_ctl_elem_info_t *uinfo; | ||
972 | |||
973 | uinfo = kmalloc(sizeof(*uinfo), GFP_KERNEL); | ||
974 | if (! uinfo) { | ||
975 | up_read(&mixer->card->controls_rwsem); | ||
976 | return -ENOMEM; | ||
977 | } | ||
978 | |||
979 | memset(uinfo, 0, sizeof(*uinfo)); | ||
980 | if (kctl->info(kctl, uinfo)) { | ||
981 | up_read(&mixer->card->controls_rwsem); | ||
982 | return 0; | ||
983 | } | ||
984 | strcpy(str, ptr->name); | ||
985 | if (!strcmp(str, "Master")) | ||
986 | strcpy(str, "Mix"); | ||
987 | if (!strcmp(str, "Master Mono")) | ||
988 | strcpy(str, "Mix Mono"); | ||
989 | slot.capture_item = 0; | ||
990 | if (!strcmp(uinfo->value.enumerated.name, str)) { | ||
991 | slot.present |= SNDRV_MIXER_OSS_PRESENT_CAPTURE; | ||
992 | } else { | ||
993 | for (slot.capture_item = 1; slot.capture_item < uinfo->value.enumerated.items; slot.capture_item++) { | ||
994 | uinfo->value.enumerated.item = slot.capture_item; | ||
995 | if (kctl->info(kctl, uinfo)) { | ||
996 | up_read(&mixer->card->controls_rwsem); | ||
997 | return 0; | ||
998 | } | ||
999 | if (!strcmp(uinfo->value.enumerated.name, str)) { | ||
1000 | slot.present |= SNDRV_MIXER_OSS_PRESENT_CAPTURE; | ||
1001 | break; | ||
1002 | } | ||
1003 | } | ||
1004 | } | ||
1005 | kfree(uinfo); | ||
1006 | } | ||
1007 | up_read(&mixer->card->controls_rwsem); | ||
1008 | if (slot.present != 0) { | ||
1009 | pslot = (struct slot *)kmalloc(sizeof(slot), GFP_KERNEL); | ||
1010 | snd_runtime_check(pslot != NULL, return -ENOMEM); | ||
1011 | *pslot = slot; | ||
1012 | pslot->signature = SNDRV_MIXER_OSS_SIGNATURE; | ||
1013 | pslot->assigned = ptr; | ||
1014 | pslot->allocated = ptr_allocated; | ||
1015 | rslot = &mixer->slots[ptr->oss_id]; | ||
1016 | mixer_slot_clear(rslot); | ||
1017 | rslot->stereo = slot.channels > 1 ? 1 : 0; | ||
1018 | rslot->get_volume = snd_mixer_oss_get_volume1; | ||
1019 | rslot->put_volume = snd_mixer_oss_put_volume1; | ||
1020 | /* note: ES18xx have both Capture Source and XX Capture Volume !!! */ | ||
1021 | if (slot.present & SNDRV_MIXER_OSS_PRESENT_CSWITCH) { | ||
1022 | rslot->get_recsrc = snd_mixer_oss_get_recsrc1_sw; | ||
1023 | rslot->put_recsrc = snd_mixer_oss_put_recsrc1_sw; | ||
1024 | } else if (slot.present & SNDRV_MIXER_OSS_PRESENT_CROUTE) { | ||
1025 | rslot->get_recsrc = snd_mixer_oss_get_recsrc1_route; | ||
1026 | rslot->put_recsrc = snd_mixer_oss_put_recsrc1_route; | ||
1027 | } else if (slot.present & SNDRV_MIXER_OSS_PRESENT_CAPTURE) { | ||
1028 | mixer->mask_recsrc |= 1 << ptr->oss_id; | ||
1029 | } | ||
1030 | rslot->private_data = pslot; | ||
1031 | rslot->private_free = snd_mixer_oss_slot_free; | ||
1032 | return 1; | ||
1033 | } | ||
1034 | return 0; | ||
1035 | } | ||
1036 | |||
1037 | /* | ||
1038 | */ | ||
1039 | #define MIXER_VOL(name) [SOUND_MIXER_##name] = #name | ||
1040 | static char *oss_mixer_names[SNDRV_OSS_MAX_MIXERS] = { | ||
1041 | MIXER_VOL(VOLUME), | ||
1042 | MIXER_VOL(BASS), | ||
1043 | MIXER_VOL(TREBLE), | ||
1044 | MIXER_VOL(SYNTH), | ||
1045 | MIXER_VOL(PCM), | ||
1046 | MIXER_VOL(SPEAKER), | ||
1047 | MIXER_VOL(LINE), | ||
1048 | MIXER_VOL(MIC), | ||
1049 | MIXER_VOL(CD), | ||
1050 | MIXER_VOL(IMIX), | ||
1051 | MIXER_VOL(ALTPCM), | ||
1052 | MIXER_VOL(RECLEV), | ||
1053 | MIXER_VOL(IGAIN), | ||
1054 | MIXER_VOL(OGAIN), | ||
1055 | MIXER_VOL(LINE1), | ||
1056 | MIXER_VOL(LINE2), | ||
1057 | MIXER_VOL(LINE3), | ||
1058 | MIXER_VOL(DIGITAL1), | ||
1059 | MIXER_VOL(DIGITAL2), | ||
1060 | MIXER_VOL(DIGITAL3), | ||
1061 | MIXER_VOL(PHONEIN), | ||
1062 | MIXER_VOL(PHONEOUT), | ||
1063 | MIXER_VOL(VIDEO), | ||
1064 | MIXER_VOL(RADIO), | ||
1065 | MIXER_VOL(MONITOR), | ||
1066 | }; | ||
1067 | |||
1068 | /* | ||
1069 | * /proc interface | ||
1070 | */ | ||
1071 | |||
1072 | static void snd_mixer_oss_proc_read(snd_info_entry_t *entry, | ||
1073 | snd_info_buffer_t * buffer) | ||
1074 | { | ||
1075 | snd_mixer_oss_t *mixer = entry->private_data; | ||
1076 | int i; | ||
1077 | |||
1078 | down(&mixer->reg_mutex); | ||
1079 | for (i = 0; i < SNDRV_OSS_MAX_MIXERS; i++) { | ||
1080 | struct slot *p; | ||
1081 | |||
1082 | if (! oss_mixer_names[i]) | ||
1083 | continue; | ||
1084 | p = (struct slot *)mixer->slots[i].private_data; | ||
1085 | snd_iprintf(buffer, "%s ", oss_mixer_names[i]); | ||
1086 | if (p && p->assigned) | ||
1087 | snd_iprintf(buffer, "\"%s\" %d\n", | ||
1088 | p->assigned->name, | ||
1089 | p->assigned->index); | ||
1090 | else | ||
1091 | snd_iprintf(buffer, "\"\" 0\n"); | ||
1092 | } | ||
1093 | up(&mixer->reg_mutex); | ||
1094 | } | ||
1095 | |||
1096 | static void snd_mixer_oss_proc_write(snd_info_entry_t *entry, | ||
1097 | snd_info_buffer_t * buffer) | ||
1098 | { | ||
1099 | snd_mixer_oss_t *mixer = entry->private_data; | ||
1100 | char line[128], str[32], idxstr[16], *cptr; | ||
1101 | int ch, idx; | ||
1102 | struct snd_mixer_oss_assign_table *tbl; | ||
1103 | struct slot *slot; | ||
1104 | |||
1105 | while (!snd_info_get_line(buffer, line, sizeof(line))) { | ||
1106 | cptr = snd_info_get_str(str, line, sizeof(str)); | ||
1107 | for (ch = 0; ch < SNDRV_OSS_MAX_MIXERS; ch++) | ||
1108 | if (oss_mixer_names[ch] && strcmp(oss_mixer_names[ch], str) == 0) | ||
1109 | break; | ||
1110 | if (ch >= SNDRV_OSS_MAX_MIXERS) { | ||
1111 | snd_printk(KERN_ERR "mixer_oss: invalid OSS volume '%s'\n", str); | ||
1112 | continue; | ||
1113 | } | ||
1114 | cptr = snd_info_get_str(str, cptr, sizeof(str)); | ||
1115 | if (! *str) { | ||
1116 | /* remove the entry */ | ||
1117 | down(&mixer->reg_mutex); | ||
1118 | mixer_slot_clear(&mixer->slots[ch]); | ||
1119 | up(&mixer->reg_mutex); | ||
1120 | continue; | ||
1121 | } | ||
1122 | snd_info_get_str(idxstr, cptr, sizeof(idxstr)); | ||
1123 | idx = simple_strtoul(idxstr, NULL, 10); | ||
1124 | if (idx >= 0x4000) { /* too big */ | ||
1125 | snd_printk(KERN_ERR "mixer_oss: invalid index %d\n", idx); | ||
1126 | continue; | ||
1127 | } | ||
1128 | down(&mixer->reg_mutex); | ||
1129 | slot = (struct slot *)mixer->slots[ch].private_data; | ||
1130 | if (slot && slot->assigned && | ||
1131 | slot->assigned->index == idx && ! strcmp(slot->assigned->name, str)) | ||
1132 | /* not changed */ | ||
1133 | goto __unlock; | ||
1134 | tbl = kmalloc(sizeof(*tbl), GFP_KERNEL); | ||
1135 | if (! tbl) { | ||
1136 | snd_printk(KERN_ERR "mixer_oss: no memory\n"); | ||
1137 | goto __unlock; | ||
1138 | } | ||
1139 | tbl->oss_id = ch; | ||
1140 | tbl->name = snd_kmalloc_strdup(str, GFP_KERNEL); | ||
1141 | if (! tbl->name) { | ||
1142 | kfree(tbl); | ||
1143 | goto __unlock; | ||
1144 | } | ||
1145 | tbl->index = idx; | ||
1146 | if (snd_mixer_oss_build_input(mixer, tbl, 1, 1) <= 0) { | ||
1147 | kfree(tbl->name); | ||
1148 | kfree(tbl); | ||
1149 | } | ||
1150 | __unlock: | ||
1151 | up(&mixer->reg_mutex); | ||
1152 | } | ||
1153 | } | ||
1154 | |||
1155 | static void snd_mixer_oss_proc_init(snd_mixer_oss_t *mixer) | ||
1156 | { | ||
1157 | snd_info_entry_t *entry; | ||
1158 | |||
1159 | entry = snd_info_create_card_entry(mixer->card, "oss_mixer", | ||
1160 | mixer->card->proc_root); | ||
1161 | if (! entry) | ||
1162 | return; | ||
1163 | entry->content = SNDRV_INFO_CONTENT_TEXT; | ||
1164 | entry->mode = S_IFREG | S_IRUGO | S_IWUSR; | ||
1165 | entry->c.text.read_size = 8192; | ||
1166 | entry->c.text.read = snd_mixer_oss_proc_read; | ||
1167 | entry->c.text.write_size = 8192; | ||
1168 | entry->c.text.write = snd_mixer_oss_proc_write; | ||
1169 | entry->private_data = mixer; | ||
1170 | if (snd_info_register(entry) < 0) { | ||
1171 | snd_info_free_entry(entry); | ||
1172 | entry = NULL; | ||
1173 | } | ||
1174 | mixer->proc_entry = entry; | ||
1175 | } | ||
1176 | |||
1177 | static void snd_mixer_oss_proc_done(snd_mixer_oss_t *mixer) | ||
1178 | { | ||
1179 | if (mixer->proc_entry) { | ||
1180 | snd_info_unregister(mixer->proc_entry); | ||
1181 | mixer->proc_entry = NULL; | ||
1182 | } | ||
1183 | } | ||
1184 | |||
1185 | static void snd_mixer_oss_build(snd_mixer_oss_t *mixer) | ||
1186 | { | ||
1187 | static struct snd_mixer_oss_assign_table table[] = { | ||
1188 | { SOUND_MIXER_VOLUME, "Master", 0 }, | ||
1189 | { SOUND_MIXER_VOLUME, "Front", 0 }, /* fallback */ | ||
1190 | { SOUND_MIXER_BASS, "Tone Control - Bass", 0 }, | ||
1191 | { SOUND_MIXER_TREBLE, "Tone Control - Treble", 0 }, | ||
1192 | { SOUND_MIXER_SYNTH, "Synth", 0 }, | ||
1193 | { SOUND_MIXER_SYNTH, "FM", 0 }, /* fallback */ | ||
1194 | { SOUND_MIXER_SYNTH, "Music", 0 }, /* fallback */ | ||
1195 | { SOUND_MIXER_PCM, "PCM", 0 }, | ||
1196 | { SOUND_MIXER_SPEAKER, "PC Speaker", 0 }, | ||
1197 | { SOUND_MIXER_LINE, "Line", 0 }, | ||
1198 | { SOUND_MIXER_MIC, "Mic", 0 }, | ||
1199 | { SOUND_MIXER_CD, "CD", 0 }, | ||
1200 | { SOUND_MIXER_IMIX, "Monitor Mix", 0 }, | ||
1201 | { SOUND_MIXER_ALTPCM, "PCM", 1 }, | ||
1202 | { SOUND_MIXER_ALTPCM, "Headphone", 0 }, /* fallback */ | ||
1203 | { SOUND_MIXER_ALTPCM, "Wave", 0 }, /* fallback */ | ||
1204 | { SOUND_MIXER_RECLEV, "-- nothing --", 0 }, | ||
1205 | { SOUND_MIXER_IGAIN, "Capture", 0 }, | ||
1206 | { SOUND_MIXER_OGAIN, "Playback", 0 }, | ||
1207 | { SOUND_MIXER_LINE1, "Aux", 0 }, | ||
1208 | { SOUND_MIXER_LINE2, "Aux", 1 }, | ||
1209 | { SOUND_MIXER_LINE3, "Aux", 2 }, | ||
1210 | { SOUND_MIXER_DIGITAL1, "Digital", 0 }, | ||
1211 | { SOUND_MIXER_DIGITAL1, "IEC958", 0 }, /* fallback */ | ||
1212 | { SOUND_MIXER_DIGITAL1, "IEC958 Optical", 0 }, /* fallback */ | ||
1213 | { SOUND_MIXER_DIGITAL1, "IEC958 Coaxial", 0 }, /* fallback */ | ||
1214 | { SOUND_MIXER_DIGITAL2, "Digital", 1 }, | ||
1215 | { SOUND_MIXER_DIGITAL3, "Digital", 2 }, | ||
1216 | { SOUND_MIXER_PHONEIN, "Phone", 0 }, | ||
1217 | { SOUND_MIXER_PHONEOUT, "Master Mono", 0 }, | ||
1218 | { SOUND_MIXER_PHONEOUT, "Phone", 0 }, /* fallback */ | ||
1219 | { SOUND_MIXER_VIDEO, "Video", 0 }, | ||
1220 | { SOUND_MIXER_RADIO, "Radio", 0 }, | ||
1221 | { SOUND_MIXER_MONITOR, "Monitor", 0 } | ||
1222 | }; | ||
1223 | unsigned int idx; | ||
1224 | |||
1225 | for (idx = 0; idx < ARRAY_SIZE(table); idx++) | ||
1226 | snd_mixer_oss_build_input(mixer, &table[idx], 0, 0); | ||
1227 | if (mixer->mask_recsrc) { | ||
1228 | mixer->get_recsrc = snd_mixer_oss_get_recsrc2; | ||
1229 | mixer->put_recsrc = snd_mixer_oss_put_recsrc2; | ||
1230 | } | ||
1231 | } | ||
1232 | |||
1233 | /* | ||
1234 | * | ||
1235 | */ | ||
1236 | |||
1237 | static int snd_mixer_oss_free1(void *private) | ||
1238 | { | ||
1239 | snd_mixer_oss_t *mixer = private; | ||
1240 | snd_card_t * card; | ||
1241 | int idx; | ||
1242 | |||
1243 | snd_assert(mixer != NULL, return -ENXIO); | ||
1244 | card = mixer->card; | ||
1245 | snd_assert(mixer == card->mixer_oss, return -ENXIO); | ||
1246 | card->mixer_oss = NULL; | ||
1247 | for (idx = 0; idx < SNDRV_OSS_MAX_MIXERS; idx++) { | ||
1248 | snd_mixer_oss_slot_t *chn = &mixer->slots[idx]; | ||
1249 | if (chn->private_free) | ||
1250 | chn->private_free(chn); | ||
1251 | } | ||
1252 | kfree(mixer); | ||
1253 | return 0; | ||
1254 | } | ||
1255 | |||
1256 | static int snd_mixer_oss_notify_handler(snd_card_t * card, int cmd) | ||
1257 | { | ||
1258 | snd_mixer_oss_t *mixer; | ||
1259 | |||
1260 | if (cmd == SND_MIXER_OSS_NOTIFY_REGISTER) { | ||
1261 | char name[128]; | ||
1262 | int idx, err; | ||
1263 | |||
1264 | mixer = kcalloc(2, sizeof(*mixer), GFP_KERNEL); | ||
1265 | if (mixer == NULL) | ||
1266 | return -ENOMEM; | ||
1267 | init_MUTEX(&mixer->reg_mutex); | ||
1268 | sprintf(name, "mixer%i%i", card->number, 0); | ||
1269 | if ((err = snd_register_oss_device(SNDRV_OSS_DEVICE_TYPE_MIXER, | ||
1270 | card, 0, | ||
1271 | &snd_mixer_oss_reg, | ||
1272 | name)) < 0) { | ||
1273 | snd_printk("unable to register OSS mixer device %i:%i\n", card->number, 0); | ||
1274 | kfree(mixer); | ||
1275 | return err; | ||
1276 | } | ||
1277 | mixer->oss_dev_alloc = 1; | ||
1278 | mixer->card = card; | ||
1279 | if (*card->mixername) | ||
1280 | strlcpy(mixer->name, card->mixername, sizeof(mixer->name)); | ||
1281 | else | ||
1282 | strlcpy(mixer->name, name, sizeof(mixer->name)); | ||
1283 | #ifdef SNDRV_OSS_INFO_DEV_MIXERS | ||
1284 | snd_oss_info_register(SNDRV_OSS_INFO_DEV_MIXERS, | ||
1285 | card->number, | ||
1286 | mixer->name); | ||
1287 | #endif | ||
1288 | for (idx = 0; idx < SNDRV_OSS_MAX_MIXERS; idx++) | ||
1289 | mixer->slots[idx].number = idx; | ||
1290 | card->mixer_oss = mixer; | ||
1291 | snd_mixer_oss_build(mixer); | ||
1292 | snd_mixer_oss_proc_init(mixer); | ||
1293 | } else if (cmd == SND_MIXER_OSS_NOTIFY_DISCONNECT) { | ||
1294 | mixer = card->mixer_oss; | ||
1295 | if (mixer == NULL || !mixer->oss_dev_alloc) | ||
1296 | return 0; | ||
1297 | snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_MIXER, mixer->card, 0); | ||
1298 | mixer->oss_dev_alloc = 0; | ||
1299 | } else { /* free */ | ||
1300 | mixer = card->mixer_oss; | ||
1301 | if (mixer == NULL) | ||
1302 | return 0; | ||
1303 | #ifdef SNDRV_OSS_INFO_DEV_MIXERS | ||
1304 | snd_oss_info_unregister(SNDRV_OSS_INFO_DEV_MIXERS, mixer->card->number); | ||
1305 | #endif | ||
1306 | if (mixer->oss_dev_alloc) | ||
1307 | snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_MIXER, mixer->card, 0); | ||
1308 | snd_mixer_oss_proc_done(mixer); | ||
1309 | return snd_mixer_oss_free1(mixer); | ||
1310 | } | ||
1311 | return 0; | ||
1312 | } | ||
1313 | |||
1314 | static int __init alsa_mixer_oss_init(void) | ||
1315 | { | ||
1316 | int idx; | ||
1317 | |||
1318 | snd_mixer_oss_notify_callback = snd_mixer_oss_notify_handler; | ||
1319 | for (idx = 0; idx < SNDRV_CARDS; idx++) { | ||
1320 | if (snd_cards[idx]) | ||
1321 | snd_mixer_oss_notify_handler(snd_cards[idx], SND_MIXER_OSS_NOTIFY_REGISTER); | ||
1322 | } | ||
1323 | return 0; | ||
1324 | } | ||
1325 | |||
1326 | static void __exit alsa_mixer_oss_exit(void) | ||
1327 | { | ||
1328 | int idx; | ||
1329 | |||
1330 | snd_mixer_oss_notify_callback = NULL; | ||
1331 | for (idx = 0; idx < SNDRV_CARDS; idx++) { | ||
1332 | if (snd_cards[idx]) | ||
1333 | snd_mixer_oss_notify_handler(snd_cards[idx], SND_MIXER_OSS_NOTIFY_FREE); | ||
1334 | } | ||
1335 | } | ||
1336 | |||
1337 | module_init(alsa_mixer_oss_init) | ||
1338 | module_exit(alsa_mixer_oss_exit) | ||
1339 | |||
1340 | EXPORT_SYMBOL(snd_mixer_oss_ioctl_card); | ||
diff --git a/sound/core/oss/mulaw.c b/sound/core/oss/mulaw.c new file mode 100644 index 000000000000..44ec4c66eb19 --- /dev/null +++ b/sound/core/oss/mulaw.c | |||
@@ -0,0 +1,308 @@ | |||
1 | /* | ||
2 | * Mu-Law conversion Plug-In Interface | ||
3 | * Copyright (c) 1999 by Jaroslav Kysela <perex@suse.cz> | ||
4 | * Uros Bizjak <uros@kss-loka.si> | ||
5 | * | ||
6 | * Based on reference implementation by Sun Microsystems, Inc. | ||
7 | * | ||
8 | * This library is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU Library General Public License as | ||
10 | * published by the Free Software Foundation; either version 2 of | ||
11 | * the License, or (at your option) any later version. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, | ||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | * GNU Library General Public License for more details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU Library General Public | ||
19 | * License along with this library; if not, write to the Free Software | ||
20 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
21 | * | ||
22 | */ | ||
23 | |||
24 | #include <sound/driver.h> | ||
25 | #include <linux/time.h> | ||
26 | #include <sound/core.h> | ||
27 | #include <sound/pcm.h> | ||
28 | #include "pcm_plugin.h" | ||
29 | |||
30 | #define SIGN_BIT (0x80) /* Sign bit for a u-law byte. */ | ||
31 | #define QUANT_MASK (0xf) /* Quantization field mask. */ | ||
32 | #define NSEGS (8) /* Number of u-law segments. */ | ||
33 | #define SEG_SHIFT (4) /* Left shift for segment number. */ | ||
34 | #define SEG_MASK (0x70) /* Segment field mask. */ | ||
35 | |||
36 | static inline int val_seg(int val) | ||
37 | { | ||
38 | int r = 0; | ||
39 | val >>= 7; | ||
40 | if (val & 0xf0) { | ||
41 | val >>= 4; | ||
42 | r += 4; | ||
43 | } | ||
44 | if (val & 0x0c) { | ||
45 | val >>= 2; | ||
46 | r += 2; | ||
47 | } | ||
48 | if (val & 0x02) | ||
49 | r += 1; | ||
50 | return r; | ||
51 | } | ||
52 | |||
53 | #define BIAS (0x84) /* Bias for linear code. */ | ||
54 | |||
55 | /* | ||
56 | * linear2ulaw() - Convert a linear PCM value to u-law | ||
57 | * | ||
58 | * In order to simplify the encoding process, the original linear magnitude | ||
59 | * is biased by adding 33 which shifts the encoding range from (0 - 8158) to | ||
60 | * (33 - 8191). The result can be seen in the following encoding table: | ||
61 | * | ||
62 | * Biased Linear Input Code Compressed Code | ||
63 | * ------------------------ --------------- | ||
64 | * 00000001wxyza 000wxyz | ||
65 | * 0000001wxyzab 001wxyz | ||
66 | * 000001wxyzabc 010wxyz | ||
67 | * 00001wxyzabcd 011wxyz | ||
68 | * 0001wxyzabcde 100wxyz | ||
69 | * 001wxyzabcdef 101wxyz | ||
70 | * 01wxyzabcdefg 110wxyz | ||
71 | * 1wxyzabcdefgh 111wxyz | ||
72 | * | ||
73 | * Each biased linear code has a leading 1 which identifies the segment | ||
74 | * number. The value of the segment number is equal to 7 minus the number | ||
75 | * of leading 0's. The quantization interval is directly available as the | ||
76 | * four bits wxyz. * The trailing bits (a - h) are ignored. | ||
77 | * | ||
78 | * Ordinarily the complement of the resulting code word is used for | ||
79 | * transmission, and so the code word is complemented before it is returned. | ||
80 | * | ||
81 | * For further information see John C. Bellamy's Digital Telephony, 1982, | ||
82 | * John Wiley & Sons, pps 98-111 and 472-476. | ||
83 | */ | ||
84 | static unsigned char linear2ulaw(int pcm_val) /* 2's complement (16-bit range) */ | ||
85 | { | ||
86 | int mask; | ||
87 | int seg; | ||
88 | unsigned char uval; | ||
89 | |||
90 | /* Get the sign and the magnitude of the value. */ | ||
91 | if (pcm_val < 0) { | ||
92 | pcm_val = BIAS - pcm_val; | ||
93 | mask = 0x7F; | ||
94 | } else { | ||
95 | pcm_val += BIAS; | ||
96 | mask = 0xFF; | ||
97 | } | ||
98 | if (pcm_val > 0x7FFF) | ||
99 | pcm_val = 0x7FFF; | ||
100 | |||
101 | /* Convert the scaled magnitude to segment number. */ | ||
102 | seg = val_seg(pcm_val); | ||
103 | |||
104 | /* | ||
105 | * Combine the sign, segment, quantization bits; | ||
106 | * and complement the code word. | ||
107 | */ | ||
108 | uval = (seg << 4) | ((pcm_val >> (seg + 3)) & 0xF); | ||
109 | return uval ^ mask; | ||
110 | } | ||
111 | |||
112 | /* | ||
113 | * ulaw2linear() - Convert a u-law value to 16-bit linear PCM | ||
114 | * | ||
115 | * First, a biased linear code is derived from the code word. An unbiased | ||
116 | * output can then be obtained by subtracting 33 from the biased code. | ||
117 | * | ||
118 | * Note that this function expects to be passed the complement of the | ||
119 | * original code word. This is in keeping with ISDN conventions. | ||
120 | */ | ||
121 | static int ulaw2linear(unsigned char u_val) | ||
122 | { | ||
123 | int t; | ||
124 | |||
125 | /* Complement to obtain normal u-law value. */ | ||
126 | u_val = ~u_val; | ||
127 | |||
128 | /* | ||
129 | * Extract and bias the quantization bits. Then | ||
130 | * shift up by the segment number and subtract out the bias. | ||
131 | */ | ||
132 | t = ((u_val & QUANT_MASK) << 3) + BIAS; | ||
133 | t <<= ((unsigned)u_val & SEG_MASK) >> SEG_SHIFT; | ||
134 | |||
135 | return ((u_val & SIGN_BIT) ? (BIAS - t) : (t - BIAS)); | ||
136 | } | ||
137 | |||
138 | /* | ||
139 | * Basic Mu-Law plugin | ||
140 | */ | ||
141 | |||
142 | typedef void (*mulaw_f)(snd_pcm_plugin_t *plugin, | ||
143 | const snd_pcm_plugin_channel_t *src_channels, | ||
144 | snd_pcm_plugin_channel_t *dst_channels, | ||
145 | snd_pcm_uframes_t frames); | ||
146 | |||
147 | typedef struct mulaw_private_data { | ||
148 | mulaw_f func; | ||
149 | int conv; | ||
150 | } mulaw_t; | ||
151 | |||
152 | static void mulaw_decode(snd_pcm_plugin_t *plugin, | ||
153 | const snd_pcm_plugin_channel_t *src_channels, | ||
154 | snd_pcm_plugin_channel_t *dst_channels, | ||
155 | snd_pcm_uframes_t frames) | ||
156 | { | ||
157 | #define PUT_S16_LABELS | ||
158 | #include "plugin_ops.h" | ||
159 | #undef PUT_S16_LABELS | ||
160 | mulaw_t *data = (mulaw_t *)plugin->extra_data; | ||
161 | void *put = put_s16_labels[data->conv]; | ||
162 | int channel; | ||
163 | int nchannels = plugin->src_format.channels; | ||
164 | for (channel = 0; channel < nchannels; ++channel) { | ||
165 | char *src; | ||
166 | char *dst; | ||
167 | int src_step, dst_step; | ||
168 | snd_pcm_uframes_t frames1; | ||
169 | if (!src_channels[channel].enabled) { | ||
170 | if (dst_channels[channel].wanted) | ||
171 | snd_pcm_area_silence(&dst_channels[channel].area, 0, frames, plugin->dst_format.format); | ||
172 | dst_channels[channel].enabled = 0; | ||
173 | continue; | ||
174 | } | ||
175 | dst_channels[channel].enabled = 1; | ||
176 | src = src_channels[channel].area.addr + src_channels[channel].area.first / 8; | ||
177 | dst = dst_channels[channel].area.addr + dst_channels[channel].area.first / 8; | ||
178 | src_step = src_channels[channel].area.step / 8; | ||
179 | dst_step = dst_channels[channel].area.step / 8; | ||
180 | frames1 = frames; | ||
181 | while (frames1-- > 0) { | ||
182 | signed short sample = ulaw2linear(*src); | ||
183 | goto *put; | ||
184 | #define PUT_S16_END after | ||
185 | #include "plugin_ops.h" | ||
186 | #undef PUT_S16_END | ||
187 | after: | ||
188 | src += src_step; | ||
189 | dst += dst_step; | ||
190 | } | ||
191 | } | ||
192 | } | ||
193 | |||
194 | static void mulaw_encode(snd_pcm_plugin_t *plugin, | ||
195 | const snd_pcm_plugin_channel_t *src_channels, | ||
196 | snd_pcm_plugin_channel_t *dst_channels, | ||
197 | snd_pcm_uframes_t frames) | ||
198 | { | ||
199 | #define GET_S16_LABELS | ||
200 | #include "plugin_ops.h" | ||
201 | #undef GET_S16_LABELS | ||
202 | mulaw_t *data = (mulaw_t *)plugin->extra_data; | ||
203 | void *get = get_s16_labels[data->conv]; | ||
204 | int channel; | ||
205 | int nchannels = plugin->src_format.channels; | ||
206 | signed short sample = 0; | ||
207 | for (channel = 0; channel < nchannels; ++channel) { | ||
208 | char *src; | ||
209 | char *dst; | ||
210 | int src_step, dst_step; | ||
211 | snd_pcm_uframes_t frames1; | ||
212 | if (!src_channels[channel].enabled) { | ||
213 | if (dst_channels[channel].wanted) | ||
214 | snd_pcm_area_silence(&dst_channels[channel].area, 0, frames, plugin->dst_format.format); | ||
215 | dst_channels[channel].enabled = 0; | ||
216 | continue; | ||
217 | } | ||
218 | dst_channels[channel].enabled = 1; | ||
219 | src = src_channels[channel].area.addr + src_channels[channel].area.first / 8; | ||
220 | dst = dst_channels[channel].area.addr + dst_channels[channel].area.first / 8; | ||
221 | src_step = src_channels[channel].area.step / 8; | ||
222 | dst_step = dst_channels[channel].area.step / 8; | ||
223 | frames1 = frames; | ||
224 | while (frames1-- > 0) { | ||
225 | goto *get; | ||
226 | #define GET_S16_END after | ||
227 | #include "plugin_ops.h" | ||
228 | #undef GET_S16_END | ||
229 | after: | ||
230 | *dst = linear2ulaw(sample); | ||
231 | src += src_step; | ||
232 | dst += dst_step; | ||
233 | } | ||
234 | } | ||
235 | } | ||
236 | |||
237 | static snd_pcm_sframes_t mulaw_transfer(snd_pcm_plugin_t *plugin, | ||
238 | const snd_pcm_plugin_channel_t *src_channels, | ||
239 | snd_pcm_plugin_channel_t *dst_channels, | ||
240 | snd_pcm_uframes_t frames) | ||
241 | { | ||
242 | mulaw_t *data; | ||
243 | |||
244 | snd_assert(plugin != NULL && src_channels != NULL && dst_channels != NULL, return -ENXIO); | ||
245 | if (frames == 0) | ||
246 | return 0; | ||
247 | #ifdef CONFIG_SND_DEBUG | ||
248 | { | ||
249 | unsigned int channel; | ||
250 | for (channel = 0; channel < plugin->src_format.channels; channel++) { | ||
251 | snd_assert(src_channels[channel].area.first % 8 == 0 && | ||
252 | src_channels[channel].area.step % 8 == 0, | ||
253 | return -ENXIO); | ||
254 | snd_assert(dst_channels[channel].area.first % 8 == 0 && | ||
255 | dst_channels[channel].area.step % 8 == 0, | ||
256 | return -ENXIO); | ||
257 | } | ||
258 | } | ||
259 | #endif | ||
260 | data = (mulaw_t *)plugin->extra_data; | ||
261 | data->func(plugin, src_channels, dst_channels, frames); | ||
262 | return frames; | ||
263 | } | ||
264 | |||
265 | int snd_pcm_plugin_build_mulaw(snd_pcm_plug_t *plug, | ||
266 | snd_pcm_plugin_format_t *src_format, | ||
267 | snd_pcm_plugin_format_t *dst_format, | ||
268 | snd_pcm_plugin_t **r_plugin) | ||
269 | { | ||
270 | int err; | ||
271 | mulaw_t *data; | ||
272 | snd_pcm_plugin_t *plugin; | ||
273 | snd_pcm_plugin_format_t *format; | ||
274 | mulaw_f func; | ||
275 | |||
276 | snd_assert(r_plugin != NULL, return -ENXIO); | ||
277 | *r_plugin = NULL; | ||
278 | |||
279 | snd_assert(src_format->rate == dst_format->rate, return -ENXIO); | ||
280 | snd_assert(src_format->channels == dst_format->channels, return -ENXIO); | ||
281 | |||
282 | if (dst_format->format == SNDRV_PCM_FORMAT_MU_LAW) { | ||
283 | format = src_format; | ||
284 | func = mulaw_encode; | ||
285 | } | ||
286 | else if (src_format->format == SNDRV_PCM_FORMAT_MU_LAW) { | ||
287 | format = dst_format; | ||
288 | func = mulaw_decode; | ||
289 | } | ||
290 | else { | ||
291 | snd_BUG(); | ||
292 | return -EINVAL; | ||
293 | } | ||
294 | snd_assert(snd_pcm_format_linear(format->format) != 0, return -ENXIO); | ||
295 | |||
296 | err = snd_pcm_plugin_build(plug, "Mu-Law<->linear conversion", | ||
297 | src_format, dst_format, | ||
298 | sizeof(mulaw_t), &plugin); | ||
299 | if (err < 0) | ||
300 | return err; | ||
301 | data = (mulaw_t*)plugin->extra_data; | ||
302 | data->func = func; | ||
303 | data->conv = getput_index(format->format); | ||
304 | snd_assert(data->conv >= 0 && data->conv < 4*2*2, return -EINVAL); | ||
305 | plugin->transfer = mulaw_transfer; | ||
306 | *r_plugin = plugin; | ||
307 | return 0; | ||
308 | } | ||
diff --git a/sound/core/oss/pcm_oss.c b/sound/core/oss/pcm_oss.c new file mode 100644 index 000000000000..1a805020f57a --- /dev/null +++ b/sound/core/oss/pcm_oss.c | |||
@@ -0,0 +1,2530 @@ | |||
1 | /* | ||
2 | * Digital Audio (PCM) abstract layer / OSS compatible | ||
3 | * Copyright (c) by Jaroslav Kysela <perex@suse.cz> | ||
4 | * | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #if 0 | ||
23 | #define PLUGIN_DEBUG | ||
24 | #endif | ||
25 | #if 0 | ||
26 | #define OSS_DEBUG | ||
27 | #endif | ||
28 | |||
29 | #include <sound/driver.h> | ||
30 | #include <linux/init.h> | ||
31 | #include <linux/smp_lock.h> | ||
32 | #include <linux/slab.h> | ||
33 | #include <linux/time.h> | ||
34 | #include <linux/vmalloc.h> | ||
35 | #include <linux/moduleparam.h> | ||
36 | #include <sound/core.h> | ||
37 | #include <sound/minors.h> | ||
38 | #include <sound/pcm.h> | ||
39 | #include <sound/pcm_params.h> | ||
40 | #include "pcm_plugin.h" | ||
41 | #include <sound/info.h> | ||
42 | #include <linux/soundcard.h> | ||
43 | #include <sound/initval.h> | ||
44 | |||
45 | #define OSS_ALSAEMULVER _SIOR ('M', 249, int) | ||
46 | |||
47 | static int dsp_map[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = 0}; | ||
48 | static int adsp_map[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = 1}; | ||
49 | static int nonblock_open = 1; | ||
50 | |||
51 | MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>, Abramo Bagnara <abramo@alsa-project.org>"); | ||
52 | MODULE_DESCRIPTION("PCM OSS emulation for ALSA."); | ||
53 | MODULE_LICENSE("GPL"); | ||
54 | module_param_array(dsp_map, int, NULL, 0444); | ||
55 | MODULE_PARM_DESC(dsp_map, "PCM device number assigned to 1st OSS device."); | ||
56 | module_param_array(adsp_map, int, NULL, 0444); | ||
57 | MODULE_PARM_DESC(adsp_map, "PCM device number assigned to 2nd OSS device."); | ||
58 | module_param(nonblock_open, bool, 0644); | ||
59 | MODULE_PARM_DESC(nonblock_open, "Don't block opening busy PCM devices."); | ||
60 | MODULE_ALIAS_SNDRV_MINOR(SNDRV_MINOR_OSS_PCM); | ||
61 | MODULE_ALIAS_SNDRV_MINOR(SNDRV_MINOR_OSS_PCM1); | ||
62 | |||
63 | extern int snd_mixer_oss_ioctl_card(snd_card_t *card, unsigned int cmd, unsigned long arg); | ||
64 | static int snd_pcm_oss_get_rate(snd_pcm_oss_file_t *pcm_oss_file); | ||
65 | static int snd_pcm_oss_get_channels(snd_pcm_oss_file_t *pcm_oss_file); | ||
66 | static int snd_pcm_oss_get_format(snd_pcm_oss_file_t *pcm_oss_file); | ||
67 | |||
68 | static inline mm_segment_t snd_enter_user(void) | ||
69 | { | ||
70 | mm_segment_t fs = get_fs(); | ||
71 | set_fs(get_ds()); | ||
72 | return fs; | ||
73 | } | ||
74 | |||
75 | static inline void snd_leave_user(mm_segment_t fs) | ||
76 | { | ||
77 | set_fs(fs); | ||
78 | } | ||
79 | |||
80 | static int snd_pcm_oss_plugin_clear(snd_pcm_substream_t *substream) | ||
81 | { | ||
82 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
83 | snd_pcm_plugin_t *plugin, *next; | ||
84 | |||
85 | plugin = runtime->oss.plugin_first; | ||
86 | while (plugin) { | ||
87 | next = plugin->next; | ||
88 | snd_pcm_plugin_free(plugin); | ||
89 | plugin = next; | ||
90 | } | ||
91 | runtime->oss.plugin_first = runtime->oss.plugin_last = NULL; | ||
92 | return 0; | ||
93 | } | ||
94 | |||
95 | static int snd_pcm_plugin_insert(snd_pcm_plugin_t *plugin) | ||
96 | { | ||
97 | snd_pcm_runtime_t *runtime = plugin->plug->runtime; | ||
98 | plugin->next = runtime->oss.plugin_first; | ||
99 | plugin->prev = NULL; | ||
100 | if (runtime->oss.plugin_first) { | ||
101 | runtime->oss.plugin_first->prev = plugin; | ||
102 | runtime->oss.plugin_first = plugin; | ||
103 | } else { | ||
104 | runtime->oss.plugin_last = | ||
105 | runtime->oss.plugin_first = plugin; | ||
106 | } | ||
107 | return 0; | ||
108 | } | ||
109 | |||
110 | int snd_pcm_plugin_append(snd_pcm_plugin_t *plugin) | ||
111 | { | ||
112 | snd_pcm_runtime_t *runtime = plugin->plug->runtime; | ||
113 | plugin->next = NULL; | ||
114 | plugin->prev = runtime->oss.plugin_last; | ||
115 | if (runtime->oss.plugin_last) { | ||
116 | runtime->oss.plugin_last->next = plugin; | ||
117 | runtime->oss.plugin_last = plugin; | ||
118 | } else { | ||
119 | runtime->oss.plugin_last = | ||
120 | runtime->oss.plugin_first = plugin; | ||
121 | } | ||
122 | return 0; | ||
123 | } | ||
124 | |||
125 | static long snd_pcm_oss_bytes(snd_pcm_substream_t *substream, long frames) | ||
126 | { | ||
127 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
128 | snd_pcm_uframes_t buffer_size = snd_pcm_lib_buffer_bytes(substream); | ||
129 | frames = frames_to_bytes(runtime, frames); | ||
130 | if (buffer_size == runtime->oss.buffer_bytes) | ||
131 | return frames; | ||
132 | return (runtime->oss.buffer_bytes * frames) / buffer_size; | ||
133 | } | ||
134 | |||
135 | static long snd_pcm_alsa_frames(snd_pcm_substream_t *substream, long bytes) | ||
136 | { | ||
137 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
138 | snd_pcm_uframes_t buffer_size = snd_pcm_lib_buffer_bytes(substream); | ||
139 | if (buffer_size == runtime->oss.buffer_bytes) | ||
140 | return bytes_to_frames(runtime, bytes); | ||
141 | return bytes_to_frames(runtime, (buffer_size * bytes) / runtime->oss.buffer_bytes); | ||
142 | } | ||
143 | |||
144 | static int snd_pcm_oss_format_from(int format) | ||
145 | { | ||
146 | switch (format) { | ||
147 | case AFMT_MU_LAW: return SNDRV_PCM_FORMAT_MU_LAW; | ||
148 | case AFMT_A_LAW: return SNDRV_PCM_FORMAT_A_LAW; | ||
149 | case AFMT_IMA_ADPCM: return SNDRV_PCM_FORMAT_IMA_ADPCM; | ||
150 | case AFMT_U8: return SNDRV_PCM_FORMAT_U8; | ||
151 | case AFMT_S16_LE: return SNDRV_PCM_FORMAT_S16_LE; | ||
152 | case AFMT_S16_BE: return SNDRV_PCM_FORMAT_S16_BE; | ||
153 | case AFMT_S8: return SNDRV_PCM_FORMAT_S8; | ||
154 | case AFMT_U16_LE: return SNDRV_PCM_FORMAT_U16_LE; | ||
155 | case AFMT_U16_BE: return SNDRV_PCM_FORMAT_U16_BE; | ||
156 | case AFMT_MPEG: return SNDRV_PCM_FORMAT_MPEG; | ||
157 | default: return SNDRV_PCM_FORMAT_U8; | ||
158 | } | ||
159 | } | ||
160 | |||
161 | static int snd_pcm_oss_format_to(int format) | ||
162 | { | ||
163 | switch (format) { | ||
164 | case SNDRV_PCM_FORMAT_MU_LAW: return AFMT_MU_LAW; | ||
165 | case SNDRV_PCM_FORMAT_A_LAW: return AFMT_A_LAW; | ||
166 | case SNDRV_PCM_FORMAT_IMA_ADPCM: return AFMT_IMA_ADPCM; | ||
167 | case SNDRV_PCM_FORMAT_U8: return AFMT_U8; | ||
168 | case SNDRV_PCM_FORMAT_S16_LE: return AFMT_S16_LE; | ||
169 | case SNDRV_PCM_FORMAT_S16_BE: return AFMT_S16_BE; | ||
170 | case SNDRV_PCM_FORMAT_S8: return AFMT_S8; | ||
171 | case SNDRV_PCM_FORMAT_U16_LE: return AFMT_U16_LE; | ||
172 | case SNDRV_PCM_FORMAT_U16_BE: return AFMT_U16_BE; | ||
173 | case SNDRV_PCM_FORMAT_MPEG: return AFMT_MPEG; | ||
174 | default: return -EINVAL; | ||
175 | } | ||
176 | } | ||
177 | |||
178 | static int snd_pcm_oss_period_size(snd_pcm_substream_t *substream, | ||
179 | snd_pcm_hw_params_t *oss_params, | ||
180 | snd_pcm_hw_params_t *slave_params) | ||
181 | { | ||
182 | size_t s; | ||
183 | size_t oss_buffer_size, oss_period_size, oss_periods; | ||
184 | size_t min_period_size, max_period_size; | ||
185 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
186 | size_t oss_frame_size; | ||
187 | |||
188 | oss_frame_size = snd_pcm_format_physical_width(params_format(oss_params)) * | ||
189 | params_channels(oss_params) / 8; | ||
190 | |||
191 | oss_buffer_size = snd_pcm_plug_client_size(substream, | ||
192 | snd_pcm_hw_param_value_max(slave_params, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, NULL)) * oss_frame_size; | ||
193 | oss_buffer_size = 1 << ld2(oss_buffer_size); | ||
194 | if (atomic_read(&runtime->mmap_count)) { | ||
195 | if (oss_buffer_size > runtime->oss.mmap_bytes) | ||
196 | oss_buffer_size = runtime->oss.mmap_bytes; | ||
197 | } | ||
198 | |||
199 | if (substream->oss.setup && | ||
200 | substream->oss.setup->period_size > 16) | ||
201 | oss_period_size = substream->oss.setup->period_size; | ||
202 | else if (runtime->oss.fragshift) { | ||
203 | oss_period_size = 1 << runtime->oss.fragshift; | ||
204 | if (oss_period_size > oss_buffer_size / 2) | ||
205 | oss_period_size = oss_buffer_size / 2; | ||
206 | } else { | ||
207 | int sd; | ||
208 | size_t bytes_per_sec = params_rate(oss_params) * snd_pcm_format_physical_width(params_format(oss_params)) * params_channels(oss_params) / 8; | ||
209 | |||
210 | oss_period_size = oss_buffer_size; | ||
211 | do { | ||
212 | oss_period_size /= 2; | ||
213 | } while (oss_period_size > bytes_per_sec); | ||
214 | if (runtime->oss.subdivision == 0) { | ||
215 | sd = 4; | ||
216 | if (oss_period_size / sd > 4096) | ||
217 | sd *= 2; | ||
218 | if (oss_period_size / sd < 4096) | ||
219 | sd = 1; | ||
220 | } else | ||
221 | sd = runtime->oss.subdivision; | ||
222 | oss_period_size /= sd; | ||
223 | if (oss_period_size < 16) | ||
224 | oss_period_size = 16; | ||
225 | } | ||
226 | |||
227 | min_period_size = snd_pcm_plug_client_size(substream, | ||
228 | snd_pcm_hw_param_value_min(slave_params, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, NULL)); | ||
229 | min_period_size *= oss_frame_size; | ||
230 | min_period_size = 1 << (ld2(min_period_size - 1) + 1); | ||
231 | if (oss_period_size < min_period_size) | ||
232 | oss_period_size = min_period_size; | ||
233 | |||
234 | max_period_size = snd_pcm_plug_client_size(substream, | ||
235 | snd_pcm_hw_param_value_max(slave_params, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, NULL)); | ||
236 | max_period_size *= oss_frame_size; | ||
237 | max_period_size = 1 << ld2(max_period_size); | ||
238 | if (oss_period_size > max_period_size) | ||
239 | oss_period_size = max_period_size; | ||
240 | |||
241 | oss_periods = oss_buffer_size / oss_period_size; | ||
242 | |||
243 | if (substream->oss.setup) { | ||
244 | if (substream->oss.setup->periods > 1) | ||
245 | oss_periods = substream->oss.setup->periods; | ||
246 | } | ||
247 | |||
248 | s = snd_pcm_hw_param_value_max(slave_params, SNDRV_PCM_HW_PARAM_PERIODS, NULL); | ||
249 | if (runtime->oss.maxfrags && s > runtime->oss.maxfrags) | ||
250 | s = runtime->oss.maxfrags; | ||
251 | if (oss_periods > s) | ||
252 | oss_periods = s; | ||
253 | |||
254 | s = snd_pcm_hw_param_value_min(slave_params, SNDRV_PCM_HW_PARAM_PERIODS, NULL); | ||
255 | if (s < 2) | ||
256 | s = 2; | ||
257 | if (oss_periods < s) | ||
258 | oss_periods = s; | ||
259 | |||
260 | while (oss_period_size * oss_periods > oss_buffer_size) | ||
261 | oss_period_size /= 2; | ||
262 | |||
263 | snd_assert(oss_period_size >= 16, return -EINVAL); | ||
264 | runtime->oss.period_bytes = oss_period_size; | ||
265 | runtime->oss.period_frames = 1; | ||
266 | runtime->oss.periods = oss_periods; | ||
267 | return 0; | ||
268 | } | ||
269 | |||
270 | static int choose_rate(snd_pcm_substream_t *substream, | ||
271 | snd_pcm_hw_params_t *params, unsigned int best_rate) | ||
272 | { | ||
273 | snd_interval_t *it; | ||
274 | snd_pcm_hw_params_t *save; | ||
275 | unsigned int rate, prev; | ||
276 | |||
277 | save = kmalloc(sizeof(*save), GFP_KERNEL); | ||
278 | if (save == NULL) | ||
279 | return -ENOMEM; | ||
280 | *save = *params; | ||
281 | it = hw_param_interval(save, SNDRV_PCM_HW_PARAM_RATE); | ||
282 | |||
283 | /* try multiples of the best rate */ | ||
284 | rate = best_rate; | ||
285 | for (;;) { | ||
286 | if (it->max < rate || (it->max == rate && it->openmax)) | ||
287 | break; | ||
288 | if (it->min < rate || (it->min == rate && !it->openmin)) { | ||
289 | int ret; | ||
290 | ret = snd_pcm_hw_param_set(substream, params, | ||
291 | SNDRV_PCM_HW_PARAM_RATE, | ||
292 | rate, 0); | ||
293 | if (ret == (int)rate) { | ||
294 | kfree(save); | ||
295 | return rate; | ||
296 | } | ||
297 | *params = *save; | ||
298 | } | ||
299 | prev = rate; | ||
300 | rate += best_rate; | ||
301 | if (rate <= prev) | ||
302 | break; | ||
303 | } | ||
304 | |||
305 | /* not found, use the nearest rate */ | ||
306 | kfree(save); | ||
307 | return snd_pcm_hw_param_near(substream, params, SNDRV_PCM_HW_PARAM_RATE, best_rate, NULL); | ||
308 | } | ||
309 | |||
310 | static int snd_pcm_oss_change_params(snd_pcm_substream_t *substream) | ||
311 | { | ||
312 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
313 | snd_pcm_hw_params_t *params, *sparams; | ||
314 | snd_pcm_sw_params_t *sw_params; | ||
315 | ssize_t oss_buffer_size, oss_period_size; | ||
316 | size_t oss_frame_size; | ||
317 | int err; | ||
318 | int direct; | ||
319 | int format, sformat, n; | ||
320 | snd_mask_t sformat_mask; | ||
321 | snd_mask_t mask; | ||
322 | |||
323 | sw_params = kmalloc(sizeof(*sw_params), GFP_KERNEL); | ||
324 | params = kmalloc(sizeof(*params), GFP_KERNEL); | ||
325 | sparams = kmalloc(sizeof(*sparams), GFP_KERNEL); | ||
326 | if (!sw_params || !params || !sparams) { | ||
327 | snd_printd("No memory\n"); | ||
328 | err = -ENOMEM; | ||
329 | goto failure; | ||
330 | } | ||
331 | |||
332 | if (atomic_read(&runtime->mmap_count)) { | ||
333 | direct = 1; | ||
334 | } else { | ||
335 | snd_pcm_oss_setup_t *setup = substream->oss.setup; | ||
336 | direct = (setup != NULL && setup->direct); | ||
337 | } | ||
338 | |||
339 | _snd_pcm_hw_params_any(sparams); | ||
340 | _snd_pcm_hw_param_setinteger(sparams, SNDRV_PCM_HW_PARAM_PERIODS); | ||
341 | _snd_pcm_hw_param_min(sparams, SNDRV_PCM_HW_PARAM_PERIODS, 2, 0); | ||
342 | snd_mask_none(&mask); | ||
343 | if (atomic_read(&runtime->mmap_count)) | ||
344 | snd_mask_set(&mask, SNDRV_PCM_ACCESS_MMAP_INTERLEAVED); | ||
345 | else { | ||
346 | snd_mask_set(&mask, SNDRV_PCM_ACCESS_RW_INTERLEAVED); | ||
347 | if (!direct) | ||
348 | snd_mask_set(&mask, SNDRV_PCM_ACCESS_RW_NONINTERLEAVED); | ||
349 | } | ||
350 | err = snd_pcm_hw_param_mask(substream, sparams, SNDRV_PCM_HW_PARAM_ACCESS, &mask); | ||
351 | if (err < 0) { | ||
352 | snd_printd("No usable accesses\n"); | ||
353 | err = -EINVAL; | ||
354 | goto failure; | ||
355 | } | ||
356 | choose_rate(substream, sparams, runtime->oss.rate); | ||
357 | snd_pcm_hw_param_near(substream, sparams, SNDRV_PCM_HW_PARAM_CHANNELS, runtime->oss.channels, NULL); | ||
358 | |||
359 | format = snd_pcm_oss_format_from(runtime->oss.format); | ||
360 | |||
361 | sformat_mask = *hw_param_mask(sparams, SNDRV_PCM_HW_PARAM_FORMAT); | ||
362 | if (direct) | ||
363 | sformat = format; | ||
364 | else | ||
365 | sformat = snd_pcm_plug_slave_format(format, &sformat_mask); | ||
366 | |||
367 | if (sformat < 0 || !snd_mask_test(&sformat_mask, sformat)) { | ||
368 | for (sformat = 0; sformat <= SNDRV_PCM_FORMAT_LAST; sformat++) { | ||
369 | if (snd_mask_test(&sformat_mask, sformat) && | ||
370 | snd_pcm_oss_format_to(sformat) >= 0) | ||
371 | break; | ||
372 | } | ||
373 | if (sformat > SNDRV_PCM_FORMAT_LAST) { | ||
374 | snd_printd("Cannot find a format!!!\n"); | ||
375 | err = -EINVAL; | ||
376 | goto failure; | ||
377 | } | ||
378 | } | ||
379 | err = _snd_pcm_hw_param_set(sparams, SNDRV_PCM_HW_PARAM_FORMAT, sformat, 0); | ||
380 | snd_assert(err >= 0, goto failure); | ||
381 | |||
382 | if (direct) { | ||
383 | memcpy(params, sparams, sizeof(*params)); | ||
384 | } else { | ||
385 | _snd_pcm_hw_params_any(params); | ||
386 | _snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_ACCESS, | ||
387 | SNDRV_PCM_ACCESS_RW_INTERLEAVED, 0); | ||
388 | _snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_FORMAT, | ||
389 | snd_pcm_oss_format_from(runtime->oss.format), 0); | ||
390 | _snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_CHANNELS, | ||
391 | runtime->oss.channels, 0); | ||
392 | _snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_RATE, | ||
393 | runtime->oss.rate, 0); | ||
394 | pdprintf("client: access = %i, format = %i, channels = %i, rate = %i\n", | ||
395 | params_access(params), params_format(params), | ||
396 | params_channels(params), params_rate(params)); | ||
397 | } | ||
398 | pdprintf("slave: access = %i, format = %i, channels = %i, rate = %i\n", | ||
399 | params_access(sparams), params_format(sparams), | ||
400 | params_channels(sparams), params_rate(sparams)); | ||
401 | |||
402 | oss_frame_size = snd_pcm_format_physical_width(params_format(params)) * | ||
403 | params_channels(params) / 8; | ||
404 | |||
405 | snd_pcm_oss_plugin_clear(substream); | ||
406 | if (!direct) { | ||
407 | /* add necessary plugins */ | ||
408 | snd_pcm_oss_plugin_clear(substream); | ||
409 | if ((err = snd_pcm_plug_format_plugins(substream, | ||
410 | params, | ||
411 | sparams)) < 0) { | ||
412 | snd_printd("snd_pcm_plug_format_plugins failed: %i\n", err); | ||
413 | snd_pcm_oss_plugin_clear(substream); | ||
414 | goto failure; | ||
415 | } | ||
416 | if (runtime->oss.plugin_first) { | ||
417 | snd_pcm_plugin_t *plugin; | ||
418 | if ((err = snd_pcm_plugin_build_io(substream, sparams, &plugin)) < 0) { | ||
419 | snd_printd("snd_pcm_plugin_build_io failed: %i\n", err); | ||
420 | snd_pcm_oss_plugin_clear(substream); | ||
421 | goto failure; | ||
422 | } | ||
423 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | ||
424 | err = snd_pcm_plugin_append(plugin); | ||
425 | } else { | ||
426 | err = snd_pcm_plugin_insert(plugin); | ||
427 | } | ||
428 | if (err < 0) { | ||
429 | snd_pcm_oss_plugin_clear(substream); | ||
430 | goto failure; | ||
431 | } | ||
432 | } | ||
433 | } | ||
434 | |||
435 | err = snd_pcm_oss_period_size(substream, params, sparams); | ||
436 | if (err < 0) | ||
437 | goto failure; | ||
438 | |||
439 | n = snd_pcm_plug_slave_size(substream, runtime->oss.period_bytes / oss_frame_size); | ||
440 | err = snd_pcm_hw_param_near(substream, sparams, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, n, NULL); | ||
441 | snd_assert(err >= 0, goto failure); | ||
442 | |||
443 | err = snd_pcm_hw_param_near(substream, sparams, SNDRV_PCM_HW_PARAM_PERIODS, | ||
444 | runtime->oss.periods, NULL); | ||
445 | snd_assert(err >= 0, goto failure); | ||
446 | |||
447 | snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DROP, NULL); | ||
448 | |||
449 | if ((err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_HW_PARAMS, sparams)) < 0) { | ||
450 | snd_printd("HW_PARAMS failed: %i\n", err); | ||
451 | goto failure; | ||
452 | } | ||
453 | |||
454 | memset(sw_params, 0, sizeof(*sw_params)); | ||
455 | if (runtime->oss.trigger) { | ||
456 | sw_params->start_threshold = 1; | ||
457 | } else { | ||
458 | sw_params->start_threshold = runtime->boundary; | ||
459 | } | ||
460 | if (atomic_read(&runtime->mmap_count) || substream->stream == SNDRV_PCM_STREAM_CAPTURE) | ||
461 | sw_params->stop_threshold = runtime->boundary; | ||
462 | else | ||
463 | sw_params->stop_threshold = runtime->buffer_size; | ||
464 | sw_params->tstamp_mode = SNDRV_PCM_TSTAMP_NONE; | ||
465 | sw_params->period_step = 1; | ||
466 | sw_params->sleep_min = 0; | ||
467 | sw_params->avail_min = 1; | ||
468 | sw_params->xfer_align = 1; | ||
469 | if (atomic_read(&runtime->mmap_count) || | ||
470 | (substream->oss.setup && substream->oss.setup->nosilence)) { | ||
471 | sw_params->silence_threshold = 0; | ||
472 | sw_params->silence_size = 0; | ||
473 | } else { | ||
474 | snd_pcm_uframes_t frames; | ||
475 | frames = runtime->period_size + 16; | ||
476 | if (frames > runtime->buffer_size) | ||
477 | frames = runtime->buffer_size; | ||
478 | sw_params->silence_threshold = frames; | ||
479 | sw_params->silence_size = frames; | ||
480 | } | ||
481 | |||
482 | if ((err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_SW_PARAMS, sw_params)) < 0) { | ||
483 | snd_printd("SW_PARAMS failed: %i\n", err); | ||
484 | goto failure; | ||
485 | } | ||
486 | |||
487 | runtime->oss.periods = params_periods(sparams); | ||
488 | oss_period_size = snd_pcm_plug_client_size(substream, params_period_size(sparams)); | ||
489 | snd_assert(oss_period_size >= 0, err = -EINVAL; goto failure); | ||
490 | if (runtime->oss.plugin_first) { | ||
491 | err = snd_pcm_plug_alloc(substream, oss_period_size); | ||
492 | if (err < 0) | ||
493 | goto failure; | ||
494 | } | ||
495 | oss_period_size *= oss_frame_size; | ||
496 | |||
497 | oss_buffer_size = oss_period_size * runtime->oss.periods; | ||
498 | snd_assert(oss_buffer_size >= 0, err = -EINVAL; goto failure); | ||
499 | |||
500 | runtime->oss.period_bytes = oss_period_size; | ||
501 | runtime->oss.buffer_bytes = oss_buffer_size; | ||
502 | |||
503 | pdprintf("oss: period bytes = %i, buffer bytes = %i\n", | ||
504 | runtime->oss.period_bytes, | ||
505 | runtime->oss.buffer_bytes); | ||
506 | pdprintf("slave: period_size = %i, buffer_size = %i\n", | ||
507 | params_period_size(sparams), | ||
508 | params_buffer_size(sparams)); | ||
509 | |||
510 | runtime->oss.format = snd_pcm_oss_format_to(params_format(params)); | ||
511 | runtime->oss.channels = params_channels(params); | ||
512 | runtime->oss.rate = params_rate(params); | ||
513 | |||
514 | runtime->oss.params = 0; | ||
515 | runtime->oss.prepare = 1; | ||
516 | vfree(runtime->oss.buffer); | ||
517 | runtime->oss.buffer = vmalloc(runtime->oss.period_bytes); | ||
518 | runtime->oss.buffer_used = 0; | ||
519 | if (runtime->dma_area) | ||
520 | snd_pcm_format_set_silence(runtime->format, runtime->dma_area, bytes_to_samples(runtime, runtime->dma_bytes)); | ||
521 | |||
522 | runtime->oss.period_frames = snd_pcm_alsa_frames(substream, oss_period_size); | ||
523 | |||
524 | err = 0; | ||
525 | failure: | ||
526 | kfree(sw_params); | ||
527 | kfree(params); | ||
528 | kfree(sparams); | ||
529 | return err; | ||
530 | } | ||
531 | |||
532 | static int snd_pcm_oss_get_active_substream(snd_pcm_oss_file_t *pcm_oss_file, snd_pcm_substream_t **r_substream) | ||
533 | { | ||
534 | int idx, err; | ||
535 | snd_pcm_substream_t *asubstream = NULL, *substream; | ||
536 | |||
537 | for (idx = 0; idx < 2; idx++) { | ||
538 | substream = pcm_oss_file->streams[idx]; | ||
539 | if (substream == NULL) | ||
540 | continue; | ||
541 | if (asubstream == NULL) | ||
542 | asubstream = substream; | ||
543 | if (substream->runtime->oss.params) { | ||
544 | err = snd_pcm_oss_change_params(substream); | ||
545 | if (err < 0) | ||
546 | return err; | ||
547 | } | ||
548 | } | ||
549 | snd_assert(asubstream != NULL, return -EIO); | ||
550 | if (r_substream) | ||
551 | *r_substream = asubstream; | ||
552 | return 0; | ||
553 | } | ||
554 | |||
555 | static int snd_pcm_oss_prepare(snd_pcm_substream_t *substream) | ||
556 | { | ||
557 | int err; | ||
558 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
559 | |||
560 | err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_PREPARE, NULL); | ||
561 | if (err < 0) { | ||
562 | snd_printd("snd_pcm_oss_prepare: SNDRV_PCM_IOCTL_PREPARE failed\n"); | ||
563 | return err; | ||
564 | } | ||
565 | runtime->oss.prepare = 0; | ||
566 | runtime->oss.prev_hw_ptr_interrupt = 0; | ||
567 | runtime->oss.period_ptr = 0; | ||
568 | runtime->oss.buffer_used = 0; | ||
569 | |||
570 | return 0; | ||
571 | } | ||
572 | |||
573 | static int snd_pcm_oss_make_ready(snd_pcm_substream_t *substream) | ||
574 | { | ||
575 | snd_pcm_runtime_t *runtime; | ||
576 | int err; | ||
577 | |||
578 | if (substream == NULL) | ||
579 | return 0; | ||
580 | runtime = substream->runtime; | ||
581 | if (runtime->oss.params) { | ||
582 | err = snd_pcm_oss_change_params(substream); | ||
583 | if (err < 0) | ||
584 | return err; | ||
585 | } | ||
586 | if (runtime->oss.prepare) { | ||
587 | err = snd_pcm_oss_prepare(substream); | ||
588 | if (err < 0) | ||
589 | return err; | ||
590 | } | ||
591 | return 0; | ||
592 | } | ||
593 | |||
594 | static int snd_pcm_oss_capture_position_fixup(snd_pcm_substream_t *substream, snd_pcm_sframes_t *delay) | ||
595 | { | ||
596 | snd_pcm_runtime_t *runtime; | ||
597 | snd_pcm_uframes_t frames; | ||
598 | int err = 0; | ||
599 | |||
600 | while (1) { | ||
601 | err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DELAY, delay); | ||
602 | if (err < 0) | ||
603 | break; | ||
604 | runtime = substream->runtime; | ||
605 | if (*delay <= (snd_pcm_sframes_t)runtime->buffer_size) | ||
606 | break; | ||
607 | /* in case of overrun, skip whole periods like OSS/Linux driver does */ | ||
608 | /* until avail(delay) <= buffer_size */ | ||
609 | frames = (*delay - runtime->buffer_size) + runtime->period_size - 1; | ||
610 | frames /= runtime->period_size; | ||
611 | frames *= runtime->period_size; | ||
612 | err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_FORWARD, &frames); | ||
613 | if (err < 0) | ||
614 | break; | ||
615 | } | ||
616 | return err; | ||
617 | } | ||
618 | |||
619 | snd_pcm_sframes_t snd_pcm_oss_write3(snd_pcm_substream_t *substream, const char *ptr, snd_pcm_uframes_t frames, int in_kernel) | ||
620 | { | ||
621 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
622 | int ret; | ||
623 | while (1) { | ||
624 | if (runtime->status->state == SNDRV_PCM_STATE_XRUN || | ||
625 | runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) { | ||
626 | #ifdef OSS_DEBUG | ||
627 | if (runtime->status->state == SNDRV_PCM_STATE_XRUN) | ||
628 | printk("pcm_oss: write: recovering from XRUN\n"); | ||
629 | else | ||
630 | printk("pcm_oss: write: recovering from SUSPEND\n"); | ||
631 | #endif | ||
632 | ret = snd_pcm_oss_prepare(substream); | ||
633 | if (ret < 0) | ||
634 | break; | ||
635 | } | ||
636 | if (in_kernel) { | ||
637 | mm_segment_t fs; | ||
638 | fs = snd_enter_user(); | ||
639 | ret = snd_pcm_lib_write(substream, (void __user *)ptr, frames); | ||
640 | snd_leave_user(fs); | ||
641 | } else { | ||
642 | ret = snd_pcm_lib_write(substream, (void __user *)ptr, frames); | ||
643 | } | ||
644 | if (ret != -EPIPE && ret != -ESTRPIPE) | ||
645 | break; | ||
646 | /* test, if we can't store new data, because the stream */ | ||
647 | /* has not been started */ | ||
648 | if (runtime->status->state == SNDRV_PCM_STATE_PREPARED) | ||
649 | return -EAGAIN; | ||
650 | } | ||
651 | return ret; | ||
652 | } | ||
653 | |||
654 | snd_pcm_sframes_t snd_pcm_oss_read3(snd_pcm_substream_t *substream, char *ptr, snd_pcm_uframes_t frames, int in_kernel) | ||
655 | { | ||
656 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
657 | snd_pcm_sframes_t delay; | ||
658 | int ret; | ||
659 | while (1) { | ||
660 | if (runtime->status->state == SNDRV_PCM_STATE_XRUN || | ||
661 | runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) { | ||
662 | #ifdef OSS_DEBUG | ||
663 | if (runtime->status->state == SNDRV_PCM_STATE_XRUN) | ||
664 | printk("pcm_oss: read: recovering from XRUN\n"); | ||
665 | else | ||
666 | printk("pcm_oss: read: recovering from SUSPEND\n"); | ||
667 | #endif | ||
668 | ret = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DRAIN, NULL); | ||
669 | if (ret < 0) | ||
670 | break; | ||
671 | } else if (runtime->status->state == SNDRV_PCM_STATE_SETUP) { | ||
672 | ret = snd_pcm_oss_prepare(substream); | ||
673 | if (ret < 0) | ||
674 | break; | ||
675 | } | ||
676 | ret = snd_pcm_oss_capture_position_fixup(substream, &delay); | ||
677 | if (ret < 0) | ||
678 | break; | ||
679 | if (in_kernel) { | ||
680 | mm_segment_t fs; | ||
681 | fs = snd_enter_user(); | ||
682 | ret = snd_pcm_lib_read(substream, (void __user *)ptr, frames); | ||
683 | snd_leave_user(fs); | ||
684 | } else { | ||
685 | ret = snd_pcm_lib_read(substream, (void __user *)ptr, frames); | ||
686 | } | ||
687 | if (ret == -EPIPE) { | ||
688 | if (runtime->status->state == SNDRV_PCM_STATE_DRAINING) { | ||
689 | ret = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DROP, NULL); | ||
690 | if (ret < 0) | ||
691 | break; | ||
692 | } | ||
693 | continue; | ||
694 | } | ||
695 | if (ret != -ESTRPIPE) | ||
696 | break; | ||
697 | } | ||
698 | return ret; | ||
699 | } | ||
700 | |||
701 | snd_pcm_sframes_t snd_pcm_oss_writev3(snd_pcm_substream_t *substream, void **bufs, snd_pcm_uframes_t frames, int in_kernel) | ||
702 | { | ||
703 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
704 | int ret; | ||
705 | while (1) { | ||
706 | if (runtime->status->state == SNDRV_PCM_STATE_XRUN || | ||
707 | runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) { | ||
708 | #ifdef OSS_DEBUG | ||
709 | if (runtime->status->state == SNDRV_PCM_STATE_XRUN) | ||
710 | printk("pcm_oss: writev: recovering from XRUN\n"); | ||
711 | else | ||
712 | printk("pcm_oss: writev: recovering from SUSPEND\n"); | ||
713 | #endif | ||
714 | ret = snd_pcm_oss_prepare(substream); | ||
715 | if (ret < 0) | ||
716 | break; | ||
717 | } | ||
718 | if (in_kernel) { | ||
719 | mm_segment_t fs; | ||
720 | fs = snd_enter_user(); | ||
721 | ret = snd_pcm_lib_writev(substream, (void __user **)bufs, frames); | ||
722 | snd_leave_user(fs); | ||
723 | } else { | ||
724 | ret = snd_pcm_lib_writev(substream, (void __user **)bufs, frames); | ||
725 | } | ||
726 | if (ret != -EPIPE && ret != -ESTRPIPE) | ||
727 | break; | ||
728 | |||
729 | /* test, if we can't store new data, because the stream */ | ||
730 | /* has not been started */ | ||
731 | if (runtime->status->state == SNDRV_PCM_STATE_PREPARED) | ||
732 | return -EAGAIN; | ||
733 | } | ||
734 | return ret; | ||
735 | } | ||
736 | |||
737 | snd_pcm_sframes_t snd_pcm_oss_readv3(snd_pcm_substream_t *substream, void **bufs, snd_pcm_uframes_t frames, int in_kernel) | ||
738 | { | ||
739 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
740 | int ret; | ||
741 | while (1) { | ||
742 | if (runtime->status->state == SNDRV_PCM_STATE_XRUN || | ||
743 | runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) { | ||
744 | #ifdef OSS_DEBUG | ||
745 | if (runtime->status->state == SNDRV_PCM_STATE_XRUN) | ||
746 | printk("pcm_oss: readv: recovering from XRUN\n"); | ||
747 | else | ||
748 | printk("pcm_oss: readv: recovering from SUSPEND\n"); | ||
749 | #endif | ||
750 | ret = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DRAIN, NULL); | ||
751 | if (ret < 0) | ||
752 | break; | ||
753 | } else if (runtime->status->state == SNDRV_PCM_STATE_SETUP) { | ||
754 | ret = snd_pcm_oss_prepare(substream); | ||
755 | if (ret < 0) | ||
756 | break; | ||
757 | } | ||
758 | if (in_kernel) { | ||
759 | mm_segment_t fs; | ||
760 | fs = snd_enter_user(); | ||
761 | ret = snd_pcm_lib_readv(substream, (void __user **)bufs, frames); | ||
762 | snd_leave_user(fs); | ||
763 | } else { | ||
764 | ret = snd_pcm_lib_readv(substream, (void __user **)bufs, frames); | ||
765 | } | ||
766 | if (ret != -EPIPE && ret != -ESTRPIPE) | ||
767 | break; | ||
768 | } | ||
769 | return ret; | ||
770 | } | ||
771 | |||
772 | static ssize_t snd_pcm_oss_write2(snd_pcm_substream_t *substream, const char *buf, size_t bytes, int in_kernel) | ||
773 | { | ||
774 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
775 | snd_pcm_sframes_t frames, frames1; | ||
776 | if (runtime->oss.plugin_first) { | ||
777 | snd_pcm_plugin_channel_t *channels; | ||
778 | size_t oss_frame_bytes = (runtime->oss.plugin_first->src_width * runtime->oss.plugin_first->src_format.channels) / 8; | ||
779 | if (!in_kernel) { | ||
780 | if (copy_from_user(runtime->oss.buffer, (const char __user *)buf, bytes)) | ||
781 | return -EFAULT; | ||
782 | buf = runtime->oss.buffer; | ||
783 | } | ||
784 | frames = bytes / oss_frame_bytes; | ||
785 | frames1 = snd_pcm_plug_client_channels_buf(substream, (char *)buf, frames, &channels); | ||
786 | if (frames1 < 0) | ||
787 | return frames1; | ||
788 | frames1 = snd_pcm_plug_write_transfer(substream, channels, frames1); | ||
789 | if (frames1 <= 0) | ||
790 | return frames1; | ||
791 | bytes = frames1 * oss_frame_bytes; | ||
792 | } else { | ||
793 | frames = bytes_to_frames(runtime, bytes); | ||
794 | frames1 = snd_pcm_oss_write3(substream, buf, frames, in_kernel); | ||
795 | if (frames1 <= 0) | ||
796 | return frames1; | ||
797 | bytes = frames_to_bytes(runtime, frames1); | ||
798 | } | ||
799 | return bytes; | ||
800 | } | ||
801 | |||
802 | static ssize_t snd_pcm_oss_write1(snd_pcm_substream_t *substream, const char __user *buf, size_t bytes) | ||
803 | { | ||
804 | size_t xfer = 0; | ||
805 | ssize_t tmp; | ||
806 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
807 | |||
808 | if (atomic_read(&runtime->mmap_count)) | ||
809 | return -ENXIO; | ||
810 | |||
811 | if ((tmp = snd_pcm_oss_make_ready(substream)) < 0) | ||
812 | return tmp; | ||
813 | while (bytes > 0) { | ||
814 | if (bytes < runtime->oss.period_bytes || runtime->oss.buffer_used > 0) { | ||
815 | tmp = bytes; | ||
816 | if (tmp + runtime->oss.buffer_used > runtime->oss.period_bytes) | ||
817 | tmp = runtime->oss.period_bytes - runtime->oss.buffer_used; | ||
818 | if (tmp > 0) { | ||
819 | if (copy_from_user(runtime->oss.buffer + runtime->oss.buffer_used, buf, tmp)) | ||
820 | return xfer > 0 ? (snd_pcm_sframes_t)xfer : -EFAULT; | ||
821 | } | ||
822 | runtime->oss.buffer_used += tmp; | ||
823 | buf += tmp; | ||
824 | bytes -= tmp; | ||
825 | xfer += tmp; | ||
826 | if ((substream->oss.setup != NULL && substream->oss.setup->partialfrag) || | ||
827 | runtime->oss.buffer_used == runtime->oss.period_bytes) { | ||
828 | tmp = snd_pcm_oss_write2(substream, runtime->oss.buffer + runtime->oss.period_ptr, | ||
829 | runtime->oss.buffer_used - runtime->oss.period_ptr, 1); | ||
830 | if (tmp <= 0) | ||
831 | return xfer > 0 ? (snd_pcm_sframes_t)xfer : tmp; | ||
832 | runtime->oss.bytes += tmp; | ||
833 | runtime->oss.period_ptr += tmp; | ||
834 | runtime->oss.period_ptr %= runtime->oss.period_bytes; | ||
835 | if (runtime->oss.period_ptr == 0 || | ||
836 | runtime->oss.period_ptr == runtime->oss.buffer_used) | ||
837 | runtime->oss.buffer_used = 0; | ||
838 | else if ((substream->ffile->f_flags & O_NONBLOCK) != 0) | ||
839 | return xfer > 0 ? xfer : -EAGAIN; | ||
840 | } | ||
841 | } else { | ||
842 | tmp = snd_pcm_oss_write2(substream, (const char *)buf, runtime->oss.period_bytes, 0); | ||
843 | if (tmp <= 0) | ||
844 | return xfer > 0 ? (snd_pcm_sframes_t)xfer : tmp; | ||
845 | runtime->oss.bytes += tmp; | ||
846 | buf += tmp; | ||
847 | bytes -= tmp; | ||
848 | xfer += tmp; | ||
849 | if ((substream->ffile->f_flags & O_NONBLOCK) != 0 && | ||
850 | tmp != runtime->oss.period_bytes) | ||
851 | break; | ||
852 | } | ||
853 | } | ||
854 | return xfer; | ||
855 | } | ||
856 | |||
857 | static ssize_t snd_pcm_oss_read2(snd_pcm_substream_t *substream, char *buf, size_t bytes, int in_kernel) | ||
858 | { | ||
859 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
860 | snd_pcm_sframes_t frames, frames1; | ||
861 | char __user *final_dst = (char __user *)buf; | ||
862 | if (runtime->oss.plugin_first) { | ||
863 | snd_pcm_plugin_channel_t *channels; | ||
864 | size_t oss_frame_bytes = (runtime->oss.plugin_last->dst_width * runtime->oss.plugin_last->dst_format.channels) / 8; | ||
865 | if (!in_kernel) | ||
866 | buf = runtime->oss.buffer; | ||
867 | frames = bytes / oss_frame_bytes; | ||
868 | frames1 = snd_pcm_plug_client_channels_buf(substream, buf, frames, &channels); | ||
869 | if (frames1 < 0) | ||
870 | return frames1; | ||
871 | frames1 = snd_pcm_plug_read_transfer(substream, channels, frames1); | ||
872 | if (frames1 <= 0) | ||
873 | return frames1; | ||
874 | bytes = frames1 * oss_frame_bytes; | ||
875 | if (!in_kernel && copy_to_user(final_dst, buf, bytes)) | ||
876 | return -EFAULT; | ||
877 | } else { | ||
878 | frames = bytes_to_frames(runtime, bytes); | ||
879 | frames1 = snd_pcm_oss_read3(substream, buf, frames, in_kernel); | ||
880 | if (frames1 <= 0) | ||
881 | return frames1; | ||
882 | bytes = frames_to_bytes(runtime, frames1); | ||
883 | } | ||
884 | return bytes; | ||
885 | } | ||
886 | |||
887 | static ssize_t snd_pcm_oss_read1(snd_pcm_substream_t *substream, char __user *buf, size_t bytes) | ||
888 | { | ||
889 | size_t xfer = 0; | ||
890 | ssize_t tmp; | ||
891 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
892 | |||
893 | if (atomic_read(&runtime->mmap_count)) | ||
894 | return -ENXIO; | ||
895 | |||
896 | if ((tmp = snd_pcm_oss_make_ready(substream)) < 0) | ||
897 | return tmp; | ||
898 | while (bytes > 0) { | ||
899 | if (bytes < runtime->oss.period_bytes || runtime->oss.buffer_used > 0) { | ||
900 | if (runtime->oss.buffer_used == 0) { | ||
901 | tmp = snd_pcm_oss_read2(substream, runtime->oss.buffer, runtime->oss.period_bytes, 1); | ||
902 | if (tmp <= 0) | ||
903 | return xfer > 0 ? (snd_pcm_sframes_t)xfer : tmp; | ||
904 | runtime->oss.bytes += tmp; | ||
905 | runtime->oss.period_ptr = tmp; | ||
906 | runtime->oss.buffer_used = tmp; | ||
907 | } | ||
908 | tmp = bytes; | ||
909 | if ((size_t) tmp > runtime->oss.buffer_used) | ||
910 | tmp = runtime->oss.buffer_used; | ||
911 | if (copy_to_user(buf, runtime->oss.buffer + (runtime->oss.period_ptr - runtime->oss.buffer_used), tmp)) | ||
912 | return xfer > 0 ? (snd_pcm_sframes_t)xfer : -EFAULT; | ||
913 | buf += tmp; | ||
914 | bytes -= tmp; | ||
915 | xfer += tmp; | ||
916 | runtime->oss.buffer_used -= tmp; | ||
917 | } else { | ||
918 | tmp = snd_pcm_oss_read2(substream, (char *)buf, runtime->oss.period_bytes, 0); | ||
919 | if (tmp <= 0) | ||
920 | return xfer > 0 ? (snd_pcm_sframes_t)xfer : tmp; | ||
921 | runtime->oss.bytes += tmp; | ||
922 | buf += tmp; | ||
923 | bytes -= tmp; | ||
924 | xfer += tmp; | ||
925 | } | ||
926 | } | ||
927 | return xfer; | ||
928 | } | ||
929 | |||
930 | static int snd_pcm_oss_reset(snd_pcm_oss_file_t *pcm_oss_file) | ||
931 | { | ||
932 | snd_pcm_substream_t *substream; | ||
933 | |||
934 | substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK]; | ||
935 | if (substream != NULL) { | ||
936 | snd_pcm_kernel_playback_ioctl(substream, SNDRV_PCM_IOCTL_DROP, NULL); | ||
937 | substream->runtime->oss.prepare = 1; | ||
938 | } | ||
939 | substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE]; | ||
940 | if (substream != NULL) { | ||
941 | snd_pcm_kernel_capture_ioctl(substream, SNDRV_PCM_IOCTL_DROP, NULL); | ||
942 | substream->runtime->oss.prepare = 1; | ||
943 | } | ||
944 | return 0; | ||
945 | } | ||
946 | |||
947 | static int snd_pcm_oss_post(snd_pcm_oss_file_t *pcm_oss_file) | ||
948 | { | ||
949 | snd_pcm_substream_t *substream; | ||
950 | int err; | ||
951 | |||
952 | substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK]; | ||
953 | if (substream != NULL) { | ||
954 | if ((err = snd_pcm_oss_make_ready(substream)) < 0) | ||
955 | return err; | ||
956 | snd_pcm_kernel_playback_ioctl(substream, SNDRV_PCM_IOCTL_START, NULL); | ||
957 | } | ||
958 | /* note: all errors from the start action are ignored */ | ||
959 | /* OSS apps do not know, how to handle them */ | ||
960 | return 0; | ||
961 | } | ||
962 | |||
963 | static int snd_pcm_oss_sync1(snd_pcm_substream_t *substream, size_t size) | ||
964 | { | ||
965 | snd_pcm_runtime_t *runtime; | ||
966 | ssize_t result = 0; | ||
967 | long res; | ||
968 | wait_queue_t wait; | ||
969 | |||
970 | runtime = substream->runtime; | ||
971 | init_waitqueue_entry(&wait, current); | ||
972 | add_wait_queue(&runtime->sleep, &wait); | ||
973 | #ifdef OSS_DEBUG | ||
974 | printk("sync1: size = %li\n", size); | ||
975 | #endif | ||
976 | while (1) { | ||
977 | result = snd_pcm_oss_write2(substream, runtime->oss.buffer, size, 1); | ||
978 | if (result > 0) { | ||
979 | runtime->oss.buffer_used = 0; | ||
980 | result = 0; | ||
981 | break; | ||
982 | } | ||
983 | if (result != 0 && result != -EAGAIN) | ||
984 | break; | ||
985 | result = 0; | ||
986 | set_current_state(TASK_INTERRUPTIBLE); | ||
987 | snd_pcm_stream_lock_irq(substream); | ||
988 | res = runtime->status->state; | ||
989 | snd_pcm_stream_unlock_irq(substream); | ||
990 | if (res != SNDRV_PCM_STATE_RUNNING) { | ||
991 | set_current_state(TASK_RUNNING); | ||
992 | break; | ||
993 | } | ||
994 | res = schedule_timeout(10 * HZ); | ||
995 | if (signal_pending(current)) { | ||
996 | result = -ERESTARTSYS; | ||
997 | break; | ||
998 | } | ||
999 | if (res == 0) { | ||
1000 | snd_printk(KERN_ERR "OSS sync error - DMA timeout\n"); | ||
1001 | result = -EIO; | ||
1002 | break; | ||
1003 | } | ||
1004 | } | ||
1005 | remove_wait_queue(&runtime->sleep, &wait); | ||
1006 | return result; | ||
1007 | } | ||
1008 | |||
1009 | static int snd_pcm_oss_sync(snd_pcm_oss_file_t *pcm_oss_file) | ||
1010 | { | ||
1011 | int err = 0; | ||
1012 | unsigned int saved_f_flags; | ||
1013 | snd_pcm_substream_t *substream; | ||
1014 | snd_pcm_runtime_t *runtime; | ||
1015 | snd_pcm_format_t format; | ||
1016 | unsigned long width; | ||
1017 | size_t size; | ||
1018 | |||
1019 | substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK]; | ||
1020 | if (substream != NULL) { | ||
1021 | runtime = substream->runtime; | ||
1022 | if (atomic_read(&runtime->mmap_count)) | ||
1023 | goto __direct; | ||
1024 | if ((err = snd_pcm_oss_make_ready(substream)) < 0) | ||
1025 | return err; | ||
1026 | format = snd_pcm_oss_format_from(runtime->oss.format); | ||
1027 | width = snd_pcm_format_physical_width(format); | ||
1028 | if (runtime->oss.buffer_used > 0) { | ||
1029 | #ifdef OSS_DEBUG | ||
1030 | printk("sync: buffer_used\n"); | ||
1031 | #endif | ||
1032 | size = (8 * (runtime->oss.period_bytes - runtime->oss.buffer_used) + 7) / width; | ||
1033 | snd_pcm_format_set_silence(format, | ||
1034 | runtime->oss.buffer + runtime->oss.buffer_used, | ||
1035 | size); | ||
1036 | err = snd_pcm_oss_sync1(substream, runtime->oss.period_bytes); | ||
1037 | if (err < 0) | ||
1038 | return err; | ||
1039 | } else if (runtime->oss.period_ptr > 0) { | ||
1040 | #ifdef OSS_DEBUG | ||
1041 | printk("sync: period_ptr\n"); | ||
1042 | #endif | ||
1043 | size = runtime->oss.period_bytes - runtime->oss.period_ptr; | ||
1044 | snd_pcm_format_set_silence(format, | ||
1045 | runtime->oss.buffer, | ||
1046 | size * 8 / width); | ||
1047 | err = snd_pcm_oss_sync1(substream, size); | ||
1048 | if (err < 0) | ||
1049 | return err; | ||
1050 | } | ||
1051 | /* | ||
1052 | * The ALSA's period might be a bit large than OSS one. | ||
1053 | * Fill the remain portion of ALSA period with zeros. | ||
1054 | */ | ||
1055 | size = runtime->control->appl_ptr % runtime->period_size; | ||
1056 | if (size > 0) { | ||
1057 | size = runtime->period_size - size; | ||
1058 | if (runtime->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED) { | ||
1059 | size = (runtime->frame_bits * size) / 8; | ||
1060 | while (size > 0) { | ||
1061 | mm_segment_t fs; | ||
1062 | size_t size1 = size < runtime->oss.period_bytes ? size : runtime->oss.period_bytes; | ||
1063 | size -= size1; | ||
1064 | size1 *= 8; | ||
1065 | size1 /= runtime->sample_bits; | ||
1066 | snd_pcm_format_set_silence(runtime->format, | ||
1067 | runtime->oss.buffer, | ||
1068 | size1); | ||
1069 | fs = snd_enter_user(); | ||
1070 | snd_pcm_lib_write(substream, (void __user *)runtime->oss.buffer, size1); | ||
1071 | snd_leave_user(fs); | ||
1072 | } | ||
1073 | } else if (runtime->access == SNDRV_PCM_ACCESS_RW_NONINTERLEAVED) { | ||
1074 | void __user *buffers[runtime->channels]; | ||
1075 | memset(buffers, 0, runtime->channels * sizeof(void *)); | ||
1076 | snd_pcm_lib_writev(substream, buffers, size); | ||
1077 | } | ||
1078 | } | ||
1079 | /* | ||
1080 | * finish sync: drain the buffer | ||
1081 | */ | ||
1082 | __direct: | ||
1083 | saved_f_flags = substream->ffile->f_flags; | ||
1084 | substream->ffile->f_flags &= ~O_NONBLOCK; | ||
1085 | err = snd_pcm_kernel_playback_ioctl(substream, SNDRV_PCM_IOCTL_DRAIN, NULL); | ||
1086 | substream->ffile->f_flags = saved_f_flags; | ||
1087 | if (err < 0) | ||
1088 | return err; | ||
1089 | runtime->oss.prepare = 1; | ||
1090 | } | ||
1091 | |||
1092 | substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE]; | ||
1093 | if (substream != NULL) { | ||
1094 | if ((err = snd_pcm_oss_make_ready(substream)) < 0) | ||
1095 | return err; | ||
1096 | runtime = substream->runtime; | ||
1097 | err = snd_pcm_kernel_capture_ioctl(substream, SNDRV_PCM_IOCTL_DROP, NULL); | ||
1098 | if (err < 0) | ||
1099 | return err; | ||
1100 | runtime->oss.buffer_used = 0; | ||
1101 | runtime->oss.prepare = 1; | ||
1102 | } | ||
1103 | return 0; | ||
1104 | } | ||
1105 | |||
1106 | static int snd_pcm_oss_set_rate(snd_pcm_oss_file_t *pcm_oss_file, int rate) | ||
1107 | { | ||
1108 | int idx; | ||
1109 | |||
1110 | for (idx = 1; idx >= 0; --idx) { | ||
1111 | snd_pcm_substream_t *substream = pcm_oss_file->streams[idx]; | ||
1112 | snd_pcm_runtime_t *runtime; | ||
1113 | if (substream == NULL) | ||
1114 | continue; | ||
1115 | runtime = substream->runtime; | ||
1116 | if (rate < 1000) | ||
1117 | rate = 1000; | ||
1118 | else if (rate > 192000) | ||
1119 | rate = 192000; | ||
1120 | if (runtime->oss.rate != rate) { | ||
1121 | runtime->oss.params = 1; | ||
1122 | runtime->oss.rate = rate; | ||
1123 | } | ||
1124 | } | ||
1125 | return snd_pcm_oss_get_rate(pcm_oss_file); | ||
1126 | } | ||
1127 | |||
1128 | static int snd_pcm_oss_get_rate(snd_pcm_oss_file_t *pcm_oss_file) | ||
1129 | { | ||
1130 | snd_pcm_substream_t *substream; | ||
1131 | int err; | ||
1132 | |||
1133 | if ((err = snd_pcm_oss_get_active_substream(pcm_oss_file, &substream)) < 0) | ||
1134 | return err; | ||
1135 | return substream->runtime->oss.rate; | ||
1136 | } | ||
1137 | |||
1138 | static int snd_pcm_oss_set_channels(snd_pcm_oss_file_t *pcm_oss_file, unsigned int channels) | ||
1139 | { | ||
1140 | int idx; | ||
1141 | if (channels < 1) | ||
1142 | channels = 1; | ||
1143 | if (channels > 128) | ||
1144 | return -EINVAL; | ||
1145 | for (idx = 1; idx >= 0; --idx) { | ||
1146 | snd_pcm_substream_t *substream = pcm_oss_file->streams[idx]; | ||
1147 | snd_pcm_runtime_t *runtime; | ||
1148 | if (substream == NULL) | ||
1149 | continue; | ||
1150 | runtime = substream->runtime; | ||
1151 | if (runtime->oss.channels != channels) { | ||
1152 | runtime->oss.params = 1; | ||
1153 | runtime->oss.channels = channels; | ||
1154 | } | ||
1155 | } | ||
1156 | return snd_pcm_oss_get_channels(pcm_oss_file); | ||
1157 | } | ||
1158 | |||
1159 | static int snd_pcm_oss_get_channels(snd_pcm_oss_file_t *pcm_oss_file) | ||
1160 | { | ||
1161 | snd_pcm_substream_t *substream; | ||
1162 | int err; | ||
1163 | |||
1164 | if ((err = snd_pcm_oss_get_active_substream(pcm_oss_file, &substream)) < 0) | ||
1165 | return err; | ||
1166 | return substream->runtime->oss.channels; | ||
1167 | } | ||
1168 | |||
1169 | static int snd_pcm_oss_get_block_size(snd_pcm_oss_file_t *pcm_oss_file) | ||
1170 | { | ||
1171 | snd_pcm_substream_t *substream; | ||
1172 | int err; | ||
1173 | |||
1174 | if ((err = snd_pcm_oss_get_active_substream(pcm_oss_file, &substream)) < 0) | ||
1175 | return err; | ||
1176 | return substream->runtime->oss.period_bytes; | ||
1177 | } | ||
1178 | |||
1179 | static int snd_pcm_oss_get_formats(snd_pcm_oss_file_t *pcm_oss_file) | ||
1180 | { | ||
1181 | snd_pcm_substream_t *substream; | ||
1182 | int err; | ||
1183 | int direct; | ||
1184 | snd_pcm_hw_params_t *params; | ||
1185 | unsigned int formats = 0; | ||
1186 | snd_mask_t format_mask; | ||
1187 | int fmt; | ||
1188 | |||
1189 | if ((err = snd_pcm_oss_get_active_substream(pcm_oss_file, &substream)) < 0) | ||
1190 | return err; | ||
1191 | if (atomic_read(&substream->runtime->mmap_count)) { | ||
1192 | direct = 1; | ||
1193 | } else { | ||
1194 | snd_pcm_oss_setup_t *setup = substream->oss.setup; | ||
1195 | direct = (setup != NULL && setup->direct); | ||
1196 | } | ||
1197 | if (!direct) | ||
1198 | return AFMT_MU_LAW | AFMT_U8 | | ||
1199 | AFMT_S16_LE | AFMT_S16_BE | | ||
1200 | AFMT_S8 | AFMT_U16_LE | | ||
1201 | AFMT_U16_BE; | ||
1202 | params = kmalloc(sizeof(*params), GFP_KERNEL); | ||
1203 | if (!params) | ||
1204 | return -ENOMEM; | ||
1205 | _snd_pcm_hw_params_any(params); | ||
1206 | err = snd_pcm_hw_refine(substream, params); | ||
1207 | format_mask = *hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); | ||
1208 | kfree(params); | ||
1209 | snd_assert(err >= 0, return err); | ||
1210 | for (fmt = 0; fmt < 32; ++fmt) { | ||
1211 | if (snd_mask_test(&format_mask, fmt)) { | ||
1212 | int f = snd_pcm_oss_format_to(fmt); | ||
1213 | if (f >= 0) | ||
1214 | formats |= f; | ||
1215 | } | ||
1216 | } | ||
1217 | return formats; | ||
1218 | } | ||
1219 | |||
1220 | static int snd_pcm_oss_set_format(snd_pcm_oss_file_t *pcm_oss_file, int format) | ||
1221 | { | ||
1222 | int formats, idx; | ||
1223 | |||
1224 | if (format != AFMT_QUERY) { | ||
1225 | formats = snd_pcm_oss_get_formats(pcm_oss_file); | ||
1226 | if (!(formats & format)) | ||
1227 | format = AFMT_U8; | ||
1228 | for (idx = 1; idx >= 0; --idx) { | ||
1229 | snd_pcm_substream_t *substream = pcm_oss_file->streams[idx]; | ||
1230 | snd_pcm_runtime_t *runtime; | ||
1231 | if (substream == NULL) | ||
1232 | continue; | ||
1233 | runtime = substream->runtime; | ||
1234 | if (runtime->oss.format != format) { | ||
1235 | runtime->oss.params = 1; | ||
1236 | runtime->oss.format = format; | ||
1237 | } | ||
1238 | } | ||
1239 | } | ||
1240 | return snd_pcm_oss_get_format(pcm_oss_file); | ||
1241 | } | ||
1242 | |||
1243 | static int snd_pcm_oss_get_format(snd_pcm_oss_file_t *pcm_oss_file) | ||
1244 | { | ||
1245 | snd_pcm_substream_t *substream; | ||
1246 | int err; | ||
1247 | |||
1248 | if ((err = snd_pcm_oss_get_active_substream(pcm_oss_file, &substream)) < 0) | ||
1249 | return err; | ||
1250 | return substream->runtime->oss.format; | ||
1251 | } | ||
1252 | |||
1253 | static int snd_pcm_oss_set_subdivide1(snd_pcm_substream_t *substream, int subdivide) | ||
1254 | { | ||
1255 | snd_pcm_runtime_t *runtime; | ||
1256 | |||
1257 | if (substream == NULL) | ||
1258 | return 0; | ||
1259 | runtime = substream->runtime; | ||
1260 | if (subdivide == 0) { | ||
1261 | subdivide = runtime->oss.subdivision; | ||
1262 | if (subdivide == 0) | ||
1263 | subdivide = 1; | ||
1264 | return subdivide; | ||
1265 | } | ||
1266 | if (runtime->oss.subdivision || runtime->oss.fragshift) | ||
1267 | return -EINVAL; | ||
1268 | if (subdivide != 1 && subdivide != 2 && subdivide != 4 && | ||
1269 | subdivide != 8 && subdivide != 16) | ||
1270 | return -EINVAL; | ||
1271 | runtime->oss.subdivision = subdivide; | ||
1272 | runtime->oss.params = 1; | ||
1273 | return subdivide; | ||
1274 | } | ||
1275 | |||
1276 | static int snd_pcm_oss_set_subdivide(snd_pcm_oss_file_t *pcm_oss_file, int subdivide) | ||
1277 | { | ||
1278 | int err = -EINVAL, idx; | ||
1279 | |||
1280 | for (idx = 1; idx >= 0; --idx) { | ||
1281 | snd_pcm_substream_t *substream = pcm_oss_file->streams[idx]; | ||
1282 | if (substream == NULL) | ||
1283 | continue; | ||
1284 | if ((err = snd_pcm_oss_set_subdivide1(substream, subdivide)) < 0) | ||
1285 | return err; | ||
1286 | } | ||
1287 | return err; | ||
1288 | } | ||
1289 | |||
1290 | static int snd_pcm_oss_set_fragment1(snd_pcm_substream_t *substream, unsigned int val) | ||
1291 | { | ||
1292 | snd_pcm_runtime_t *runtime; | ||
1293 | |||
1294 | if (substream == NULL) | ||
1295 | return 0; | ||
1296 | runtime = substream->runtime; | ||
1297 | if (runtime->oss.subdivision || runtime->oss.fragshift) | ||
1298 | return -EINVAL; | ||
1299 | runtime->oss.fragshift = val & 0xffff; | ||
1300 | runtime->oss.maxfrags = (val >> 16) & 0xffff; | ||
1301 | if (runtime->oss.fragshift < 4) /* < 16 */ | ||
1302 | runtime->oss.fragshift = 4; | ||
1303 | if (runtime->oss.maxfrags < 2) | ||
1304 | runtime->oss.maxfrags = 2; | ||
1305 | runtime->oss.params = 1; | ||
1306 | return 0; | ||
1307 | } | ||
1308 | |||
1309 | static int snd_pcm_oss_set_fragment(snd_pcm_oss_file_t *pcm_oss_file, unsigned int val) | ||
1310 | { | ||
1311 | int err = -EINVAL, idx; | ||
1312 | |||
1313 | for (idx = 1; idx >= 0; --idx) { | ||
1314 | snd_pcm_substream_t *substream = pcm_oss_file->streams[idx]; | ||
1315 | if (substream == NULL) | ||
1316 | continue; | ||
1317 | if ((err = snd_pcm_oss_set_fragment1(substream, val)) < 0) | ||
1318 | return err; | ||
1319 | } | ||
1320 | return err; | ||
1321 | } | ||
1322 | |||
1323 | static int snd_pcm_oss_nonblock(struct file * file) | ||
1324 | { | ||
1325 | file->f_flags |= O_NONBLOCK; | ||
1326 | return 0; | ||
1327 | } | ||
1328 | |||
1329 | static int snd_pcm_oss_get_caps1(snd_pcm_substream_t *substream, int res) | ||
1330 | { | ||
1331 | |||
1332 | if (substream == NULL) { | ||
1333 | res &= ~DSP_CAP_DUPLEX; | ||
1334 | return res; | ||
1335 | } | ||
1336 | #ifdef DSP_CAP_MULTI | ||
1337 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | ||
1338 | if (substream->pstr->substream_count > 1) | ||
1339 | res |= DSP_CAP_MULTI; | ||
1340 | #endif | ||
1341 | /* DSP_CAP_REALTIME is set all times: */ | ||
1342 | /* all ALSA drivers can return actual pointer in ring buffer */ | ||
1343 | #if defined(DSP_CAP_REALTIME) && 0 | ||
1344 | { | ||
1345 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
1346 | if (runtime->info & (SNDRV_PCM_INFO_BLOCK_TRANSFER|SNDRV_PCM_INFO_BATCH)) | ||
1347 | res &= ~DSP_CAP_REALTIME; | ||
1348 | } | ||
1349 | #endif | ||
1350 | return res; | ||
1351 | } | ||
1352 | |||
1353 | static int snd_pcm_oss_get_caps(snd_pcm_oss_file_t *pcm_oss_file) | ||
1354 | { | ||
1355 | int result, idx; | ||
1356 | |||
1357 | result = DSP_CAP_TRIGGER | DSP_CAP_MMAP | DSP_CAP_DUPLEX | DSP_CAP_REALTIME; | ||
1358 | for (idx = 0; idx < 2; idx++) { | ||
1359 | snd_pcm_substream_t *substream = pcm_oss_file->streams[idx]; | ||
1360 | result = snd_pcm_oss_get_caps1(substream, result); | ||
1361 | } | ||
1362 | result |= 0x0001; /* revision - same as SB AWE 64 */ | ||
1363 | return result; | ||
1364 | } | ||
1365 | |||
1366 | static void snd_pcm_oss_simulate_fill(snd_pcm_substream_t *substream, snd_pcm_uframes_t hw_ptr) | ||
1367 | { | ||
1368 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
1369 | snd_pcm_uframes_t appl_ptr; | ||
1370 | appl_ptr = hw_ptr + runtime->buffer_size; | ||
1371 | appl_ptr %= runtime->boundary; | ||
1372 | runtime->control->appl_ptr = appl_ptr; | ||
1373 | } | ||
1374 | |||
1375 | static int snd_pcm_oss_set_trigger(snd_pcm_oss_file_t *pcm_oss_file, int trigger) | ||
1376 | { | ||
1377 | snd_pcm_runtime_t *runtime; | ||
1378 | snd_pcm_substream_t *psubstream = NULL, *csubstream = NULL; | ||
1379 | int err, cmd; | ||
1380 | |||
1381 | #ifdef OSS_DEBUG | ||
1382 | printk("pcm_oss: trigger = 0x%x\n", trigger); | ||
1383 | #endif | ||
1384 | |||
1385 | psubstream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK]; | ||
1386 | csubstream = pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE]; | ||
1387 | |||
1388 | if (psubstream) { | ||
1389 | if ((err = snd_pcm_oss_make_ready(psubstream)) < 0) | ||
1390 | return err; | ||
1391 | } | ||
1392 | if (csubstream) { | ||
1393 | if ((err = snd_pcm_oss_make_ready(csubstream)) < 0) | ||
1394 | return err; | ||
1395 | } | ||
1396 | if (psubstream) { | ||
1397 | runtime = psubstream->runtime; | ||
1398 | if (trigger & PCM_ENABLE_OUTPUT) { | ||
1399 | if (runtime->oss.trigger) | ||
1400 | goto _skip1; | ||
1401 | if (atomic_read(&psubstream->runtime->mmap_count)) | ||
1402 | snd_pcm_oss_simulate_fill(psubstream, runtime->hw_ptr_interrupt); | ||
1403 | runtime->oss.trigger = 1; | ||
1404 | runtime->start_threshold = 1; | ||
1405 | cmd = SNDRV_PCM_IOCTL_START; | ||
1406 | } else { | ||
1407 | if (!runtime->oss.trigger) | ||
1408 | goto _skip1; | ||
1409 | runtime->oss.trigger = 0; | ||
1410 | runtime->start_threshold = runtime->boundary; | ||
1411 | cmd = SNDRV_PCM_IOCTL_DROP; | ||
1412 | runtime->oss.prepare = 1; | ||
1413 | } | ||
1414 | err = snd_pcm_kernel_playback_ioctl(psubstream, cmd, NULL); | ||
1415 | if (err < 0) | ||
1416 | return err; | ||
1417 | } | ||
1418 | _skip1: | ||
1419 | if (csubstream) { | ||
1420 | runtime = csubstream->runtime; | ||
1421 | if (trigger & PCM_ENABLE_INPUT) { | ||
1422 | if (runtime->oss.trigger) | ||
1423 | goto _skip2; | ||
1424 | runtime->oss.trigger = 1; | ||
1425 | runtime->start_threshold = 1; | ||
1426 | cmd = SNDRV_PCM_IOCTL_START; | ||
1427 | } else { | ||
1428 | if (!runtime->oss.trigger) | ||
1429 | goto _skip2; | ||
1430 | runtime->oss.trigger = 0; | ||
1431 | runtime->start_threshold = runtime->boundary; | ||
1432 | cmd = SNDRV_PCM_IOCTL_DROP; | ||
1433 | runtime->oss.prepare = 1; | ||
1434 | } | ||
1435 | err = snd_pcm_kernel_capture_ioctl(csubstream, cmd, NULL); | ||
1436 | if (err < 0) | ||
1437 | return err; | ||
1438 | } | ||
1439 | _skip2: | ||
1440 | return 0; | ||
1441 | } | ||
1442 | |||
1443 | static int snd_pcm_oss_get_trigger(snd_pcm_oss_file_t *pcm_oss_file) | ||
1444 | { | ||
1445 | snd_pcm_substream_t *psubstream = NULL, *csubstream = NULL; | ||
1446 | int result = 0; | ||
1447 | |||
1448 | psubstream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK]; | ||
1449 | csubstream = pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE]; | ||
1450 | if (psubstream && psubstream->runtime && psubstream->runtime->oss.trigger) | ||
1451 | result |= PCM_ENABLE_OUTPUT; | ||
1452 | if (csubstream && csubstream->runtime && csubstream->runtime->oss.trigger) | ||
1453 | result |= PCM_ENABLE_INPUT; | ||
1454 | return result; | ||
1455 | } | ||
1456 | |||
1457 | static int snd_pcm_oss_get_odelay(snd_pcm_oss_file_t *pcm_oss_file) | ||
1458 | { | ||
1459 | snd_pcm_substream_t *substream; | ||
1460 | snd_pcm_runtime_t *runtime; | ||
1461 | snd_pcm_sframes_t delay; | ||
1462 | int err; | ||
1463 | |||
1464 | substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK]; | ||
1465 | if (substream == NULL) | ||
1466 | return -EINVAL; | ||
1467 | if ((err = snd_pcm_oss_make_ready(substream)) < 0) | ||
1468 | return err; | ||
1469 | runtime = substream->runtime; | ||
1470 | if (runtime->oss.params || runtime->oss.prepare) | ||
1471 | return 0; | ||
1472 | err = snd_pcm_kernel_playback_ioctl(substream, SNDRV_PCM_IOCTL_DELAY, &delay); | ||
1473 | if (err == -EPIPE) | ||
1474 | delay = 0; /* hack for broken OSS applications */ | ||
1475 | else if (err < 0) | ||
1476 | return err; | ||
1477 | return snd_pcm_oss_bytes(substream, delay); | ||
1478 | } | ||
1479 | |||
1480 | static int snd_pcm_oss_get_ptr(snd_pcm_oss_file_t *pcm_oss_file, int stream, struct count_info __user * _info) | ||
1481 | { | ||
1482 | snd_pcm_substream_t *substream; | ||
1483 | snd_pcm_runtime_t *runtime; | ||
1484 | snd_pcm_sframes_t delay; | ||
1485 | int fixup; | ||
1486 | struct count_info info; | ||
1487 | int err; | ||
1488 | |||
1489 | if (_info == NULL) | ||
1490 | return -EFAULT; | ||
1491 | substream = pcm_oss_file->streams[stream]; | ||
1492 | if (substream == NULL) | ||
1493 | return -EINVAL; | ||
1494 | if ((err = snd_pcm_oss_make_ready(substream)) < 0) | ||
1495 | return err; | ||
1496 | runtime = substream->runtime; | ||
1497 | if (runtime->oss.params || runtime->oss.prepare) { | ||
1498 | memset(&info, 0, sizeof(info)); | ||
1499 | if (copy_to_user(_info, &info, sizeof(info))) | ||
1500 | return -EFAULT; | ||
1501 | return 0; | ||
1502 | } | ||
1503 | if (stream == SNDRV_PCM_STREAM_PLAYBACK) { | ||
1504 | err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DELAY, &delay); | ||
1505 | if (err == -EPIPE || err == -ESTRPIPE || (! err && delay < 0)) { | ||
1506 | err = 0; | ||
1507 | delay = 0; | ||
1508 | fixup = 0; | ||
1509 | } else { | ||
1510 | fixup = runtime->oss.buffer_used; | ||
1511 | } | ||
1512 | } else { | ||
1513 | err = snd_pcm_oss_capture_position_fixup(substream, &delay); | ||
1514 | fixup = -runtime->oss.buffer_used; | ||
1515 | } | ||
1516 | if (err < 0) | ||
1517 | return err; | ||
1518 | info.ptr = snd_pcm_oss_bytes(substream, runtime->status->hw_ptr % runtime->buffer_size); | ||
1519 | if (atomic_read(&runtime->mmap_count)) { | ||
1520 | snd_pcm_sframes_t n; | ||
1521 | n = (delay = runtime->hw_ptr_interrupt) - runtime->oss.prev_hw_ptr_interrupt; | ||
1522 | if (n < 0) | ||
1523 | n += runtime->boundary; | ||
1524 | info.blocks = n / runtime->period_size; | ||
1525 | runtime->oss.prev_hw_ptr_interrupt = delay; | ||
1526 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | ||
1527 | snd_pcm_oss_simulate_fill(substream, delay); | ||
1528 | info.bytes = snd_pcm_oss_bytes(substream, runtime->status->hw_ptr) & INT_MAX; | ||
1529 | } else { | ||
1530 | delay = snd_pcm_oss_bytes(substream, delay) + fixup; | ||
1531 | info.blocks = delay / runtime->oss.period_bytes; | ||
1532 | if (stream == SNDRV_PCM_STREAM_PLAYBACK) | ||
1533 | info.bytes = (runtime->oss.bytes - delay) & INT_MAX; | ||
1534 | else | ||
1535 | info.bytes = (runtime->oss.bytes + delay) & INT_MAX; | ||
1536 | } | ||
1537 | if (copy_to_user(_info, &info, sizeof(info))) | ||
1538 | return -EFAULT; | ||
1539 | return 0; | ||
1540 | } | ||
1541 | |||
1542 | static int snd_pcm_oss_get_space(snd_pcm_oss_file_t *pcm_oss_file, int stream, struct audio_buf_info __user *_info) | ||
1543 | { | ||
1544 | snd_pcm_substream_t *substream; | ||
1545 | snd_pcm_runtime_t *runtime; | ||
1546 | snd_pcm_sframes_t avail; | ||
1547 | int fixup; | ||
1548 | struct audio_buf_info info; | ||
1549 | int err; | ||
1550 | |||
1551 | if (_info == NULL) | ||
1552 | return -EFAULT; | ||
1553 | substream = pcm_oss_file->streams[stream]; | ||
1554 | if (substream == NULL) | ||
1555 | return -EINVAL; | ||
1556 | runtime = substream->runtime; | ||
1557 | |||
1558 | if (runtime->oss.params && | ||
1559 | (err = snd_pcm_oss_change_params(substream)) < 0) | ||
1560 | return err; | ||
1561 | |||
1562 | info.fragsize = runtime->oss.period_bytes; | ||
1563 | info.fragstotal = runtime->periods; | ||
1564 | if (runtime->oss.prepare) { | ||
1565 | if (stream == SNDRV_PCM_STREAM_PLAYBACK) { | ||
1566 | info.bytes = runtime->oss.period_bytes * runtime->oss.periods; | ||
1567 | info.fragments = runtime->oss.periods; | ||
1568 | } else { | ||
1569 | info.bytes = 0; | ||
1570 | info.fragments = 0; | ||
1571 | } | ||
1572 | } else { | ||
1573 | if (stream == SNDRV_PCM_STREAM_PLAYBACK) { | ||
1574 | err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DELAY, &avail); | ||
1575 | if (err == -EPIPE || err == -ESTRPIPE || (! err && avail < 0)) { | ||
1576 | avail = runtime->buffer_size; | ||
1577 | err = 0; | ||
1578 | fixup = 0; | ||
1579 | } else { | ||
1580 | avail = runtime->buffer_size - avail; | ||
1581 | fixup = -runtime->oss.buffer_used; | ||
1582 | } | ||
1583 | } else { | ||
1584 | err = snd_pcm_oss_capture_position_fixup(substream, &avail); | ||
1585 | fixup = runtime->oss.buffer_used; | ||
1586 | } | ||
1587 | if (err < 0) | ||
1588 | return err; | ||
1589 | info.bytes = snd_pcm_oss_bytes(substream, avail) + fixup; | ||
1590 | info.fragments = info.bytes / runtime->oss.period_bytes; | ||
1591 | } | ||
1592 | |||
1593 | #ifdef OSS_DEBUG | ||
1594 | printk("pcm_oss: space: bytes = %i, fragments = %i, fragstotal = %i, fragsize = %i\n", info.bytes, info.fragments, info.fragstotal, info.fragsize); | ||
1595 | #endif | ||
1596 | if (copy_to_user(_info, &info, sizeof(info))) | ||
1597 | return -EFAULT; | ||
1598 | return 0; | ||
1599 | } | ||
1600 | |||
1601 | static int snd_pcm_oss_get_mapbuf(snd_pcm_oss_file_t *pcm_oss_file, int stream, struct buffmem_desc __user * _info) | ||
1602 | { | ||
1603 | // it won't be probably implemented | ||
1604 | // snd_printd("TODO: snd_pcm_oss_get_mapbuf\n"); | ||
1605 | return -EINVAL; | ||
1606 | } | ||
1607 | |||
1608 | static snd_pcm_oss_setup_t *snd_pcm_oss_look_for_setup(snd_pcm_t *pcm, int stream, const char *task_name) | ||
1609 | { | ||
1610 | const char *ptr, *ptrl; | ||
1611 | snd_pcm_oss_setup_t *setup; | ||
1612 | |||
1613 | down(&pcm->streams[stream].oss.setup_mutex); | ||
1614 | for (setup = pcm->streams[stream].oss.setup_list; setup; setup = setup->next) { | ||
1615 | if (!strcmp(setup->task_name, task_name)) { | ||
1616 | up(&pcm->streams[stream].oss.setup_mutex); | ||
1617 | return setup; | ||
1618 | } | ||
1619 | } | ||
1620 | ptr = ptrl = task_name; | ||
1621 | while (*ptr) { | ||
1622 | if (*ptr == '/') | ||
1623 | ptrl = ptr + 1; | ||
1624 | ptr++; | ||
1625 | } | ||
1626 | if (ptrl == task_name) { | ||
1627 | goto __not_found; | ||
1628 | return NULL; | ||
1629 | } | ||
1630 | for (setup = pcm->streams[stream].oss.setup_list; setup; setup = setup->next) { | ||
1631 | if (!strcmp(setup->task_name, ptrl)) { | ||
1632 | up(&pcm->streams[stream].oss.setup_mutex); | ||
1633 | return setup; | ||
1634 | } | ||
1635 | } | ||
1636 | __not_found: | ||
1637 | up(&pcm->streams[stream].oss.setup_mutex); | ||
1638 | return NULL; | ||
1639 | } | ||
1640 | |||
1641 | static void snd_pcm_oss_init_substream(snd_pcm_substream_t *substream, | ||
1642 | snd_pcm_oss_setup_t *setup, | ||
1643 | int minor) | ||
1644 | { | ||
1645 | snd_pcm_runtime_t *runtime; | ||
1646 | |||
1647 | substream->oss.oss = 1; | ||
1648 | substream->oss.setup = setup; | ||
1649 | runtime = substream->runtime; | ||
1650 | runtime->oss.params = 1; | ||
1651 | runtime->oss.trigger = 1; | ||
1652 | runtime->oss.rate = 8000; | ||
1653 | switch (SNDRV_MINOR_OSS_DEVICE(minor)) { | ||
1654 | case SNDRV_MINOR_OSS_PCM_8: | ||
1655 | runtime->oss.format = AFMT_U8; | ||
1656 | break; | ||
1657 | case SNDRV_MINOR_OSS_PCM_16: | ||
1658 | runtime->oss.format = AFMT_S16_LE; | ||
1659 | break; | ||
1660 | default: | ||
1661 | runtime->oss.format = AFMT_MU_LAW; | ||
1662 | } | ||
1663 | runtime->oss.channels = 1; | ||
1664 | runtime->oss.fragshift = 0; | ||
1665 | runtime->oss.maxfrags = 0; | ||
1666 | runtime->oss.subdivision = 0; | ||
1667 | } | ||
1668 | |||
1669 | static void snd_pcm_oss_release_substream(snd_pcm_substream_t *substream) | ||
1670 | { | ||
1671 | snd_pcm_runtime_t *runtime; | ||
1672 | runtime = substream->runtime; | ||
1673 | vfree(runtime->oss.buffer); | ||
1674 | snd_pcm_oss_plugin_clear(substream); | ||
1675 | substream->oss.file = NULL; | ||
1676 | substream->oss.oss = 0; | ||
1677 | } | ||
1678 | |||
1679 | static int snd_pcm_oss_release_file(snd_pcm_oss_file_t *pcm_oss_file) | ||
1680 | { | ||
1681 | int cidx; | ||
1682 | snd_assert(pcm_oss_file != NULL, return -ENXIO); | ||
1683 | for (cidx = 0; cidx < 2; ++cidx) { | ||
1684 | snd_pcm_substream_t *substream = pcm_oss_file->streams[cidx]; | ||
1685 | snd_pcm_runtime_t *runtime; | ||
1686 | if (substream == NULL) | ||
1687 | continue; | ||
1688 | runtime = substream->runtime; | ||
1689 | |||
1690 | snd_pcm_stream_lock_irq(substream); | ||
1691 | if (snd_pcm_running(substream)) | ||
1692 | snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP); | ||
1693 | snd_pcm_stream_unlock_irq(substream); | ||
1694 | if (substream->open_flag) { | ||
1695 | if (substream->ops->hw_free != NULL) | ||
1696 | substream->ops->hw_free(substream); | ||
1697 | substream->ops->close(substream); | ||
1698 | substream->open_flag = 0; | ||
1699 | } | ||
1700 | substream->ffile = NULL; | ||
1701 | snd_pcm_oss_release_substream(substream); | ||
1702 | snd_pcm_release_substream(substream); | ||
1703 | } | ||
1704 | kfree(pcm_oss_file); | ||
1705 | return 0; | ||
1706 | } | ||
1707 | |||
1708 | static int snd_pcm_oss_open_file(struct file *file, | ||
1709 | snd_pcm_t *pcm, | ||
1710 | snd_pcm_oss_file_t **rpcm_oss_file, | ||
1711 | int minor, | ||
1712 | snd_pcm_oss_setup_t *psetup, | ||
1713 | snd_pcm_oss_setup_t *csetup) | ||
1714 | { | ||
1715 | int err = 0; | ||
1716 | snd_pcm_oss_file_t *pcm_oss_file; | ||
1717 | snd_pcm_substream_t *psubstream = NULL, *csubstream = NULL; | ||
1718 | unsigned int f_mode = file->f_mode; | ||
1719 | |||
1720 | snd_assert(rpcm_oss_file != NULL, return -EINVAL); | ||
1721 | *rpcm_oss_file = NULL; | ||
1722 | |||
1723 | pcm_oss_file = kcalloc(1, sizeof(*pcm_oss_file), GFP_KERNEL); | ||
1724 | if (pcm_oss_file == NULL) | ||
1725 | return -ENOMEM; | ||
1726 | |||
1727 | if ((f_mode & (FMODE_WRITE|FMODE_READ)) == (FMODE_WRITE|FMODE_READ) && | ||
1728 | (pcm->info_flags & SNDRV_PCM_INFO_HALF_DUPLEX)) | ||
1729 | f_mode = FMODE_WRITE; | ||
1730 | if ((f_mode & FMODE_WRITE) && !(psetup && psetup->disable)) { | ||
1731 | if ((err = snd_pcm_open_substream(pcm, SNDRV_PCM_STREAM_PLAYBACK, | ||
1732 | &psubstream)) < 0) { | ||
1733 | snd_pcm_oss_release_file(pcm_oss_file); | ||
1734 | return err; | ||
1735 | } | ||
1736 | pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK] = psubstream; | ||
1737 | } | ||
1738 | if ((f_mode & FMODE_READ) && !(csetup && csetup->disable)) { | ||
1739 | if ((err = snd_pcm_open_substream(pcm, SNDRV_PCM_STREAM_CAPTURE, | ||
1740 | &csubstream)) < 0) { | ||
1741 | if (!(f_mode & FMODE_WRITE) || err != -ENODEV) { | ||
1742 | snd_pcm_oss_release_file(pcm_oss_file); | ||
1743 | return err; | ||
1744 | } else { | ||
1745 | csubstream = NULL; | ||
1746 | } | ||
1747 | } | ||
1748 | pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE] = csubstream; | ||
1749 | } | ||
1750 | |||
1751 | if (psubstream == NULL && csubstream == NULL) { | ||
1752 | snd_pcm_oss_release_file(pcm_oss_file); | ||
1753 | return -EINVAL; | ||
1754 | } | ||
1755 | if (psubstream != NULL) { | ||
1756 | psubstream->oss.file = pcm_oss_file; | ||
1757 | err = snd_pcm_hw_constraints_init(psubstream); | ||
1758 | if (err < 0) { | ||
1759 | snd_printd("snd_pcm_hw_constraint_init failed\n"); | ||
1760 | snd_pcm_oss_release_file(pcm_oss_file); | ||
1761 | return err; | ||
1762 | } | ||
1763 | if ((err = psubstream->ops->open(psubstream)) < 0) { | ||
1764 | snd_pcm_oss_release_file(pcm_oss_file); | ||
1765 | return err; | ||
1766 | } | ||
1767 | psubstream->open_flag = 1; | ||
1768 | err = snd_pcm_hw_constraints_complete(psubstream); | ||
1769 | if (err < 0) { | ||
1770 | snd_printd("snd_pcm_hw_constraint_complete failed\n"); | ||
1771 | snd_pcm_oss_release_file(pcm_oss_file); | ||
1772 | return err; | ||
1773 | } | ||
1774 | psubstream->ffile = file; | ||
1775 | snd_pcm_oss_init_substream(psubstream, psetup, minor); | ||
1776 | } | ||
1777 | if (csubstream != NULL) { | ||
1778 | csubstream->oss.file = pcm_oss_file; | ||
1779 | err = snd_pcm_hw_constraints_init(csubstream); | ||
1780 | if (err < 0) { | ||
1781 | snd_printd("snd_pcm_hw_constraint_init failed\n"); | ||
1782 | snd_pcm_oss_release_file(pcm_oss_file); | ||
1783 | return err; | ||
1784 | } | ||
1785 | if ((err = csubstream->ops->open(csubstream)) < 0) { | ||
1786 | snd_pcm_oss_release_file(pcm_oss_file); | ||
1787 | return err; | ||
1788 | } | ||
1789 | csubstream->open_flag = 1; | ||
1790 | err = snd_pcm_hw_constraints_complete(csubstream); | ||
1791 | if (err < 0) { | ||
1792 | snd_printd("snd_pcm_hw_constraint_complete failed\n"); | ||
1793 | snd_pcm_oss_release_file(pcm_oss_file); | ||
1794 | return err; | ||
1795 | } | ||
1796 | csubstream->ffile = file; | ||
1797 | snd_pcm_oss_init_substream(csubstream, csetup, minor); | ||
1798 | } | ||
1799 | |||
1800 | file->private_data = pcm_oss_file; | ||
1801 | *rpcm_oss_file = pcm_oss_file; | ||
1802 | return 0; | ||
1803 | } | ||
1804 | |||
1805 | |||
1806 | static int snd_pcm_oss_open(struct inode *inode, struct file *file) | ||
1807 | { | ||
1808 | int minor = iminor(inode); | ||
1809 | int cardnum = SNDRV_MINOR_OSS_CARD(minor); | ||
1810 | int device; | ||
1811 | int err; | ||
1812 | char task_name[32]; | ||
1813 | snd_pcm_t *pcm; | ||
1814 | snd_pcm_oss_file_t *pcm_oss_file; | ||
1815 | snd_pcm_oss_setup_t *psetup = NULL, *csetup = NULL; | ||
1816 | int nonblock; | ||
1817 | wait_queue_t wait; | ||
1818 | |||
1819 | snd_assert(cardnum >= 0 && cardnum < SNDRV_CARDS, return -ENXIO); | ||
1820 | device = SNDRV_MINOR_OSS_DEVICE(minor) == SNDRV_MINOR_OSS_PCM1 ? | ||
1821 | adsp_map[cardnum] : dsp_map[cardnum]; | ||
1822 | |||
1823 | pcm = snd_pcm_devices[(cardnum * SNDRV_PCM_DEVICES) + device]; | ||
1824 | if (pcm == NULL) { | ||
1825 | err = -ENODEV; | ||
1826 | goto __error1; | ||
1827 | } | ||
1828 | err = snd_card_file_add(pcm->card, file); | ||
1829 | if (err < 0) | ||
1830 | goto __error1; | ||
1831 | if (!try_module_get(pcm->card->module)) { | ||
1832 | err = -EFAULT; | ||
1833 | goto __error2; | ||
1834 | } | ||
1835 | if (snd_task_name(current, task_name, sizeof(task_name)) < 0) { | ||
1836 | err = -EFAULT; | ||
1837 | goto __error; | ||
1838 | } | ||
1839 | if (file->f_mode & FMODE_WRITE) | ||
1840 | psetup = snd_pcm_oss_look_for_setup(pcm, SNDRV_PCM_STREAM_PLAYBACK, task_name); | ||
1841 | if (file->f_mode & FMODE_READ) | ||
1842 | csetup = snd_pcm_oss_look_for_setup(pcm, SNDRV_PCM_STREAM_CAPTURE, task_name); | ||
1843 | |||
1844 | nonblock = !!(file->f_flags & O_NONBLOCK); | ||
1845 | if (psetup && !psetup->disable) { | ||
1846 | if (psetup->nonblock) | ||
1847 | nonblock = 1; | ||
1848 | else if (psetup->block) | ||
1849 | nonblock = 0; | ||
1850 | } else if (csetup && !csetup->disable) { | ||
1851 | if (csetup->nonblock) | ||
1852 | nonblock = 1; | ||
1853 | else if (csetup->block) | ||
1854 | nonblock = 0; | ||
1855 | } | ||
1856 | if (!nonblock) | ||
1857 | nonblock = nonblock_open; | ||
1858 | |||
1859 | init_waitqueue_entry(&wait, current); | ||
1860 | add_wait_queue(&pcm->open_wait, &wait); | ||
1861 | down(&pcm->open_mutex); | ||
1862 | while (1) { | ||
1863 | err = snd_pcm_oss_open_file(file, pcm, &pcm_oss_file, | ||
1864 | minor, psetup, csetup); | ||
1865 | if (err >= 0) | ||
1866 | break; | ||
1867 | if (err == -EAGAIN) { | ||
1868 | if (nonblock) { | ||
1869 | err = -EBUSY; | ||
1870 | break; | ||
1871 | } | ||
1872 | } else | ||
1873 | break; | ||
1874 | set_current_state(TASK_INTERRUPTIBLE); | ||
1875 | up(&pcm->open_mutex); | ||
1876 | schedule(); | ||
1877 | down(&pcm->open_mutex); | ||
1878 | if (signal_pending(current)) { | ||
1879 | err = -ERESTARTSYS; | ||
1880 | break; | ||
1881 | } | ||
1882 | } | ||
1883 | remove_wait_queue(&pcm->open_wait, &wait); | ||
1884 | up(&pcm->open_mutex); | ||
1885 | if (err < 0) | ||
1886 | goto __error; | ||
1887 | return err; | ||
1888 | |||
1889 | __error: | ||
1890 | module_put(pcm->card->module); | ||
1891 | __error2: | ||
1892 | snd_card_file_remove(pcm->card, file); | ||
1893 | __error1: | ||
1894 | return err; | ||
1895 | } | ||
1896 | |||
1897 | static int snd_pcm_oss_release(struct inode *inode, struct file *file) | ||
1898 | { | ||
1899 | snd_pcm_t *pcm; | ||
1900 | snd_pcm_substream_t *substream; | ||
1901 | snd_pcm_oss_file_t *pcm_oss_file; | ||
1902 | |||
1903 | pcm_oss_file = file->private_data; | ||
1904 | substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK]; | ||
1905 | if (substream == NULL) | ||
1906 | substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE]; | ||
1907 | snd_assert(substream != NULL, return -ENXIO); | ||
1908 | pcm = substream->pcm; | ||
1909 | snd_pcm_oss_sync(pcm_oss_file); | ||
1910 | down(&pcm->open_mutex); | ||
1911 | snd_pcm_oss_release_file(pcm_oss_file); | ||
1912 | up(&pcm->open_mutex); | ||
1913 | wake_up(&pcm->open_wait); | ||
1914 | module_put(pcm->card->module); | ||
1915 | snd_card_file_remove(pcm->card, file); | ||
1916 | return 0; | ||
1917 | } | ||
1918 | |||
1919 | static long snd_pcm_oss_ioctl(struct file *file, unsigned int cmd, unsigned long arg) | ||
1920 | { | ||
1921 | snd_pcm_oss_file_t *pcm_oss_file; | ||
1922 | int __user *p = (int __user *)arg; | ||
1923 | int res; | ||
1924 | |||
1925 | pcm_oss_file = file->private_data; | ||
1926 | if (cmd == OSS_GETVERSION) | ||
1927 | return put_user(SNDRV_OSS_VERSION, p); | ||
1928 | if (cmd == OSS_ALSAEMULVER) | ||
1929 | return put_user(1, p); | ||
1930 | #if defined(CONFIG_SND_MIXER_OSS) || (defined(MODULE) && defined(CONFIG_SND_MIXER_OSS_MODULE)) | ||
1931 | if (((cmd >> 8) & 0xff) == 'M') { /* mixer ioctl - for OSS compatibility */ | ||
1932 | snd_pcm_substream_t *substream; | ||
1933 | int idx; | ||
1934 | for (idx = 0; idx < 2; ++idx) { | ||
1935 | substream = pcm_oss_file->streams[idx]; | ||
1936 | if (substream != NULL) | ||
1937 | break; | ||
1938 | } | ||
1939 | snd_assert(substream != NULL, return -ENXIO); | ||
1940 | return snd_mixer_oss_ioctl_card(substream->pcm->card, cmd, arg); | ||
1941 | } | ||
1942 | #endif | ||
1943 | if (((cmd >> 8) & 0xff) != 'P') | ||
1944 | return -EINVAL; | ||
1945 | #ifdef OSS_DEBUG | ||
1946 | printk("pcm_oss: ioctl = 0x%x\n", cmd); | ||
1947 | #endif | ||
1948 | switch (cmd) { | ||
1949 | case SNDCTL_DSP_RESET: | ||
1950 | return snd_pcm_oss_reset(pcm_oss_file); | ||
1951 | case SNDCTL_DSP_SYNC: | ||
1952 | return snd_pcm_oss_sync(pcm_oss_file); | ||
1953 | case SNDCTL_DSP_SPEED: | ||
1954 | if (get_user(res, p)) | ||
1955 | return -EFAULT; | ||
1956 | if ((res = snd_pcm_oss_set_rate(pcm_oss_file, res))<0) | ||
1957 | return res; | ||
1958 | return put_user(res, p); | ||
1959 | case SOUND_PCM_READ_RATE: | ||
1960 | res = snd_pcm_oss_get_rate(pcm_oss_file); | ||
1961 | if (res < 0) | ||
1962 | return res; | ||
1963 | return put_user(res, p); | ||
1964 | case SNDCTL_DSP_STEREO: | ||
1965 | if (get_user(res, p)) | ||
1966 | return -EFAULT; | ||
1967 | res = res > 0 ? 2 : 1; | ||
1968 | if ((res = snd_pcm_oss_set_channels(pcm_oss_file, res)) < 0) | ||
1969 | return res; | ||
1970 | return put_user(--res, p); | ||
1971 | case SNDCTL_DSP_GETBLKSIZE: | ||
1972 | res = snd_pcm_oss_get_block_size(pcm_oss_file); | ||
1973 | if (res < 0) | ||
1974 | return res; | ||
1975 | return put_user(res, p); | ||
1976 | case SNDCTL_DSP_SETFMT: | ||
1977 | if (get_user(res, p)) | ||
1978 | return -EFAULT; | ||
1979 | res = snd_pcm_oss_set_format(pcm_oss_file, res); | ||
1980 | if (res < 0) | ||
1981 | return res; | ||
1982 | return put_user(res, p); | ||
1983 | case SOUND_PCM_READ_BITS: | ||
1984 | res = snd_pcm_oss_get_format(pcm_oss_file); | ||
1985 | if (res < 0) | ||
1986 | return res; | ||
1987 | return put_user(res, p); | ||
1988 | case SNDCTL_DSP_CHANNELS: | ||
1989 | if (get_user(res, p)) | ||
1990 | return -EFAULT; | ||
1991 | res = snd_pcm_oss_set_channels(pcm_oss_file, res); | ||
1992 | if (res < 0) | ||
1993 | return res; | ||
1994 | return put_user(res, p); | ||
1995 | case SOUND_PCM_READ_CHANNELS: | ||
1996 | res = snd_pcm_oss_get_channels(pcm_oss_file); | ||
1997 | if (res < 0) | ||
1998 | return res; | ||
1999 | return put_user(res, p); | ||
2000 | case SOUND_PCM_WRITE_FILTER: | ||
2001 | case SOUND_PCM_READ_FILTER: | ||
2002 | return -EIO; | ||
2003 | case SNDCTL_DSP_POST: | ||
2004 | return snd_pcm_oss_post(pcm_oss_file); | ||
2005 | case SNDCTL_DSP_SUBDIVIDE: | ||
2006 | if (get_user(res, p)) | ||
2007 | return -EFAULT; | ||
2008 | res = snd_pcm_oss_set_subdivide(pcm_oss_file, res); | ||
2009 | if (res < 0) | ||
2010 | return res; | ||
2011 | return put_user(res, p); | ||
2012 | case SNDCTL_DSP_SETFRAGMENT: | ||
2013 | if (get_user(res, p)) | ||
2014 | return -EFAULT; | ||
2015 | return snd_pcm_oss_set_fragment(pcm_oss_file, res); | ||
2016 | case SNDCTL_DSP_GETFMTS: | ||
2017 | res = snd_pcm_oss_get_formats(pcm_oss_file); | ||
2018 | if (res < 0) | ||
2019 | return res; | ||
2020 | return put_user(res, p); | ||
2021 | case SNDCTL_DSP_GETOSPACE: | ||
2022 | case SNDCTL_DSP_GETISPACE: | ||
2023 | return snd_pcm_oss_get_space(pcm_oss_file, | ||
2024 | cmd == SNDCTL_DSP_GETISPACE ? | ||
2025 | SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK, | ||
2026 | (struct audio_buf_info __user *) arg); | ||
2027 | case SNDCTL_DSP_NONBLOCK: | ||
2028 | return snd_pcm_oss_nonblock(file); | ||
2029 | case SNDCTL_DSP_GETCAPS: | ||
2030 | res = snd_pcm_oss_get_caps(pcm_oss_file); | ||
2031 | if (res < 0) | ||
2032 | return res; | ||
2033 | return put_user(res, p); | ||
2034 | case SNDCTL_DSP_GETTRIGGER: | ||
2035 | res = snd_pcm_oss_get_trigger(pcm_oss_file); | ||
2036 | if (res < 0) | ||
2037 | return res; | ||
2038 | return put_user(res, p); | ||
2039 | case SNDCTL_DSP_SETTRIGGER: | ||
2040 | if (get_user(res, p)) | ||
2041 | return -EFAULT; | ||
2042 | return snd_pcm_oss_set_trigger(pcm_oss_file, res); | ||
2043 | case SNDCTL_DSP_GETIPTR: | ||
2044 | case SNDCTL_DSP_GETOPTR: | ||
2045 | return snd_pcm_oss_get_ptr(pcm_oss_file, | ||
2046 | cmd == SNDCTL_DSP_GETIPTR ? | ||
2047 | SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK, | ||
2048 | (struct count_info __user *) arg); | ||
2049 | case SNDCTL_DSP_MAPINBUF: | ||
2050 | case SNDCTL_DSP_MAPOUTBUF: | ||
2051 | return snd_pcm_oss_get_mapbuf(pcm_oss_file, | ||
2052 | cmd == SNDCTL_DSP_MAPINBUF ? | ||
2053 | SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK, | ||
2054 | (struct buffmem_desc __user *) arg); | ||
2055 | case SNDCTL_DSP_SETSYNCRO: | ||
2056 | /* stop DMA now.. */ | ||
2057 | return 0; | ||
2058 | case SNDCTL_DSP_SETDUPLEX: | ||
2059 | if (snd_pcm_oss_get_caps(pcm_oss_file) & DSP_CAP_DUPLEX) | ||
2060 | return 0; | ||
2061 | return -EIO; | ||
2062 | case SNDCTL_DSP_GETODELAY: | ||
2063 | res = snd_pcm_oss_get_odelay(pcm_oss_file); | ||
2064 | if (res < 0) { | ||
2065 | /* it's for sure, some broken apps don't check for error codes */ | ||
2066 | put_user(0, p); | ||
2067 | return res; | ||
2068 | } | ||
2069 | return put_user(res, p); | ||
2070 | case SNDCTL_DSP_PROFILE: | ||
2071 | return 0; /* silently ignore */ | ||
2072 | default: | ||
2073 | snd_printd("pcm_oss: unknown command = 0x%x\n", cmd); | ||
2074 | } | ||
2075 | return -EINVAL; | ||
2076 | } | ||
2077 | |||
2078 | #ifdef CONFIG_COMPAT | ||
2079 | /* all compatible */ | ||
2080 | #define snd_pcm_oss_ioctl_compat snd_pcm_oss_ioctl | ||
2081 | #else | ||
2082 | #define snd_pcm_oss_ioctl_compat NULL | ||
2083 | #endif | ||
2084 | |||
2085 | static ssize_t snd_pcm_oss_read(struct file *file, char __user *buf, size_t count, loff_t *offset) | ||
2086 | { | ||
2087 | snd_pcm_oss_file_t *pcm_oss_file; | ||
2088 | snd_pcm_substream_t *substream; | ||
2089 | |||
2090 | pcm_oss_file = file->private_data; | ||
2091 | substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE]; | ||
2092 | if (substream == NULL) | ||
2093 | return -ENXIO; | ||
2094 | #ifndef OSS_DEBUG | ||
2095 | return snd_pcm_oss_read1(substream, buf, count); | ||
2096 | #else | ||
2097 | { | ||
2098 | ssize_t res = snd_pcm_oss_read1(substream, buf, count); | ||
2099 | printk("pcm_oss: read %li bytes (returned %li bytes)\n", (long)count, (long)res); | ||
2100 | return res; | ||
2101 | } | ||
2102 | #endif | ||
2103 | } | ||
2104 | |||
2105 | static ssize_t snd_pcm_oss_write(struct file *file, const char __user *buf, size_t count, loff_t *offset) | ||
2106 | { | ||
2107 | snd_pcm_oss_file_t *pcm_oss_file; | ||
2108 | snd_pcm_substream_t *substream; | ||
2109 | long result; | ||
2110 | |||
2111 | pcm_oss_file = file->private_data; | ||
2112 | substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK]; | ||
2113 | if (substream == NULL) | ||
2114 | return -ENXIO; | ||
2115 | up(&file->f_dentry->d_inode->i_sem); | ||
2116 | result = snd_pcm_oss_write1(substream, buf, count); | ||
2117 | down(&file->f_dentry->d_inode->i_sem); | ||
2118 | #ifdef OSS_DEBUG | ||
2119 | printk("pcm_oss: write %li bytes (wrote %li bytes)\n", (long)count, (long)result); | ||
2120 | #endif | ||
2121 | return result; | ||
2122 | } | ||
2123 | |||
2124 | static int snd_pcm_oss_playback_ready(snd_pcm_substream_t *substream) | ||
2125 | { | ||
2126 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
2127 | if (atomic_read(&runtime->mmap_count)) | ||
2128 | return runtime->oss.prev_hw_ptr_interrupt != runtime->hw_ptr_interrupt; | ||
2129 | else | ||
2130 | return snd_pcm_playback_avail(runtime) >= runtime->oss.period_frames; | ||
2131 | } | ||
2132 | |||
2133 | static int snd_pcm_oss_capture_ready(snd_pcm_substream_t *substream) | ||
2134 | { | ||
2135 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
2136 | if (atomic_read(&runtime->mmap_count)) | ||
2137 | return runtime->oss.prev_hw_ptr_interrupt != runtime->hw_ptr_interrupt; | ||
2138 | else | ||
2139 | return snd_pcm_capture_avail(runtime) >= runtime->oss.period_frames; | ||
2140 | } | ||
2141 | |||
2142 | static unsigned int snd_pcm_oss_poll(struct file *file, poll_table * wait) | ||
2143 | { | ||
2144 | snd_pcm_oss_file_t *pcm_oss_file; | ||
2145 | unsigned int mask; | ||
2146 | snd_pcm_substream_t *psubstream = NULL, *csubstream = NULL; | ||
2147 | |||
2148 | pcm_oss_file = file->private_data; | ||
2149 | |||
2150 | psubstream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK]; | ||
2151 | csubstream = pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE]; | ||
2152 | |||
2153 | mask = 0; | ||
2154 | if (psubstream != NULL) { | ||
2155 | snd_pcm_runtime_t *runtime = psubstream->runtime; | ||
2156 | poll_wait(file, &runtime->sleep, wait); | ||
2157 | snd_pcm_stream_lock_irq(psubstream); | ||
2158 | if (runtime->status->state != SNDRV_PCM_STATE_DRAINING && | ||
2159 | (runtime->status->state != SNDRV_PCM_STATE_RUNNING || | ||
2160 | snd_pcm_oss_playback_ready(psubstream))) | ||
2161 | mask |= POLLOUT | POLLWRNORM; | ||
2162 | snd_pcm_stream_unlock_irq(psubstream); | ||
2163 | } | ||
2164 | if (csubstream != NULL) { | ||
2165 | snd_pcm_runtime_t *runtime = csubstream->runtime; | ||
2166 | enum sndrv_pcm_state ostate; | ||
2167 | poll_wait(file, &runtime->sleep, wait); | ||
2168 | snd_pcm_stream_lock_irq(csubstream); | ||
2169 | if ((ostate = runtime->status->state) != SNDRV_PCM_STATE_RUNNING || | ||
2170 | snd_pcm_oss_capture_ready(csubstream)) | ||
2171 | mask |= POLLIN | POLLRDNORM; | ||
2172 | snd_pcm_stream_unlock_irq(csubstream); | ||
2173 | if (ostate != SNDRV_PCM_STATE_RUNNING && runtime->oss.trigger) { | ||
2174 | snd_pcm_oss_file_t ofile; | ||
2175 | memset(&ofile, 0, sizeof(ofile)); | ||
2176 | ofile.streams[SNDRV_PCM_STREAM_CAPTURE] = pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE]; | ||
2177 | runtime->oss.trigger = 0; | ||
2178 | snd_pcm_oss_set_trigger(&ofile, PCM_ENABLE_INPUT); | ||
2179 | } | ||
2180 | } | ||
2181 | |||
2182 | return mask; | ||
2183 | } | ||
2184 | |||
2185 | static int snd_pcm_oss_mmap(struct file *file, struct vm_area_struct *area) | ||
2186 | { | ||
2187 | snd_pcm_oss_file_t *pcm_oss_file; | ||
2188 | snd_pcm_substream_t *substream = NULL; | ||
2189 | snd_pcm_runtime_t *runtime; | ||
2190 | int err; | ||
2191 | |||
2192 | #ifdef OSS_DEBUG | ||
2193 | printk("pcm_oss: mmap begin\n"); | ||
2194 | #endif | ||
2195 | pcm_oss_file = file->private_data; | ||
2196 | switch ((area->vm_flags & (VM_READ | VM_WRITE))) { | ||
2197 | case VM_READ | VM_WRITE: | ||
2198 | substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK]; | ||
2199 | if (substream) | ||
2200 | break; | ||
2201 | /* Fall through */ | ||
2202 | case VM_READ: | ||
2203 | substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE]; | ||
2204 | break; | ||
2205 | case VM_WRITE: | ||
2206 | substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK]; | ||
2207 | break; | ||
2208 | default: | ||
2209 | return -EINVAL; | ||
2210 | } | ||
2211 | /* set VM_READ access as well to fix memset() routines that do | ||
2212 | reads before writes (to improve performance) */ | ||
2213 | area->vm_flags |= VM_READ; | ||
2214 | if (substream == NULL) | ||
2215 | return -ENXIO; | ||
2216 | runtime = substream->runtime; | ||
2217 | if (!(runtime->info & SNDRV_PCM_INFO_MMAP_VALID)) | ||
2218 | return -EIO; | ||
2219 | if (runtime->info & SNDRV_PCM_INFO_INTERLEAVED) | ||
2220 | runtime->access = SNDRV_PCM_ACCESS_MMAP_INTERLEAVED; | ||
2221 | else | ||
2222 | return -EIO; | ||
2223 | |||
2224 | if (runtime->oss.params) { | ||
2225 | if ((err = snd_pcm_oss_change_params(substream)) < 0) | ||
2226 | return err; | ||
2227 | } | ||
2228 | if (runtime->oss.plugin_first != NULL) | ||
2229 | return -EIO; | ||
2230 | |||
2231 | if (area->vm_pgoff != 0) | ||
2232 | return -EINVAL; | ||
2233 | |||
2234 | err = snd_pcm_mmap_data(substream, file, area); | ||
2235 | if (err < 0) | ||
2236 | return err; | ||
2237 | runtime->oss.mmap_bytes = area->vm_end - area->vm_start; | ||
2238 | runtime->silence_threshold = 0; | ||
2239 | runtime->silence_size = 0; | ||
2240 | #ifdef OSS_DEBUG | ||
2241 | printk("pcm_oss: mmap ok, bytes = 0x%x\n", runtime->oss.mmap_bytes); | ||
2242 | #endif | ||
2243 | /* In mmap mode we never stop */ | ||
2244 | runtime->stop_threshold = runtime->boundary; | ||
2245 | |||
2246 | return 0; | ||
2247 | } | ||
2248 | |||
2249 | /* | ||
2250 | * /proc interface | ||
2251 | */ | ||
2252 | |||
2253 | static void snd_pcm_oss_proc_read(snd_info_entry_t *entry, | ||
2254 | snd_info_buffer_t * buffer) | ||
2255 | { | ||
2256 | snd_pcm_str_t *pstr = (snd_pcm_str_t *)entry->private_data; | ||
2257 | snd_pcm_oss_setup_t *setup = pstr->oss.setup_list; | ||
2258 | down(&pstr->oss.setup_mutex); | ||
2259 | while (setup) { | ||
2260 | snd_iprintf(buffer, "%s %u %u%s%s%s%s%s%s\n", | ||
2261 | setup->task_name, | ||
2262 | setup->periods, | ||
2263 | setup->period_size, | ||
2264 | setup->disable ? " disable" : "", | ||
2265 | setup->direct ? " direct" : "", | ||
2266 | setup->block ? " block" : "", | ||
2267 | setup->nonblock ? " non-block" : "", | ||
2268 | setup->partialfrag ? " partial-frag" : "", | ||
2269 | setup->nosilence ? " no-silence" : ""); | ||
2270 | setup = setup->next; | ||
2271 | } | ||
2272 | up(&pstr->oss.setup_mutex); | ||
2273 | } | ||
2274 | |||
2275 | static void snd_pcm_oss_proc_free_setup_list(snd_pcm_str_t * pstr) | ||
2276 | { | ||
2277 | unsigned int idx; | ||
2278 | snd_pcm_substream_t *substream; | ||
2279 | snd_pcm_oss_setup_t *setup, *setupn; | ||
2280 | |||
2281 | for (idx = 0, substream = pstr->substream; | ||
2282 | idx < pstr->substream_count; idx++, substream = substream->next) | ||
2283 | substream->oss.setup = NULL; | ||
2284 | for (setup = pstr->oss.setup_list, pstr->oss.setup_list = NULL; | ||
2285 | setup; setup = setupn) { | ||
2286 | setupn = setup->next; | ||
2287 | kfree(setup->task_name); | ||
2288 | kfree(setup); | ||
2289 | } | ||
2290 | pstr->oss.setup_list = NULL; | ||
2291 | } | ||
2292 | |||
2293 | static void snd_pcm_oss_proc_write(snd_info_entry_t *entry, | ||
2294 | snd_info_buffer_t * buffer) | ||
2295 | { | ||
2296 | snd_pcm_str_t *pstr = (snd_pcm_str_t *)entry->private_data; | ||
2297 | char line[128], str[32], task_name[32], *ptr; | ||
2298 | int idx1; | ||
2299 | snd_pcm_oss_setup_t *setup, *setup1, template; | ||
2300 | |||
2301 | while (!snd_info_get_line(buffer, line, sizeof(line))) { | ||
2302 | down(&pstr->oss.setup_mutex); | ||
2303 | memset(&template, 0, sizeof(template)); | ||
2304 | ptr = snd_info_get_str(task_name, line, sizeof(task_name)); | ||
2305 | if (!strcmp(task_name, "clear") || !strcmp(task_name, "erase")) { | ||
2306 | snd_pcm_oss_proc_free_setup_list(pstr); | ||
2307 | up(&pstr->oss.setup_mutex); | ||
2308 | continue; | ||
2309 | } | ||
2310 | for (setup = pstr->oss.setup_list; setup; setup = setup->next) { | ||
2311 | if (!strcmp(setup->task_name, task_name)) { | ||
2312 | template = *setup; | ||
2313 | break; | ||
2314 | } | ||
2315 | } | ||
2316 | ptr = snd_info_get_str(str, ptr, sizeof(str)); | ||
2317 | template.periods = simple_strtoul(str, NULL, 10); | ||
2318 | ptr = snd_info_get_str(str, ptr, sizeof(str)); | ||
2319 | template.period_size = simple_strtoul(str, NULL, 10); | ||
2320 | for (idx1 = 31; idx1 >= 0; idx1--) | ||
2321 | if (template.period_size & (1 << idx1)) | ||
2322 | break; | ||
2323 | for (idx1--; idx1 >= 0; idx1--) | ||
2324 | template.period_size &= ~(1 << idx1); | ||
2325 | do { | ||
2326 | ptr = snd_info_get_str(str, ptr, sizeof(str)); | ||
2327 | if (!strcmp(str, "disable")) { | ||
2328 | template.disable = 1; | ||
2329 | } else if (!strcmp(str, "direct")) { | ||
2330 | template.direct = 1; | ||
2331 | } else if (!strcmp(str, "block")) { | ||
2332 | template.block = 1; | ||
2333 | } else if (!strcmp(str, "non-block")) { | ||
2334 | template.nonblock = 1; | ||
2335 | } else if (!strcmp(str, "partial-frag")) { | ||
2336 | template.partialfrag = 1; | ||
2337 | } else if (!strcmp(str, "no-silence")) { | ||
2338 | template.nosilence = 1; | ||
2339 | } | ||
2340 | } while (*str); | ||
2341 | if (setup == NULL) { | ||
2342 | setup = (snd_pcm_oss_setup_t *) kmalloc(sizeof(snd_pcm_oss_setup_t), GFP_KERNEL); | ||
2343 | if (setup) { | ||
2344 | if (pstr->oss.setup_list == NULL) { | ||
2345 | pstr->oss.setup_list = setup; | ||
2346 | } else { | ||
2347 | for (setup1 = pstr->oss.setup_list; setup1->next; setup1 = setup1->next); | ||
2348 | setup1->next = setup; | ||
2349 | } | ||
2350 | template.task_name = snd_kmalloc_strdup(task_name, GFP_KERNEL); | ||
2351 | } else { | ||
2352 | buffer->error = -ENOMEM; | ||
2353 | } | ||
2354 | } | ||
2355 | if (setup) | ||
2356 | *setup = template; | ||
2357 | up(&pstr->oss.setup_mutex); | ||
2358 | } | ||
2359 | } | ||
2360 | |||
2361 | static void snd_pcm_oss_proc_init(snd_pcm_t *pcm) | ||
2362 | { | ||
2363 | int stream; | ||
2364 | for (stream = 0; stream < 2; ++stream) { | ||
2365 | snd_info_entry_t *entry; | ||
2366 | snd_pcm_str_t *pstr = &pcm->streams[stream]; | ||
2367 | if (pstr->substream_count == 0) | ||
2368 | continue; | ||
2369 | if ((entry = snd_info_create_card_entry(pcm->card, "oss", pstr->proc_root)) != NULL) { | ||
2370 | entry->content = SNDRV_INFO_CONTENT_TEXT; | ||
2371 | entry->mode = S_IFREG | S_IRUGO | S_IWUSR; | ||
2372 | entry->c.text.read_size = 8192; | ||
2373 | entry->c.text.read = snd_pcm_oss_proc_read; | ||
2374 | entry->c.text.write_size = 8192; | ||
2375 | entry->c.text.write = snd_pcm_oss_proc_write; | ||
2376 | entry->private_data = pstr; | ||
2377 | if (snd_info_register(entry) < 0) { | ||
2378 | snd_info_free_entry(entry); | ||
2379 | entry = NULL; | ||
2380 | } | ||
2381 | } | ||
2382 | pstr->oss.proc_entry = entry; | ||
2383 | } | ||
2384 | } | ||
2385 | |||
2386 | static void snd_pcm_oss_proc_done(snd_pcm_t *pcm) | ||
2387 | { | ||
2388 | int stream; | ||
2389 | for (stream = 0; stream < 2; ++stream) { | ||
2390 | snd_pcm_str_t *pstr = &pcm->streams[stream]; | ||
2391 | if (pstr->oss.proc_entry) { | ||
2392 | snd_info_unregister(pstr->oss.proc_entry); | ||
2393 | pstr->oss.proc_entry = NULL; | ||
2394 | snd_pcm_oss_proc_free_setup_list(pstr); | ||
2395 | } | ||
2396 | } | ||
2397 | } | ||
2398 | |||
2399 | /* | ||
2400 | * ENTRY functions | ||
2401 | */ | ||
2402 | |||
2403 | static struct file_operations snd_pcm_oss_f_reg = | ||
2404 | { | ||
2405 | .owner = THIS_MODULE, | ||
2406 | .read = snd_pcm_oss_read, | ||
2407 | .write = snd_pcm_oss_write, | ||
2408 | .open = snd_pcm_oss_open, | ||
2409 | .release = snd_pcm_oss_release, | ||
2410 | .poll = snd_pcm_oss_poll, | ||
2411 | .unlocked_ioctl = snd_pcm_oss_ioctl, | ||
2412 | .compat_ioctl = snd_pcm_oss_ioctl_compat, | ||
2413 | .mmap = snd_pcm_oss_mmap, | ||
2414 | }; | ||
2415 | |||
2416 | static snd_minor_t snd_pcm_oss_reg = | ||
2417 | { | ||
2418 | .comment = "digital audio", | ||
2419 | .f_ops = &snd_pcm_oss_f_reg, | ||
2420 | }; | ||
2421 | |||
2422 | static void register_oss_dsp(snd_pcm_t *pcm, int index) | ||
2423 | { | ||
2424 | char name[128]; | ||
2425 | sprintf(name, "dsp%i%i", pcm->card->number, pcm->device); | ||
2426 | if (snd_register_oss_device(SNDRV_OSS_DEVICE_TYPE_PCM, | ||
2427 | pcm->card, index, &snd_pcm_oss_reg, | ||
2428 | name) < 0) { | ||
2429 | snd_printk("unable to register OSS PCM device %i:%i\n", pcm->card->number, pcm->device); | ||
2430 | } | ||
2431 | } | ||
2432 | |||
2433 | static int snd_pcm_oss_register_minor(snd_pcm_t * pcm) | ||
2434 | { | ||
2435 | pcm->oss.reg = 0; | ||
2436 | if (dsp_map[pcm->card->number] == (int)pcm->device) { | ||
2437 | char name[128]; | ||
2438 | int duplex; | ||
2439 | register_oss_dsp(pcm, 0); | ||
2440 | duplex = (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream_count > 0 && | ||
2441 | pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream_count && | ||
2442 | !(pcm->info_flags & SNDRV_PCM_INFO_HALF_DUPLEX)); | ||
2443 | sprintf(name, "%s%s", pcm->name, duplex ? " (DUPLEX)" : ""); | ||
2444 | #ifdef SNDRV_OSS_INFO_DEV_AUDIO | ||
2445 | snd_oss_info_register(SNDRV_OSS_INFO_DEV_AUDIO, | ||
2446 | pcm->card->number, | ||
2447 | name); | ||
2448 | #endif | ||
2449 | pcm->oss.reg++; | ||
2450 | pcm->oss.reg_mask |= 1; | ||
2451 | } | ||
2452 | if (adsp_map[pcm->card->number] == (int)pcm->device) { | ||
2453 | register_oss_dsp(pcm, 1); | ||
2454 | pcm->oss.reg++; | ||
2455 | pcm->oss.reg_mask |= 2; | ||
2456 | } | ||
2457 | |||
2458 | if (pcm->oss.reg) | ||
2459 | snd_pcm_oss_proc_init(pcm); | ||
2460 | |||
2461 | return 0; | ||
2462 | } | ||
2463 | |||
2464 | static int snd_pcm_oss_disconnect_minor(snd_pcm_t * pcm) | ||
2465 | { | ||
2466 | if (pcm->oss.reg) { | ||
2467 | if (pcm->oss.reg_mask & 1) { | ||
2468 | pcm->oss.reg_mask &= ~1; | ||
2469 | snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_PCM, | ||
2470 | pcm->card, 0); | ||
2471 | } | ||
2472 | if (pcm->oss.reg_mask & 2) { | ||
2473 | pcm->oss.reg_mask &= ~2; | ||
2474 | snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_PCM, | ||
2475 | pcm->card, 1); | ||
2476 | } | ||
2477 | } | ||
2478 | return 0; | ||
2479 | } | ||
2480 | |||
2481 | static int snd_pcm_oss_unregister_minor(snd_pcm_t * pcm) | ||
2482 | { | ||
2483 | snd_pcm_oss_disconnect_minor(pcm); | ||
2484 | if (pcm->oss.reg) { | ||
2485 | if (dsp_map[pcm->card->number] == (int)pcm->device) { | ||
2486 | #ifdef SNDRV_OSS_INFO_DEV_AUDIO | ||
2487 | snd_oss_info_unregister(SNDRV_OSS_INFO_DEV_AUDIO, pcm->card->number); | ||
2488 | #endif | ||
2489 | } | ||
2490 | pcm->oss.reg = 0; | ||
2491 | snd_pcm_oss_proc_done(pcm); | ||
2492 | } | ||
2493 | return 0; | ||
2494 | } | ||
2495 | |||
2496 | static snd_pcm_notify_t snd_pcm_oss_notify = | ||
2497 | { | ||
2498 | .n_register = snd_pcm_oss_register_minor, | ||
2499 | .n_disconnect = snd_pcm_oss_disconnect_minor, | ||
2500 | .n_unregister = snd_pcm_oss_unregister_minor, | ||
2501 | }; | ||
2502 | |||
2503 | static int __init alsa_pcm_oss_init(void) | ||
2504 | { | ||
2505 | int i; | ||
2506 | int err; | ||
2507 | |||
2508 | /* check device map table */ | ||
2509 | for (i = 0; i < SNDRV_CARDS; i++) { | ||
2510 | if (dsp_map[i] < 0 || dsp_map[i] >= SNDRV_PCM_DEVICES) { | ||
2511 | snd_printk("invalid dsp_map[%d] = %d\n", i, dsp_map[i]); | ||
2512 | dsp_map[i] = 0; | ||
2513 | } | ||
2514 | if (adsp_map[i] < 0 || adsp_map[i] >= SNDRV_PCM_DEVICES) { | ||
2515 | snd_printk("invalid adsp_map[%d] = %d\n", i, adsp_map[i]); | ||
2516 | adsp_map[i] = 1; | ||
2517 | } | ||
2518 | } | ||
2519 | if ((err = snd_pcm_notify(&snd_pcm_oss_notify, 0)) < 0) | ||
2520 | return err; | ||
2521 | return 0; | ||
2522 | } | ||
2523 | |||
2524 | static void __exit alsa_pcm_oss_exit(void) | ||
2525 | { | ||
2526 | snd_pcm_notify(&snd_pcm_oss_notify, 1); | ||
2527 | } | ||
2528 | |||
2529 | module_init(alsa_pcm_oss_init) | ||
2530 | module_exit(alsa_pcm_oss_exit) | ||
diff --git a/sound/core/oss/pcm_plugin.c b/sound/core/oss/pcm_plugin.c new file mode 100644 index 000000000000..6bb31009f0b4 --- /dev/null +++ b/sound/core/oss/pcm_plugin.c | |||
@@ -0,0 +1,921 @@ | |||
1 | /* | ||
2 | * PCM Plug-In shared (kernel/library) code | ||
3 | * Copyright (c) 1999 by Jaroslav Kysela <perex@suse.cz> | ||
4 | * Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org> | ||
5 | * | ||
6 | * | ||
7 | * This library is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU Library General Public License as | ||
9 | * published by the Free Software Foundation; either version 2 of | ||
10 | * the License, or (at your option) any later version. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | * GNU Library General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU Library General Public | ||
18 | * License along with this library; if not, write to the Free Software | ||
19 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
20 | * | ||
21 | */ | ||
22 | |||
23 | #if 0 | ||
24 | #define PLUGIN_DEBUG | ||
25 | #endif | ||
26 | |||
27 | #include <sound/driver.h> | ||
28 | #include <linux/slab.h> | ||
29 | #include <linux/time.h> | ||
30 | #include <linux/vmalloc.h> | ||
31 | #include <sound/core.h> | ||
32 | #include <sound/pcm.h> | ||
33 | #include <sound/pcm_params.h> | ||
34 | #include "pcm_plugin.h" | ||
35 | |||
36 | #define snd_pcm_plug_first(plug) ((plug)->runtime->oss.plugin_first) | ||
37 | #define snd_pcm_plug_last(plug) ((plug)->runtime->oss.plugin_last) | ||
38 | |||
39 | static int snd_pcm_plugin_src_channels_mask(snd_pcm_plugin_t *plugin, | ||
40 | bitset_t *dst_vmask, | ||
41 | bitset_t **src_vmask) | ||
42 | { | ||
43 | bitset_t *vmask = plugin->src_vmask; | ||
44 | bitset_copy(vmask, dst_vmask, plugin->src_format.channels); | ||
45 | *src_vmask = vmask; | ||
46 | return 0; | ||
47 | } | ||
48 | |||
49 | static int snd_pcm_plugin_dst_channels_mask(snd_pcm_plugin_t *plugin, | ||
50 | bitset_t *src_vmask, | ||
51 | bitset_t **dst_vmask) | ||
52 | { | ||
53 | bitset_t *vmask = plugin->dst_vmask; | ||
54 | bitset_copy(vmask, src_vmask, plugin->dst_format.channels); | ||
55 | *dst_vmask = vmask; | ||
56 | return 0; | ||
57 | } | ||
58 | |||
59 | /* | ||
60 | * because some cards might have rates "very close", we ignore | ||
61 | * all "resampling" requests within +-5% | ||
62 | */ | ||
63 | static int rate_match(unsigned int src_rate, unsigned int dst_rate) | ||
64 | { | ||
65 | unsigned int low = (src_rate * 95) / 100; | ||
66 | unsigned int high = (src_rate * 105) / 100; | ||
67 | return dst_rate >= low && dst_rate <= high; | ||
68 | } | ||
69 | |||
70 | static int snd_pcm_plugin_alloc(snd_pcm_plugin_t *plugin, snd_pcm_uframes_t frames) | ||
71 | { | ||
72 | snd_pcm_plugin_format_t *format; | ||
73 | ssize_t width; | ||
74 | size_t size; | ||
75 | unsigned int channel; | ||
76 | snd_pcm_plugin_channel_t *c; | ||
77 | |||
78 | if (plugin->stream == SNDRV_PCM_STREAM_PLAYBACK) { | ||
79 | format = &plugin->src_format; | ||
80 | } else { | ||
81 | format = &plugin->dst_format; | ||
82 | } | ||
83 | if ((width = snd_pcm_format_physical_width(format->format)) < 0) | ||
84 | return width; | ||
85 | size = frames * format->channels * width; | ||
86 | snd_assert((size % 8) == 0, return -ENXIO); | ||
87 | size /= 8; | ||
88 | if (plugin->buf_frames < frames) { | ||
89 | vfree(plugin->buf); | ||
90 | plugin->buf = vmalloc(size); | ||
91 | plugin->buf_frames = frames; | ||
92 | } | ||
93 | if (!plugin->buf) { | ||
94 | plugin->buf_frames = 0; | ||
95 | return -ENOMEM; | ||
96 | } | ||
97 | c = plugin->buf_channels; | ||
98 | if (plugin->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED) { | ||
99 | for (channel = 0; channel < format->channels; channel++, c++) { | ||
100 | c->frames = frames; | ||
101 | c->enabled = 1; | ||
102 | c->wanted = 0; | ||
103 | c->area.addr = plugin->buf; | ||
104 | c->area.first = channel * width; | ||
105 | c->area.step = format->channels * width; | ||
106 | } | ||
107 | } else if (plugin->access == SNDRV_PCM_ACCESS_RW_NONINTERLEAVED) { | ||
108 | snd_assert((size % format->channels) == 0,); | ||
109 | size /= format->channels; | ||
110 | for (channel = 0; channel < format->channels; channel++, c++) { | ||
111 | c->frames = frames; | ||
112 | c->enabled = 1; | ||
113 | c->wanted = 0; | ||
114 | c->area.addr = plugin->buf + (channel * size); | ||
115 | c->area.first = 0; | ||
116 | c->area.step = width; | ||
117 | } | ||
118 | } else | ||
119 | return -EINVAL; | ||
120 | return 0; | ||
121 | } | ||
122 | |||
123 | int snd_pcm_plug_alloc(snd_pcm_plug_t *plug, snd_pcm_uframes_t frames) | ||
124 | { | ||
125 | int err; | ||
126 | snd_assert(snd_pcm_plug_first(plug) != NULL, return -ENXIO); | ||
127 | if (snd_pcm_plug_stream(plug) == SNDRV_PCM_STREAM_PLAYBACK) { | ||
128 | snd_pcm_plugin_t *plugin = snd_pcm_plug_first(plug); | ||
129 | while (plugin->next) { | ||
130 | if (plugin->dst_frames) | ||
131 | frames = plugin->dst_frames(plugin, frames); | ||
132 | snd_assert(frames > 0, return -ENXIO); | ||
133 | plugin = plugin->next; | ||
134 | err = snd_pcm_plugin_alloc(plugin, frames); | ||
135 | if (err < 0) | ||
136 | return err; | ||
137 | } | ||
138 | } else { | ||
139 | snd_pcm_plugin_t *plugin = snd_pcm_plug_last(plug); | ||
140 | while (plugin->prev) { | ||
141 | if (plugin->src_frames) | ||
142 | frames = plugin->src_frames(plugin, frames); | ||
143 | snd_assert(frames > 0, return -ENXIO); | ||
144 | plugin = plugin->prev; | ||
145 | err = snd_pcm_plugin_alloc(plugin, frames); | ||
146 | if (err < 0) | ||
147 | return err; | ||
148 | } | ||
149 | } | ||
150 | return 0; | ||
151 | } | ||
152 | |||
153 | |||
154 | snd_pcm_sframes_t snd_pcm_plugin_client_channels(snd_pcm_plugin_t *plugin, | ||
155 | snd_pcm_uframes_t frames, | ||
156 | snd_pcm_plugin_channel_t **channels) | ||
157 | { | ||
158 | *channels = plugin->buf_channels; | ||
159 | return frames; | ||
160 | } | ||
161 | |||
162 | int snd_pcm_plugin_build(snd_pcm_plug_t *plug, | ||
163 | const char *name, | ||
164 | snd_pcm_plugin_format_t *src_format, | ||
165 | snd_pcm_plugin_format_t *dst_format, | ||
166 | size_t extra, | ||
167 | snd_pcm_plugin_t **ret) | ||
168 | { | ||
169 | snd_pcm_plugin_t *plugin; | ||
170 | unsigned int channels; | ||
171 | |||
172 | snd_assert(plug != NULL, return -ENXIO); | ||
173 | snd_assert(src_format != NULL && dst_format != NULL, return -ENXIO); | ||
174 | plugin = kcalloc(1, sizeof(*plugin) + extra, GFP_KERNEL); | ||
175 | if (plugin == NULL) | ||
176 | return -ENOMEM; | ||
177 | plugin->name = name; | ||
178 | plugin->plug = plug; | ||
179 | plugin->stream = snd_pcm_plug_stream(plug); | ||
180 | plugin->access = SNDRV_PCM_ACCESS_RW_INTERLEAVED; | ||
181 | plugin->src_format = *src_format; | ||
182 | plugin->src_width = snd_pcm_format_physical_width(src_format->format); | ||
183 | snd_assert(plugin->src_width > 0, ); | ||
184 | plugin->dst_format = *dst_format; | ||
185 | plugin->dst_width = snd_pcm_format_physical_width(dst_format->format); | ||
186 | snd_assert(plugin->dst_width > 0, ); | ||
187 | if (plugin->stream == SNDRV_PCM_STREAM_PLAYBACK) | ||
188 | channels = src_format->channels; | ||
189 | else | ||
190 | channels = dst_format->channels; | ||
191 | plugin->buf_channels = kcalloc(channels, sizeof(*plugin->buf_channels), GFP_KERNEL); | ||
192 | if (plugin->buf_channels == NULL) { | ||
193 | snd_pcm_plugin_free(plugin); | ||
194 | return -ENOMEM; | ||
195 | } | ||
196 | plugin->src_vmask = bitset_alloc(src_format->channels); | ||
197 | if (plugin->src_vmask == NULL) { | ||
198 | snd_pcm_plugin_free(plugin); | ||
199 | return -ENOMEM; | ||
200 | } | ||
201 | plugin->dst_vmask = bitset_alloc(dst_format->channels); | ||
202 | if (plugin->dst_vmask == NULL) { | ||
203 | snd_pcm_plugin_free(plugin); | ||
204 | return -ENOMEM; | ||
205 | } | ||
206 | plugin->client_channels = snd_pcm_plugin_client_channels; | ||
207 | plugin->src_channels_mask = snd_pcm_plugin_src_channels_mask; | ||
208 | plugin->dst_channels_mask = snd_pcm_plugin_dst_channels_mask; | ||
209 | *ret = plugin; | ||
210 | return 0; | ||
211 | } | ||
212 | |||
213 | int snd_pcm_plugin_free(snd_pcm_plugin_t *plugin) | ||
214 | { | ||
215 | if (! plugin) | ||
216 | return 0; | ||
217 | if (plugin->private_free) | ||
218 | plugin->private_free(plugin); | ||
219 | kfree(plugin->buf_channels); | ||
220 | vfree(plugin->buf); | ||
221 | kfree(plugin->src_vmask); | ||
222 | kfree(plugin->dst_vmask); | ||
223 | kfree(plugin); | ||
224 | return 0; | ||
225 | } | ||
226 | |||
227 | snd_pcm_sframes_t snd_pcm_plug_client_size(snd_pcm_plug_t *plug, snd_pcm_uframes_t drv_frames) | ||
228 | { | ||
229 | snd_pcm_plugin_t *plugin, *plugin_prev, *plugin_next; | ||
230 | int stream = snd_pcm_plug_stream(plug); | ||
231 | |||
232 | snd_assert(plug != NULL, return -ENXIO); | ||
233 | if (drv_frames == 0) | ||
234 | return 0; | ||
235 | if (stream == SNDRV_PCM_STREAM_PLAYBACK) { | ||
236 | plugin = snd_pcm_plug_last(plug); | ||
237 | while (plugin && drv_frames > 0) { | ||
238 | plugin_prev = plugin->prev; | ||
239 | if (plugin->src_frames) | ||
240 | drv_frames = plugin->src_frames(plugin, drv_frames); | ||
241 | plugin = plugin_prev; | ||
242 | } | ||
243 | } else if (stream == SNDRV_PCM_STREAM_CAPTURE) { | ||
244 | plugin = snd_pcm_plug_first(plug); | ||
245 | while (plugin && drv_frames > 0) { | ||
246 | plugin_next = plugin->next; | ||
247 | if (plugin->dst_frames) | ||
248 | drv_frames = plugin->dst_frames(plugin, drv_frames); | ||
249 | plugin = plugin_next; | ||
250 | } | ||
251 | } else | ||
252 | snd_BUG(); | ||
253 | return drv_frames; | ||
254 | } | ||
255 | |||
256 | snd_pcm_sframes_t snd_pcm_plug_slave_size(snd_pcm_plug_t *plug, snd_pcm_uframes_t clt_frames) | ||
257 | { | ||
258 | snd_pcm_plugin_t *plugin, *plugin_prev, *plugin_next; | ||
259 | snd_pcm_sframes_t frames; | ||
260 | int stream = snd_pcm_plug_stream(plug); | ||
261 | |||
262 | snd_assert(plug != NULL, return -ENXIO); | ||
263 | if (clt_frames == 0) | ||
264 | return 0; | ||
265 | frames = clt_frames; | ||
266 | if (stream == SNDRV_PCM_STREAM_PLAYBACK) { | ||
267 | plugin = snd_pcm_plug_first(plug); | ||
268 | while (plugin && frames > 0) { | ||
269 | plugin_next = plugin->next; | ||
270 | if (plugin->dst_frames) { | ||
271 | frames = plugin->dst_frames(plugin, frames); | ||
272 | if (frames < 0) | ||
273 | return frames; | ||
274 | } | ||
275 | plugin = plugin_next; | ||
276 | } | ||
277 | } else if (stream == SNDRV_PCM_STREAM_CAPTURE) { | ||
278 | plugin = snd_pcm_plug_last(plug); | ||
279 | while (plugin) { | ||
280 | plugin_prev = plugin->prev; | ||
281 | if (plugin->src_frames) { | ||
282 | frames = plugin->src_frames(plugin, frames); | ||
283 | if (frames < 0) | ||
284 | return frames; | ||
285 | } | ||
286 | plugin = plugin_prev; | ||
287 | } | ||
288 | } else | ||
289 | snd_BUG(); | ||
290 | return frames; | ||
291 | } | ||
292 | |||
293 | static int snd_pcm_plug_formats(snd_mask_t *mask, int format) | ||
294 | { | ||
295 | snd_mask_t formats = *mask; | ||
296 | u64 linfmts = (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 | | ||
297 | SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_S16_LE | | ||
298 | SNDRV_PCM_FMTBIT_U16_BE | SNDRV_PCM_FMTBIT_S16_BE | | ||
299 | SNDRV_PCM_FMTBIT_U24_LE | SNDRV_PCM_FMTBIT_S24_LE | | ||
300 | SNDRV_PCM_FMTBIT_U24_BE | SNDRV_PCM_FMTBIT_S24_BE | | ||
301 | SNDRV_PCM_FMTBIT_U32_LE | SNDRV_PCM_FMTBIT_S32_LE | | ||
302 | SNDRV_PCM_FMTBIT_U32_BE | SNDRV_PCM_FMTBIT_S32_BE); | ||
303 | snd_mask_set(&formats, SNDRV_PCM_FORMAT_MU_LAW); | ||
304 | |||
305 | if (formats.bits[0] & (u32)linfmts) | ||
306 | formats.bits[0] |= (u32)linfmts; | ||
307 | if (formats.bits[1] & (u32)(linfmts >> 32)) | ||
308 | formats.bits[1] |= (u32)(linfmts >> 32); | ||
309 | return snd_mask_test(&formats, format); | ||
310 | } | ||
311 | |||
312 | static int preferred_formats[] = { | ||
313 | SNDRV_PCM_FORMAT_S16_LE, | ||
314 | SNDRV_PCM_FORMAT_S16_BE, | ||
315 | SNDRV_PCM_FORMAT_U16_LE, | ||
316 | SNDRV_PCM_FORMAT_U16_BE, | ||
317 | SNDRV_PCM_FORMAT_S24_LE, | ||
318 | SNDRV_PCM_FORMAT_S24_BE, | ||
319 | SNDRV_PCM_FORMAT_U24_LE, | ||
320 | SNDRV_PCM_FORMAT_U24_BE, | ||
321 | SNDRV_PCM_FORMAT_S32_LE, | ||
322 | SNDRV_PCM_FORMAT_S32_BE, | ||
323 | SNDRV_PCM_FORMAT_U32_LE, | ||
324 | SNDRV_PCM_FORMAT_U32_BE, | ||
325 | SNDRV_PCM_FORMAT_S8, | ||
326 | SNDRV_PCM_FORMAT_U8 | ||
327 | }; | ||
328 | |||
329 | int snd_pcm_plug_slave_format(int format, snd_mask_t *format_mask) | ||
330 | { | ||
331 | if (snd_mask_test(format_mask, format)) | ||
332 | return format; | ||
333 | if (! snd_pcm_plug_formats(format_mask, format)) | ||
334 | return -EINVAL; | ||
335 | if (snd_pcm_format_linear(format)) { | ||
336 | int width = snd_pcm_format_width(format); | ||
337 | int unsignd = snd_pcm_format_unsigned(format); | ||
338 | int big = snd_pcm_format_big_endian(format); | ||
339 | int format1; | ||
340 | int wid, width1=width; | ||
341 | int dwidth1 = 8; | ||
342 | for (wid = 0; wid < 4; ++wid) { | ||
343 | int end, big1 = big; | ||
344 | for (end = 0; end < 2; ++end) { | ||
345 | int sgn, unsignd1 = unsignd; | ||
346 | for (sgn = 0; sgn < 2; ++sgn) { | ||
347 | format1 = snd_pcm_build_linear_format(width1, unsignd1, big1); | ||
348 | if (format1 >= 0 && | ||
349 | snd_mask_test(format_mask, format1)) | ||
350 | goto _found; | ||
351 | unsignd1 = !unsignd1; | ||
352 | } | ||
353 | big1 = !big1; | ||
354 | } | ||
355 | if (width1 == 32) { | ||
356 | dwidth1 = -dwidth1; | ||
357 | width1 = width; | ||
358 | } | ||
359 | width1 += dwidth1; | ||
360 | } | ||
361 | return -EINVAL; | ||
362 | _found: | ||
363 | return format1; | ||
364 | } else { | ||
365 | unsigned int i; | ||
366 | switch (format) { | ||
367 | case SNDRV_PCM_FORMAT_MU_LAW: | ||
368 | for (i = 0; i < ARRAY_SIZE(preferred_formats); ++i) { | ||
369 | int format1 = preferred_formats[i]; | ||
370 | if (snd_mask_test(format_mask, format1)) | ||
371 | return format1; | ||
372 | } | ||
373 | default: | ||
374 | return -EINVAL; | ||
375 | } | ||
376 | } | ||
377 | } | ||
378 | |||
379 | int snd_pcm_plug_format_plugins(snd_pcm_plug_t *plug, | ||
380 | snd_pcm_hw_params_t *params, | ||
381 | snd_pcm_hw_params_t *slave_params) | ||
382 | { | ||
383 | snd_pcm_plugin_format_t tmpformat; | ||
384 | snd_pcm_plugin_format_t dstformat; | ||
385 | snd_pcm_plugin_format_t srcformat; | ||
386 | int src_access, dst_access; | ||
387 | snd_pcm_plugin_t *plugin = NULL; | ||
388 | int err; | ||
389 | int stream = snd_pcm_plug_stream(plug); | ||
390 | int slave_interleaved = (params_channels(slave_params) == 1 || | ||
391 | params_access(slave_params) == SNDRV_PCM_ACCESS_RW_INTERLEAVED); | ||
392 | |||
393 | switch (stream) { | ||
394 | case SNDRV_PCM_STREAM_PLAYBACK: | ||
395 | dstformat.format = params_format(slave_params); | ||
396 | dstformat.rate = params_rate(slave_params); | ||
397 | dstformat.channels = params_channels(slave_params); | ||
398 | srcformat.format = params_format(params); | ||
399 | srcformat.rate = params_rate(params); | ||
400 | srcformat.channels = params_channels(params); | ||
401 | src_access = SNDRV_PCM_ACCESS_RW_INTERLEAVED; | ||
402 | dst_access = (slave_interleaved ? SNDRV_PCM_ACCESS_RW_INTERLEAVED : | ||
403 | SNDRV_PCM_ACCESS_RW_NONINTERLEAVED); | ||
404 | break; | ||
405 | case SNDRV_PCM_STREAM_CAPTURE: | ||
406 | dstformat.format = params_format(params); | ||
407 | dstformat.rate = params_rate(params); | ||
408 | dstformat.channels = params_channels(params); | ||
409 | srcformat.format = params_format(slave_params); | ||
410 | srcformat.rate = params_rate(slave_params); | ||
411 | srcformat.channels = params_channels(slave_params); | ||
412 | src_access = (slave_interleaved ? SNDRV_PCM_ACCESS_RW_INTERLEAVED : | ||
413 | SNDRV_PCM_ACCESS_RW_NONINTERLEAVED); | ||
414 | dst_access = SNDRV_PCM_ACCESS_RW_INTERLEAVED; | ||
415 | break; | ||
416 | default: | ||
417 | snd_BUG(); | ||
418 | return -EINVAL; | ||
419 | } | ||
420 | tmpformat = srcformat; | ||
421 | |||
422 | pdprintf("srcformat: format=%i, rate=%i, channels=%i\n", | ||
423 | srcformat.format, | ||
424 | srcformat.rate, | ||
425 | srcformat.channels); | ||
426 | pdprintf("dstformat: format=%i, rate=%i, channels=%i\n", | ||
427 | dstformat.format, | ||
428 | dstformat.rate, | ||
429 | dstformat.channels); | ||
430 | |||
431 | /* Format change (linearization) */ | ||
432 | if ((srcformat.format != dstformat.format || | ||
433 | !rate_match(srcformat.rate, dstformat.rate) || | ||
434 | srcformat.channels != dstformat.channels) && | ||
435 | !snd_pcm_format_linear(srcformat.format)) { | ||
436 | if (snd_pcm_format_linear(dstformat.format)) | ||
437 | tmpformat.format = dstformat.format; | ||
438 | else | ||
439 | tmpformat.format = SNDRV_PCM_FORMAT_S16; | ||
440 | switch (srcformat.format) { | ||
441 | case SNDRV_PCM_FORMAT_MU_LAW: | ||
442 | err = snd_pcm_plugin_build_mulaw(plug, | ||
443 | &srcformat, &tmpformat, | ||
444 | &plugin); | ||
445 | break; | ||
446 | default: | ||
447 | return -EINVAL; | ||
448 | } | ||
449 | pdprintf("format change: src=%i, dst=%i returns %i\n", srcformat.format, tmpformat.format, err); | ||
450 | if (err < 0) | ||
451 | return err; | ||
452 | err = snd_pcm_plugin_append(plugin); | ||
453 | if (err < 0) { | ||
454 | snd_pcm_plugin_free(plugin); | ||
455 | return err; | ||
456 | } | ||
457 | srcformat = tmpformat; | ||
458 | src_access = dst_access; | ||
459 | } | ||
460 | |||
461 | /* channels reduction */ | ||
462 | if (srcformat.channels > dstformat.channels) { | ||
463 | int sv = srcformat.channels; | ||
464 | int dv = dstformat.channels; | ||
465 | route_ttable_entry_t *ttable = kcalloc(dv * sv, sizeof(*ttable), GFP_KERNEL); | ||
466 | if (ttable == NULL) | ||
467 | return -ENOMEM; | ||
468 | #if 1 | ||
469 | if (sv == 2 && dv == 1) { | ||
470 | ttable[0] = HALF; | ||
471 | ttable[1] = HALF; | ||
472 | } else | ||
473 | #endif | ||
474 | { | ||
475 | int v; | ||
476 | for (v = 0; v < dv; ++v) | ||
477 | ttable[v * sv + v] = FULL; | ||
478 | } | ||
479 | tmpformat.channels = dstformat.channels; | ||
480 | if (rate_match(srcformat.rate, dstformat.rate) && | ||
481 | snd_pcm_format_linear(dstformat.format)) | ||
482 | tmpformat.format = dstformat.format; | ||
483 | err = snd_pcm_plugin_build_route(plug, | ||
484 | &srcformat, &tmpformat, | ||
485 | ttable, &plugin); | ||
486 | kfree(ttable); | ||
487 | pdprintf("channels reduction: src=%i, dst=%i returns %i\n", srcformat.channels, tmpformat.channels, err); | ||
488 | if (err < 0) { | ||
489 | snd_pcm_plugin_free(plugin); | ||
490 | return err; | ||
491 | } | ||
492 | err = snd_pcm_plugin_append(plugin); | ||
493 | if (err < 0) { | ||
494 | snd_pcm_plugin_free(plugin); | ||
495 | return err; | ||
496 | } | ||
497 | srcformat = tmpformat; | ||
498 | src_access = dst_access; | ||
499 | } | ||
500 | |||
501 | /* rate resampling */ | ||
502 | if (!rate_match(srcformat.rate, dstformat.rate)) { | ||
503 | tmpformat.rate = dstformat.rate; | ||
504 | if (srcformat.channels == dstformat.channels && | ||
505 | snd_pcm_format_linear(dstformat.format)) | ||
506 | tmpformat.format = dstformat.format; | ||
507 | err = snd_pcm_plugin_build_rate(plug, | ||
508 | &srcformat, &tmpformat, | ||
509 | &plugin); | ||
510 | pdprintf("rate down resampling: src=%i, dst=%i returns %i\n", srcformat.rate, tmpformat.rate, err); | ||
511 | if (err < 0) { | ||
512 | snd_pcm_plugin_free(plugin); | ||
513 | return err; | ||
514 | } | ||
515 | err = snd_pcm_plugin_append(plugin); | ||
516 | if (err < 0) { | ||
517 | snd_pcm_plugin_free(plugin); | ||
518 | return err; | ||
519 | } | ||
520 | srcformat = tmpformat; | ||
521 | src_access = dst_access; | ||
522 | } | ||
523 | |||
524 | /* channels extension */ | ||
525 | if (srcformat.channels < dstformat.channels) { | ||
526 | int sv = srcformat.channels; | ||
527 | int dv = dstformat.channels; | ||
528 | route_ttable_entry_t *ttable = kcalloc(dv * sv, sizeof(*ttable), GFP_KERNEL); | ||
529 | if (ttable == NULL) | ||
530 | return -ENOMEM; | ||
531 | #if 0 | ||
532 | { | ||
533 | int v; | ||
534 | for (v = 0; v < sv; ++v) | ||
535 | ttable[v * sv + v] = FULL; | ||
536 | } | ||
537 | #else | ||
538 | { | ||
539 | /* Playback is spreaded on all channels */ | ||
540 | int vd, vs; | ||
541 | for (vd = 0, vs = 0; vd < dv; ++vd) { | ||
542 | ttable[vd * sv + vs] = FULL; | ||
543 | vs++; | ||
544 | if (vs == sv) | ||
545 | vs = 0; | ||
546 | } | ||
547 | } | ||
548 | #endif | ||
549 | tmpformat.channels = dstformat.channels; | ||
550 | if (snd_pcm_format_linear(dstformat.format)) | ||
551 | tmpformat.format = dstformat.format; | ||
552 | err = snd_pcm_plugin_build_route(plug, | ||
553 | &srcformat, &tmpformat, | ||
554 | ttable, &plugin); | ||
555 | kfree(ttable); | ||
556 | pdprintf("channels extension: src=%i, dst=%i returns %i\n", srcformat.channels, tmpformat.channels, err); | ||
557 | if (err < 0) { | ||
558 | snd_pcm_plugin_free(plugin); | ||
559 | return err; | ||
560 | } | ||
561 | err = snd_pcm_plugin_append(plugin); | ||
562 | if (err < 0) { | ||
563 | snd_pcm_plugin_free(plugin); | ||
564 | return err; | ||
565 | } | ||
566 | srcformat = tmpformat; | ||
567 | src_access = dst_access; | ||
568 | } | ||
569 | |||
570 | /* format change */ | ||
571 | if (srcformat.format != dstformat.format) { | ||
572 | tmpformat.format = dstformat.format; | ||
573 | if (tmpformat.format == SNDRV_PCM_FORMAT_MU_LAW) { | ||
574 | err = snd_pcm_plugin_build_mulaw(plug, | ||
575 | &srcformat, &tmpformat, | ||
576 | &plugin); | ||
577 | } | ||
578 | else if (snd_pcm_format_linear(srcformat.format) && | ||
579 | snd_pcm_format_linear(tmpformat.format)) { | ||
580 | err = snd_pcm_plugin_build_linear(plug, | ||
581 | &srcformat, &tmpformat, | ||
582 | &plugin); | ||
583 | } | ||
584 | else | ||
585 | return -EINVAL; | ||
586 | pdprintf("format change: src=%i, dst=%i returns %i\n", srcformat.format, tmpformat.format, err); | ||
587 | if (err < 0) | ||
588 | return err; | ||
589 | err = snd_pcm_plugin_append(plugin); | ||
590 | if (err < 0) { | ||
591 | snd_pcm_plugin_free(plugin); | ||
592 | return err; | ||
593 | } | ||
594 | srcformat = tmpformat; | ||
595 | src_access = dst_access; | ||
596 | } | ||
597 | |||
598 | /* de-interleave */ | ||
599 | if (src_access != dst_access) { | ||
600 | err = snd_pcm_plugin_build_copy(plug, | ||
601 | &srcformat, | ||
602 | &tmpformat, | ||
603 | &plugin); | ||
604 | pdprintf("interleave change (copy: returns %i)\n", err); | ||
605 | if (err < 0) | ||
606 | return err; | ||
607 | err = snd_pcm_plugin_append(plugin); | ||
608 | if (err < 0) { | ||
609 | snd_pcm_plugin_free(plugin); | ||
610 | return err; | ||
611 | } | ||
612 | } | ||
613 | |||
614 | return 0; | ||
615 | } | ||
616 | |||
617 | snd_pcm_sframes_t snd_pcm_plug_client_channels_buf(snd_pcm_plug_t *plug, | ||
618 | char *buf, | ||
619 | snd_pcm_uframes_t count, | ||
620 | snd_pcm_plugin_channel_t **channels) | ||
621 | { | ||
622 | snd_pcm_plugin_t *plugin; | ||
623 | snd_pcm_plugin_channel_t *v; | ||
624 | snd_pcm_plugin_format_t *format; | ||
625 | int width, nchannels, channel; | ||
626 | int stream = snd_pcm_plug_stream(plug); | ||
627 | |||
628 | snd_assert(buf != NULL, return -ENXIO); | ||
629 | if (stream == SNDRV_PCM_STREAM_PLAYBACK) { | ||
630 | plugin = snd_pcm_plug_first(plug); | ||
631 | format = &plugin->src_format; | ||
632 | } else { | ||
633 | plugin = snd_pcm_plug_last(plug); | ||
634 | format = &plugin->dst_format; | ||
635 | } | ||
636 | v = plugin->buf_channels; | ||
637 | *channels = v; | ||
638 | if ((width = snd_pcm_format_physical_width(format->format)) < 0) | ||
639 | return width; | ||
640 | nchannels = format->channels; | ||
641 | snd_assert(plugin->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED || format->channels <= 1, return -ENXIO); | ||
642 | for (channel = 0; channel < nchannels; channel++, v++) { | ||
643 | v->frames = count; | ||
644 | v->enabled = 1; | ||
645 | v->wanted = (stream == SNDRV_PCM_STREAM_CAPTURE); | ||
646 | v->area.addr = buf; | ||
647 | v->area.first = channel * width; | ||
648 | v->area.step = nchannels * width; | ||
649 | } | ||
650 | return count; | ||
651 | } | ||
652 | |||
653 | static int snd_pcm_plug_playback_channels_mask(snd_pcm_plug_t *plug, | ||
654 | bitset_t *client_vmask) | ||
655 | { | ||
656 | snd_pcm_plugin_t *plugin = snd_pcm_plug_last(plug); | ||
657 | if (plugin == NULL) { | ||
658 | return 0; | ||
659 | } else { | ||
660 | int schannels = plugin->dst_format.channels; | ||
661 | bitset_t bs[bitset_size(schannels)]; | ||
662 | bitset_t *srcmask; | ||
663 | bitset_t *dstmask = bs; | ||
664 | int err; | ||
665 | bitset_one(dstmask, schannels); | ||
666 | if (plugin == NULL) { | ||
667 | bitset_and(client_vmask, dstmask, schannels); | ||
668 | return 0; | ||
669 | } | ||
670 | while (1) { | ||
671 | err = plugin->src_channels_mask(plugin, dstmask, &srcmask); | ||
672 | if (err < 0) | ||
673 | return err; | ||
674 | dstmask = srcmask; | ||
675 | if (plugin->prev == NULL) | ||
676 | break; | ||
677 | plugin = plugin->prev; | ||
678 | } | ||
679 | bitset_and(client_vmask, dstmask, plugin->src_format.channels); | ||
680 | return 0; | ||
681 | } | ||
682 | } | ||
683 | |||
684 | static int snd_pcm_plug_playback_disable_useless_channels(snd_pcm_plug_t *plug, | ||
685 | snd_pcm_plugin_channel_t *src_channels) | ||
686 | { | ||
687 | snd_pcm_plugin_t *plugin = snd_pcm_plug_first(plug); | ||
688 | unsigned int nchannels = plugin->src_format.channels; | ||
689 | bitset_t bs[bitset_size(nchannels)]; | ||
690 | bitset_t *srcmask = bs; | ||
691 | int err; | ||
692 | unsigned int channel; | ||
693 | for (channel = 0; channel < nchannels; channel++) { | ||
694 | if (src_channels[channel].enabled) | ||
695 | bitset_set(srcmask, channel); | ||
696 | else | ||
697 | bitset_reset(srcmask, channel); | ||
698 | } | ||
699 | err = snd_pcm_plug_playback_channels_mask(plug, srcmask); | ||
700 | if (err < 0) | ||
701 | return err; | ||
702 | for (channel = 0; channel < nchannels; channel++) { | ||
703 | if (!bitset_get(srcmask, channel)) | ||
704 | src_channels[channel].enabled = 0; | ||
705 | } | ||
706 | return 0; | ||
707 | } | ||
708 | |||
709 | static int snd_pcm_plug_capture_disable_useless_channels(snd_pcm_plug_t *plug, | ||
710 | snd_pcm_plugin_channel_t *src_channels, | ||
711 | snd_pcm_plugin_channel_t *client_channels) | ||
712 | { | ||
713 | snd_pcm_plugin_t *plugin = snd_pcm_plug_last(plug); | ||
714 | unsigned int nchannels = plugin->dst_format.channels; | ||
715 | bitset_t bs[bitset_size(nchannels)]; | ||
716 | bitset_t *dstmask = bs; | ||
717 | bitset_t *srcmask; | ||
718 | int err; | ||
719 | unsigned int channel; | ||
720 | for (channel = 0; channel < nchannels; channel++) { | ||
721 | if (client_channels[channel].enabled) | ||
722 | bitset_set(dstmask, channel); | ||
723 | else | ||
724 | bitset_reset(dstmask, channel); | ||
725 | } | ||
726 | while (plugin) { | ||
727 | err = plugin->src_channels_mask(plugin, dstmask, &srcmask); | ||
728 | if (err < 0) | ||
729 | return err; | ||
730 | dstmask = srcmask; | ||
731 | plugin = plugin->prev; | ||
732 | } | ||
733 | plugin = snd_pcm_plug_first(plug); | ||
734 | nchannels = plugin->src_format.channels; | ||
735 | for (channel = 0; channel < nchannels; channel++) { | ||
736 | if (!bitset_get(dstmask, channel)) | ||
737 | src_channels[channel].enabled = 0; | ||
738 | } | ||
739 | return 0; | ||
740 | } | ||
741 | |||
742 | snd_pcm_sframes_t snd_pcm_plug_write_transfer(snd_pcm_plug_t *plug, snd_pcm_plugin_channel_t *src_channels, snd_pcm_uframes_t size) | ||
743 | { | ||
744 | snd_pcm_plugin_t *plugin, *next; | ||
745 | snd_pcm_plugin_channel_t *dst_channels; | ||
746 | int err; | ||
747 | snd_pcm_sframes_t frames = size; | ||
748 | |||
749 | if ((err = snd_pcm_plug_playback_disable_useless_channels(plug, src_channels)) < 0) | ||
750 | return err; | ||
751 | |||
752 | plugin = snd_pcm_plug_first(plug); | ||
753 | while (plugin && frames > 0) { | ||
754 | if ((next = plugin->next) != NULL) { | ||
755 | snd_pcm_sframes_t frames1 = frames; | ||
756 | if (plugin->dst_frames) | ||
757 | frames1 = plugin->dst_frames(plugin, frames); | ||
758 | if ((err = next->client_channels(next, frames1, &dst_channels)) < 0) { | ||
759 | return err; | ||
760 | } | ||
761 | if (err != frames1) { | ||
762 | frames = err; | ||
763 | if (plugin->src_frames) | ||
764 | frames = plugin->src_frames(plugin, frames1); | ||
765 | } | ||
766 | } else | ||
767 | dst_channels = NULL; | ||
768 | pdprintf("write plugin: %s, %li\n", plugin->name, frames); | ||
769 | if ((frames = plugin->transfer(plugin, src_channels, dst_channels, frames)) < 0) | ||
770 | return frames; | ||
771 | src_channels = dst_channels; | ||
772 | plugin = next; | ||
773 | } | ||
774 | return snd_pcm_plug_client_size(plug, frames); | ||
775 | } | ||
776 | |||
777 | snd_pcm_sframes_t snd_pcm_plug_read_transfer(snd_pcm_plug_t *plug, snd_pcm_plugin_channel_t *dst_channels_final, snd_pcm_uframes_t size) | ||
778 | { | ||
779 | snd_pcm_plugin_t *plugin, *next; | ||
780 | snd_pcm_plugin_channel_t *src_channels, *dst_channels; | ||
781 | snd_pcm_sframes_t frames = size; | ||
782 | int err; | ||
783 | |||
784 | frames = snd_pcm_plug_slave_size(plug, frames); | ||
785 | if (frames < 0) | ||
786 | return frames; | ||
787 | |||
788 | src_channels = NULL; | ||
789 | plugin = snd_pcm_plug_first(plug); | ||
790 | while (plugin && frames > 0) { | ||
791 | if ((next = plugin->next) != NULL) { | ||
792 | if ((err = plugin->client_channels(plugin, frames, &dst_channels)) < 0) { | ||
793 | return err; | ||
794 | } | ||
795 | frames = err; | ||
796 | if (!plugin->prev) { | ||
797 | if ((err = snd_pcm_plug_capture_disable_useless_channels(plug, dst_channels, dst_channels_final)) < 0) | ||
798 | return err; | ||
799 | } | ||
800 | } else { | ||
801 | dst_channels = dst_channels_final; | ||
802 | } | ||
803 | pdprintf("read plugin: %s, %li\n", plugin->name, frames); | ||
804 | if ((frames = plugin->transfer(plugin, src_channels, dst_channels, frames)) < 0) | ||
805 | return frames; | ||
806 | plugin = next; | ||
807 | src_channels = dst_channels; | ||
808 | } | ||
809 | return frames; | ||
810 | } | ||
811 | |||
812 | int snd_pcm_area_silence(const snd_pcm_channel_area_t *dst_area, size_t dst_offset, | ||
813 | size_t samples, int format) | ||
814 | { | ||
815 | /* FIXME: sub byte resolution and odd dst_offset */ | ||
816 | unsigned char *dst; | ||
817 | unsigned int dst_step; | ||
818 | int width; | ||
819 | const unsigned char *silence; | ||
820 | if (!dst_area->addr) | ||
821 | return 0; | ||
822 | dst = dst_area->addr + (dst_area->first + dst_area->step * dst_offset) / 8; | ||
823 | width = snd_pcm_format_physical_width(format); | ||
824 | if (width <= 0) | ||
825 | return -EINVAL; | ||
826 | if (dst_area->step == (unsigned int) width && width >= 8) | ||
827 | return snd_pcm_format_set_silence(format, dst, samples); | ||
828 | silence = snd_pcm_format_silence_64(format); | ||
829 | if (! silence) | ||
830 | return -EINVAL; | ||
831 | dst_step = dst_area->step / 8; | ||
832 | if (width == 4) { | ||
833 | /* Ima ADPCM */ | ||
834 | int dstbit = dst_area->first % 8; | ||
835 | int dstbit_step = dst_area->step % 8; | ||
836 | while (samples-- > 0) { | ||
837 | if (dstbit) | ||
838 | *dst &= 0xf0; | ||
839 | else | ||
840 | *dst &= 0x0f; | ||
841 | dst += dst_step; | ||
842 | dstbit += dstbit_step; | ||
843 | if (dstbit == 8) { | ||
844 | dst++; | ||
845 | dstbit = 0; | ||
846 | } | ||
847 | } | ||
848 | } else { | ||
849 | width /= 8; | ||
850 | while (samples-- > 0) { | ||
851 | memcpy(dst, silence, width); | ||
852 | dst += dst_step; | ||
853 | } | ||
854 | } | ||
855 | return 0; | ||
856 | } | ||
857 | |||
858 | int snd_pcm_area_copy(const snd_pcm_channel_area_t *src_area, size_t src_offset, | ||
859 | const snd_pcm_channel_area_t *dst_area, size_t dst_offset, | ||
860 | size_t samples, int format) | ||
861 | { | ||
862 | /* FIXME: sub byte resolution and odd dst_offset */ | ||
863 | char *src, *dst; | ||
864 | int width; | ||
865 | int src_step, dst_step; | ||
866 | src = src_area->addr + (src_area->first + src_area->step * src_offset) / 8; | ||
867 | if (!src_area->addr) | ||
868 | return snd_pcm_area_silence(dst_area, dst_offset, samples, format); | ||
869 | dst = dst_area->addr + (dst_area->first + dst_area->step * dst_offset) / 8; | ||
870 | if (!dst_area->addr) | ||
871 | return 0; | ||
872 | width = snd_pcm_format_physical_width(format); | ||
873 | if (width <= 0) | ||
874 | return -EINVAL; | ||
875 | if (src_area->step == (unsigned int) width && | ||
876 | dst_area->step == (unsigned int) width && width >= 8) { | ||
877 | size_t bytes = samples * width / 8; | ||
878 | memcpy(dst, src, bytes); | ||
879 | return 0; | ||
880 | } | ||
881 | src_step = src_area->step / 8; | ||
882 | dst_step = dst_area->step / 8; | ||
883 | if (width == 4) { | ||
884 | /* Ima ADPCM */ | ||
885 | int srcbit = src_area->first % 8; | ||
886 | int srcbit_step = src_area->step % 8; | ||
887 | int dstbit = dst_area->first % 8; | ||
888 | int dstbit_step = dst_area->step % 8; | ||
889 | while (samples-- > 0) { | ||
890 | unsigned char srcval; | ||
891 | if (srcbit) | ||
892 | srcval = *src & 0x0f; | ||
893 | else | ||
894 | srcval = (*src & 0xf0) >> 4; | ||
895 | if (dstbit) | ||
896 | *dst = (*dst & 0xf0) | srcval; | ||
897 | else | ||
898 | *dst = (*dst & 0x0f) | (srcval << 4); | ||
899 | src += src_step; | ||
900 | srcbit += srcbit_step; | ||
901 | if (srcbit == 8) { | ||
902 | src++; | ||
903 | srcbit = 0; | ||
904 | } | ||
905 | dst += dst_step; | ||
906 | dstbit += dstbit_step; | ||
907 | if (dstbit == 8) { | ||
908 | dst++; | ||
909 | dstbit = 0; | ||
910 | } | ||
911 | } | ||
912 | } else { | ||
913 | width /= 8; | ||
914 | while (samples-- > 0) { | ||
915 | memcpy(dst, src, width); | ||
916 | src += src_step; | ||
917 | dst += dst_step; | ||
918 | } | ||
919 | } | ||
920 | return 0; | ||
921 | } | ||
diff --git a/sound/core/oss/pcm_plugin.h b/sound/core/oss/pcm_plugin.h new file mode 100644 index 000000000000..0f86ce477490 --- /dev/null +++ b/sound/core/oss/pcm_plugin.h | |||
@@ -0,0 +1,250 @@ | |||
1 | #ifndef __PCM_PLUGIN_H | ||
2 | #define __PCM_PLUGIN_H | ||
3 | |||
4 | /* | ||
5 | * Digital Audio (Plugin interface) abstract layer | ||
6 | * Copyright (c) by Jaroslav Kysela <perex@suse.cz> | ||
7 | * | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU General Public License as published by | ||
11 | * the Free Software Foundation; either version 2 of the License, or | ||
12 | * (at your option) any later version. | ||
13 | * | ||
14 | * This program is distributed in the hope that it will be useful, | ||
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
17 | * GNU General Public License for more details. | ||
18 | * | ||
19 | * You should have received a copy of the GNU General Public License | ||
20 | * along with this program; if not, write to the Free Software | ||
21 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
22 | * | ||
23 | */ | ||
24 | |||
25 | #ifndef ATTRIBUTE_UNUSED | ||
26 | #define ATTRIBUTE_UNUSED __attribute__ ((__unused__)) | ||
27 | #endif | ||
28 | |||
29 | typedef unsigned int bitset_t; | ||
30 | |||
31 | static inline size_t bitset_size(int nbits) | ||
32 | { | ||
33 | return (nbits + sizeof(bitset_t) * 8 - 1) / (sizeof(bitset_t) * 8); | ||
34 | } | ||
35 | |||
36 | static inline bitset_t *bitset_alloc(int nbits) | ||
37 | { | ||
38 | return kcalloc(bitset_size(nbits), sizeof(bitset_t), GFP_KERNEL); | ||
39 | } | ||
40 | |||
41 | static inline void bitset_set(bitset_t *bitmap, unsigned int pos) | ||
42 | { | ||
43 | size_t bits = sizeof(*bitmap) * 8; | ||
44 | bitmap[pos / bits] |= 1 << (pos % bits); | ||
45 | } | ||
46 | |||
47 | static inline void bitset_reset(bitset_t *bitmap, unsigned int pos) | ||
48 | { | ||
49 | size_t bits = sizeof(*bitmap) * 8; | ||
50 | bitmap[pos / bits] &= ~(1 << (pos % bits)); | ||
51 | } | ||
52 | |||
53 | static inline int bitset_get(bitset_t *bitmap, unsigned int pos) | ||
54 | { | ||
55 | size_t bits = sizeof(*bitmap) * 8; | ||
56 | return !!(bitmap[pos / bits] & (1 << (pos % bits))); | ||
57 | } | ||
58 | |||
59 | static inline void bitset_copy(bitset_t *dst, bitset_t *src, unsigned int nbits) | ||
60 | { | ||
61 | memcpy(dst, src, bitset_size(nbits) * sizeof(bitset_t)); | ||
62 | } | ||
63 | |||
64 | static inline void bitset_and(bitset_t *dst, bitset_t *bs, unsigned int nbits) | ||
65 | { | ||
66 | bitset_t *end = dst + bitset_size(nbits); | ||
67 | while (dst < end) | ||
68 | *dst++ &= *bs++; | ||
69 | } | ||
70 | |||
71 | static inline void bitset_or(bitset_t *dst, bitset_t *bs, unsigned int nbits) | ||
72 | { | ||
73 | bitset_t *end = dst + bitset_size(nbits); | ||
74 | while (dst < end) | ||
75 | *dst++ |= *bs++; | ||
76 | } | ||
77 | |||
78 | static inline void bitset_zero(bitset_t *dst, unsigned int nbits) | ||
79 | { | ||
80 | bitset_t *end = dst + bitset_size(nbits); | ||
81 | while (dst < end) | ||
82 | *dst++ = 0; | ||
83 | } | ||
84 | |||
85 | static inline void bitset_one(bitset_t *dst, unsigned int nbits) | ||
86 | { | ||
87 | bitset_t *end = dst + bitset_size(nbits); | ||
88 | while (dst < end) | ||
89 | *dst++ = ~(bitset_t)0; | ||
90 | } | ||
91 | |||
92 | #define snd_pcm_plug_t snd_pcm_substream_t | ||
93 | #define snd_pcm_plug_stream(plug) ((plug)->stream) | ||
94 | |||
95 | typedef enum { | ||
96 | INIT = 0, | ||
97 | PREPARE = 1, | ||
98 | } snd_pcm_plugin_action_t; | ||
99 | |||
100 | typedef struct _snd_pcm_channel_area { | ||
101 | void *addr; /* base address of channel samples */ | ||
102 | unsigned int first; /* offset to first sample in bits */ | ||
103 | unsigned int step; /* samples distance in bits */ | ||
104 | } snd_pcm_channel_area_t; | ||
105 | |||
106 | typedef struct _snd_pcm_plugin_channel { | ||
107 | void *aptr; /* pointer to the allocated area */ | ||
108 | snd_pcm_channel_area_t area; | ||
109 | snd_pcm_uframes_t frames; /* allocated frames */ | ||
110 | unsigned int enabled:1; /* channel need to be processed */ | ||
111 | unsigned int wanted:1; /* channel is wanted */ | ||
112 | } snd_pcm_plugin_channel_t; | ||
113 | |||
114 | typedef struct _snd_pcm_plugin_format { | ||
115 | int format; | ||
116 | unsigned int rate; | ||
117 | unsigned int channels; | ||
118 | } snd_pcm_plugin_format_t; | ||
119 | |||
120 | struct _snd_pcm_plugin { | ||
121 | const char *name; /* plug-in name */ | ||
122 | int stream; | ||
123 | snd_pcm_plugin_format_t src_format; /* source format */ | ||
124 | snd_pcm_plugin_format_t dst_format; /* destination format */ | ||
125 | int src_width; /* sample width in bits */ | ||
126 | int dst_width; /* sample width in bits */ | ||
127 | int access; | ||
128 | snd_pcm_sframes_t (*src_frames)(snd_pcm_plugin_t *plugin, snd_pcm_uframes_t dst_frames); | ||
129 | snd_pcm_sframes_t (*dst_frames)(snd_pcm_plugin_t *plugin, snd_pcm_uframes_t src_frames); | ||
130 | snd_pcm_sframes_t (*client_channels)(snd_pcm_plugin_t *plugin, | ||
131 | snd_pcm_uframes_t frames, | ||
132 | snd_pcm_plugin_channel_t **channels); | ||
133 | int (*src_channels_mask)(snd_pcm_plugin_t *plugin, | ||
134 | bitset_t *dst_vmask, | ||
135 | bitset_t **src_vmask); | ||
136 | int (*dst_channels_mask)(snd_pcm_plugin_t *plugin, | ||
137 | bitset_t *src_vmask, | ||
138 | bitset_t **dst_vmask); | ||
139 | snd_pcm_sframes_t (*transfer)(snd_pcm_plugin_t *plugin, | ||
140 | const snd_pcm_plugin_channel_t *src_channels, | ||
141 | snd_pcm_plugin_channel_t *dst_channels, | ||
142 | snd_pcm_uframes_t frames); | ||
143 | int (*action)(snd_pcm_plugin_t *plugin, | ||
144 | snd_pcm_plugin_action_t action, | ||
145 | unsigned long data); | ||
146 | snd_pcm_plugin_t *prev; | ||
147 | snd_pcm_plugin_t *next; | ||
148 | snd_pcm_plug_t *plug; | ||
149 | void *private_data; | ||
150 | void (*private_free)(snd_pcm_plugin_t *plugin); | ||
151 | char *buf; | ||
152 | snd_pcm_uframes_t buf_frames; | ||
153 | snd_pcm_plugin_channel_t *buf_channels; | ||
154 | bitset_t *src_vmask; | ||
155 | bitset_t *dst_vmask; | ||
156 | char extra_data[0]; | ||
157 | }; | ||
158 | |||
159 | int snd_pcm_plugin_build(snd_pcm_plug_t *handle, | ||
160 | const char *name, | ||
161 | snd_pcm_plugin_format_t *src_format, | ||
162 | snd_pcm_plugin_format_t *dst_format, | ||
163 | size_t extra, | ||
164 | snd_pcm_plugin_t **ret); | ||
165 | int snd_pcm_plugin_free(snd_pcm_plugin_t *plugin); | ||
166 | int snd_pcm_plugin_clear(snd_pcm_plugin_t **first); | ||
167 | int snd_pcm_plug_alloc(snd_pcm_plug_t *plug, snd_pcm_uframes_t frames); | ||
168 | snd_pcm_sframes_t snd_pcm_plug_client_size(snd_pcm_plug_t *handle, snd_pcm_uframes_t drv_size); | ||
169 | snd_pcm_sframes_t snd_pcm_plug_slave_size(snd_pcm_plug_t *handle, snd_pcm_uframes_t clt_size); | ||
170 | |||
171 | #define FULL ROUTE_PLUGIN_RESOLUTION | ||
172 | #define HALF ROUTE_PLUGIN_RESOLUTION / 2 | ||
173 | typedef int route_ttable_entry_t; | ||
174 | |||
175 | int snd_pcm_plugin_build_io(snd_pcm_plug_t *handle, | ||
176 | snd_pcm_hw_params_t *params, | ||
177 | snd_pcm_plugin_t **r_plugin); | ||
178 | int snd_pcm_plugin_build_linear(snd_pcm_plug_t *handle, | ||
179 | snd_pcm_plugin_format_t *src_format, | ||
180 | snd_pcm_plugin_format_t *dst_format, | ||
181 | snd_pcm_plugin_t **r_plugin); | ||
182 | int snd_pcm_plugin_build_mulaw(snd_pcm_plug_t *handle, | ||
183 | snd_pcm_plugin_format_t *src_format, | ||
184 | snd_pcm_plugin_format_t *dst_format, | ||
185 | snd_pcm_plugin_t **r_plugin); | ||
186 | int snd_pcm_plugin_build_rate(snd_pcm_plug_t *handle, | ||
187 | snd_pcm_plugin_format_t *src_format, | ||
188 | snd_pcm_plugin_format_t *dst_format, | ||
189 | snd_pcm_plugin_t **r_plugin); | ||
190 | int snd_pcm_plugin_build_route(snd_pcm_plug_t *handle, | ||
191 | snd_pcm_plugin_format_t *src_format, | ||
192 | snd_pcm_plugin_format_t *dst_format, | ||
193 | route_ttable_entry_t *ttable, | ||
194 | snd_pcm_plugin_t **r_plugin); | ||
195 | int snd_pcm_plugin_build_copy(snd_pcm_plug_t *handle, | ||
196 | snd_pcm_plugin_format_t *src_format, | ||
197 | snd_pcm_plugin_format_t *dst_format, | ||
198 | snd_pcm_plugin_t **r_plugin); | ||
199 | |||
200 | int snd_pcm_plug_format_plugins(snd_pcm_plug_t *substream, | ||
201 | snd_pcm_hw_params_t *params, | ||
202 | snd_pcm_hw_params_t *slave_params); | ||
203 | |||
204 | int snd_pcm_plug_slave_format(int format, snd_mask_t *format_mask); | ||
205 | |||
206 | int snd_pcm_plugin_append(snd_pcm_plugin_t *plugin); | ||
207 | |||
208 | snd_pcm_sframes_t snd_pcm_plug_write_transfer(snd_pcm_plug_t *handle, snd_pcm_plugin_channel_t *src_channels, snd_pcm_uframes_t size); | ||
209 | snd_pcm_sframes_t snd_pcm_plug_read_transfer(snd_pcm_plug_t *handle, snd_pcm_plugin_channel_t *dst_channels_final, snd_pcm_uframes_t size); | ||
210 | |||
211 | snd_pcm_sframes_t snd_pcm_plug_client_channels_buf(snd_pcm_plug_t *handle, | ||
212 | char *buf, snd_pcm_uframes_t count, | ||
213 | snd_pcm_plugin_channel_t **channels); | ||
214 | |||
215 | snd_pcm_sframes_t snd_pcm_plugin_client_channels(snd_pcm_plugin_t *plugin, | ||
216 | snd_pcm_uframes_t frames, | ||
217 | snd_pcm_plugin_channel_t **channels); | ||
218 | |||
219 | int snd_pcm_area_silence(const snd_pcm_channel_area_t *dst_channel, size_t dst_offset, | ||
220 | size_t samples, int format); | ||
221 | int snd_pcm_area_copy(const snd_pcm_channel_area_t *src_channel, size_t src_offset, | ||
222 | const snd_pcm_channel_area_t *dst_channel, size_t dst_offset, | ||
223 | size_t samples, int format); | ||
224 | |||
225 | void *snd_pcm_plug_buf_alloc(snd_pcm_plug_t *plug, snd_pcm_uframes_t size); | ||
226 | void snd_pcm_plug_buf_unlock(snd_pcm_plug_t *plug, void *ptr); | ||
227 | snd_pcm_sframes_t snd_pcm_oss_write3(snd_pcm_substream_t *substream, const char *ptr, snd_pcm_uframes_t size, int in_kernel); | ||
228 | snd_pcm_sframes_t snd_pcm_oss_read3(snd_pcm_substream_t *substream, char *ptr, snd_pcm_uframes_t size, int in_kernel); | ||
229 | snd_pcm_sframes_t snd_pcm_oss_writev3(snd_pcm_substream_t *substream, void **bufs, snd_pcm_uframes_t frames, int in_kernel); | ||
230 | snd_pcm_sframes_t snd_pcm_oss_readv3(snd_pcm_substream_t *substream, void **bufs, snd_pcm_uframes_t frames, int in_kernel); | ||
231 | |||
232 | |||
233 | |||
234 | #define ROUTE_PLUGIN_RESOLUTION 16 | ||
235 | |||
236 | int getput_index(int format); | ||
237 | int copy_index(int format); | ||
238 | int conv_index(int src_format, int dst_format); | ||
239 | |||
240 | void zero_channel(snd_pcm_plugin_t *plugin, | ||
241 | const snd_pcm_plugin_channel_t *dst_channel, | ||
242 | size_t samples); | ||
243 | |||
244 | #ifdef PLUGIN_DEBUG | ||
245 | #define pdprintf( fmt, args... ) printk( "plugin: " fmt, ##args) | ||
246 | #else | ||
247 | #define pdprintf( fmt, args... ) | ||
248 | #endif | ||
249 | |||
250 | #endif /* __PCM_PLUGIN_H */ | ||
diff --git a/sound/core/oss/plugin_ops.h b/sound/core/oss/plugin_ops.h new file mode 100644 index 000000000000..0607e9566084 --- /dev/null +++ b/sound/core/oss/plugin_ops.h | |||
@@ -0,0 +1,536 @@ | |||
1 | /* | ||
2 | * Plugin sample operators with fast switch | ||
3 | * Copyright (c) 2000 by Jaroslav Kysela <perex@suse.cz> | ||
4 | * | ||
5 | * | ||
6 | * This library is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU Library General Public License as | ||
8 | * published by the Free Software Foundation; either version 2 of | ||
9 | * the License, or (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU Library General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU Library General Public | ||
17 | * License along with this library; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | |||
23 | #define as_u8(ptr) (*(u_int8_t*)(ptr)) | ||
24 | #define as_u16(ptr) (*(u_int16_t*)(ptr)) | ||
25 | #define as_u32(ptr) (*(u_int32_t*)(ptr)) | ||
26 | #define as_u64(ptr) (*(u_int64_t*)(ptr)) | ||
27 | #define as_s8(ptr) (*(int8_t*)(ptr)) | ||
28 | #define as_s16(ptr) (*(int16_t*)(ptr)) | ||
29 | #define as_s32(ptr) (*(int32_t*)(ptr)) | ||
30 | #define as_s64(ptr) (*(int64_t*)(ptr)) | ||
31 | |||
32 | #ifdef COPY_LABELS | ||
33 | static void *copy_labels[4] = { | ||
34 | &©_8, | ||
35 | &©_16, | ||
36 | &©_32, | ||
37 | &©_64 | ||
38 | }; | ||
39 | #endif | ||
40 | |||
41 | #ifdef COPY_END | ||
42 | while(0) { | ||
43 | copy_8: as_s8(dst) = as_s8(src); goto COPY_END; | ||
44 | copy_16: as_s16(dst) = as_s16(src); goto COPY_END; | ||
45 | copy_32: as_s32(dst) = as_s32(src); goto COPY_END; | ||
46 | copy_64: as_s64(dst) = as_s64(src); goto COPY_END; | ||
47 | } | ||
48 | #endif | ||
49 | |||
50 | #ifdef CONV_LABELS | ||
51 | /* src_wid src_endswap sign_toggle dst_wid dst_endswap */ | ||
52 | static void *conv_labels[4 * 2 * 2 * 4 * 2] = { | ||
53 | &&conv_xxx1_xxx1, /* 8h -> 8h */ | ||
54 | &&conv_xxx1_xxx1, /* 8h -> 8s */ | ||
55 | &&conv_xxx1_xx10, /* 8h -> 16h */ | ||
56 | &&conv_xxx1_xx01, /* 8h -> 16s */ | ||
57 | &&conv_xxx1_x100, /* 8h -> 24h */ | ||
58 | &&conv_xxx1_001x, /* 8h -> 24s */ | ||
59 | &&conv_xxx1_1000, /* 8h -> 32h */ | ||
60 | &&conv_xxx1_0001, /* 8h -> 32s */ | ||
61 | &&conv_xxx1_xxx9, /* 8h ^> 8h */ | ||
62 | &&conv_xxx1_xxx9, /* 8h ^> 8s */ | ||
63 | &&conv_xxx1_xx90, /* 8h ^> 16h */ | ||
64 | &&conv_xxx1_xx09, /* 8h ^> 16s */ | ||
65 | &&conv_xxx1_x900, /* 8h ^> 24h */ | ||
66 | &&conv_xxx1_009x, /* 8h ^> 24s */ | ||
67 | &&conv_xxx1_9000, /* 8h ^> 32h */ | ||
68 | &&conv_xxx1_0009, /* 8h ^> 32s */ | ||
69 | &&conv_xxx1_xxx1, /* 8s -> 8h */ | ||
70 | &&conv_xxx1_xxx1, /* 8s -> 8s */ | ||
71 | &&conv_xxx1_xx10, /* 8s -> 16h */ | ||
72 | &&conv_xxx1_xx01, /* 8s -> 16s */ | ||
73 | &&conv_xxx1_x100, /* 8s -> 24h */ | ||
74 | &&conv_xxx1_001x, /* 8s -> 24s */ | ||
75 | &&conv_xxx1_1000, /* 8s -> 32h */ | ||
76 | &&conv_xxx1_0001, /* 8s -> 32s */ | ||
77 | &&conv_xxx1_xxx9, /* 8s ^> 8h */ | ||
78 | &&conv_xxx1_xxx9, /* 8s ^> 8s */ | ||
79 | &&conv_xxx1_xx90, /* 8s ^> 16h */ | ||
80 | &&conv_xxx1_xx09, /* 8s ^> 16s */ | ||
81 | &&conv_xxx1_x900, /* 8s ^> 24h */ | ||
82 | &&conv_xxx1_009x, /* 8s ^> 24s */ | ||
83 | &&conv_xxx1_9000, /* 8s ^> 32h */ | ||
84 | &&conv_xxx1_0009, /* 8s ^> 32s */ | ||
85 | &&conv_xx12_xxx1, /* 16h -> 8h */ | ||
86 | &&conv_xx12_xxx1, /* 16h -> 8s */ | ||
87 | &&conv_xx12_xx12, /* 16h -> 16h */ | ||
88 | &&conv_xx12_xx21, /* 16h -> 16s */ | ||
89 | &&conv_xx12_x120, /* 16h -> 24h */ | ||
90 | &&conv_xx12_021x, /* 16h -> 24s */ | ||
91 | &&conv_xx12_1200, /* 16h -> 32h */ | ||
92 | &&conv_xx12_0021, /* 16h -> 32s */ | ||
93 | &&conv_xx12_xxx9, /* 16h ^> 8h */ | ||
94 | &&conv_xx12_xxx9, /* 16h ^> 8s */ | ||
95 | &&conv_xx12_xx92, /* 16h ^> 16h */ | ||
96 | &&conv_xx12_xx29, /* 16h ^> 16s */ | ||
97 | &&conv_xx12_x920, /* 16h ^> 24h */ | ||
98 | &&conv_xx12_029x, /* 16h ^> 24s */ | ||
99 | &&conv_xx12_9200, /* 16h ^> 32h */ | ||
100 | &&conv_xx12_0029, /* 16h ^> 32s */ | ||
101 | &&conv_xx12_xxx2, /* 16s -> 8h */ | ||
102 | &&conv_xx12_xxx2, /* 16s -> 8s */ | ||
103 | &&conv_xx12_xx21, /* 16s -> 16h */ | ||
104 | &&conv_xx12_xx12, /* 16s -> 16s */ | ||
105 | &&conv_xx12_x210, /* 16s -> 24h */ | ||
106 | &&conv_xx12_012x, /* 16s -> 24s */ | ||
107 | &&conv_xx12_2100, /* 16s -> 32h */ | ||
108 | &&conv_xx12_0012, /* 16s -> 32s */ | ||
109 | &&conv_xx12_xxxA, /* 16s ^> 8h */ | ||
110 | &&conv_xx12_xxxA, /* 16s ^> 8s */ | ||
111 | &&conv_xx12_xxA1, /* 16s ^> 16h */ | ||
112 | &&conv_xx12_xx1A, /* 16s ^> 16s */ | ||
113 | &&conv_xx12_xA10, /* 16s ^> 24h */ | ||
114 | &&conv_xx12_01Ax, /* 16s ^> 24s */ | ||
115 | &&conv_xx12_A100, /* 16s ^> 32h */ | ||
116 | &&conv_xx12_001A, /* 16s ^> 32s */ | ||
117 | &&conv_x123_xxx1, /* 24h -> 8h */ | ||
118 | &&conv_x123_xxx1, /* 24h -> 8s */ | ||
119 | &&conv_x123_xx12, /* 24h -> 16h */ | ||
120 | &&conv_x123_xx21, /* 24h -> 16s */ | ||
121 | &&conv_x123_x123, /* 24h -> 24h */ | ||
122 | &&conv_x123_321x, /* 24h -> 24s */ | ||
123 | &&conv_x123_1230, /* 24h -> 32h */ | ||
124 | &&conv_x123_0321, /* 24h -> 32s */ | ||
125 | &&conv_x123_xxx9, /* 24h ^> 8h */ | ||
126 | &&conv_x123_xxx9, /* 24h ^> 8s */ | ||
127 | &&conv_x123_xx92, /* 24h ^> 16h */ | ||
128 | &&conv_x123_xx29, /* 24h ^> 16s */ | ||
129 | &&conv_x123_x923, /* 24h ^> 24h */ | ||
130 | &&conv_x123_329x, /* 24h ^> 24s */ | ||
131 | &&conv_x123_9230, /* 24h ^> 32h */ | ||
132 | &&conv_x123_0329, /* 24h ^> 32s */ | ||
133 | &&conv_123x_xxx3, /* 24s -> 8h */ | ||
134 | &&conv_123x_xxx3, /* 24s -> 8s */ | ||
135 | &&conv_123x_xx32, /* 24s -> 16h */ | ||
136 | &&conv_123x_xx23, /* 24s -> 16s */ | ||
137 | &&conv_123x_x321, /* 24s -> 24h */ | ||
138 | &&conv_123x_123x, /* 24s -> 24s */ | ||
139 | &&conv_123x_3210, /* 24s -> 32h */ | ||
140 | &&conv_123x_0123, /* 24s -> 32s */ | ||
141 | &&conv_123x_xxxB, /* 24s ^> 8h */ | ||
142 | &&conv_123x_xxxB, /* 24s ^> 8s */ | ||
143 | &&conv_123x_xxB2, /* 24s ^> 16h */ | ||
144 | &&conv_123x_xx2B, /* 24s ^> 16s */ | ||
145 | &&conv_123x_xB21, /* 24s ^> 24h */ | ||
146 | &&conv_123x_12Bx, /* 24s ^> 24s */ | ||
147 | &&conv_123x_B210, /* 24s ^> 32h */ | ||
148 | &&conv_123x_012B, /* 24s ^> 32s */ | ||
149 | &&conv_1234_xxx1, /* 32h -> 8h */ | ||
150 | &&conv_1234_xxx1, /* 32h -> 8s */ | ||
151 | &&conv_1234_xx12, /* 32h -> 16h */ | ||
152 | &&conv_1234_xx21, /* 32h -> 16s */ | ||
153 | &&conv_1234_x123, /* 32h -> 24h */ | ||
154 | &&conv_1234_321x, /* 32h -> 24s */ | ||
155 | &&conv_1234_1234, /* 32h -> 32h */ | ||
156 | &&conv_1234_4321, /* 32h -> 32s */ | ||
157 | &&conv_1234_xxx9, /* 32h ^> 8h */ | ||
158 | &&conv_1234_xxx9, /* 32h ^> 8s */ | ||
159 | &&conv_1234_xx92, /* 32h ^> 16h */ | ||
160 | &&conv_1234_xx29, /* 32h ^> 16s */ | ||
161 | &&conv_1234_x923, /* 32h ^> 24h */ | ||
162 | &&conv_1234_329x, /* 32h ^> 24s */ | ||
163 | &&conv_1234_9234, /* 32h ^> 32h */ | ||
164 | &&conv_1234_4329, /* 32h ^> 32s */ | ||
165 | &&conv_1234_xxx4, /* 32s -> 8h */ | ||
166 | &&conv_1234_xxx4, /* 32s -> 8s */ | ||
167 | &&conv_1234_xx43, /* 32s -> 16h */ | ||
168 | &&conv_1234_xx34, /* 32s -> 16s */ | ||
169 | &&conv_1234_x432, /* 32s -> 24h */ | ||
170 | &&conv_1234_234x, /* 32s -> 24s */ | ||
171 | &&conv_1234_4321, /* 32s -> 32h */ | ||
172 | &&conv_1234_1234, /* 32s -> 32s */ | ||
173 | &&conv_1234_xxxC, /* 32s ^> 8h */ | ||
174 | &&conv_1234_xxxC, /* 32s ^> 8s */ | ||
175 | &&conv_1234_xxC3, /* 32s ^> 16h */ | ||
176 | &&conv_1234_xx3C, /* 32s ^> 16s */ | ||
177 | &&conv_1234_xC32, /* 32s ^> 24h */ | ||
178 | &&conv_1234_23Cx, /* 32s ^> 24s */ | ||
179 | &&conv_1234_C321, /* 32s ^> 32h */ | ||
180 | &&conv_1234_123C, /* 32s ^> 32s */ | ||
181 | }; | ||
182 | #endif | ||
183 | |||
184 | #ifdef CONV_END | ||
185 | while(0) { | ||
186 | conv_xxx1_xxx1: as_u8(dst) = as_u8(src); goto CONV_END; | ||
187 | conv_xxx1_xx10: as_u16(dst) = (u_int16_t)as_u8(src) << 8; goto CONV_END; | ||
188 | conv_xxx1_xx01: as_u16(dst) = (u_int16_t)as_u8(src); goto CONV_END; | ||
189 | conv_xxx1_x100: as_u32(dst) = (u_int32_t)as_u8(src) << 16; goto CONV_END; | ||
190 | conv_xxx1_001x: as_u32(dst) = (u_int32_t)as_u8(src) << 8; goto CONV_END; | ||
191 | conv_xxx1_1000: as_u32(dst) = (u_int32_t)as_u8(src) << 24; goto CONV_END; | ||
192 | conv_xxx1_0001: as_u32(dst) = (u_int32_t)as_u8(src); goto CONV_END; | ||
193 | conv_xxx1_xxx9: as_u8(dst) = as_u8(src) ^ 0x80; goto CONV_END; | ||
194 | conv_xxx1_xx90: as_u16(dst) = (u_int16_t)(as_u8(src) ^ 0x80) << 8; goto CONV_END; | ||
195 | conv_xxx1_xx09: as_u16(dst) = (u_int16_t)(as_u8(src) ^ 0x80); goto CONV_END; | ||
196 | conv_xxx1_x900: as_u32(dst) = (u_int32_t)(as_u8(src) ^ 0x80) << 16; goto CONV_END; | ||
197 | conv_xxx1_009x: as_u32(dst) = (u_int32_t)(as_u8(src) ^ 0x80) << 8; goto CONV_END; | ||
198 | conv_xxx1_9000: as_u32(dst) = (u_int32_t)(as_u8(src) ^ 0x80) << 24; goto CONV_END; | ||
199 | conv_xxx1_0009: as_u32(dst) = (u_int32_t)(as_u8(src) ^ 0x80); goto CONV_END; | ||
200 | conv_xx12_xxx1: as_u8(dst) = as_u16(src) >> 8; goto CONV_END; | ||
201 | conv_xx12_xx12: as_u16(dst) = as_u16(src); goto CONV_END; | ||
202 | conv_xx12_xx21: as_u16(dst) = swab16(as_u16(src)); goto CONV_END; | ||
203 | conv_xx12_x120: as_u32(dst) = (u_int32_t)as_u16(src) << 8; goto CONV_END; | ||
204 | conv_xx12_021x: as_u32(dst) = (u_int32_t)swab16(as_u16(src)) << 8; goto CONV_END; | ||
205 | conv_xx12_1200: as_u32(dst) = (u_int32_t)as_u16(src) << 16; goto CONV_END; | ||
206 | conv_xx12_0021: as_u32(dst) = (u_int32_t)swab16(as_u16(src)); goto CONV_END; | ||
207 | conv_xx12_xxx9: as_u8(dst) = (as_u16(src) >> 8) ^ 0x80; goto CONV_END; | ||
208 | conv_xx12_xx92: as_u16(dst) = as_u16(src) ^ 0x8000; goto CONV_END; | ||
209 | conv_xx12_xx29: as_u16(dst) = swab16(as_u16(src)) ^ 0x80; goto CONV_END; | ||
210 | conv_xx12_x920: as_u32(dst) = (u_int32_t)(as_u16(src) ^ 0x8000) << 8; goto CONV_END; | ||
211 | conv_xx12_029x: as_u32(dst) = (u_int32_t)(swab16(as_u16(src)) ^ 0x80) << 8; goto CONV_END; | ||
212 | conv_xx12_9200: as_u32(dst) = (u_int32_t)(as_u16(src) ^ 0x8000) << 16; goto CONV_END; | ||
213 | conv_xx12_0029: as_u32(dst) = (u_int32_t)(swab16(as_u16(src)) ^ 0x80); goto CONV_END; | ||
214 | conv_xx12_xxx2: as_u8(dst) = as_u16(src) & 0xff; goto CONV_END; | ||
215 | conv_xx12_x210: as_u32(dst) = (u_int32_t)swab16(as_u16(src)) << 8; goto CONV_END; | ||
216 | conv_xx12_012x: as_u32(dst) = (u_int32_t)as_u16(src) << 8; goto CONV_END; | ||
217 | conv_xx12_2100: as_u32(dst) = (u_int32_t)swab16(as_u16(src)) << 16; goto CONV_END; | ||
218 | conv_xx12_0012: as_u32(dst) = (u_int32_t)as_u16(src); goto CONV_END; | ||
219 | conv_xx12_xxxA: as_u8(dst) = (as_u16(src) ^ 0x80) & 0xff; goto CONV_END; | ||
220 | conv_xx12_xxA1: as_u16(dst) = swab16(as_u16(src) ^ 0x80); goto CONV_END; | ||
221 | conv_xx12_xx1A: as_u16(dst) = as_u16(src) ^ 0x80; goto CONV_END; | ||
222 | conv_xx12_xA10: as_u32(dst) = (u_int32_t)swab16(as_u16(src) ^ 0x80) << 8; goto CONV_END; | ||
223 | conv_xx12_01Ax: as_u32(dst) = (u_int32_t)(as_u16(src) ^ 0x80) << 8; goto CONV_END; | ||
224 | conv_xx12_A100: as_u32(dst) = (u_int32_t)swab16(as_u16(src) ^ 0x80) << 16; goto CONV_END; | ||
225 | conv_xx12_001A: as_u32(dst) = (u_int32_t)(as_u16(src) ^ 0x80); goto CONV_END; | ||
226 | conv_x123_xxx1: as_u8(dst) = as_u32(src) >> 16; goto CONV_END; | ||
227 | conv_x123_xx12: as_u16(dst) = as_u32(src) >> 8; goto CONV_END; | ||
228 | conv_x123_xx21: as_u16(dst) = swab16(as_u32(src) >> 8); goto CONV_END; | ||
229 | conv_x123_x123: as_u32(dst) = as_u32(src); goto CONV_END; | ||
230 | conv_x123_321x: as_u32(dst) = swab32(as_u32(src)); goto CONV_END; | ||
231 | conv_x123_1230: as_u32(dst) = as_u32(src) << 8; goto CONV_END; | ||
232 | conv_x123_0321: as_u32(dst) = swab32(as_u32(src)) >> 8; goto CONV_END; | ||
233 | conv_x123_xxx9: as_u8(dst) = (as_u32(src) >> 16) ^ 0x80; goto CONV_END; | ||
234 | conv_x123_xx92: as_u16(dst) = (as_u32(src) >> 8) ^ 0x8000; goto CONV_END; | ||
235 | conv_x123_xx29: as_u16(dst) = swab16(as_u32(src) >> 8) ^ 0x80; goto CONV_END; | ||
236 | conv_x123_x923: as_u32(dst) = as_u32(src) ^ 0x800000; goto CONV_END; | ||
237 | conv_x123_329x: as_u32(dst) = swab32(as_u32(src)) ^ 0x8000; goto CONV_END; | ||
238 | conv_x123_9230: as_u32(dst) = (as_u32(src) ^ 0x800000) << 8; goto CONV_END; | ||
239 | conv_x123_0329: as_u32(dst) = (swab32(as_u32(src)) >> 8) ^ 0x80; goto CONV_END; | ||
240 | conv_123x_xxx3: as_u8(dst) = (as_u32(src) >> 8) & 0xff; goto CONV_END; | ||
241 | conv_123x_xx32: as_u16(dst) = swab16(as_u32(src) >> 8); goto CONV_END; | ||
242 | conv_123x_xx23: as_u16(dst) = (as_u32(src) >> 8) & 0xffff; goto CONV_END; | ||
243 | conv_123x_x321: as_u32(dst) = swab32(as_u32(src)); goto CONV_END; | ||
244 | conv_123x_123x: as_u32(dst) = as_u32(src); goto CONV_END; | ||
245 | conv_123x_3210: as_u32(dst) = swab32(as_u32(src)) << 8; goto CONV_END; | ||
246 | conv_123x_0123: as_u32(dst) = as_u32(src) >> 8; goto CONV_END; | ||
247 | conv_123x_xxxB: as_u8(dst) = ((as_u32(src) >> 8) & 0xff) ^ 0x80; goto CONV_END; | ||
248 | conv_123x_xxB2: as_u16(dst) = swab16((as_u32(src) >> 8) ^ 0x80); goto CONV_END; | ||
249 | conv_123x_xx2B: as_u16(dst) = ((as_u32(src) >> 8) & 0xffff) ^ 0x80; goto CONV_END; | ||
250 | conv_123x_xB21: as_u32(dst) = swab32(as_u32(src)) ^ 0x800000; goto CONV_END; | ||
251 | conv_123x_12Bx: as_u32(dst) = as_u32(src) ^ 0x8000; goto CONV_END; | ||
252 | conv_123x_B210: as_u32(dst) = swab32(as_u32(src) ^ 0x8000) << 8; goto CONV_END; | ||
253 | conv_123x_012B: as_u32(dst) = (as_u32(src) >> 8) ^ 0x80; goto CONV_END; | ||
254 | conv_1234_xxx1: as_u8(dst) = as_u32(src) >> 24; goto CONV_END; | ||
255 | conv_1234_xx12: as_u16(dst) = as_u32(src) >> 16; goto CONV_END; | ||
256 | conv_1234_xx21: as_u16(dst) = swab16(as_u32(src) >> 16); goto CONV_END; | ||
257 | conv_1234_x123: as_u32(dst) = as_u32(src) >> 8; goto CONV_END; | ||
258 | conv_1234_321x: as_u32(dst) = swab32(as_u32(src)) << 8; goto CONV_END; | ||
259 | conv_1234_1234: as_u32(dst) = as_u32(src); goto CONV_END; | ||
260 | conv_1234_4321: as_u32(dst) = swab32(as_u32(src)); goto CONV_END; | ||
261 | conv_1234_xxx9: as_u8(dst) = (as_u32(src) >> 24) ^ 0x80; goto CONV_END; | ||
262 | conv_1234_xx92: as_u16(dst) = (as_u32(src) >> 16) ^ 0x8000; goto CONV_END; | ||
263 | conv_1234_xx29: as_u16(dst) = swab16(as_u32(src) >> 16) ^ 0x80; goto CONV_END; | ||
264 | conv_1234_x923: as_u32(dst) = (as_u32(src) >> 8) ^ 0x800000; goto CONV_END; | ||
265 | conv_1234_329x: as_u32(dst) = (swab32(as_u32(src)) ^ 0x80) << 8; goto CONV_END; | ||
266 | conv_1234_9234: as_u32(dst) = as_u32(src) ^ 0x80000000; goto CONV_END; | ||
267 | conv_1234_4329: as_u32(dst) = swab32(as_u32(src)) ^ 0x80; goto CONV_END; | ||
268 | conv_1234_xxx4: as_u8(dst) = as_u32(src) & 0xff; goto CONV_END; | ||
269 | conv_1234_xx43: as_u16(dst) = swab16(as_u32(src)); goto CONV_END; | ||
270 | conv_1234_xx34: as_u16(dst) = as_u32(src) & 0xffff; goto CONV_END; | ||
271 | conv_1234_x432: as_u32(dst) = swab32(as_u32(src)) >> 8; goto CONV_END; | ||
272 | conv_1234_234x: as_u32(dst) = as_u32(src) << 8; goto CONV_END; | ||
273 | conv_1234_xxxC: as_u8(dst) = (as_u32(src) & 0xff) ^ 0x80; goto CONV_END; | ||
274 | conv_1234_xxC3: as_u16(dst) = swab16(as_u32(src) ^ 0x80); goto CONV_END; | ||
275 | conv_1234_xx3C: as_u16(dst) = (as_u32(src) & 0xffff) ^ 0x80; goto CONV_END; | ||
276 | conv_1234_xC32: as_u32(dst) = (swab32(as_u32(src)) >> 8) ^ 0x800000; goto CONV_END; | ||
277 | conv_1234_23Cx: as_u32(dst) = (as_u32(src) ^ 0x80) << 8; goto CONV_END; | ||
278 | conv_1234_C321: as_u32(dst) = swab32(as_u32(src) ^ 0x80); goto CONV_END; | ||
279 | conv_1234_123C: as_u32(dst) = as_u32(src) ^ 0x80; goto CONV_END; | ||
280 | } | ||
281 | #endif | ||
282 | |||
283 | #ifdef GET_S16_LABELS | ||
284 | /* src_wid src_endswap unsigned */ | ||
285 | static void *get_s16_labels[4 * 2 * 2] = { | ||
286 | &&get_s16_xxx1_xx10, /* 8h -> 16h */ | ||
287 | &&get_s16_xxx1_xx90, /* 8h ^> 16h */ | ||
288 | &&get_s16_xxx1_xx10, /* 8s -> 16h */ | ||
289 | &&get_s16_xxx1_xx90, /* 8s ^> 16h */ | ||
290 | &&get_s16_xx12_xx12, /* 16h -> 16h */ | ||
291 | &&get_s16_xx12_xx92, /* 16h ^> 16h */ | ||
292 | &&get_s16_xx12_xx21, /* 16s -> 16h */ | ||
293 | &&get_s16_xx12_xxA1, /* 16s ^> 16h */ | ||
294 | &&get_s16_x123_xx12, /* 24h -> 16h */ | ||
295 | &&get_s16_x123_xx92, /* 24h ^> 16h */ | ||
296 | &&get_s16_123x_xx32, /* 24s -> 16h */ | ||
297 | &&get_s16_123x_xxB2, /* 24s ^> 16h */ | ||
298 | &&get_s16_1234_xx12, /* 32h -> 16h */ | ||
299 | &&get_s16_1234_xx92, /* 32h ^> 16h */ | ||
300 | &&get_s16_1234_xx43, /* 32s -> 16h */ | ||
301 | &&get_s16_1234_xxC3, /* 32s ^> 16h */ | ||
302 | }; | ||
303 | #endif | ||
304 | |||
305 | #ifdef GET_S16_END | ||
306 | while(0) { | ||
307 | get_s16_xxx1_xx10: sample = (u_int16_t)as_u8(src) << 8; goto GET_S16_END; | ||
308 | get_s16_xxx1_xx90: sample = (u_int16_t)(as_u8(src) ^ 0x80) << 8; goto GET_S16_END; | ||
309 | get_s16_xx12_xx12: sample = as_u16(src); goto GET_S16_END; | ||
310 | get_s16_xx12_xx92: sample = as_u16(src) ^ 0x8000; goto GET_S16_END; | ||
311 | get_s16_xx12_xx21: sample = swab16(as_u16(src)); goto GET_S16_END; | ||
312 | get_s16_xx12_xxA1: sample = swab16(as_u16(src) ^ 0x80); goto GET_S16_END; | ||
313 | get_s16_x123_xx12: sample = as_u32(src) >> 8; goto GET_S16_END; | ||
314 | get_s16_x123_xx92: sample = (as_u32(src) >> 8) ^ 0x8000; goto GET_S16_END; | ||
315 | get_s16_123x_xx32: sample = swab16(as_u32(src) >> 8); goto GET_S16_END; | ||
316 | get_s16_123x_xxB2: sample = swab16((as_u32(src) >> 8) ^ 0x8000); goto GET_S16_END; | ||
317 | get_s16_1234_xx12: sample = as_u32(src) >> 16; goto GET_S16_END; | ||
318 | get_s16_1234_xx92: sample = (as_u32(src) >> 16) ^ 0x8000; goto GET_S16_END; | ||
319 | get_s16_1234_xx43: sample = swab16(as_u32(src)); goto GET_S16_END; | ||
320 | get_s16_1234_xxC3: sample = swab16(as_u32(src) ^ 0x80); goto GET_S16_END; | ||
321 | } | ||
322 | #endif | ||
323 | |||
324 | #ifdef PUT_S16_LABELS | ||
325 | /* dst_wid dst_endswap unsigned */ | ||
326 | static void *put_s16_labels[4 * 2 * 2] = { | ||
327 | &&put_s16_xx12_xxx1, /* 16h -> 8h */ | ||
328 | &&put_s16_xx12_xxx9, /* 16h ^> 8h */ | ||
329 | &&put_s16_xx12_xxx1, /* 16h -> 8s */ | ||
330 | &&put_s16_xx12_xxx9, /* 16h ^> 8s */ | ||
331 | &&put_s16_xx12_xx12, /* 16h -> 16h */ | ||
332 | &&put_s16_xx12_xx92, /* 16h ^> 16h */ | ||
333 | &&put_s16_xx12_xx21, /* 16h -> 16s */ | ||
334 | &&put_s16_xx12_xx29, /* 16h ^> 16s */ | ||
335 | &&put_s16_xx12_x120, /* 16h -> 24h */ | ||
336 | &&put_s16_xx12_x920, /* 16h ^> 24h */ | ||
337 | &&put_s16_xx12_021x, /* 16h -> 24s */ | ||
338 | &&put_s16_xx12_029x, /* 16h ^> 24s */ | ||
339 | &&put_s16_xx12_1200, /* 16h -> 32h */ | ||
340 | &&put_s16_xx12_9200, /* 16h ^> 32h */ | ||
341 | &&put_s16_xx12_0021, /* 16h -> 32s */ | ||
342 | &&put_s16_xx12_0029, /* 16h ^> 32s */ | ||
343 | }; | ||
344 | #endif | ||
345 | |||
346 | #ifdef PUT_S16_END | ||
347 | while (0) { | ||
348 | put_s16_xx12_xxx1: as_u8(dst) = sample >> 8; goto PUT_S16_END; | ||
349 | put_s16_xx12_xxx9: as_u8(dst) = (sample >> 8) ^ 0x80; goto PUT_S16_END; | ||
350 | put_s16_xx12_xx12: as_u16(dst) = sample; goto PUT_S16_END; | ||
351 | put_s16_xx12_xx92: as_u16(dst) = sample ^ 0x8000; goto PUT_S16_END; | ||
352 | put_s16_xx12_xx21: as_u16(dst) = swab16(sample); goto PUT_S16_END; | ||
353 | put_s16_xx12_xx29: as_u16(dst) = swab16(sample) ^ 0x80; goto PUT_S16_END; | ||
354 | put_s16_xx12_x120: as_u32(dst) = (u_int32_t)sample << 8; goto PUT_S16_END; | ||
355 | put_s16_xx12_x920: as_u32(dst) = (u_int32_t)(sample ^ 0x8000) << 8; goto PUT_S16_END; | ||
356 | put_s16_xx12_021x: as_u32(dst) = (u_int32_t)swab16(sample) << 8; goto PUT_S16_END; | ||
357 | put_s16_xx12_029x: as_u32(dst) = (u_int32_t)(swab16(sample) ^ 0x80) << 8; goto PUT_S16_END; | ||
358 | put_s16_xx12_1200: as_u32(dst) = (u_int32_t)sample << 16; goto PUT_S16_END; | ||
359 | put_s16_xx12_9200: as_u32(dst) = (u_int32_t)(sample ^ 0x8000) << 16; goto PUT_S16_END; | ||
360 | put_s16_xx12_0021: as_u32(dst) = (u_int32_t)swab16(sample); goto PUT_S16_END; | ||
361 | put_s16_xx12_0029: as_u32(dst) = (u_int32_t)swab16(sample) ^ 0x80; goto PUT_S16_END; | ||
362 | } | ||
363 | #endif | ||
364 | |||
365 | #if 0 | ||
366 | #ifdef GET32_LABELS | ||
367 | /* src_wid src_endswap unsigned */ | ||
368 | static void *get32_labels[4 * 2 * 2] = { | ||
369 | &&get32_xxx1_1000, /* 8h -> 32h */ | ||
370 | &&get32_xxx1_9000, /* 8h ^> 32h */ | ||
371 | &&get32_xxx1_1000, /* 8s -> 32h */ | ||
372 | &&get32_xxx1_9000, /* 8s ^> 32h */ | ||
373 | &&get32_xx12_1200, /* 16h -> 32h */ | ||
374 | &&get32_xx12_9200, /* 16h ^> 32h */ | ||
375 | &&get32_xx12_2100, /* 16s -> 32h */ | ||
376 | &&get32_xx12_A100, /* 16s ^> 32h */ | ||
377 | &&get32_x123_1230, /* 24h -> 32h */ | ||
378 | &&get32_x123_9230, /* 24h ^> 32h */ | ||
379 | &&get32_123x_3210, /* 24s -> 32h */ | ||
380 | &&get32_123x_B210, /* 24s ^> 32h */ | ||
381 | &&get32_1234_1234, /* 32h -> 32h */ | ||
382 | &&get32_1234_9234, /* 32h ^> 32h */ | ||
383 | &&get32_1234_4321, /* 32s -> 32h */ | ||
384 | &&get32_1234_C321, /* 32s ^> 32h */ | ||
385 | }; | ||
386 | #endif | ||
387 | |||
388 | #ifdef GET32_END | ||
389 | while (0) { | ||
390 | get32_xxx1_1000: sample = (u_int32_t)as_u8(src) << 24; goto GET32_END; | ||
391 | get32_xxx1_9000: sample = (u_int32_t)(as_u8(src) ^ 0x80) << 24; goto GET32_END; | ||
392 | get32_xx12_1200: sample = (u_int32_t)as_u16(src) << 16; goto GET32_END; | ||
393 | get32_xx12_9200: sample = (u_int32_t)(as_u16(src) ^ 0x8000) << 16; goto GET32_END; | ||
394 | get32_xx12_2100: sample = (u_int32_t)swab16(as_u16(src)) << 16; goto GET32_END; | ||
395 | get32_xx12_A100: sample = (u_int32_t)swab16(as_u16(src) ^ 0x80) << 16; goto GET32_END; | ||
396 | get32_x123_1230: sample = as_u32(src) << 8; goto GET32_END; | ||
397 | get32_x123_9230: sample = (as_u32(src) << 8) ^ 0x80000000; goto GET32_END; | ||
398 | get32_123x_3210: sample = swab32(as_u32(src) >> 8); goto GET32_END; | ||
399 | get32_123x_B210: sample = swab32((as_u32(src) >> 8) ^ 0x80); goto GET32_END; | ||
400 | get32_1234_1234: sample = as_u32(src); goto GET32_END; | ||
401 | get32_1234_9234: sample = as_u32(src) ^ 0x80000000; goto GET32_END; | ||
402 | get32_1234_4321: sample = swab32(as_u32(src)); goto GET32_END; | ||
403 | get32_1234_C321: sample = swab32(as_u32(src) ^ 0x80); goto GET32_END; | ||
404 | } | ||
405 | #endif | ||
406 | #endif | ||
407 | |||
408 | #ifdef PUT_U32_LABELS | ||
409 | /* dst_wid dst_endswap unsigned */ | ||
410 | static void *put_u32_labels[4 * 2 * 2] = { | ||
411 | &&put_u32_1234_xxx9, /* u32h -> s8h */ | ||
412 | &&put_u32_1234_xxx1, /* u32h -> u8h */ | ||
413 | &&put_u32_1234_xxx9, /* u32h -> s8s */ | ||
414 | &&put_u32_1234_xxx1, /* u32h -> u8s */ | ||
415 | &&put_u32_1234_xx92, /* u32h -> s16h */ | ||
416 | &&put_u32_1234_xx12, /* u32h -> u16h */ | ||
417 | &&put_u32_1234_xx29, /* u32h -> s16s */ | ||
418 | &&put_u32_1234_xx21, /* u32h -> u16s */ | ||
419 | &&put_u32_1234_x923, /* u32h -> s24h */ | ||
420 | &&put_u32_1234_x123, /* u32h -> u24h */ | ||
421 | &&put_u32_1234_329x, /* u32h -> s24s */ | ||
422 | &&put_u32_1234_321x, /* u32h -> u24s */ | ||
423 | &&put_u32_1234_9234, /* u32h -> s32h */ | ||
424 | &&put_u32_1234_1234, /* u32h -> u32h */ | ||
425 | &&put_u32_1234_4329, /* u32h -> s32s */ | ||
426 | &&put_u32_1234_4321, /* u32h -> u32s */ | ||
427 | }; | ||
428 | #endif | ||
429 | |||
430 | #ifdef PUT_U32_END | ||
431 | while (0) { | ||
432 | put_u32_1234_xxx1: as_u8(dst) = sample >> 24; goto PUT_U32_END; | ||
433 | put_u32_1234_xxx9: as_u8(dst) = (sample >> 24) ^ 0x80; goto PUT_U32_END; | ||
434 | put_u32_1234_xx12: as_u16(dst) = sample >> 16; goto PUT_U32_END; | ||
435 | put_u32_1234_xx92: as_u16(dst) = (sample >> 16) ^ 0x8000; goto PUT_U32_END; | ||
436 | put_u32_1234_xx21: as_u16(dst) = swab16(sample >> 16); goto PUT_U32_END; | ||
437 | put_u32_1234_xx29: as_u16(dst) = swab16(sample >> 16) ^ 0x80; goto PUT_U32_END; | ||
438 | put_u32_1234_x123: as_u32(dst) = sample >> 8; goto PUT_U32_END; | ||
439 | put_u32_1234_x923: as_u32(dst) = (sample >> 8) ^ 0x800000; goto PUT_U32_END; | ||
440 | put_u32_1234_321x: as_u32(dst) = swab32(sample) << 8; goto PUT_U32_END; | ||
441 | put_u32_1234_329x: as_u32(dst) = (swab32(sample) ^ 0x80) << 8; goto PUT_U32_END; | ||
442 | put_u32_1234_1234: as_u32(dst) = sample; goto PUT_U32_END; | ||
443 | put_u32_1234_9234: as_u32(dst) = sample ^ 0x80000000; goto PUT_U32_END; | ||
444 | put_u32_1234_4321: as_u32(dst) = swab32(sample); goto PUT_U32_END; | ||
445 | put_u32_1234_4329: as_u32(dst) = swab32(sample) ^ 0x80; goto PUT_U32_END; | ||
446 | } | ||
447 | #endif | ||
448 | |||
449 | #ifdef GET_U_LABELS | ||
450 | /* width endswap unsigned*/ | ||
451 | static void *get_u_labels[4 * 2 * 2] = { | ||
452 | &&get_u_s8, /* s8 -> u8 */ | ||
453 | &&get_u_u8, /* u8 -> u8 */ | ||
454 | &&get_u_s8, /* s8 -> u8 */ | ||
455 | &&get_u_u8, /* u8 -> u8 */ | ||
456 | &&get_u_s16h, /* s16h -> u16h */ | ||
457 | &&get_u_u16h, /* u16h -> u16h */ | ||
458 | &&get_u_s16s, /* s16s -> u16h */ | ||
459 | &&get_u_u16s, /* u16s -> u16h */ | ||
460 | &&get_u_s24h, /* s24h -> u32h */ | ||
461 | &&get_u_u24h, /* u24h -> u32h */ | ||
462 | &&get_u_s24s, /* s24s -> u32h */ | ||
463 | &&get_u_u24s, /* u24s -> u32h */ | ||
464 | &&get_u_s32h, /* s32h -> u32h */ | ||
465 | &&get_u_u32h, /* u32h -> u32h */ | ||
466 | &&get_u_s32s, /* s32s -> u32h */ | ||
467 | &&get_u_u32s, /* u32s -> u32h */ | ||
468 | }; | ||
469 | #endif | ||
470 | |||
471 | #ifdef GET_U_END | ||
472 | while (0) { | ||
473 | get_u_s8: sample = as_u8(src) ^ 0x80; goto GET_U_END; | ||
474 | get_u_u8: sample = as_u8(src); goto GET_U_END; | ||
475 | get_u_s16h: sample = as_u16(src) ^ 0x8000; goto GET_U_END; | ||
476 | get_u_u16h: sample = as_u16(src); goto GET_U_END; | ||
477 | get_u_s16s: sample = swab16(as_u16(src) ^ 0x80); goto GET_U_END; | ||
478 | get_u_u16s: sample = swab16(as_u16(src)); goto GET_U_END; | ||
479 | get_u_s24h: sample = (as_u32(src) ^ 0x800000); goto GET_U_END; | ||
480 | get_u_u24h: sample = as_u32(src); goto GET_U_END; | ||
481 | get_u_s24s: sample = swab32(as_u32(src) ^ 0x800000); goto GET_U_END; | ||
482 | get_u_u24s: sample = swab32(as_u32(src)); goto GET_U_END; | ||
483 | get_u_s32h: sample = as_u32(src) ^ 0x80000000; goto GET_U_END; | ||
484 | get_u_u32h: sample = as_u32(src); goto GET_U_END; | ||
485 | get_u_s32s: sample = swab32(as_u32(src) ^ 0x80); goto GET_U_END; | ||
486 | get_u_u32s: sample = swab32(as_u32(src)); goto GET_U_END; | ||
487 | } | ||
488 | #endif | ||
489 | |||
490 | #if 0 | ||
491 | #ifdef PUT_LABELS | ||
492 | /* width endswap unsigned */ | ||
493 | static void *put_labels[4 * 2 * 2] = { | ||
494 | &&put_s8, /* s8 -> s8 */ | ||
495 | &&put_u8, /* u8 -> s8 */ | ||
496 | &&put_s8, /* s8 -> s8 */ | ||
497 | &&put_u8, /* u8 -> s8 */ | ||
498 | &&put_s16h, /* s16h -> s16h */ | ||
499 | &&put_u16h, /* u16h -> s16h */ | ||
500 | &&put_s16s, /* s16s -> s16h */ | ||
501 | &&put_u16s, /* u16s -> s16h */ | ||
502 | &&put_s24h, /* s24h -> s32h */ | ||
503 | &&put_u24h, /* u24h -> s32h */ | ||
504 | &&put_s24s, /* s24s -> s32h */ | ||
505 | &&put_u24s, /* u24s -> s32h */ | ||
506 | &&put_s32h, /* s32h -> s32h */ | ||
507 | &&put_u32h, /* u32h -> s32h */ | ||
508 | &&put_s32s, /* s32s -> s32h */ | ||
509 | &&put_u32s, /* u32s -> s32h */ | ||
510 | }; | ||
511 | #endif | ||
512 | |||
513 | #ifdef PUT_END | ||
514 | put_s8: as_s8(dst) = sample; goto PUT_END; | ||
515 | put_u8: as_u8(dst) = sample ^ 0x80; goto PUT_END; | ||
516 | put_s16h: as_s16(dst) = sample; goto PUT_END; | ||
517 | put_u16h: as_u16(dst) = sample ^ 0x8000; goto PUT_END; | ||
518 | put_s16s: as_s16(dst) = swab16(sample); goto PUT_END; | ||
519 | put_u16s: as_u16(dst) = swab16(sample ^ 0x80); goto PUT_END; | ||
520 | put_s24h: as_s24(dst) = sample & 0xffffff; goto PUT_END; | ||
521 | put_u24h: as_u24(dst) = sample ^ 0x80000000; goto PUT_END; | ||
522 | put_s24s: as_s24(dst) = swab32(sample & 0xffffff); goto PUT_END; | ||
523 | put_u24s: as_u24(dst) = swab32(sample ^ 0x80); goto PUT_END; | ||
524 | put_s32h: as_s32(dst) = sample; goto PUT_END; | ||
525 | put_u32h: as_u32(dst) = sample ^ 0x80000000; goto PUT_END; | ||
526 | put_s32s: as_s32(dst) = swab32(sample); goto PUT_END; | ||
527 | put_u32s: as_u32(dst) = swab32(sample ^ 0x80); goto PUT_END; | ||
528 | #endif | ||
529 | #endif | ||
530 | |||
531 | #undef as_u8 | ||
532 | #undef as_u16 | ||
533 | #undef as_u32 | ||
534 | #undef as_s8 | ||
535 | #undef as_s16 | ||
536 | #undef as_s32 | ||
diff --git a/sound/core/oss/rate.c b/sound/core/oss/rate.c new file mode 100644 index 000000000000..1096ec186714 --- /dev/null +++ b/sound/core/oss/rate.c | |||
@@ -0,0 +1,378 @@ | |||
1 | /* | ||
2 | * Rate conversion Plug-In | ||
3 | * Copyright (c) 1999 by Jaroslav Kysela <perex@suse.cz> | ||
4 | * | ||
5 | * | ||
6 | * This library is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU Library General Public License as | ||
8 | * published by the Free Software Foundation; either version 2 of | ||
9 | * the License, or (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU Library General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU Library General Public | ||
17 | * License along with this library; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #include <sound/driver.h> | ||
23 | #include <linux/time.h> | ||
24 | #include <sound/core.h> | ||
25 | #include <sound/pcm.h> | ||
26 | #include "pcm_plugin.h" | ||
27 | |||
28 | #define SHIFT 11 | ||
29 | #define BITS (1<<SHIFT) | ||
30 | #define R_MASK (BITS-1) | ||
31 | |||
32 | /* | ||
33 | * Basic rate conversion plugin | ||
34 | */ | ||
35 | |||
36 | typedef struct { | ||
37 | signed short last_S1; | ||
38 | signed short last_S2; | ||
39 | } rate_channel_t; | ||
40 | |||
41 | typedef void (*rate_f)(snd_pcm_plugin_t *plugin, | ||
42 | const snd_pcm_plugin_channel_t *src_channels, | ||
43 | snd_pcm_plugin_channel_t *dst_channels, | ||
44 | int src_frames, int dst_frames); | ||
45 | |||
46 | typedef struct rate_private_data { | ||
47 | unsigned int pitch; | ||
48 | unsigned int pos; | ||
49 | rate_f func; | ||
50 | int get, put; | ||
51 | snd_pcm_sframes_t old_src_frames, old_dst_frames; | ||
52 | rate_channel_t channels[0]; | ||
53 | } rate_t; | ||
54 | |||
55 | static void rate_init(snd_pcm_plugin_t *plugin) | ||
56 | { | ||
57 | unsigned int channel; | ||
58 | rate_t *data = (rate_t *)plugin->extra_data; | ||
59 | data->pos = 0; | ||
60 | for (channel = 0; channel < plugin->src_format.channels; channel++) { | ||
61 | data->channels[channel].last_S1 = 0; | ||
62 | data->channels[channel].last_S2 = 0; | ||
63 | } | ||
64 | } | ||
65 | |||
66 | static void resample_expand(snd_pcm_plugin_t *plugin, | ||
67 | const snd_pcm_plugin_channel_t *src_channels, | ||
68 | snd_pcm_plugin_channel_t *dst_channels, | ||
69 | int src_frames, int dst_frames) | ||
70 | { | ||
71 | unsigned int pos = 0; | ||
72 | signed int val; | ||
73 | signed short S1, S2; | ||
74 | char *src, *dst; | ||
75 | unsigned int channel; | ||
76 | int src_step, dst_step; | ||
77 | int src_frames1, dst_frames1; | ||
78 | rate_t *data = (rate_t *)plugin->extra_data; | ||
79 | rate_channel_t *rchannels = data->channels; | ||
80 | |||
81 | #define GET_S16_LABELS | ||
82 | #define PUT_S16_LABELS | ||
83 | #include "plugin_ops.h" | ||
84 | #undef GET_S16_LABELS | ||
85 | #undef PUT_S16_LABELS | ||
86 | void *get = get_s16_labels[data->get]; | ||
87 | void *put = put_s16_labels[data->put]; | ||
88 | signed short sample = 0; | ||
89 | |||
90 | for (channel = 0; channel < plugin->src_format.channels; channel++) { | ||
91 | pos = data->pos; | ||
92 | S1 = rchannels->last_S1; | ||
93 | S2 = rchannels->last_S2; | ||
94 | if (!src_channels[channel].enabled) { | ||
95 | if (dst_channels[channel].wanted) | ||
96 | snd_pcm_area_silence(&dst_channels[channel].area, 0, dst_frames, plugin->dst_format.format); | ||
97 | dst_channels[channel].enabled = 0; | ||
98 | continue; | ||
99 | } | ||
100 | dst_channels[channel].enabled = 1; | ||
101 | src = (char *)src_channels[channel].area.addr + src_channels[channel].area.first / 8; | ||
102 | dst = (char *)dst_channels[channel].area.addr + dst_channels[channel].area.first / 8; | ||
103 | src_step = src_channels[channel].area.step / 8; | ||
104 | dst_step = dst_channels[channel].area.step / 8; | ||
105 | src_frames1 = src_frames; | ||
106 | dst_frames1 = dst_frames; | ||
107 | while (dst_frames1-- > 0) { | ||
108 | if (pos & ~R_MASK) { | ||
109 | pos &= R_MASK; | ||
110 | S1 = S2; | ||
111 | if (src_frames1-- > 0) { | ||
112 | goto *get; | ||
113 | #define GET_S16_END after_get | ||
114 | #include "plugin_ops.h" | ||
115 | #undef GET_S16_END | ||
116 | after_get: | ||
117 | S2 = sample; | ||
118 | src += src_step; | ||
119 | } | ||
120 | } | ||
121 | val = S1 + ((S2 - S1) * (signed int)pos) / BITS; | ||
122 | if (val < -32768) | ||
123 | val = -32768; | ||
124 | else if (val > 32767) | ||
125 | val = 32767; | ||
126 | sample = val; | ||
127 | goto *put; | ||
128 | #define PUT_S16_END after_put | ||
129 | #include "plugin_ops.h" | ||
130 | #undef PUT_S16_END | ||
131 | after_put: | ||
132 | dst += dst_step; | ||
133 | pos += data->pitch; | ||
134 | } | ||
135 | rchannels->last_S1 = S1; | ||
136 | rchannels->last_S2 = S2; | ||
137 | rchannels++; | ||
138 | } | ||
139 | data->pos = pos; | ||
140 | } | ||
141 | |||
142 | static void resample_shrink(snd_pcm_plugin_t *plugin, | ||
143 | const snd_pcm_plugin_channel_t *src_channels, | ||
144 | snd_pcm_plugin_channel_t *dst_channels, | ||
145 | int src_frames, int dst_frames) | ||
146 | { | ||
147 | unsigned int pos = 0; | ||
148 | signed int val; | ||
149 | signed short S1, S2; | ||
150 | char *src, *dst; | ||
151 | unsigned int channel; | ||
152 | int src_step, dst_step; | ||
153 | int src_frames1, dst_frames1; | ||
154 | rate_t *data = (rate_t *)plugin->extra_data; | ||
155 | rate_channel_t *rchannels = data->channels; | ||
156 | |||
157 | #define GET_S16_LABELS | ||
158 | #define PUT_S16_LABELS | ||
159 | #include "plugin_ops.h" | ||
160 | #undef GET_S16_LABELS | ||
161 | #undef PUT_S16_LABELS | ||
162 | void *get = get_s16_labels[data->get]; | ||
163 | void *put = put_s16_labels[data->put]; | ||
164 | signed short sample = 0; | ||
165 | |||
166 | for (channel = 0; channel < plugin->src_format.channels; ++channel) { | ||
167 | pos = data->pos; | ||
168 | S1 = rchannels->last_S1; | ||
169 | S2 = rchannels->last_S2; | ||
170 | if (!src_channels[channel].enabled) { | ||
171 | if (dst_channels[channel].wanted) | ||
172 | snd_pcm_area_silence(&dst_channels[channel].area, 0, dst_frames, plugin->dst_format.format); | ||
173 | dst_channels[channel].enabled = 0; | ||
174 | continue; | ||
175 | } | ||
176 | dst_channels[channel].enabled = 1; | ||
177 | src = (char *)src_channels[channel].area.addr + src_channels[channel].area.first / 8; | ||
178 | dst = (char *)dst_channels[channel].area.addr + dst_channels[channel].area.first / 8; | ||
179 | src_step = src_channels[channel].area.step / 8; | ||
180 | dst_step = dst_channels[channel].area.step / 8; | ||
181 | src_frames1 = src_frames; | ||
182 | dst_frames1 = dst_frames; | ||
183 | while (dst_frames1 > 0) { | ||
184 | S1 = S2; | ||
185 | if (src_frames1-- > 0) { | ||
186 | goto *get; | ||
187 | #define GET_S16_END after_get | ||
188 | #include "plugin_ops.h" | ||
189 | #undef GET_S16_END | ||
190 | after_get: | ||
191 | S2 = sample; | ||
192 | src += src_step; | ||
193 | } | ||
194 | if (pos & ~R_MASK) { | ||
195 | pos &= R_MASK; | ||
196 | val = S1 + ((S2 - S1) * (signed int)pos) / BITS; | ||
197 | if (val < -32768) | ||
198 | val = -32768; | ||
199 | else if (val > 32767) | ||
200 | val = 32767; | ||
201 | sample = val; | ||
202 | goto *put; | ||
203 | #define PUT_S16_END after_put | ||
204 | #include "plugin_ops.h" | ||
205 | #undef PUT_S16_END | ||
206 | after_put: | ||
207 | dst += dst_step; | ||
208 | dst_frames1--; | ||
209 | } | ||
210 | pos += data->pitch; | ||
211 | } | ||
212 | rchannels->last_S1 = S1; | ||
213 | rchannels->last_S2 = S2; | ||
214 | rchannels++; | ||
215 | } | ||
216 | data->pos = pos; | ||
217 | } | ||
218 | |||
219 | static snd_pcm_sframes_t rate_src_frames(snd_pcm_plugin_t *plugin, snd_pcm_uframes_t frames) | ||
220 | { | ||
221 | rate_t *data; | ||
222 | snd_pcm_sframes_t res; | ||
223 | |||
224 | snd_assert(plugin != NULL, return -ENXIO); | ||
225 | if (frames == 0) | ||
226 | return 0; | ||
227 | data = (rate_t *)plugin->extra_data; | ||
228 | if (plugin->src_format.rate < plugin->dst_format.rate) { | ||
229 | res = (((frames * data->pitch) + (BITS/2)) >> SHIFT); | ||
230 | } else { | ||
231 | res = (((frames << SHIFT) + (data->pitch / 2)) / data->pitch); | ||
232 | } | ||
233 | if (data->old_src_frames > 0) { | ||
234 | snd_pcm_sframes_t frames1 = frames, res1 = data->old_dst_frames; | ||
235 | while (data->old_src_frames < frames1) { | ||
236 | frames1 >>= 1; | ||
237 | res1 <<= 1; | ||
238 | } | ||
239 | while (data->old_src_frames > frames1) { | ||
240 | frames1 <<= 1; | ||
241 | res1 >>= 1; | ||
242 | } | ||
243 | if (data->old_src_frames == frames1) | ||
244 | return res1; | ||
245 | } | ||
246 | data->old_src_frames = frames; | ||
247 | data->old_dst_frames = res; | ||
248 | return res; | ||
249 | } | ||
250 | |||
251 | static snd_pcm_sframes_t rate_dst_frames(snd_pcm_plugin_t *plugin, snd_pcm_uframes_t frames) | ||
252 | { | ||
253 | rate_t *data; | ||
254 | snd_pcm_sframes_t res; | ||
255 | |||
256 | snd_assert(plugin != NULL, return -ENXIO); | ||
257 | if (frames == 0) | ||
258 | return 0; | ||
259 | data = (rate_t *)plugin->extra_data; | ||
260 | if (plugin->src_format.rate < plugin->dst_format.rate) { | ||
261 | res = (((frames << SHIFT) + (data->pitch / 2)) / data->pitch); | ||
262 | } else { | ||
263 | res = (((frames * data->pitch) + (BITS/2)) >> SHIFT); | ||
264 | } | ||
265 | if (data->old_dst_frames > 0) { | ||
266 | snd_pcm_sframes_t frames1 = frames, res1 = data->old_src_frames; | ||
267 | while (data->old_dst_frames < frames1) { | ||
268 | frames1 >>= 1; | ||
269 | res1 <<= 1; | ||
270 | } | ||
271 | while (data->old_dst_frames > frames1) { | ||
272 | frames1 <<= 1; | ||
273 | res1 >>= 1; | ||
274 | } | ||
275 | if (data->old_dst_frames == frames1) | ||
276 | return res1; | ||
277 | } | ||
278 | data->old_dst_frames = frames; | ||
279 | data->old_src_frames = res; | ||
280 | return res; | ||
281 | } | ||
282 | |||
283 | static snd_pcm_sframes_t rate_transfer(snd_pcm_plugin_t *plugin, | ||
284 | const snd_pcm_plugin_channel_t *src_channels, | ||
285 | snd_pcm_plugin_channel_t *dst_channels, | ||
286 | snd_pcm_uframes_t frames) | ||
287 | { | ||
288 | snd_pcm_uframes_t dst_frames; | ||
289 | rate_t *data; | ||
290 | |||
291 | snd_assert(plugin != NULL && src_channels != NULL && dst_channels != NULL, return -ENXIO); | ||
292 | if (frames == 0) | ||
293 | return 0; | ||
294 | #ifdef CONFIG_SND_DEBUG | ||
295 | { | ||
296 | unsigned int channel; | ||
297 | for (channel = 0; channel < plugin->src_format.channels; channel++) { | ||
298 | snd_assert(src_channels[channel].area.first % 8 == 0 && | ||
299 | src_channels[channel].area.step % 8 == 0, | ||
300 | return -ENXIO); | ||
301 | snd_assert(dst_channels[channel].area.first % 8 == 0 && | ||
302 | dst_channels[channel].area.step % 8 == 0, | ||
303 | return -ENXIO); | ||
304 | } | ||
305 | } | ||
306 | #endif | ||
307 | |||
308 | dst_frames = rate_dst_frames(plugin, frames); | ||
309 | if (dst_frames > dst_channels[0].frames) | ||
310 | dst_frames = dst_channels[0].frames; | ||
311 | data = (rate_t *)plugin->extra_data; | ||
312 | data->func(plugin, src_channels, dst_channels, frames, dst_frames); | ||
313 | return dst_frames; | ||
314 | } | ||
315 | |||
316 | static int rate_action(snd_pcm_plugin_t *plugin, | ||
317 | snd_pcm_plugin_action_t action, | ||
318 | unsigned long udata ATTRIBUTE_UNUSED) | ||
319 | { | ||
320 | snd_assert(plugin != NULL, return -ENXIO); | ||
321 | switch (action) { | ||
322 | case INIT: | ||
323 | case PREPARE: | ||
324 | rate_init(plugin); | ||
325 | break; | ||
326 | default: | ||
327 | break; | ||
328 | } | ||
329 | return 0; /* silenty ignore other actions */ | ||
330 | } | ||
331 | |||
332 | int snd_pcm_plugin_build_rate(snd_pcm_plug_t *plug, | ||
333 | snd_pcm_plugin_format_t *src_format, | ||
334 | snd_pcm_plugin_format_t *dst_format, | ||
335 | snd_pcm_plugin_t **r_plugin) | ||
336 | { | ||
337 | int err; | ||
338 | rate_t *data; | ||
339 | snd_pcm_plugin_t *plugin; | ||
340 | |||
341 | snd_assert(r_plugin != NULL, return -ENXIO); | ||
342 | *r_plugin = NULL; | ||
343 | |||
344 | snd_assert(src_format->channels == dst_format->channels, return -ENXIO); | ||
345 | snd_assert(src_format->channels > 0, return -ENXIO); | ||
346 | snd_assert(snd_pcm_format_linear(src_format->format) != 0, return -ENXIO); | ||
347 | snd_assert(snd_pcm_format_linear(dst_format->format) != 0, return -ENXIO); | ||
348 | snd_assert(src_format->rate != dst_format->rate, return -ENXIO); | ||
349 | |||
350 | err = snd_pcm_plugin_build(plug, "rate conversion", | ||
351 | src_format, dst_format, | ||
352 | sizeof(rate_t) + src_format->channels * sizeof(rate_channel_t), | ||
353 | &plugin); | ||
354 | if (err < 0) | ||
355 | return err; | ||
356 | data = (rate_t *)plugin->extra_data; | ||
357 | data->get = getput_index(src_format->format); | ||
358 | snd_assert(data->get >= 0 && data->get < 4*2*2, return -EINVAL); | ||
359 | data->put = getput_index(dst_format->format); | ||
360 | snd_assert(data->put >= 0 && data->put < 4*2*2, return -EINVAL); | ||
361 | |||
362 | if (src_format->rate < dst_format->rate) { | ||
363 | data->pitch = ((src_format->rate << SHIFT) + (dst_format->rate >> 1)) / dst_format->rate; | ||
364 | data->func = resample_expand; | ||
365 | } else { | ||
366 | data->pitch = ((dst_format->rate << SHIFT) + (src_format->rate >> 1)) / src_format->rate; | ||
367 | data->func = resample_shrink; | ||
368 | } | ||
369 | data->pos = 0; | ||
370 | rate_init(plugin); | ||
371 | data->old_src_frames = data->old_dst_frames = 0; | ||
372 | plugin->transfer = rate_transfer; | ||
373 | plugin->src_frames = rate_src_frames; | ||
374 | plugin->dst_frames = rate_dst_frames; | ||
375 | plugin->action = rate_action; | ||
376 | *r_plugin = plugin; | ||
377 | return 0; | ||
378 | } | ||
diff --git a/sound/core/oss/route.c b/sound/core/oss/route.c new file mode 100644 index 000000000000..c955b7dfdb30 --- /dev/null +++ b/sound/core/oss/route.c | |||
@@ -0,0 +1,519 @@ | |||
1 | /* | ||
2 | * Attenuated route Plug-In | ||
3 | * Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org> | ||
4 | * | ||
5 | * | ||
6 | * This library is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU Library General Public License as | ||
8 | * published by the Free Software Foundation; either version 2 of | ||
9 | * the License, or (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU Library General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU Library General Public | ||
17 | * License along with this library; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #include <sound/driver.h> | ||
23 | #include <linux/slab.h> | ||
24 | #include <linux/time.h> | ||
25 | #include <sound/core.h> | ||
26 | #include <sound/pcm.h> | ||
27 | #include "pcm_plugin.h" | ||
28 | |||
29 | /* The best possible hack to support missing optimization in gcc 2.7.2.3 */ | ||
30 | #if ROUTE_PLUGIN_RESOLUTION & (ROUTE_PLUGIN_RESOLUTION - 1) != 0 | ||
31 | #define div(a) a /= ROUTE_PLUGIN_RESOLUTION | ||
32 | #elif ROUTE_PLUGIN_RESOLUTION == 16 | ||
33 | #define div(a) a >>= 4 | ||
34 | #else | ||
35 | #error "Add some code here" | ||
36 | #endif | ||
37 | |||
38 | typedef struct ttable_dst ttable_dst_t; | ||
39 | typedef struct route_private_data route_t; | ||
40 | |||
41 | typedef void (*route_channel_f)(snd_pcm_plugin_t *plugin, | ||
42 | const snd_pcm_plugin_channel_t *src_channels, | ||
43 | snd_pcm_plugin_channel_t *dst_channel, | ||
44 | ttable_dst_t* ttable, snd_pcm_uframes_t frames); | ||
45 | |||
46 | typedef struct { | ||
47 | int channel; | ||
48 | int as_int; | ||
49 | } ttable_src_t; | ||
50 | |||
51 | struct ttable_dst { | ||
52 | int att; /* Attenuated */ | ||
53 | unsigned int nsrcs; | ||
54 | ttable_src_t* srcs; | ||
55 | route_channel_f func; | ||
56 | }; | ||
57 | |||
58 | struct route_private_data { | ||
59 | enum {R_UINT32=0, R_UINT64=1} sum_type; | ||
60 | int get, put; | ||
61 | int conv; | ||
62 | int src_sample_size; | ||
63 | ttable_dst_t ttable[0]; | ||
64 | }; | ||
65 | |||
66 | typedef union { | ||
67 | u_int32_t as_uint32; | ||
68 | u_int64_t as_uint64; | ||
69 | } sum_t; | ||
70 | |||
71 | |||
72 | static void route_to_channel_from_zero(snd_pcm_plugin_t *plugin, | ||
73 | const snd_pcm_plugin_channel_t *src_channels ATTRIBUTE_UNUSED, | ||
74 | snd_pcm_plugin_channel_t *dst_channel, | ||
75 | ttable_dst_t* ttable ATTRIBUTE_UNUSED, snd_pcm_uframes_t frames) | ||
76 | { | ||
77 | if (dst_channel->wanted) | ||
78 | snd_pcm_area_silence(&dst_channel->area, 0, frames, plugin->dst_format.format); | ||
79 | dst_channel->enabled = 0; | ||
80 | } | ||
81 | |||
82 | static void route_to_channel_from_one(snd_pcm_plugin_t *plugin, | ||
83 | const snd_pcm_plugin_channel_t *src_channels, | ||
84 | snd_pcm_plugin_channel_t *dst_channel, | ||
85 | ttable_dst_t* ttable, snd_pcm_uframes_t frames) | ||
86 | { | ||
87 | #define CONV_LABELS | ||
88 | #include "plugin_ops.h" | ||
89 | #undef CONV_LABELS | ||
90 | route_t *data = (route_t *)plugin->extra_data; | ||
91 | void *conv; | ||
92 | const snd_pcm_plugin_channel_t *src_channel = NULL; | ||
93 | unsigned int srcidx; | ||
94 | char *src, *dst; | ||
95 | int src_step, dst_step; | ||
96 | for (srcidx = 0; srcidx < ttable->nsrcs; ++srcidx) { | ||
97 | src_channel = &src_channels[ttable->srcs[srcidx].channel]; | ||
98 | if (src_channel->area.addr != NULL) | ||
99 | break; | ||
100 | } | ||
101 | if (srcidx == ttable->nsrcs) { | ||
102 | route_to_channel_from_zero(plugin, src_channels, dst_channel, ttable, frames); | ||
103 | return; | ||
104 | } | ||
105 | |||
106 | dst_channel->enabled = 1; | ||
107 | conv = conv_labels[data->conv]; | ||
108 | src = src_channel->area.addr + src_channel->area.first / 8; | ||
109 | src_step = src_channel->area.step / 8; | ||
110 | dst = dst_channel->area.addr + dst_channel->area.first / 8; | ||
111 | dst_step = dst_channel->area.step / 8; | ||
112 | while (frames-- > 0) { | ||
113 | goto *conv; | ||
114 | #define CONV_END after | ||
115 | #include "plugin_ops.h" | ||
116 | #undef CONV_END | ||
117 | after: | ||
118 | src += src_step; | ||
119 | dst += dst_step; | ||
120 | } | ||
121 | } | ||
122 | |||
123 | static void route_to_channel(snd_pcm_plugin_t *plugin, | ||
124 | const snd_pcm_plugin_channel_t *src_channels, | ||
125 | snd_pcm_plugin_channel_t *dst_channel, | ||
126 | ttable_dst_t* ttable, snd_pcm_uframes_t frames) | ||
127 | { | ||
128 | #define GET_U_LABELS | ||
129 | #define PUT_U32_LABELS | ||
130 | #include "plugin_ops.h" | ||
131 | #undef GET_U_LABELS | ||
132 | #undef PUT_U32_LABELS | ||
133 | static void *zero_labels[2] = { &&zero_int32, &&zero_int64 }; | ||
134 | /* sum_type att */ | ||
135 | static void *add_labels[2 * 2] = { &&add_int32_noatt, &&add_int32_att, | ||
136 | &&add_int64_noatt, &&add_int64_att, | ||
137 | }; | ||
138 | /* sum_type att shift */ | ||
139 | static void *norm_labels[2 * 2 * 4] = { NULL, | ||
140 | &&norm_int32_8_noatt, | ||
141 | &&norm_int32_16_noatt, | ||
142 | &&norm_int32_24_noatt, | ||
143 | NULL, | ||
144 | &&norm_int32_8_att, | ||
145 | &&norm_int32_16_att, | ||
146 | &&norm_int32_24_att, | ||
147 | &&norm_int64_0_noatt, | ||
148 | &&norm_int64_8_noatt, | ||
149 | &&norm_int64_16_noatt, | ||
150 | &&norm_int64_24_noatt, | ||
151 | &&norm_int64_0_att, | ||
152 | &&norm_int64_8_att, | ||
153 | &&norm_int64_16_att, | ||
154 | &&norm_int64_24_att, | ||
155 | }; | ||
156 | route_t *data = (route_t *)plugin->extra_data; | ||
157 | void *zero, *get, *add, *norm, *put_u32; | ||
158 | int nsrcs = ttable->nsrcs; | ||
159 | char *dst; | ||
160 | int dst_step; | ||
161 | char *srcs[nsrcs]; | ||
162 | int src_steps[nsrcs]; | ||
163 | ttable_src_t src_tt[nsrcs]; | ||
164 | u_int32_t sample = 0; | ||
165 | int srcidx, srcidx1 = 0; | ||
166 | for (srcidx = 0; srcidx < nsrcs; ++srcidx) { | ||
167 | const snd_pcm_plugin_channel_t *src_channel = &src_channels[ttable->srcs[srcidx].channel]; | ||
168 | if (!src_channel->enabled) | ||
169 | continue; | ||
170 | srcs[srcidx1] = src_channel->area.addr + src_channel->area.first / 8; | ||
171 | src_steps[srcidx1] = src_channel->area.step / 8; | ||
172 | src_tt[srcidx1] = ttable->srcs[srcidx]; | ||
173 | srcidx1++; | ||
174 | } | ||
175 | nsrcs = srcidx1; | ||
176 | if (nsrcs == 0) { | ||
177 | route_to_channel_from_zero(plugin, src_channels, dst_channel, ttable, frames); | ||
178 | return; | ||
179 | } else if (nsrcs == 1 && src_tt[0].as_int == ROUTE_PLUGIN_RESOLUTION) { | ||
180 | route_to_channel_from_one(plugin, src_channels, dst_channel, ttable, frames); | ||
181 | return; | ||
182 | } | ||
183 | |||
184 | dst_channel->enabled = 1; | ||
185 | zero = zero_labels[data->sum_type]; | ||
186 | get = get_u_labels[data->get]; | ||
187 | add = add_labels[data->sum_type * 2 + ttable->att]; | ||
188 | norm = norm_labels[data->sum_type * 8 + ttable->att * 4 + 4 - data->src_sample_size]; | ||
189 | put_u32 = put_u32_labels[data->put]; | ||
190 | dst = dst_channel->area.addr + dst_channel->area.first / 8; | ||
191 | dst_step = dst_channel->area.step / 8; | ||
192 | |||
193 | while (frames-- > 0) { | ||
194 | ttable_src_t *ttp = src_tt; | ||
195 | sum_t sum; | ||
196 | |||
197 | /* Zero sum */ | ||
198 | goto *zero; | ||
199 | zero_int32: | ||
200 | sum.as_uint32 = 0; | ||
201 | goto zero_end; | ||
202 | zero_int64: | ||
203 | sum.as_uint64 = 0; | ||
204 | goto zero_end; | ||
205 | zero_end: | ||
206 | for (srcidx = 0; srcidx < nsrcs; ++srcidx) { | ||
207 | char *src = srcs[srcidx]; | ||
208 | |||
209 | /* Get sample */ | ||
210 | goto *get; | ||
211 | #define GET_U_END after_get | ||
212 | #include "plugin_ops.h" | ||
213 | #undef GET_U_END | ||
214 | after_get: | ||
215 | |||
216 | /* Sum */ | ||
217 | goto *add; | ||
218 | add_int32_att: | ||
219 | sum.as_uint32 += sample * ttp->as_int; | ||
220 | goto after_sum; | ||
221 | add_int32_noatt: | ||
222 | if (ttp->as_int) | ||
223 | sum.as_uint32 += sample; | ||
224 | goto after_sum; | ||
225 | add_int64_att: | ||
226 | sum.as_uint64 += (u_int64_t) sample * ttp->as_int; | ||
227 | goto after_sum; | ||
228 | add_int64_noatt: | ||
229 | if (ttp->as_int) | ||
230 | sum.as_uint64 += sample; | ||
231 | goto after_sum; | ||
232 | after_sum: | ||
233 | srcs[srcidx] += src_steps[srcidx]; | ||
234 | ttp++; | ||
235 | } | ||
236 | |||
237 | /* Normalization */ | ||
238 | goto *norm; | ||
239 | norm_int32_8_att: | ||
240 | sum.as_uint64 = sum.as_uint32; | ||
241 | norm_int64_8_att: | ||
242 | sum.as_uint64 <<= 8; | ||
243 | norm_int64_0_att: | ||
244 | div(sum.as_uint64); | ||
245 | goto norm_int; | ||
246 | |||
247 | norm_int32_16_att: | ||
248 | sum.as_uint64 = sum.as_uint32; | ||
249 | norm_int64_16_att: | ||
250 | sum.as_uint64 <<= 16; | ||
251 | div(sum.as_uint64); | ||
252 | goto norm_int; | ||
253 | |||
254 | norm_int32_24_att: | ||
255 | sum.as_uint64 = sum.as_uint32; | ||
256 | norm_int64_24_att: | ||
257 | sum.as_uint64 <<= 24; | ||
258 | div(sum.as_uint64); | ||
259 | goto norm_int; | ||
260 | |||
261 | norm_int32_8_noatt: | ||
262 | sum.as_uint64 = sum.as_uint32; | ||
263 | norm_int64_8_noatt: | ||
264 | sum.as_uint64 <<= 8; | ||
265 | goto norm_int; | ||
266 | |||
267 | norm_int32_16_noatt: | ||
268 | sum.as_uint64 = sum.as_uint32; | ||
269 | norm_int64_16_noatt: | ||
270 | sum.as_uint64 <<= 16; | ||
271 | goto norm_int; | ||
272 | |||
273 | norm_int32_24_noatt: | ||
274 | sum.as_uint64 = sum.as_uint32; | ||
275 | norm_int64_24_noatt: | ||
276 | sum.as_uint64 <<= 24; | ||
277 | goto norm_int; | ||
278 | |||
279 | norm_int64_0_noatt: | ||
280 | norm_int: | ||
281 | if (sum.as_uint64 > (u_int32_t)0xffffffff) | ||
282 | sample = (u_int32_t)0xffffffff; | ||
283 | else | ||
284 | sample = sum.as_uint64; | ||
285 | goto after_norm; | ||
286 | |||
287 | after_norm: | ||
288 | |||
289 | /* Put sample */ | ||
290 | goto *put_u32; | ||
291 | #define PUT_U32_END after_put_u32 | ||
292 | #include "plugin_ops.h" | ||
293 | #undef PUT_U32_END | ||
294 | after_put_u32: | ||
295 | |||
296 | dst += dst_step; | ||
297 | } | ||
298 | } | ||
299 | |||
300 | static int route_src_channels_mask(snd_pcm_plugin_t *plugin, | ||
301 | bitset_t *dst_vmask, | ||
302 | bitset_t **src_vmask) | ||
303 | { | ||
304 | route_t *data = (route_t *)plugin->extra_data; | ||
305 | int schannels = plugin->src_format.channels; | ||
306 | int dchannels = plugin->dst_format.channels; | ||
307 | bitset_t *vmask = plugin->src_vmask; | ||
308 | int channel; | ||
309 | ttable_dst_t *dp = data->ttable; | ||
310 | bitset_zero(vmask, schannels); | ||
311 | for (channel = 0; channel < dchannels; channel++, dp++) { | ||
312 | unsigned int src; | ||
313 | ttable_src_t *sp; | ||
314 | if (!bitset_get(dst_vmask, channel)) | ||
315 | continue; | ||
316 | sp = dp->srcs; | ||
317 | for (src = 0; src < dp->nsrcs; src++, sp++) | ||
318 | bitset_set(vmask, sp->channel); | ||
319 | } | ||
320 | *src_vmask = vmask; | ||
321 | return 0; | ||
322 | } | ||
323 | |||
324 | static int route_dst_channels_mask(snd_pcm_plugin_t *plugin, | ||
325 | bitset_t *src_vmask, | ||
326 | bitset_t **dst_vmask) | ||
327 | { | ||
328 | route_t *data = (route_t *)plugin->extra_data; | ||
329 | int dchannels = plugin->dst_format.channels; | ||
330 | bitset_t *vmask = plugin->dst_vmask; | ||
331 | int channel; | ||
332 | ttable_dst_t *dp = data->ttable; | ||
333 | bitset_zero(vmask, dchannels); | ||
334 | for (channel = 0; channel < dchannels; channel++, dp++) { | ||
335 | unsigned int src; | ||
336 | ttable_src_t *sp; | ||
337 | sp = dp->srcs; | ||
338 | for (src = 0; src < dp->nsrcs; src++, sp++) { | ||
339 | if (bitset_get(src_vmask, sp->channel)) { | ||
340 | bitset_set(vmask, channel); | ||
341 | break; | ||
342 | } | ||
343 | } | ||
344 | } | ||
345 | *dst_vmask = vmask; | ||
346 | return 0; | ||
347 | } | ||
348 | |||
349 | static void route_free(snd_pcm_plugin_t *plugin) | ||
350 | { | ||
351 | route_t *data = (route_t *)plugin->extra_data; | ||
352 | unsigned int dst_channel; | ||
353 | for (dst_channel = 0; dst_channel < plugin->dst_format.channels; ++dst_channel) { | ||
354 | kfree(data->ttable[dst_channel].srcs); | ||
355 | } | ||
356 | } | ||
357 | |||
358 | static int route_load_ttable(snd_pcm_plugin_t *plugin, | ||
359 | const route_ttable_entry_t* src_ttable) | ||
360 | { | ||
361 | route_t *data; | ||
362 | unsigned int src_channel, dst_channel; | ||
363 | const route_ttable_entry_t *sptr; | ||
364 | ttable_dst_t *dptr; | ||
365 | if (src_ttable == NULL) | ||
366 | return 0; | ||
367 | data = (route_t *)plugin->extra_data; | ||
368 | dptr = data->ttable; | ||
369 | sptr = src_ttable; | ||
370 | plugin->private_free = route_free; | ||
371 | for (dst_channel = 0; dst_channel < plugin->dst_format.channels; ++dst_channel) { | ||
372 | route_ttable_entry_t t = 0; | ||
373 | int att = 0; | ||
374 | int nsrcs = 0; | ||
375 | ttable_src_t srcs[plugin->src_format.channels]; | ||
376 | for (src_channel = 0; src_channel < plugin->src_format.channels; ++src_channel) { | ||
377 | snd_assert(*sptr >= 0 || *sptr <= FULL, return -ENXIO); | ||
378 | if (*sptr != 0) { | ||
379 | srcs[nsrcs].channel = src_channel; | ||
380 | srcs[nsrcs].as_int = *sptr; | ||
381 | if (*sptr != FULL) | ||
382 | att = 1; | ||
383 | t += *sptr; | ||
384 | nsrcs++; | ||
385 | } | ||
386 | sptr++; | ||
387 | } | ||
388 | dptr->att = att; | ||
389 | dptr->nsrcs = nsrcs; | ||
390 | if (nsrcs == 0) | ||
391 | dptr->func = route_to_channel_from_zero; | ||
392 | else if (nsrcs == 1 && !att) | ||
393 | dptr->func = route_to_channel_from_one; | ||
394 | else | ||
395 | dptr->func = route_to_channel; | ||
396 | if (nsrcs > 0) { | ||
397 | int srcidx; | ||
398 | dptr->srcs = kcalloc(nsrcs, sizeof(*srcs), GFP_KERNEL); | ||
399 | for(srcidx = 0; srcidx < nsrcs; srcidx++) | ||
400 | dptr->srcs[srcidx] = srcs[srcidx]; | ||
401 | } else | ||
402 | dptr->srcs = NULL; | ||
403 | dptr++; | ||
404 | } | ||
405 | return 0; | ||
406 | } | ||
407 | |||
408 | static snd_pcm_sframes_t route_transfer(snd_pcm_plugin_t *plugin, | ||
409 | const snd_pcm_plugin_channel_t *src_channels, | ||
410 | snd_pcm_plugin_channel_t *dst_channels, | ||
411 | snd_pcm_uframes_t frames) | ||
412 | { | ||
413 | route_t *data; | ||
414 | int src_nchannels, dst_nchannels; | ||
415 | int dst_channel; | ||
416 | ttable_dst_t *ttp; | ||
417 | snd_pcm_plugin_channel_t *dvp; | ||
418 | |||
419 | snd_assert(plugin != NULL && src_channels != NULL && dst_channels != NULL, return -ENXIO); | ||
420 | if (frames == 0) | ||
421 | return 0; | ||
422 | data = (route_t *)plugin->extra_data; | ||
423 | |||
424 | src_nchannels = plugin->src_format.channels; | ||
425 | dst_nchannels = plugin->dst_format.channels; | ||
426 | |||
427 | #ifdef CONFIG_SND_DEBUG | ||
428 | { | ||
429 | int src_channel; | ||
430 | for (src_channel = 0; src_channel < src_nchannels; ++src_channel) { | ||
431 | snd_assert(src_channels[src_channel].area.first % 8 == 0 || | ||
432 | src_channels[src_channel].area.step % 8 == 0, | ||
433 | return -ENXIO); | ||
434 | } | ||
435 | for (dst_channel = 0; dst_channel < dst_nchannels; ++dst_channel) { | ||
436 | snd_assert(dst_channels[dst_channel].area.first % 8 == 0 || | ||
437 | dst_channels[dst_channel].area.step % 8 == 0, | ||
438 | return -ENXIO); | ||
439 | } | ||
440 | } | ||
441 | #endif | ||
442 | |||
443 | ttp = data->ttable; | ||
444 | dvp = dst_channels; | ||
445 | for (dst_channel = 0; dst_channel < dst_nchannels; ++dst_channel) { | ||
446 | ttp->func(plugin, src_channels, dvp, ttp, frames); | ||
447 | dvp++; | ||
448 | ttp++; | ||
449 | } | ||
450 | return frames; | ||
451 | } | ||
452 | |||
453 | int getput_index(int format) | ||
454 | { | ||
455 | int sign, width, endian; | ||
456 | sign = !snd_pcm_format_signed(format); | ||
457 | width = snd_pcm_format_width(format) / 8 - 1; | ||
458 | if (width < 0 || width > 3) { | ||
459 | snd_printk(KERN_ERR "snd-pcm-oss: invalid format %d\n", format); | ||
460 | width = 0; | ||
461 | } | ||
462 | #ifdef SNDRV_LITTLE_ENDIAN | ||
463 | endian = snd_pcm_format_big_endian(format); | ||
464 | #else | ||
465 | endian = snd_pcm_format_little_endian(format); | ||
466 | #endif | ||
467 | if (endian < 0) | ||
468 | endian = 0; | ||
469 | return width * 4 + endian * 2 + sign; | ||
470 | } | ||
471 | |||
472 | int snd_pcm_plugin_build_route(snd_pcm_plug_t *plug, | ||
473 | snd_pcm_plugin_format_t *src_format, | ||
474 | snd_pcm_plugin_format_t *dst_format, | ||
475 | route_ttable_entry_t *ttable, | ||
476 | snd_pcm_plugin_t **r_plugin) | ||
477 | { | ||
478 | route_t *data; | ||
479 | snd_pcm_plugin_t *plugin; | ||
480 | int err; | ||
481 | |||
482 | snd_assert(r_plugin != NULL, return -ENXIO); | ||
483 | *r_plugin = NULL; | ||
484 | snd_assert(src_format->rate == dst_format->rate, return -ENXIO); | ||
485 | snd_assert(snd_pcm_format_linear(src_format->format) != 0 && | ||
486 | snd_pcm_format_linear(dst_format->format) != 0, | ||
487 | return -ENXIO); | ||
488 | |||
489 | err = snd_pcm_plugin_build(plug, "attenuated route conversion", | ||
490 | src_format, dst_format, | ||
491 | sizeof(route_t) + sizeof(data->ttable[0]) * dst_format->channels, | ||
492 | &plugin); | ||
493 | if (err < 0) | ||
494 | return err; | ||
495 | |||
496 | data = (route_t *) plugin->extra_data; | ||
497 | |||
498 | data->get = getput_index(src_format->format); | ||
499 | snd_assert(data->get >= 0 && data->get < 4*2*2, return -EINVAL); | ||
500 | data->put = getput_index(dst_format->format); | ||
501 | snd_assert(data->get >= 0 && data->get < 4*2*2, return -EINVAL); | ||
502 | data->conv = conv_index(src_format->format, dst_format->format); | ||
503 | |||
504 | if (snd_pcm_format_width(src_format->format) == 32) | ||
505 | data->sum_type = R_UINT64; | ||
506 | else | ||
507 | data->sum_type = R_UINT32; | ||
508 | data->src_sample_size = snd_pcm_format_width(src_format->format) / 8; | ||
509 | |||
510 | if ((err = route_load_ttable(plugin, ttable)) < 0) { | ||
511 | snd_pcm_plugin_free(plugin); | ||
512 | return err; | ||
513 | } | ||
514 | plugin->transfer = route_transfer; | ||
515 | plugin->src_channels_mask = route_src_channels_mask; | ||
516 | plugin->dst_channels_mask = route_dst_channels_mask; | ||
517 | *r_plugin = plugin; | ||
518 | return 0; | ||
519 | } | ||
diff --git a/sound/core/pcm.c b/sound/core/pcm.c new file mode 100644 index 000000000000..8d94325529a8 --- /dev/null +++ b/sound/core/pcm.c | |||
@@ -0,0 +1,1074 @@ | |||
1 | /* | ||
2 | * Digital Audio (PCM) abstract layer | ||
3 | * Copyright (c) by Jaroslav Kysela <perex@suse.cz> | ||
4 | * | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #include <sound/driver.h> | ||
23 | #include <linux/init.h> | ||
24 | #include <linux/slab.h> | ||
25 | #include <linux/time.h> | ||
26 | #include <sound/core.h> | ||
27 | #include <sound/minors.h> | ||
28 | #include <sound/pcm.h> | ||
29 | #include <sound/control.h> | ||
30 | #include <sound/info.h> | ||
31 | |||
32 | MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>, Abramo Bagnara <abramo@alsa-project.org>"); | ||
33 | MODULE_DESCRIPTION("Midlevel PCM code for ALSA."); | ||
34 | MODULE_LICENSE("GPL"); | ||
35 | |||
36 | snd_pcm_t *snd_pcm_devices[SNDRV_CARDS * SNDRV_PCM_DEVICES]; | ||
37 | static LIST_HEAD(snd_pcm_notify_list); | ||
38 | static DECLARE_MUTEX(register_mutex); | ||
39 | |||
40 | static int snd_pcm_free(snd_pcm_t *pcm); | ||
41 | static int snd_pcm_dev_free(snd_device_t *device); | ||
42 | static int snd_pcm_dev_register(snd_device_t *device); | ||
43 | static int snd_pcm_dev_disconnect(snd_device_t *device); | ||
44 | static int snd_pcm_dev_unregister(snd_device_t *device); | ||
45 | |||
46 | static int snd_pcm_control_ioctl(snd_card_t * card, | ||
47 | snd_ctl_file_t * control, | ||
48 | unsigned int cmd, unsigned long arg) | ||
49 | { | ||
50 | unsigned int tmp; | ||
51 | |||
52 | tmp = card->number * SNDRV_PCM_DEVICES; | ||
53 | switch (cmd) { | ||
54 | case SNDRV_CTL_IOCTL_PCM_NEXT_DEVICE: | ||
55 | { | ||
56 | int device; | ||
57 | |||
58 | if (get_user(device, (int __user *)arg)) | ||
59 | return -EFAULT; | ||
60 | device = device < 0 ? 0 : device + 1; | ||
61 | while (device < SNDRV_PCM_DEVICES) { | ||
62 | if (snd_pcm_devices[tmp + device]) | ||
63 | break; | ||
64 | device++; | ||
65 | } | ||
66 | if (device == SNDRV_PCM_DEVICES) | ||
67 | device = -1; | ||
68 | if (put_user(device, (int __user *)arg)) | ||
69 | return -EFAULT; | ||
70 | return 0; | ||
71 | } | ||
72 | case SNDRV_CTL_IOCTL_PCM_INFO: | ||
73 | { | ||
74 | snd_pcm_info_t __user *info; | ||
75 | unsigned int device, subdevice; | ||
76 | snd_pcm_stream_t stream; | ||
77 | snd_pcm_t *pcm; | ||
78 | snd_pcm_str_t *pstr; | ||
79 | snd_pcm_substream_t *substream; | ||
80 | info = (snd_pcm_info_t __user *)arg; | ||
81 | if (get_user(device, &info->device)) | ||
82 | return -EFAULT; | ||
83 | if (device >= SNDRV_PCM_DEVICES) | ||
84 | return -ENXIO; | ||
85 | pcm = snd_pcm_devices[tmp + device]; | ||
86 | if (pcm == NULL) | ||
87 | return -ENXIO; | ||
88 | if (get_user(stream, &info->stream)) | ||
89 | return -EFAULT; | ||
90 | if (stream < 0 || stream > 1) | ||
91 | return -EINVAL; | ||
92 | pstr = &pcm->streams[stream]; | ||
93 | if (pstr->substream_count == 0) | ||
94 | return -ENOENT; | ||
95 | if (get_user(subdevice, &info->subdevice)) | ||
96 | return -EFAULT; | ||
97 | if (subdevice >= pstr->substream_count) | ||
98 | return -ENXIO; | ||
99 | for (substream = pstr->substream; substream; substream = substream->next) | ||
100 | if (substream->number == (int)subdevice) | ||
101 | break; | ||
102 | if (substream == NULL) | ||
103 | return -ENXIO; | ||
104 | return snd_pcm_info_user(substream, info); | ||
105 | } | ||
106 | case SNDRV_CTL_IOCTL_PCM_PREFER_SUBDEVICE: | ||
107 | { | ||
108 | int val; | ||
109 | |||
110 | if (get_user(val, (int __user *)arg)) | ||
111 | return -EFAULT; | ||
112 | control->prefer_pcm_subdevice = val; | ||
113 | return 0; | ||
114 | } | ||
115 | } | ||
116 | return -ENOIOCTLCMD; | ||
117 | } | ||
118 | #define STATE(v) [SNDRV_PCM_STATE_##v] = #v | ||
119 | #define STREAM(v) [SNDRV_PCM_STREAM_##v] = #v | ||
120 | #define READY(v) [SNDRV_PCM_READY_##v] = #v | ||
121 | #define XRUN(v) [SNDRV_PCM_XRUN_##v] = #v | ||
122 | #define SILENCE(v) [SNDRV_PCM_SILENCE_##v] = #v | ||
123 | #define TSTAMP(v) [SNDRV_PCM_TSTAMP_##v] = #v | ||
124 | #define ACCESS(v) [SNDRV_PCM_ACCESS_##v] = #v | ||
125 | #define START(v) [SNDRV_PCM_START_##v] = #v | ||
126 | #define FORMAT(v) [SNDRV_PCM_FORMAT_##v] = #v | ||
127 | #define SUBFORMAT(v) [SNDRV_PCM_SUBFORMAT_##v] = #v | ||
128 | |||
129 | static char *snd_pcm_stream_names[] = { | ||
130 | STREAM(PLAYBACK), | ||
131 | STREAM(CAPTURE), | ||
132 | }; | ||
133 | |||
134 | static char *snd_pcm_state_names[] = { | ||
135 | STATE(OPEN), | ||
136 | STATE(SETUP), | ||
137 | STATE(PREPARED), | ||
138 | STATE(RUNNING), | ||
139 | STATE(XRUN), | ||
140 | STATE(DRAINING), | ||
141 | STATE(PAUSED), | ||
142 | STATE(SUSPENDED), | ||
143 | }; | ||
144 | |||
145 | static char *snd_pcm_access_names[] = { | ||
146 | ACCESS(MMAP_INTERLEAVED), | ||
147 | ACCESS(MMAP_NONINTERLEAVED), | ||
148 | ACCESS(MMAP_COMPLEX), | ||
149 | ACCESS(RW_INTERLEAVED), | ||
150 | ACCESS(RW_NONINTERLEAVED), | ||
151 | }; | ||
152 | |||
153 | static char *snd_pcm_format_names[] = { | ||
154 | FORMAT(S8), | ||
155 | FORMAT(U8), | ||
156 | FORMAT(S16_LE), | ||
157 | FORMAT(S16_BE), | ||
158 | FORMAT(U16_LE), | ||
159 | FORMAT(U16_BE), | ||
160 | FORMAT(S24_LE), | ||
161 | FORMAT(S24_BE), | ||
162 | FORMAT(U24_LE), | ||
163 | FORMAT(U24_BE), | ||
164 | FORMAT(S32_LE), | ||
165 | FORMAT(S32_BE), | ||
166 | FORMAT(U32_LE), | ||
167 | FORMAT(U32_BE), | ||
168 | FORMAT(FLOAT_LE), | ||
169 | FORMAT(FLOAT_BE), | ||
170 | FORMAT(FLOAT64_LE), | ||
171 | FORMAT(FLOAT64_BE), | ||
172 | FORMAT(IEC958_SUBFRAME_LE), | ||
173 | FORMAT(IEC958_SUBFRAME_BE), | ||
174 | FORMAT(MU_LAW), | ||
175 | FORMAT(A_LAW), | ||
176 | FORMAT(IMA_ADPCM), | ||
177 | FORMAT(MPEG), | ||
178 | FORMAT(GSM), | ||
179 | FORMAT(SPECIAL), | ||
180 | FORMAT(S24_3LE), | ||
181 | FORMAT(S24_3BE), | ||
182 | FORMAT(U24_3LE), | ||
183 | FORMAT(U24_3BE), | ||
184 | FORMAT(S20_3LE), | ||
185 | FORMAT(S20_3BE), | ||
186 | FORMAT(U20_3LE), | ||
187 | FORMAT(U20_3BE), | ||
188 | FORMAT(S18_3LE), | ||
189 | FORMAT(S18_3BE), | ||
190 | FORMAT(U18_3LE), | ||
191 | FORMAT(U18_3BE), | ||
192 | }; | ||
193 | |||
194 | static char *snd_pcm_subformat_names[] = { | ||
195 | SUBFORMAT(STD), | ||
196 | }; | ||
197 | |||
198 | static char *snd_pcm_tstamp_mode_names[] = { | ||
199 | TSTAMP(NONE), | ||
200 | TSTAMP(MMAP), | ||
201 | }; | ||
202 | |||
203 | static const char *snd_pcm_stream_name(snd_pcm_stream_t stream) | ||
204 | { | ||
205 | snd_assert(stream <= SNDRV_PCM_STREAM_LAST, return NULL); | ||
206 | return snd_pcm_stream_names[stream]; | ||
207 | } | ||
208 | |||
209 | static const char *snd_pcm_access_name(snd_pcm_access_t access) | ||
210 | { | ||
211 | snd_assert(access <= SNDRV_PCM_ACCESS_LAST, return NULL); | ||
212 | return snd_pcm_access_names[access]; | ||
213 | } | ||
214 | |||
215 | const char *snd_pcm_format_name(snd_pcm_format_t format) | ||
216 | { | ||
217 | snd_assert(format <= SNDRV_PCM_FORMAT_LAST, return NULL); | ||
218 | return snd_pcm_format_names[format]; | ||
219 | } | ||
220 | |||
221 | static const char *snd_pcm_subformat_name(snd_pcm_subformat_t subformat) | ||
222 | { | ||
223 | snd_assert(subformat <= SNDRV_PCM_SUBFORMAT_LAST, return NULL); | ||
224 | return snd_pcm_subformat_names[subformat]; | ||
225 | } | ||
226 | |||
227 | static const char *snd_pcm_tstamp_mode_name(snd_pcm_tstamp_t mode) | ||
228 | { | ||
229 | snd_assert(mode <= SNDRV_PCM_TSTAMP_LAST, return NULL); | ||
230 | return snd_pcm_tstamp_mode_names[mode]; | ||
231 | } | ||
232 | |||
233 | static const char *snd_pcm_state_name(snd_pcm_state_t state) | ||
234 | { | ||
235 | snd_assert(state <= SNDRV_PCM_STATE_LAST, return NULL); | ||
236 | return snd_pcm_state_names[state]; | ||
237 | } | ||
238 | |||
239 | #if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE) | ||
240 | #include <linux/soundcard.h> | ||
241 | static const char *snd_pcm_oss_format_name(int format) | ||
242 | { | ||
243 | switch (format) { | ||
244 | case AFMT_MU_LAW: | ||
245 | return "MU_LAW"; | ||
246 | case AFMT_A_LAW: | ||
247 | return "A_LAW"; | ||
248 | case AFMT_IMA_ADPCM: | ||
249 | return "IMA_ADPCM"; | ||
250 | case AFMT_U8: | ||
251 | return "U8"; | ||
252 | case AFMT_S16_LE: | ||
253 | return "S16_LE"; | ||
254 | case AFMT_S16_BE: | ||
255 | return "S16_BE"; | ||
256 | case AFMT_S8: | ||
257 | return "S8"; | ||
258 | case AFMT_U16_LE: | ||
259 | return "U16_LE"; | ||
260 | case AFMT_U16_BE: | ||
261 | return "U16_BE"; | ||
262 | case AFMT_MPEG: | ||
263 | return "MPEG"; | ||
264 | default: | ||
265 | return "unknown"; | ||
266 | } | ||
267 | } | ||
268 | #endif | ||
269 | |||
270 | #ifdef CONFIG_PROC_FS | ||
271 | static void snd_pcm_proc_info_read(snd_pcm_substream_t *substream, snd_info_buffer_t *buffer) | ||
272 | { | ||
273 | snd_pcm_info_t *info; | ||
274 | int err; | ||
275 | |||
276 | snd_runtime_check(substream, return); | ||
277 | |||
278 | info = kmalloc(sizeof(*info), GFP_KERNEL); | ||
279 | if (! info) { | ||
280 | printk(KERN_DEBUG "snd_pcm_proc_info_read: cannot malloc\n"); | ||
281 | return; | ||
282 | } | ||
283 | |||
284 | err = snd_pcm_info(substream, info); | ||
285 | if (err < 0) { | ||
286 | snd_iprintf(buffer, "error %d\n", err); | ||
287 | kfree(info); | ||
288 | return; | ||
289 | } | ||
290 | snd_iprintf(buffer, "card: %d\n", info->card); | ||
291 | snd_iprintf(buffer, "device: %d\n", info->device); | ||
292 | snd_iprintf(buffer, "subdevice: %d\n", info->subdevice); | ||
293 | snd_iprintf(buffer, "stream: %s\n", snd_pcm_stream_name(info->stream)); | ||
294 | snd_iprintf(buffer, "id: %s\n", info->id); | ||
295 | snd_iprintf(buffer, "name: %s\n", info->name); | ||
296 | snd_iprintf(buffer, "subname: %s\n", info->subname); | ||
297 | snd_iprintf(buffer, "class: %d\n", info->dev_class); | ||
298 | snd_iprintf(buffer, "subclass: %d\n", info->dev_subclass); | ||
299 | snd_iprintf(buffer, "subdevices_count: %d\n", info->subdevices_count); | ||
300 | snd_iprintf(buffer, "subdevices_avail: %d\n", info->subdevices_avail); | ||
301 | kfree(info); | ||
302 | } | ||
303 | |||
304 | static void snd_pcm_stream_proc_info_read(snd_info_entry_t *entry, snd_info_buffer_t *buffer) | ||
305 | { | ||
306 | snd_pcm_proc_info_read(((snd_pcm_str_t *)entry->private_data)->substream, buffer); | ||
307 | } | ||
308 | |||
309 | static void snd_pcm_substream_proc_info_read(snd_info_entry_t *entry, snd_info_buffer_t *buffer) | ||
310 | { | ||
311 | snd_pcm_proc_info_read((snd_pcm_substream_t *)entry->private_data, buffer); | ||
312 | } | ||
313 | |||
314 | static void snd_pcm_substream_proc_hw_params_read(snd_info_entry_t *entry, snd_info_buffer_t *buffer) | ||
315 | { | ||
316 | snd_pcm_substream_t *substream = (snd_pcm_substream_t *)entry->private_data; | ||
317 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
318 | if (!runtime) { | ||
319 | snd_iprintf(buffer, "closed\n"); | ||
320 | return; | ||
321 | } | ||
322 | snd_pcm_stream_lock_irq(substream); | ||
323 | if (runtime->status->state == SNDRV_PCM_STATE_OPEN) { | ||
324 | snd_iprintf(buffer, "no setup\n"); | ||
325 | snd_pcm_stream_unlock_irq(substream); | ||
326 | return; | ||
327 | } | ||
328 | snd_iprintf(buffer, "access: %s\n", snd_pcm_access_name(runtime->access)); | ||
329 | snd_iprintf(buffer, "format: %s\n", snd_pcm_format_name(runtime->format)); | ||
330 | snd_iprintf(buffer, "subformat: %s\n", snd_pcm_subformat_name(runtime->subformat)); | ||
331 | snd_iprintf(buffer, "channels: %u\n", runtime->channels); | ||
332 | snd_iprintf(buffer, "rate: %u (%u/%u)\n", runtime->rate, runtime->rate_num, runtime->rate_den); | ||
333 | snd_iprintf(buffer, "period_size: %lu\n", runtime->period_size); | ||
334 | snd_iprintf(buffer, "buffer_size: %lu\n", runtime->buffer_size); | ||
335 | snd_iprintf(buffer, "tick_time: %u\n", runtime->tick_time); | ||
336 | #if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE) | ||
337 | if (substream->oss.oss) { | ||
338 | snd_iprintf(buffer, "OSS format: %s\n", snd_pcm_oss_format_name(runtime->oss.format)); | ||
339 | snd_iprintf(buffer, "OSS channels: %u\n", runtime->oss.channels); | ||
340 | snd_iprintf(buffer, "OSS rate: %u\n", runtime->oss.rate); | ||
341 | snd_iprintf(buffer, "OSS period bytes: %lu\n", (unsigned long)runtime->oss.period_bytes); | ||
342 | snd_iprintf(buffer, "OSS periods: %u\n", runtime->oss.periods); | ||
343 | snd_iprintf(buffer, "OSS period frames: %lu\n", (unsigned long)runtime->oss.period_frames); | ||
344 | } | ||
345 | #endif | ||
346 | snd_pcm_stream_unlock_irq(substream); | ||
347 | } | ||
348 | |||
349 | static void snd_pcm_substream_proc_sw_params_read(snd_info_entry_t *entry, snd_info_buffer_t *buffer) | ||
350 | { | ||
351 | snd_pcm_substream_t *substream = (snd_pcm_substream_t *)entry->private_data; | ||
352 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
353 | if (!runtime) { | ||
354 | snd_iprintf(buffer, "closed\n"); | ||
355 | return; | ||
356 | } | ||
357 | snd_pcm_stream_lock_irq(substream); | ||
358 | if (runtime->status->state == SNDRV_PCM_STATE_OPEN) { | ||
359 | snd_iprintf(buffer, "no setup\n"); | ||
360 | snd_pcm_stream_unlock_irq(substream); | ||
361 | return; | ||
362 | } | ||
363 | snd_iprintf(buffer, "tstamp_mode: %s\n", snd_pcm_tstamp_mode_name(runtime->tstamp_mode)); | ||
364 | snd_iprintf(buffer, "period_step: %u\n", runtime->period_step); | ||
365 | snd_iprintf(buffer, "sleep_min: %u\n", runtime->sleep_min); | ||
366 | snd_iprintf(buffer, "avail_min: %lu\n", runtime->control->avail_min); | ||
367 | snd_iprintf(buffer, "xfer_align: %lu\n", runtime->xfer_align); | ||
368 | snd_iprintf(buffer, "start_threshold: %lu\n", runtime->start_threshold); | ||
369 | snd_iprintf(buffer, "stop_threshold: %lu\n", runtime->stop_threshold); | ||
370 | snd_iprintf(buffer, "silence_threshold: %lu\n", runtime->silence_threshold); | ||
371 | snd_iprintf(buffer, "silence_size: %lu\n", runtime->silence_size); | ||
372 | snd_iprintf(buffer, "boundary: %lu\n", runtime->boundary); | ||
373 | snd_pcm_stream_unlock_irq(substream); | ||
374 | } | ||
375 | |||
376 | static void snd_pcm_substream_proc_status_read(snd_info_entry_t *entry, snd_info_buffer_t *buffer) | ||
377 | { | ||
378 | snd_pcm_substream_t *substream = (snd_pcm_substream_t *)entry->private_data; | ||
379 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
380 | snd_pcm_status_t status; | ||
381 | int err; | ||
382 | if (!runtime) { | ||
383 | snd_iprintf(buffer, "closed\n"); | ||
384 | return; | ||
385 | } | ||
386 | memset(&status, 0, sizeof(status)); | ||
387 | err = snd_pcm_status(substream, &status); | ||
388 | if (err < 0) { | ||
389 | snd_iprintf(buffer, "error %d\n", err); | ||
390 | return; | ||
391 | } | ||
392 | snd_iprintf(buffer, "state: %s\n", snd_pcm_state_name(status.state)); | ||
393 | snd_iprintf(buffer, "trigger_time: %ld.%09ld\n", | ||
394 | status.trigger_tstamp.tv_sec, status.trigger_tstamp.tv_nsec); | ||
395 | snd_iprintf(buffer, "tstamp : %ld.%09ld\n", | ||
396 | status.tstamp.tv_sec, status.tstamp.tv_nsec); | ||
397 | snd_iprintf(buffer, "delay : %ld\n", status.delay); | ||
398 | snd_iprintf(buffer, "avail : %ld\n", status.avail); | ||
399 | snd_iprintf(buffer, "avail_max : %ld\n", status.avail_max); | ||
400 | snd_iprintf(buffer, "-----\n"); | ||
401 | snd_iprintf(buffer, "hw_ptr : %ld\n", runtime->status->hw_ptr); | ||
402 | snd_iprintf(buffer, "appl_ptr : %ld\n", runtime->control->appl_ptr); | ||
403 | } | ||
404 | #endif | ||
405 | |||
406 | #ifdef CONFIG_SND_DEBUG | ||
407 | static void snd_pcm_xrun_debug_read(snd_info_entry_t *entry, snd_info_buffer_t *buffer) | ||
408 | { | ||
409 | snd_pcm_str_t *pstr = (snd_pcm_str_t *)entry->private_data; | ||
410 | snd_iprintf(buffer, "%d\n", pstr->xrun_debug); | ||
411 | } | ||
412 | |||
413 | static void snd_pcm_xrun_debug_write(snd_info_entry_t *entry, snd_info_buffer_t *buffer) | ||
414 | { | ||
415 | snd_pcm_str_t *pstr = (snd_pcm_str_t *)entry->private_data; | ||
416 | char line[64]; | ||
417 | if (!snd_info_get_line(buffer, line, sizeof(line))) | ||
418 | pstr->xrun_debug = simple_strtoul(line, NULL, 10); | ||
419 | } | ||
420 | #endif | ||
421 | |||
422 | static int snd_pcm_stream_proc_init(snd_pcm_str_t *pstr) | ||
423 | { | ||
424 | snd_pcm_t *pcm = pstr->pcm; | ||
425 | snd_info_entry_t *entry; | ||
426 | char name[16]; | ||
427 | |||
428 | sprintf(name, "pcm%i%c", pcm->device, | ||
429 | pstr->stream == SNDRV_PCM_STREAM_PLAYBACK ? 'p' : 'c'); | ||
430 | if ((entry = snd_info_create_card_entry(pcm->card, name, pcm->card->proc_root)) == NULL) | ||
431 | return -ENOMEM; | ||
432 | entry->mode = S_IFDIR | S_IRUGO | S_IXUGO; | ||
433 | if (snd_info_register(entry) < 0) { | ||
434 | snd_info_free_entry(entry); | ||
435 | return -ENOMEM; | ||
436 | } | ||
437 | pstr->proc_root = entry; | ||
438 | |||
439 | if ((entry = snd_info_create_card_entry(pcm->card, "info", pstr->proc_root)) != NULL) { | ||
440 | snd_info_set_text_ops(entry, pstr, 256, snd_pcm_stream_proc_info_read); | ||
441 | if (snd_info_register(entry) < 0) { | ||
442 | snd_info_free_entry(entry); | ||
443 | entry = NULL; | ||
444 | } | ||
445 | } | ||
446 | pstr->proc_info_entry = entry; | ||
447 | |||
448 | #ifdef CONFIG_SND_DEBUG | ||
449 | if ((entry = snd_info_create_card_entry(pcm->card, "xrun_debug", pstr->proc_root)) != NULL) { | ||
450 | entry->c.text.read_size = 64; | ||
451 | entry->c.text.read = snd_pcm_xrun_debug_read; | ||
452 | entry->c.text.write_size = 64; | ||
453 | entry->c.text.write = snd_pcm_xrun_debug_write; | ||
454 | entry->private_data = pstr; | ||
455 | if (snd_info_register(entry) < 0) { | ||
456 | snd_info_free_entry(entry); | ||
457 | entry = NULL; | ||
458 | } | ||
459 | } | ||
460 | pstr->proc_xrun_debug_entry = entry; | ||
461 | #endif | ||
462 | return 0; | ||
463 | } | ||
464 | |||
465 | static int snd_pcm_stream_proc_done(snd_pcm_str_t *pstr) | ||
466 | { | ||
467 | #ifdef CONFIG_SND_DEBUG | ||
468 | if (pstr->proc_xrun_debug_entry) { | ||
469 | snd_info_unregister(pstr->proc_xrun_debug_entry); | ||
470 | pstr->proc_xrun_debug_entry = NULL; | ||
471 | } | ||
472 | #endif | ||
473 | if (pstr->proc_info_entry) { | ||
474 | snd_info_unregister(pstr->proc_info_entry); | ||
475 | pstr->proc_info_entry = NULL; | ||
476 | } | ||
477 | if (pstr->proc_root) { | ||
478 | snd_info_unregister(pstr->proc_root); | ||
479 | pstr->proc_root = NULL; | ||
480 | } | ||
481 | return 0; | ||
482 | } | ||
483 | |||
484 | static int snd_pcm_substream_proc_init(snd_pcm_substream_t *substream) | ||
485 | { | ||
486 | snd_info_entry_t *entry; | ||
487 | snd_card_t *card; | ||
488 | char name[16]; | ||
489 | |||
490 | card = substream->pcm->card; | ||
491 | |||
492 | sprintf(name, "sub%i", substream->number); | ||
493 | if ((entry = snd_info_create_card_entry(card, name, substream->pstr->proc_root)) == NULL) | ||
494 | return -ENOMEM; | ||
495 | entry->mode = S_IFDIR | S_IRUGO | S_IXUGO; | ||
496 | if (snd_info_register(entry) < 0) { | ||
497 | snd_info_free_entry(entry); | ||
498 | return -ENOMEM; | ||
499 | } | ||
500 | substream->proc_root = entry; | ||
501 | |||
502 | if ((entry = snd_info_create_card_entry(card, "info", substream->proc_root)) != NULL) { | ||
503 | snd_info_set_text_ops(entry, substream, 256, snd_pcm_substream_proc_info_read); | ||
504 | if (snd_info_register(entry) < 0) { | ||
505 | snd_info_free_entry(entry); | ||
506 | entry = NULL; | ||
507 | } | ||
508 | } | ||
509 | substream->proc_info_entry = entry; | ||
510 | |||
511 | if ((entry = snd_info_create_card_entry(card, "hw_params", substream->proc_root)) != NULL) { | ||
512 | snd_info_set_text_ops(entry, substream, 256, snd_pcm_substream_proc_hw_params_read); | ||
513 | if (snd_info_register(entry) < 0) { | ||
514 | snd_info_free_entry(entry); | ||
515 | entry = NULL; | ||
516 | } | ||
517 | } | ||
518 | substream->proc_hw_params_entry = entry; | ||
519 | |||
520 | if ((entry = snd_info_create_card_entry(card, "sw_params", substream->proc_root)) != NULL) { | ||
521 | snd_info_set_text_ops(entry, substream, 256, snd_pcm_substream_proc_sw_params_read); | ||
522 | if (snd_info_register(entry) < 0) { | ||
523 | snd_info_free_entry(entry); | ||
524 | entry = NULL; | ||
525 | } | ||
526 | } | ||
527 | substream->proc_sw_params_entry = entry; | ||
528 | |||
529 | if ((entry = snd_info_create_card_entry(card, "status", substream->proc_root)) != NULL) { | ||
530 | snd_info_set_text_ops(entry, substream, 256, snd_pcm_substream_proc_status_read); | ||
531 | if (snd_info_register(entry) < 0) { | ||
532 | snd_info_free_entry(entry); | ||
533 | entry = NULL; | ||
534 | } | ||
535 | } | ||
536 | substream->proc_status_entry = entry; | ||
537 | |||
538 | return 0; | ||
539 | } | ||
540 | |||
541 | static int snd_pcm_substream_proc_done(snd_pcm_substream_t *substream) | ||
542 | { | ||
543 | if (substream->proc_info_entry) { | ||
544 | snd_info_unregister(substream->proc_info_entry); | ||
545 | substream->proc_info_entry = NULL; | ||
546 | } | ||
547 | if (substream->proc_hw_params_entry) { | ||
548 | snd_info_unregister(substream->proc_hw_params_entry); | ||
549 | substream->proc_hw_params_entry = NULL; | ||
550 | } | ||
551 | if (substream->proc_sw_params_entry) { | ||
552 | snd_info_unregister(substream->proc_sw_params_entry); | ||
553 | substream->proc_sw_params_entry = NULL; | ||
554 | } | ||
555 | if (substream->proc_status_entry) { | ||
556 | snd_info_unregister(substream->proc_status_entry); | ||
557 | substream->proc_status_entry = NULL; | ||
558 | } | ||
559 | if (substream->proc_root) { | ||
560 | snd_info_unregister(substream->proc_root); | ||
561 | substream->proc_root = NULL; | ||
562 | } | ||
563 | return 0; | ||
564 | } | ||
565 | |||
566 | /** | ||
567 | * snd_pcm_new_stream - create a new PCM stream | ||
568 | * @pcm: the pcm instance | ||
569 | * @stream: the stream direction, SNDRV_PCM_STREAM_XXX | ||
570 | * @substream_count: the number of substreams | ||
571 | * | ||
572 | * Creates a new stream for the pcm. | ||
573 | * The corresponding stream on the pcm must have been empty before | ||
574 | * calling this, i.e. zero must be given to the argument of | ||
575 | * snd_pcm_new(). | ||
576 | * | ||
577 | * Returns zero if successful, or a negative error code on failure. | ||
578 | */ | ||
579 | int snd_pcm_new_stream(snd_pcm_t *pcm, int stream, int substream_count) | ||
580 | { | ||
581 | int idx, err; | ||
582 | snd_pcm_str_t *pstr = &pcm->streams[stream]; | ||
583 | snd_pcm_substream_t *substream, *prev; | ||
584 | |||
585 | #if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE) | ||
586 | init_MUTEX(&pstr->oss.setup_mutex); | ||
587 | #endif | ||
588 | pstr->stream = stream; | ||
589 | pstr->pcm = pcm; | ||
590 | pstr->substream_count = substream_count; | ||
591 | pstr->reg = &snd_pcm_reg[stream]; | ||
592 | if (substream_count > 0) { | ||
593 | err = snd_pcm_stream_proc_init(pstr); | ||
594 | if (err < 0) | ||
595 | return err; | ||
596 | } | ||
597 | prev = NULL; | ||
598 | for (idx = 0, prev = NULL; idx < substream_count; idx++) { | ||
599 | substream = kcalloc(1, sizeof(*substream), GFP_KERNEL); | ||
600 | if (substream == NULL) | ||
601 | return -ENOMEM; | ||
602 | substream->pcm = pcm; | ||
603 | substream->pstr = pstr; | ||
604 | substream->number = idx; | ||
605 | substream->stream = stream; | ||
606 | sprintf(substream->name, "subdevice #%i", idx); | ||
607 | substream->buffer_bytes_max = UINT_MAX; | ||
608 | if (prev == NULL) | ||
609 | pstr->substream = substream; | ||
610 | else | ||
611 | prev->next = substream; | ||
612 | err = snd_pcm_substream_proc_init(substream); | ||
613 | if (err < 0) { | ||
614 | kfree(substream); | ||
615 | return err; | ||
616 | } | ||
617 | substream->group = &substream->self_group; | ||
618 | spin_lock_init(&substream->self_group.lock); | ||
619 | INIT_LIST_HEAD(&substream->self_group.substreams); | ||
620 | list_add_tail(&substream->link_list, &substream->self_group.substreams); | ||
621 | spin_lock_init(&substream->timer_lock); | ||
622 | prev = substream; | ||
623 | } | ||
624 | return 0; | ||
625 | } | ||
626 | |||
627 | /** | ||
628 | * snd_pcm_new - create a new PCM instance | ||
629 | * @card: the card instance | ||
630 | * @id: the id string | ||
631 | * @device: the device index (zero based) | ||
632 | * @playback_count: the number of substreams for playback | ||
633 | * @capture_count: the number of substreams for capture | ||
634 | * @rpcm: the pointer to store the new pcm instance | ||
635 | * | ||
636 | * Creates a new PCM instance. | ||
637 | * | ||
638 | * The pcm operators have to be set afterwards to the new instance | ||
639 | * via snd_pcm_set_ops(). | ||
640 | * | ||
641 | * Returns zero if successful, or a negative error code on failure. | ||
642 | */ | ||
643 | int snd_pcm_new(snd_card_t * card, char *id, int device, | ||
644 | int playback_count, int capture_count, | ||
645 | snd_pcm_t ** rpcm) | ||
646 | { | ||
647 | snd_pcm_t *pcm; | ||
648 | int err; | ||
649 | static snd_device_ops_t ops = { | ||
650 | .dev_free = snd_pcm_dev_free, | ||
651 | .dev_register = snd_pcm_dev_register, | ||
652 | .dev_disconnect = snd_pcm_dev_disconnect, | ||
653 | .dev_unregister = snd_pcm_dev_unregister | ||
654 | }; | ||
655 | |||
656 | snd_assert(rpcm != NULL, return -EINVAL); | ||
657 | *rpcm = NULL; | ||
658 | snd_assert(card != NULL, return -ENXIO); | ||
659 | pcm = kcalloc(1, sizeof(*pcm), GFP_KERNEL); | ||
660 | if (pcm == NULL) | ||
661 | return -ENOMEM; | ||
662 | pcm->card = card; | ||
663 | pcm->device = device; | ||
664 | if (id) { | ||
665 | strlcpy(pcm->id, id, sizeof(pcm->id)); | ||
666 | } | ||
667 | if ((err = snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_PLAYBACK, playback_count)) < 0) { | ||
668 | snd_pcm_free(pcm); | ||
669 | return err; | ||
670 | } | ||
671 | if ((err = snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_CAPTURE, capture_count)) < 0) { | ||
672 | snd_pcm_free(pcm); | ||
673 | return err; | ||
674 | } | ||
675 | init_MUTEX(&pcm->open_mutex); | ||
676 | init_waitqueue_head(&pcm->open_wait); | ||
677 | if ((err = snd_device_new(card, SNDRV_DEV_PCM, pcm, &ops)) < 0) { | ||
678 | snd_pcm_free(pcm); | ||
679 | return err; | ||
680 | } | ||
681 | *rpcm = pcm; | ||
682 | return 0; | ||
683 | } | ||
684 | |||
685 | static void snd_pcm_free_stream(snd_pcm_str_t * pstr) | ||
686 | { | ||
687 | snd_pcm_substream_t *substream, *substream_next; | ||
688 | #if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE) | ||
689 | snd_pcm_oss_setup_t *setup, *setupn; | ||
690 | #endif | ||
691 | substream = pstr->substream; | ||
692 | while (substream) { | ||
693 | substream_next = substream->next; | ||
694 | snd_pcm_substream_proc_done(substream); | ||
695 | kfree(substream); | ||
696 | substream = substream_next; | ||
697 | } | ||
698 | snd_pcm_stream_proc_done(pstr); | ||
699 | #if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE) | ||
700 | for (setup = pstr->oss.setup_list; setup; setup = setupn) { | ||
701 | setupn = setup->next; | ||
702 | kfree(setup->task_name); | ||
703 | kfree(setup); | ||
704 | } | ||
705 | #endif | ||
706 | } | ||
707 | |||
708 | static int snd_pcm_free(snd_pcm_t *pcm) | ||
709 | { | ||
710 | snd_assert(pcm != NULL, return -ENXIO); | ||
711 | if (pcm->private_free) | ||
712 | pcm->private_free(pcm); | ||
713 | snd_pcm_lib_preallocate_free_for_all(pcm); | ||
714 | snd_pcm_free_stream(&pcm->streams[SNDRV_PCM_STREAM_PLAYBACK]); | ||
715 | snd_pcm_free_stream(&pcm->streams[SNDRV_PCM_STREAM_CAPTURE]); | ||
716 | kfree(pcm); | ||
717 | return 0; | ||
718 | } | ||
719 | |||
720 | static int snd_pcm_dev_free(snd_device_t *device) | ||
721 | { | ||
722 | snd_pcm_t *pcm = device->device_data; | ||
723 | return snd_pcm_free(pcm); | ||
724 | } | ||
725 | |||
726 | static void snd_pcm_tick_timer_func(unsigned long data) | ||
727 | { | ||
728 | snd_pcm_substream_t *substream = (snd_pcm_substream_t*) data; | ||
729 | snd_pcm_tick_elapsed(substream); | ||
730 | } | ||
731 | |||
732 | int snd_pcm_open_substream(snd_pcm_t *pcm, int stream, | ||
733 | snd_pcm_substream_t **rsubstream) | ||
734 | { | ||
735 | snd_pcm_str_t * pstr; | ||
736 | snd_pcm_substream_t * substream; | ||
737 | snd_pcm_runtime_t * runtime; | ||
738 | snd_ctl_file_t *kctl; | ||
739 | snd_card_t *card; | ||
740 | struct list_head *list; | ||
741 | int prefer_subdevice = -1; | ||
742 | size_t size; | ||
743 | |||
744 | snd_assert(rsubstream != NULL, return -EINVAL); | ||
745 | *rsubstream = NULL; | ||
746 | snd_assert(pcm != NULL, return -ENXIO); | ||
747 | pstr = &pcm->streams[stream]; | ||
748 | if (pstr->substream == NULL) | ||
749 | return -ENODEV; | ||
750 | |||
751 | card = pcm->card; | ||
752 | down_read(&card->controls_rwsem); | ||
753 | list_for_each(list, &card->ctl_files) { | ||
754 | kctl = snd_ctl_file(list); | ||
755 | if (kctl->pid == current->pid) { | ||
756 | prefer_subdevice = kctl->prefer_pcm_subdevice; | ||
757 | break; | ||
758 | } | ||
759 | } | ||
760 | up_read(&card->controls_rwsem); | ||
761 | |||
762 | if (pstr->substream_count == 0) | ||
763 | return -ENODEV; | ||
764 | switch (stream) { | ||
765 | case SNDRV_PCM_STREAM_PLAYBACK: | ||
766 | if (pcm->info_flags & SNDRV_PCM_INFO_HALF_DUPLEX) { | ||
767 | for (substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream; substream; substream = substream->next) { | ||
768 | if (SUBSTREAM_BUSY(substream)) | ||
769 | return -EAGAIN; | ||
770 | } | ||
771 | } | ||
772 | break; | ||
773 | case SNDRV_PCM_STREAM_CAPTURE: | ||
774 | if (pcm->info_flags & SNDRV_PCM_INFO_HALF_DUPLEX) { | ||
775 | for (substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; substream; substream = substream->next) { | ||
776 | if (SUBSTREAM_BUSY(substream)) | ||
777 | return -EAGAIN; | ||
778 | } | ||
779 | } | ||
780 | break; | ||
781 | default: | ||
782 | return -EINVAL; | ||
783 | } | ||
784 | |||
785 | if (prefer_subdevice >= 0) { | ||
786 | for (substream = pstr->substream; substream; substream = substream->next) | ||
787 | if (!SUBSTREAM_BUSY(substream) && substream->number == prefer_subdevice) | ||
788 | goto __ok; | ||
789 | } | ||
790 | for (substream = pstr->substream; substream; substream = substream->next) | ||
791 | if (!SUBSTREAM_BUSY(substream)) | ||
792 | break; | ||
793 | __ok: | ||
794 | if (substream == NULL) | ||
795 | return -EAGAIN; | ||
796 | |||
797 | runtime = kcalloc(1, sizeof(*runtime), GFP_KERNEL); | ||
798 | if (runtime == NULL) | ||
799 | return -ENOMEM; | ||
800 | |||
801 | size = PAGE_ALIGN(sizeof(snd_pcm_mmap_status_t)); | ||
802 | runtime->status = snd_malloc_pages(size, GFP_KERNEL); | ||
803 | if (runtime->status == NULL) { | ||
804 | kfree(runtime); | ||
805 | return -ENOMEM; | ||
806 | } | ||
807 | memset((void*)runtime->status, 0, size); | ||
808 | |||
809 | size = PAGE_ALIGN(sizeof(snd_pcm_mmap_control_t)); | ||
810 | runtime->control = snd_malloc_pages(size, GFP_KERNEL); | ||
811 | if (runtime->control == NULL) { | ||
812 | snd_free_pages((void*)runtime->status, PAGE_ALIGN(sizeof(snd_pcm_mmap_status_t))); | ||
813 | kfree(runtime); | ||
814 | return -ENOMEM; | ||
815 | } | ||
816 | memset((void*)runtime->control, 0, size); | ||
817 | |||
818 | init_waitqueue_head(&runtime->sleep); | ||
819 | atomic_set(&runtime->mmap_count, 0); | ||
820 | init_timer(&runtime->tick_timer); | ||
821 | runtime->tick_timer.function = snd_pcm_tick_timer_func; | ||
822 | runtime->tick_timer.data = (unsigned long) substream; | ||
823 | |||
824 | runtime->status->state = SNDRV_PCM_STATE_OPEN; | ||
825 | |||
826 | substream->runtime = runtime; | ||
827 | substream->private_data = pcm->private_data; | ||
828 | pstr->substream_opened++; | ||
829 | *rsubstream = substream; | ||
830 | return 0; | ||
831 | } | ||
832 | |||
833 | void snd_pcm_release_substream(snd_pcm_substream_t *substream) | ||
834 | { | ||
835 | snd_pcm_runtime_t * runtime; | ||
836 | substream->file = NULL; | ||
837 | runtime = substream->runtime; | ||
838 | snd_assert(runtime != NULL, return); | ||
839 | if (runtime->private_free != NULL) | ||
840 | runtime->private_free(runtime); | ||
841 | snd_free_pages((void*)runtime->status, PAGE_ALIGN(sizeof(snd_pcm_mmap_status_t))); | ||
842 | snd_free_pages((void*)runtime->control, PAGE_ALIGN(sizeof(snd_pcm_mmap_control_t))); | ||
843 | kfree(runtime->hw_constraints.rules); | ||
844 | kfree(runtime); | ||
845 | substream->runtime = NULL; | ||
846 | substream->pstr->substream_opened--; | ||
847 | } | ||
848 | |||
849 | static int snd_pcm_dev_register(snd_device_t *device) | ||
850 | { | ||
851 | int idx, cidx, err; | ||
852 | unsigned short minor; | ||
853 | snd_pcm_substream_t *substream; | ||
854 | struct list_head *list; | ||
855 | char str[16]; | ||
856 | snd_pcm_t *pcm = device->device_data; | ||
857 | |||
858 | snd_assert(pcm != NULL && device != NULL, return -ENXIO); | ||
859 | down(®ister_mutex); | ||
860 | idx = (pcm->card->number * SNDRV_PCM_DEVICES) + pcm->device; | ||
861 | if (snd_pcm_devices[idx]) { | ||
862 | up(®ister_mutex); | ||
863 | return -EBUSY; | ||
864 | } | ||
865 | snd_pcm_devices[idx] = pcm; | ||
866 | for (cidx = 0; cidx < 2; cidx++) { | ||
867 | int devtype = -1; | ||
868 | if (pcm->streams[cidx].substream == NULL) | ||
869 | continue; | ||
870 | switch (cidx) { | ||
871 | case SNDRV_PCM_STREAM_PLAYBACK: | ||
872 | sprintf(str, "pcmC%iD%ip", pcm->card->number, pcm->device); | ||
873 | minor = SNDRV_MINOR_PCM_PLAYBACK + idx; | ||
874 | devtype = SNDRV_DEVICE_TYPE_PCM_PLAYBACK; | ||
875 | break; | ||
876 | case SNDRV_PCM_STREAM_CAPTURE: | ||
877 | sprintf(str, "pcmC%iD%ic", pcm->card->number, pcm->device); | ||
878 | minor = SNDRV_MINOR_PCM_CAPTURE + idx; | ||
879 | devtype = SNDRV_DEVICE_TYPE_PCM_CAPTURE; | ||
880 | break; | ||
881 | } | ||
882 | if ((err = snd_register_device(devtype, pcm->card, pcm->device, pcm->streams[cidx].reg, str)) < 0) { | ||
883 | snd_pcm_devices[idx] = NULL; | ||
884 | up(®ister_mutex); | ||
885 | return err; | ||
886 | } | ||
887 | for (substream = pcm->streams[cidx].substream; substream; substream = substream->next) | ||
888 | snd_pcm_timer_init(substream); | ||
889 | } | ||
890 | list_for_each(list, &snd_pcm_notify_list) { | ||
891 | snd_pcm_notify_t *notify; | ||
892 | notify = list_entry(list, snd_pcm_notify_t, list); | ||
893 | notify->n_register(pcm); | ||
894 | } | ||
895 | up(®ister_mutex); | ||
896 | return 0; | ||
897 | } | ||
898 | |||
899 | static int snd_pcm_dev_disconnect(snd_device_t *device) | ||
900 | { | ||
901 | snd_pcm_t *pcm = device->device_data; | ||
902 | struct list_head *list; | ||
903 | snd_pcm_substream_t *substream; | ||
904 | int idx, cidx; | ||
905 | |||
906 | down(®ister_mutex); | ||
907 | idx = (pcm->card->number * SNDRV_PCM_DEVICES) + pcm->device; | ||
908 | snd_pcm_devices[idx] = NULL; | ||
909 | for (cidx = 0; cidx < 2; cidx++) | ||
910 | for (substream = pcm->streams[cidx].substream; substream; substream = substream->next) | ||
911 | if (substream->runtime) | ||
912 | substream->runtime->status->state = SNDRV_PCM_STATE_DISCONNECTED; | ||
913 | list_for_each(list, &snd_pcm_notify_list) { | ||
914 | snd_pcm_notify_t *notify; | ||
915 | notify = list_entry(list, snd_pcm_notify_t, list); | ||
916 | notify->n_disconnect(pcm); | ||
917 | } | ||
918 | up(®ister_mutex); | ||
919 | return 0; | ||
920 | } | ||
921 | |||
922 | static int snd_pcm_dev_unregister(snd_device_t *device) | ||
923 | { | ||
924 | int idx, cidx, devtype; | ||
925 | snd_pcm_substream_t *substream; | ||
926 | struct list_head *list; | ||
927 | snd_pcm_t *pcm = device->device_data; | ||
928 | |||
929 | snd_assert(pcm != NULL, return -ENXIO); | ||
930 | down(®ister_mutex); | ||
931 | idx = (pcm->card->number * SNDRV_PCM_DEVICES) + pcm->device; | ||
932 | snd_pcm_devices[idx] = NULL; | ||
933 | for (cidx = 0; cidx < 2; cidx++) { | ||
934 | devtype = -1; | ||
935 | switch (cidx) { | ||
936 | case SNDRV_PCM_STREAM_PLAYBACK: | ||
937 | devtype = SNDRV_DEVICE_TYPE_PCM_PLAYBACK; | ||
938 | break; | ||
939 | case SNDRV_PCM_STREAM_CAPTURE: | ||
940 | devtype = SNDRV_DEVICE_TYPE_PCM_CAPTURE; | ||
941 | break; | ||
942 | } | ||
943 | snd_unregister_device(devtype, pcm->card, pcm->device); | ||
944 | for (substream = pcm->streams[cidx].substream; substream; substream = substream->next) | ||
945 | snd_pcm_timer_done(substream); | ||
946 | } | ||
947 | list_for_each(list, &snd_pcm_notify_list) { | ||
948 | snd_pcm_notify_t *notify; | ||
949 | notify = list_entry(list, snd_pcm_notify_t, list); | ||
950 | notify->n_unregister(pcm); | ||
951 | } | ||
952 | up(®ister_mutex); | ||
953 | return snd_pcm_free(pcm); | ||
954 | } | ||
955 | |||
956 | int snd_pcm_notify(snd_pcm_notify_t *notify, int nfree) | ||
957 | { | ||
958 | int idx; | ||
959 | |||
960 | snd_assert(notify != NULL && notify->n_register != NULL && notify->n_unregister != NULL, return -EINVAL); | ||
961 | down(®ister_mutex); | ||
962 | if (nfree) { | ||
963 | list_del(¬ify->list); | ||
964 | for (idx = 0; idx < SNDRV_CARDS * SNDRV_PCM_DEVICES; idx++) { | ||
965 | if (snd_pcm_devices[idx] == NULL) | ||
966 | continue; | ||
967 | notify->n_unregister(snd_pcm_devices[idx]); | ||
968 | } | ||
969 | } else { | ||
970 | list_add_tail(¬ify->list, &snd_pcm_notify_list); | ||
971 | for (idx = 0; idx < SNDRV_CARDS * SNDRV_PCM_DEVICES; idx++) { | ||
972 | if (snd_pcm_devices[idx] == NULL) | ||
973 | continue; | ||
974 | notify->n_register(snd_pcm_devices[idx]); | ||
975 | } | ||
976 | } | ||
977 | up(®ister_mutex); | ||
978 | return 0; | ||
979 | } | ||
980 | |||
981 | /* | ||
982 | * Info interface | ||
983 | */ | ||
984 | |||
985 | static void snd_pcm_proc_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer) | ||
986 | { | ||
987 | int idx; | ||
988 | snd_pcm_t *pcm; | ||
989 | |||
990 | down(®ister_mutex); | ||
991 | for (idx = 0; idx < SNDRV_CARDS * SNDRV_PCM_DEVICES; idx++) { | ||
992 | pcm = snd_pcm_devices[idx]; | ||
993 | if (pcm == NULL) | ||
994 | continue; | ||
995 | snd_iprintf(buffer, "%02i-%02i: %s : %s", idx / SNDRV_PCM_DEVICES, | ||
996 | idx % SNDRV_PCM_DEVICES, pcm->id, pcm->name); | ||
997 | if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) | ||
998 | snd_iprintf(buffer, " : playback %i", pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream_count); | ||
999 | if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) | ||
1000 | snd_iprintf(buffer, " : capture %i", pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream_count); | ||
1001 | snd_iprintf(buffer, "\n"); | ||
1002 | } | ||
1003 | up(®ister_mutex); | ||
1004 | } | ||
1005 | |||
1006 | /* | ||
1007 | * ENTRY functions | ||
1008 | */ | ||
1009 | |||
1010 | static snd_info_entry_t *snd_pcm_proc_entry = NULL; | ||
1011 | |||
1012 | static int __init alsa_pcm_init(void) | ||
1013 | { | ||
1014 | snd_info_entry_t *entry; | ||
1015 | |||
1016 | snd_ctl_register_ioctl(snd_pcm_control_ioctl); | ||
1017 | snd_ctl_register_ioctl_compat(snd_pcm_control_ioctl); | ||
1018 | if ((entry = snd_info_create_module_entry(THIS_MODULE, "pcm", NULL)) != NULL) { | ||
1019 | snd_info_set_text_ops(entry, NULL, SNDRV_CARDS * SNDRV_PCM_DEVICES * 128, snd_pcm_proc_read); | ||
1020 | if (snd_info_register(entry) < 0) { | ||
1021 | snd_info_free_entry(entry); | ||
1022 | entry = NULL; | ||
1023 | } | ||
1024 | } | ||
1025 | snd_pcm_proc_entry = entry; | ||
1026 | return 0; | ||
1027 | } | ||
1028 | |||
1029 | static void __exit alsa_pcm_exit(void) | ||
1030 | { | ||
1031 | snd_ctl_unregister_ioctl(snd_pcm_control_ioctl); | ||
1032 | snd_ctl_unregister_ioctl_compat(snd_pcm_control_ioctl); | ||
1033 | if (snd_pcm_proc_entry) { | ||
1034 | snd_info_unregister(snd_pcm_proc_entry); | ||
1035 | snd_pcm_proc_entry = NULL; | ||
1036 | } | ||
1037 | } | ||
1038 | |||
1039 | module_init(alsa_pcm_init) | ||
1040 | module_exit(alsa_pcm_exit) | ||
1041 | |||
1042 | EXPORT_SYMBOL(snd_pcm_devices); | ||
1043 | EXPORT_SYMBOL(snd_pcm_new); | ||
1044 | EXPORT_SYMBOL(snd_pcm_new_stream); | ||
1045 | EXPORT_SYMBOL(snd_pcm_notify); | ||
1046 | EXPORT_SYMBOL(snd_pcm_open_substream); | ||
1047 | EXPORT_SYMBOL(snd_pcm_release_substream); | ||
1048 | EXPORT_SYMBOL(snd_pcm_format_name); | ||
1049 | /* pcm_native.c */ | ||
1050 | EXPORT_SYMBOL(snd_pcm_link_rwlock); | ||
1051 | EXPORT_SYMBOL(snd_pcm_start); | ||
1052 | #ifdef CONFIG_PM | ||
1053 | EXPORT_SYMBOL(snd_pcm_suspend); | ||
1054 | EXPORT_SYMBOL(snd_pcm_suspend_all); | ||
1055 | #endif | ||
1056 | EXPORT_SYMBOL(snd_pcm_kernel_playback_ioctl); | ||
1057 | EXPORT_SYMBOL(snd_pcm_kernel_capture_ioctl); | ||
1058 | EXPORT_SYMBOL(snd_pcm_kernel_ioctl); | ||
1059 | EXPORT_SYMBOL(snd_pcm_mmap_data); | ||
1060 | #if SNDRV_PCM_INFO_MMAP_IOMEM | ||
1061 | EXPORT_SYMBOL(snd_pcm_lib_mmap_iomem); | ||
1062 | #endif | ||
1063 | /* pcm_misc.c */ | ||
1064 | EXPORT_SYMBOL(snd_pcm_format_signed); | ||
1065 | EXPORT_SYMBOL(snd_pcm_format_unsigned); | ||
1066 | EXPORT_SYMBOL(snd_pcm_format_linear); | ||
1067 | EXPORT_SYMBOL(snd_pcm_format_little_endian); | ||
1068 | EXPORT_SYMBOL(snd_pcm_format_big_endian); | ||
1069 | EXPORT_SYMBOL(snd_pcm_format_width); | ||
1070 | EXPORT_SYMBOL(snd_pcm_format_physical_width); | ||
1071 | EXPORT_SYMBOL(snd_pcm_format_silence_64); | ||
1072 | EXPORT_SYMBOL(snd_pcm_format_set_silence); | ||
1073 | EXPORT_SYMBOL(snd_pcm_build_linear_format); | ||
1074 | EXPORT_SYMBOL(snd_pcm_limit_hw_rates); | ||
diff --git a/sound/core/pcm_compat.c b/sound/core/pcm_compat.c new file mode 100644 index 000000000000..3920bf0eebbf --- /dev/null +++ b/sound/core/pcm_compat.c | |||
@@ -0,0 +1,513 @@ | |||
1 | /* | ||
2 | * 32bit -> 64bit ioctl wrapper for PCM 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 pcm_native.c */ | ||
22 | |||
23 | #include <linux/compat.h> | ||
24 | |||
25 | static int snd_pcm_ioctl_delay_compat(snd_pcm_substream_t *substream, | ||
26 | s32 __user *src) | ||
27 | { | ||
28 | snd_pcm_sframes_t delay; | ||
29 | mm_segment_t fs; | ||
30 | int err; | ||
31 | |||
32 | fs = snd_enter_user(); | ||
33 | err = snd_pcm_delay(substream, &delay); | ||
34 | snd_leave_user(fs); | ||
35 | if (err < 0) | ||
36 | return err; | ||
37 | if (put_user(delay, src)) | ||
38 | return -EFAULT; | ||
39 | return err; | ||
40 | } | ||
41 | |||
42 | static int snd_pcm_ioctl_rewind_compat(snd_pcm_substream_t *substream, | ||
43 | u32 __user *src) | ||
44 | { | ||
45 | snd_pcm_uframes_t frames; | ||
46 | int err; | ||
47 | |||
48 | if (get_user(frames, src)) | ||
49 | return -EFAULT; | ||
50 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | ||
51 | err = snd_pcm_playback_rewind(substream, frames); | ||
52 | else | ||
53 | err = snd_pcm_capture_rewind(substream, frames); | ||
54 | if (put_user(err, src)) | ||
55 | return -EFAULT; | ||
56 | return err < 0 ? err : 0; | ||
57 | } | ||
58 | |||
59 | static int snd_pcm_ioctl_forward_compat(snd_pcm_substream_t *substream, | ||
60 | u32 __user *src) | ||
61 | { | ||
62 | snd_pcm_uframes_t frames; | ||
63 | int err; | ||
64 | |||
65 | if (get_user(frames, src)) | ||
66 | return -EFAULT; | ||
67 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | ||
68 | err = snd_pcm_playback_forward(substream, frames); | ||
69 | else | ||
70 | err = snd_pcm_capture_forward(substream, frames); | ||
71 | if (put_user(err, src)) | ||
72 | return -EFAULT; | ||
73 | return err < 0 ? err : 0; | ||
74 | } | ||
75 | |||
76 | struct sndrv_pcm_hw_params32 { | ||
77 | u32 flags; | ||
78 | struct sndrv_mask masks[SNDRV_PCM_HW_PARAM_LAST_MASK - SNDRV_PCM_HW_PARAM_FIRST_MASK + 1]; /* this must be identical */ | ||
79 | struct sndrv_mask mres[5]; /* reserved masks */ | ||
80 | struct sndrv_interval intervals[SNDRV_PCM_HW_PARAM_LAST_INTERVAL - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL + 1]; | ||
81 | struct sndrv_interval ires[9]; /* reserved intervals */ | ||
82 | u32 rmask; | ||
83 | u32 cmask; | ||
84 | u32 info; | ||
85 | u32 msbits; | ||
86 | u32 rate_num; | ||
87 | u32 rate_den; | ||
88 | u32 fifo_size; | ||
89 | unsigned char reserved[64]; | ||
90 | }; | ||
91 | |||
92 | struct sndrv_pcm_sw_params32 { | ||
93 | s32 tstamp_mode; | ||
94 | u32 period_step; | ||
95 | u32 sleep_min; | ||
96 | u32 avail_min; | ||
97 | u32 xfer_align; | ||
98 | u32 start_threshold; | ||
99 | u32 stop_threshold; | ||
100 | u32 silence_threshold; | ||
101 | u32 silence_size; | ||
102 | u32 boundary; | ||
103 | unsigned char reserved[64]; | ||
104 | }; | ||
105 | |||
106 | static int snd_pcm_ioctl_sw_params_compat(snd_pcm_substream_t *substream, | ||
107 | struct sndrv_pcm_sw_params32 __user *src) | ||
108 | { | ||
109 | snd_pcm_sw_params_t params; | ||
110 | int err; | ||
111 | |||
112 | memset(¶ms, 0, sizeof(params)); | ||
113 | if (get_user(params.tstamp_mode, &src->tstamp_mode) || | ||
114 | get_user(params.period_step, &src->period_step) || | ||
115 | get_user(params.sleep_min, &src->sleep_min) || | ||
116 | get_user(params.avail_min, &src->avail_min) || | ||
117 | get_user(params.xfer_align, &src->xfer_align) || | ||
118 | get_user(params.start_threshold, &src->start_threshold) || | ||
119 | get_user(params.stop_threshold, &src->stop_threshold) || | ||
120 | get_user(params.silence_threshold, &src->silence_threshold) || | ||
121 | get_user(params.silence_size, &src->silence_size)) | ||
122 | return -EFAULT; | ||
123 | err = snd_pcm_sw_params(substream, ¶ms); | ||
124 | if (err < 0) | ||
125 | return err; | ||
126 | if (put_user(params.boundary, &src->boundary)) | ||
127 | return -EFAULT; | ||
128 | return err; | ||
129 | } | ||
130 | |||
131 | struct sndrv_pcm_channel_info32 { | ||
132 | u32 channel; | ||
133 | u32 offset; | ||
134 | u32 first; | ||
135 | u32 step; | ||
136 | }; | ||
137 | |||
138 | static int snd_pcm_ioctl_channel_info_compat(snd_pcm_substream_t *substream, | ||
139 | struct sndrv_pcm_channel_info32 __user *src) | ||
140 | { | ||
141 | snd_pcm_channel_info_t info; | ||
142 | int err; | ||
143 | |||
144 | if (get_user(info.channel, &src->channel) || | ||
145 | get_user(info.offset, &src->offset) || | ||
146 | get_user(info.first, &src->first) || | ||
147 | get_user(info.step, &src->step)) | ||
148 | return -EFAULT; | ||
149 | err = snd_pcm_channel_info(substream, &info); | ||
150 | if (err < 0) | ||
151 | return err; | ||
152 | if (put_user(info.channel, &src->channel) || | ||
153 | put_user(info.offset, &src->offset) || | ||
154 | put_user(info.first, &src->first) || | ||
155 | put_user(info.step, &src->step)) | ||
156 | return -EFAULT; | ||
157 | return err; | ||
158 | } | ||
159 | |||
160 | struct sndrv_pcm_status32 { | ||
161 | s32 state; | ||
162 | struct compat_timespec trigger_tstamp; | ||
163 | struct compat_timespec tstamp; | ||
164 | u32 appl_ptr; | ||
165 | u32 hw_ptr; | ||
166 | s32 delay; | ||
167 | u32 avail; | ||
168 | u32 avail_max; | ||
169 | u32 overrange; | ||
170 | s32 suspended_state; | ||
171 | unsigned char reserved[60]; | ||
172 | } __attribute__((packed)); | ||
173 | |||
174 | |||
175 | static int snd_pcm_status_user_compat(snd_pcm_substream_t *substream, | ||
176 | struct sndrv_pcm_status32 __user *src) | ||
177 | { | ||
178 | snd_pcm_status_t status; | ||
179 | int err; | ||
180 | |||
181 | err = snd_pcm_status(substream, &status); | ||
182 | if (err < 0) | ||
183 | return err; | ||
184 | |||
185 | if (put_user(status.state, &src->state) || | ||
186 | put_user(status.trigger_tstamp.tv_sec, &src->trigger_tstamp.tv_sec) || | ||
187 | put_user(status.trigger_tstamp.tv_nsec, &src->trigger_tstamp.tv_nsec) || | ||
188 | put_user(status.tstamp.tv_sec, &src->tstamp.tv_sec) || | ||
189 | put_user(status.tstamp.tv_nsec, &src->tstamp.tv_nsec) || | ||
190 | put_user(status.appl_ptr, &src->appl_ptr) || | ||
191 | put_user(status.hw_ptr, &src->hw_ptr) || | ||
192 | put_user(status.delay, &src->delay) || | ||
193 | put_user(status.avail, &src->avail) || | ||
194 | put_user(status.avail_max, &src->avail_max) || | ||
195 | put_user(status.overrange, &src->overrange) || | ||
196 | put_user(status.suspended_state, &src->suspended_state)) | ||
197 | return -EFAULT; | ||
198 | |||
199 | return err; | ||
200 | } | ||
201 | |||
202 | /* recalcuate the boundary within 32bit */ | ||
203 | static void recalculate_boundary(snd_pcm_runtime_t *runtime) | ||
204 | { | ||
205 | if (! runtime->buffer_size) | ||
206 | return; | ||
207 | runtime->boundary = runtime->buffer_size; | ||
208 | while (runtime->boundary * 2 <= 0x7fffffffUL - runtime->buffer_size) | ||
209 | runtime->boundary *= 2; | ||
210 | } | ||
211 | |||
212 | /* both for HW_PARAMS and HW_REFINE */ | ||
213 | static int snd_pcm_ioctl_hw_params_compat(snd_pcm_substream_t *substream, | ||
214 | int refine, | ||
215 | struct sndrv_pcm_hw_params32 __user *data32) | ||
216 | { | ||
217 | struct sndrv_pcm_hw_params *data; | ||
218 | snd_pcm_runtime_t *runtime; | ||
219 | int err; | ||
220 | |||
221 | if (! (runtime = substream->runtime)) | ||
222 | return -ENOTTY; | ||
223 | |||
224 | data = kmalloc(sizeof(*data), GFP_KERNEL); | ||
225 | if (data == NULL) | ||
226 | return -ENOMEM; | ||
227 | /* only fifo_size is different, so just copy all */ | ||
228 | if (copy_from_user(data, data32, sizeof(*data32))) { | ||
229 | err = -EFAULT; | ||
230 | goto error; | ||
231 | } | ||
232 | if (refine) | ||
233 | err = snd_pcm_hw_refine(substream, data); | ||
234 | else | ||
235 | err = snd_pcm_hw_params(substream, data); | ||
236 | if (err < 0) | ||
237 | goto error; | ||
238 | if (copy_to_user(data32, data, sizeof(*data32)) || | ||
239 | put_user(data->fifo_size, &data32->fifo_size)) { | ||
240 | err = -EFAULT; | ||
241 | goto error; | ||
242 | } | ||
243 | |||
244 | if (! refine) | ||
245 | recalculate_boundary(runtime); | ||
246 | error: | ||
247 | kfree(data); | ||
248 | return err; | ||
249 | } | ||
250 | |||
251 | |||
252 | /* | ||
253 | */ | ||
254 | struct sndrv_xferi32 { | ||
255 | s32 result; | ||
256 | u32 buf; | ||
257 | u32 frames; | ||
258 | }; | ||
259 | |||
260 | static int snd_pcm_ioctl_xferi_compat(snd_pcm_substream_t *substream, | ||
261 | int dir, struct sndrv_xferi32 __user *data32) | ||
262 | { | ||
263 | compat_caddr_t buf; | ||
264 | u32 frames; | ||
265 | int err; | ||
266 | |||
267 | if (! substream->runtime) | ||
268 | return -ENOTTY; | ||
269 | if (substream->stream != dir) | ||
270 | return -EINVAL; | ||
271 | if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) | ||
272 | return -EBADFD; | ||
273 | |||
274 | if (get_user(buf, &data32->buf) || | ||
275 | get_user(frames, &data32->frames)) | ||
276 | return -EFAULT; | ||
277 | |||
278 | if (dir == SNDRV_PCM_STREAM_PLAYBACK) | ||
279 | err = snd_pcm_lib_write(substream, compat_ptr(buf), frames); | ||
280 | else | ||
281 | err = snd_pcm_lib_read(substream, compat_ptr(buf), frames); | ||
282 | if (err < 0) | ||
283 | return err; | ||
284 | /* copy the result */ | ||
285 | if (put_user(err, &data32->result)) | ||
286 | return -EFAULT; | ||
287 | return 0; | ||
288 | } | ||
289 | |||
290 | |||
291 | /* snd_xfern needs remapping of bufs */ | ||
292 | struct sndrv_xfern32 { | ||
293 | s32 result; | ||
294 | u32 bufs; /* this is void **; */ | ||
295 | u32 frames; | ||
296 | }; | ||
297 | |||
298 | /* | ||
299 | * xfern ioctl nees to copy (up to) 128 pointers on stack. | ||
300 | * although we may pass the copied pointers through f_op->ioctl, but the ioctl | ||
301 | * handler there expands again the same 128 pointers on stack, so it is better | ||
302 | * to handle the function (calling pcm_readv/writev) directly in this handler. | ||
303 | */ | ||
304 | static int snd_pcm_ioctl_xfern_compat(snd_pcm_substream_t *substream, | ||
305 | int dir, struct sndrv_xfern32 __user *data32) | ||
306 | { | ||
307 | compat_caddr_t buf; | ||
308 | compat_caddr_t __user *bufptr; | ||
309 | u32 frames; | ||
310 | void __user **bufs; | ||
311 | int err, ch, i; | ||
312 | |||
313 | if (! substream->runtime) | ||
314 | return -ENOTTY; | ||
315 | if (substream->stream != dir) | ||
316 | return -EINVAL; | ||
317 | |||
318 | if ((ch = substream->runtime->channels) > 128) | ||
319 | return -EINVAL; | ||
320 | if (get_user(buf, &data32->bufs) || | ||
321 | get_user(frames, &data32->frames)) | ||
322 | return -EFAULT; | ||
323 | bufptr = compat_ptr(buf); | ||
324 | bufs = kmalloc(sizeof(void __user *) * ch, GFP_KERNEL); | ||
325 | if (bufs == NULL) | ||
326 | return -ENOMEM; | ||
327 | for (i = 0; i < ch; i++) { | ||
328 | u32 ptr; | ||
329 | if (get_user(ptr, bufptr)) { | ||
330 | kfree(bufs); | ||
331 | return -EFAULT; | ||
332 | } | ||
333 | bufs[ch] = compat_ptr(ptr); | ||
334 | bufptr++; | ||
335 | } | ||
336 | if (dir == SNDRV_PCM_STREAM_PLAYBACK) | ||
337 | err = snd_pcm_lib_writev(substream, bufs, frames); | ||
338 | else | ||
339 | err = snd_pcm_lib_readv(substream, bufs, frames); | ||
340 | if (err >= 0) { | ||
341 | if (put_user(err, &data32->result)) | ||
342 | err = -EFAULT; | ||
343 | } | ||
344 | kfree(bufs); | ||
345 | return err; | ||
346 | } | ||
347 | |||
348 | |||
349 | struct sndrv_pcm_mmap_status32 { | ||
350 | s32 state; | ||
351 | s32 pad1; | ||
352 | u32 hw_ptr; | ||
353 | struct compat_timespec tstamp; | ||
354 | s32 suspended_state; | ||
355 | } __attribute__((packed)); | ||
356 | |||
357 | struct sndrv_pcm_mmap_control32 { | ||
358 | u32 appl_ptr; | ||
359 | u32 avail_min; | ||
360 | }; | ||
361 | |||
362 | struct sndrv_pcm_sync_ptr32 { | ||
363 | u32 flags; | ||
364 | union { | ||
365 | struct sndrv_pcm_mmap_status32 status; | ||
366 | unsigned char reserved[64]; | ||
367 | } s; | ||
368 | union { | ||
369 | struct sndrv_pcm_mmap_control32 control; | ||
370 | unsigned char reserved[64]; | ||
371 | } c; | ||
372 | } __attribute__((packed)); | ||
373 | |||
374 | static int snd_pcm_ioctl_sync_ptr_compat(snd_pcm_substream_t *substream, | ||
375 | struct sndrv_pcm_sync_ptr32 __user *src) | ||
376 | { | ||
377 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
378 | volatile struct sndrv_pcm_mmap_status *status; | ||
379 | volatile struct sndrv_pcm_mmap_control *control; | ||
380 | u32 sflags; | ||
381 | struct sndrv_pcm_mmap_control scontrol; | ||
382 | struct sndrv_pcm_mmap_status sstatus; | ||
383 | int err; | ||
384 | |||
385 | snd_assert(runtime, return -EINVAL); | ||
386 | |||
387 | if (get_user(sflags, &src->flags) || | ||
388 | get_user(scontrol.appl_ptr, &src->c.control.appl_ptr) || | ||
389 | get_user(scontrol.avail_min, &src->c.control.avail_min)) | ||
390 | return -EFAULT; | ||
391 | if (sflags & SNDRV_PCM_SYNC_PTR_HWSYNC) { | ||
392 | err = snd_pcm_hwsync(substream); | ||
393 | if (err < 0) | ||
394 | return err; | ||
395 | } | ||
396 | status = runtime->status; | ||
397 | control = runtime->control; | ||
398 | snd_pcm_stream_lock_irq(substream); | ||
399 | if (!(sflags & SNDRV_PCM_SYNC_PTR_APPL)) | ||
400 | control->appl_ptr = scontrol.appl_ptr; | ||
401 | else | ||
402 | scontrol.appl_ptr = control->appl_ptr; | ||
403 | if (!(sflags & SNDRV_PCM_SYNC_PTR_AVAIL_MIN)) | ||
404 | control->avail_min = scontrol.avail_min; | ||
405 | else | ||
406 | scontrol.avail_min = control->avail_min; | ||
407 | sstatus.state = status->state; | ||
408 | sstatus.hw_ptr = status->hw_ptr; | ||
409 | sstatus.tstamp = status->tstamp; | ||
410 | sstatus.suspended_state = status->suspended_state; | ||
411 | snd_pcm_stream_unlock_irq(substream); | ||
412 | if (put_user(sstatus.state, &src->s.status.state) || | ||
413 | put_user(sstatus.hw_ptr, &src->s.status.hw_ptr) || | ||
414 | put_user(sstatus.tstamp.tv_sec, &src->s.status.tstamp.tv_sec) || | ||
415 | put_user(sstatus.tstamp.tv_nsec, &src->s.status.tstamp.tv_nsec) || | ||
416 | put_user(sstatus.suspended_state, &src->s.status.suspended_state) || | ||
417 | put_user(scontrol.appl_ptr, &src->c.control.appl_ptr) || | ||
418 | put_user(scontrol.avail_min, &src->c.control.avail_min)) | ||
419 | return -EFAULT; | ||
420 | |||
421 | return 0; | ||
422 | } | ||
423 | |||
424 | |||
425 | /* | ||
426 | */ | ||
427 | enum { | ||
428 | SNDRV_PCM_IOCTL_HW_REFINE32 = _IOWR('A', 0x10, struct sndrv_pcm_hw_params32), | ||
429 | SNDRV_PCM_IOCTL_HW_PARAMS32 = _IOWR('A', 0x11, struct sndrv_pcm_hw_params32), | ||
430 | SNDRV_PCM_IOCTL_SW_PARAMS32 = _IOWR('A', 0x13, struct sndrv_pcm_sw_params32), | ||
431 | SNDRV_PCM_IOCTL_STATUS32 = _IOR('A', 0x20, struct sndrv_pcm_status32), | ||
432 | SNDRV_PCM_IOCTL_DELAY32 = _IOR('A', 0x21, s32), | ||
433 | SNDRV_PCM_IOCTL_CHANNEL_INFO32 = _IOR('A', 0x32, struct sndrv_pcm_channel_info32), | ||
434 | SNDRV_PCM_IOCTL_REWIND32 = _IOW('A', 0x46, u32), | ||
435 | SNDRV_PCM_IOCTL_FORWARD32 = _IOW('A', 0x49, u32), | ||
436 | SNDRV_PCM_IOCTL_WRITEI_FRAMES32 = _IOW('A', 0x50, struct sndrv_xferi32), | ||
437 | SNDRV_PCM_IOCTL_READI_FRAMES32 = _IOR('A', 0x51, struct sndrv_xferi32), | ||
438 | SNDRV_PCM_IOCTL_WRITEN_FRAMES32 = _IOW('A', 0x52, struct sndrv_xfern32), | ||
439 | SNDRV_PCM_IOCTL_READN_FRAMES32 = _IOR('A', 0x53, struct sndrv_xfern32), | ||
440 | SNDRV_PCM_IOCTL_SYNC_PTR32 = _IOWR('A', 0x23, struct sndrv_pcm_sync_ptr32), | ||
441 | |||
442 | }; | ||
443 | |||
444 | static long snd_pcm_ioctl_compat(struct file *file, unsigned int cmd, unsigned long arg) | ||
445 | { | ||
446 | snd_pcm_file_t *pcm_file; | ||
447 | snd_pcm_substream_t *substream; | ||
448 | void __user *argp = compat_ptr(arg); | ||
449 | |||
450 | pcm_file = file->private_data; | ||
451 | if (! pcm_file) | ||
452 | return -ENOTTY; | ||
453 | substream = pcm_file->substream; | ||
454 | if (! substream) | ||
455 | return -ENOTTY; | ||
456 | |||
457 | /* | ||
458 | * When PCM is used on 32bit mode, we need to disable | ||
459 | * mmap of PCM status/control records because of the size | ||
460 | * incompatibility. | ||
461 | */ | ||
462 | substream->no_mmap_ctrl = 1; | ||
463 | |||
464 | switch (cmd) { | ||
465 | case SNDRV_PCM_IOCTL_PVERSION: | ||
466 | case SNDRV_PCM_IOCTL_INFO: | ||
467 | case SNDRV_PCM_IOCTL_TSTAMP: | ||
468 | case SNDRV_PCM_IOCTL_HWSYNC: | ||
469 | case SNDRV_PCM_IOCTL_PREPARE: | ||
470 | case SNDRV_PCM_IOCTL_RESET: | ||
471 | case SNDRV_PCM_IOCTL_START: | ||
472 | case SNDRV_PCM_IOCTL_DROP: | ||
473 | case SNDRV_PCM_IOCTL_DRAIN: | ||
474 | case SNDRV_PCM_IOCTL_PAUSE: | ||
475 | case SNDRV_PCM_IOCTL_HW_FREE: | ||
476 | case SNDRV_PCM_IOCTL_RESUME: | ||
477 | case SNDRV_PCM_IOCTL_XRUN: | ||
478 | case SNDRV_PCM_IOCTL_LINK: | ||
479 | case SNDRV_PCM_IOCTL_UNLINK: | ||
480 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | ||
481 | return snd_pcm_playback_ioctl1(substream, cmd, argp); | ||
482 | else | ||
483 | return snd_pcm_capture_ioctl1(substream, cmd, argp); | ||
484 | case SNDRV_PCM_IOCTL_HW_REFINE32: | ||
485 | return snd_pcm_ioctl_hw_params_compat(substream, 1, argp); | ||
486 | case SNDRV_PCM_IOCTL_HW_PARAMS32: | ||
487 | return snd_pcm_ioctl_hw_params_compat(substream, 0, argp); | ||
488 | case SNDRV_PCM_IOCTL_SW_PARAMS32: | ||
489 | return snd_pcm_ioctl_sw_params_compat(substream, argp); | ||
490 | case SNDRV_PCM_IOCTL_STATUS32: | ||
491 | return snd_pcm_status_user_compat(substream, argp); | ||
492 | case SNDRV_PCM_IOCTL_SYNC_PTR32: | ||
493 | return snd_pcm_ioctl_sync_ptr_compat(substream, argp); | ||
494 | case SNDRV_PCM_IOCTL_CHANNEL_INFO32: | ||
495 | return snd_pcm_ioctl_channel_info_compat(substream, argp); | ||
496 | case SNDRV_PCM_IOCTL_WRITEI_FRAMES32: | ||
497 | return snd_pcm_ioctl_xferi_compat(substream, SNDRV_PCM_STREAM_PLAYBACK, argp); | ||
498 | case SNDRV_PCM_IOCTL_READI_FRAMES32: | ||
499 | return snd_pcm_ioctl_xferi_compat(substream, SNDRV_PCM_STREAM_CAPTURE, argp); | ||
500 | case SNDRV_PCM_IOCTL_WRITEN_FRAMES32: | ||
501 | return snd_pcm_ioctl_xfern_compat(substream, SNDRV_PCM_STREAM_PLAYBACK, argp); | ||
502 | case SNDRV_PCM_IOCTL_READN_FRAMES32: | ||
503 | return snd_pcm_ioctl_xfern_compat(substream, SNDRV_PCM_STREAM_CAPTURE, argp); | ||
504 | case SNDRV_PCM_IOCTL_DELAY32: | ||
505 | return snd_pcm_ioctl_delay_compat(substream, argp); | ||
506 | case SNDRV_PCM_IOCTL_REWIND32: | ||
507 | return snd_pcm_ioctl_rewind_compat(substream, argp); | ||
508 | case SNDRV_PCM_IOCTL_FORWARD32: | ||
509 | return snd_pcm_ioctl_forward_compat(substream, argp); | ||
510 | } | ||
511 | |||
512 | return -ENOIOCTLCMD; | ||
513 | } | ||
diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c new file mode 100644 index 000000000000..151fd99ca2c9 --- /dev/null +++ b/sound/core/pcm_lib.c | |||
@@ -0,0 +1,2612 @@ | |||
1 | /* | ||
2 | * Digital Audio (PCM) abstract layer | ||
3 | * Copyright (c) by Jaroslav Kysela <perex@suse.cz> | ||
4 | * Abramo Bagnara <abramo@alsa-project.org> | ||
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 <linux/slab.h> | ||
25 | #include <linux/time.h> | ||
26 | #include <sound/core.h> | ||
27 | #include <sound/control.h> | ||
28 | #include <sound/info.h> | ||
29 | #include <sound/pcm.h> | ||
30 | #include <sound/pcm_params.h> | ||
31 | #include <sound/timer.h> | ||
32 | |||
33 | /* | ||
34 | * fill ring buffer with silence | ||
35 | * runtime->silence_start: starting pointer to silence area | ||
36 | * runtime->silence_filled: size filled with silence | ||
37 | * runtime->silence_threshold: threshold from application | ||
38 | * runtime->silence_size: maximal size from application | ||
39 | * | ||
40 | * when runtime->silence_size >= runtime->boundary - fill processed area with silence immediately | ||
41 | */ | ||
42 | void snd_pcm_playback_silence(snd_pcm_substream_t *substream, snd_pcm_uframes_t new_hw_ptr) | ||
43 | { | ||
44 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
45 | snd_pcm_uframes_t frames, ofs, transfer; | ||
46 | |||
47 | if (runtime->silence_size < runtime->boundary) { | ||
48 | snd_pcm_sframes_t noise_dist, n; | ||
49 | if (runtime->silence_start != runtime->control->appl_ptr) { | ||
50 | n = runtime->control->appl_ptr - runtime->silence_start; | ||
51 | if (n < 0) | ||
52 | n += runtime->boundary; | ||
53 | if ((snd_pcm_uframes_t)n < runtime->silence_filled) | ||
54 | runtime->silence_filled -= n; | ||
55 | else | ||
56 | runtime->silence_filled = 0; | ||
57 | runtime->silence_start = runtime->control->appl_ptr; | ||
58 | } | ||
59 | if (runtime->silence_filled == runtime->buffer_size) | ||
60 | return; | ||
61 | snd_assert(runtime->silence_filled <= runtime->buffer_size, return); | ||
62 | noise_dist = snd_pcm_playback_hw_avail(runtime) + runtime->silence_filled; | ||
63 | if (noise_dist >= (snd_pcm_sframes_t) runtime->silence_threshold) | ||
64 | return; | ||
65 | frames = runtime->silence_threshold - noise_dist; | ||
66 | if (frames > runtime->silence_size) | ||
67 | frames = runtime->silence_size; | ||
68 | } else { | ||
69 | if (new_hw_ptr == ULONG_MAX) { /* initialization */ | ||
70 | snd_pcm_sframes_t avail = snd_pcm_playback_hw_avail(runtime); | ||
71 | runtime->silence_filled = avail > 0 ? avail : 0; | ||
72 | runtime->silence_start = (runtime->status->hw_ptr + | ||
73 | runtime->silence_filled) % | ||
74 | runtime->boundary; | ||
75 | } else { | ||
76 | ofs = runtime->status->hw_ptr; | ||
77 | frames = new_hw_ptr - ofs; | ||
78 | if ((snd_pcm_sframes_t)frames < 0) | ||
79 | frames += runtime->boundary; | ||
80 | runtime->silence_filled -= frames; | ||
81 | if ((snd_pcm_sframes_t)runtime->silence_filled < 0) { | ||
82 | runtime->silence_filled = 0; | ||
83 | runtime->silence_start = (ofs + frames) - runtime->buffer_size; | ||
84 | } else { | ||
85 | runtime->silence_start = ofs - runtime->silence_filled; | ||
86 | } | ||
87 | if ((snd_pcm_sframes_t)runtime->silence_start < 0) | ||
88 | runtime->silence_start += runtime->boundary; | ||
89 | } | ||
90 | frames = runtime->buffer_size - runtime->silence_filled; | ||
91 | } | ||
92 | snd_assert(frames <= runtime->buffer_size, return); | ||
93 | if (frames == 0) | ||
94 | return; | ||
95 | ofs = (runtime->silence_start + runtime->silence_filled) % runtime->buffer_size; | ||
96 | while (frames > 0) { | ||
97 | transfer = ofs + frames > runtime->buffer_size ? runtime->buffer_size - ofs : frames; | ||
98 | if (runtime->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED || | ||
99 | runtime->access == SNDRV_PCM_ACCESS_MMAP_INTERLEAVED) { | ||
100 | if (substream->ops->silence) { | ||
101 | int err; | ||
102 | err = substream->ops->silence(substream, -1, ofs, transfer); | ||
103 | snd_assert(err >= 0, ); | ||
104 | } else { | ||
105 | char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, ofs); | ||
106 | snd_pcm_format_set_silence(runtime->format, hwbuf, transfer * runtime->channels); | ||
107 | } | ||
108 | } else { | ||
109 | unsigned int c; | ||
110 | unsigned int channels = runtime->channels; | ||
111 | if (substream->ops->silence) { | ||
112 | for (c = 0; c < channels; ++c) { | ||
113 | int err; | ||
114 | err = substream->ops->silence(substream, c, ofs, transfer); | ||
115 | snd_assert(err >= 0, ); | ||
116 | } | ||
117 | } else { | ||
118 | size_t dma_csize = runtime->dma_bytes / channels; | ||
119 | for (c = 0; c < channels; ++c) { | ||
120 | char *hwbuf = runtime->dma_area + (c * dma_csize) + samples_to_bytes(runtime, ofs); | ||
121 | snd_pcm_format_set_silence(runtime->format, hwbuf, transfer); | ||
122 | } | ||
123 | } | ||
124 | } | ||
125 | runtime->silence_filled += transfer; | ||
126 | frames -= transfer; | ||
127 | ofs = 0; | ||
128 | } | ||
129 | } | ||
130 | |||
131 | static void xrun(snd_pcm_substream_t *substream) | ||
132 | { | ||
133 | snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN); | ||
134 | #ifdef CONFIG_SND_DEBUG | ||
135 | if (substream->pstr->xrun_debug) { | ||
136 | snd_printd(KERN_DEBUG "XRUN: pcmC%dD%d%c\n", | ||
137 | substream->pcm->card->number, | ||
138 | substream->pcm->device, | ||
139 | substream->stream ? 'c' : 'p'); | ||
140 | if (substream->pstr->xrun_debug > 1) | ||
141 | dump_stack(); | ||
142 | } | ||
143 | #endif | ||
144 | } | ||
145 | |||
146 | static inline snd_pcm_uframes_t snd_pcm_update_hw_ptr_pos(snd_pcm_substream_t *substream, | ||
147 | snd_pcm_runtime_t *runtime) | ||
148 | { | ||
149 | snd_pcm_uframes_t pos; | ||
150 | |||
151 | pos = substream->ops->pointer(substream); | ||
152 | if (pos == SNDRV_PCM_POS_XRUN) | ||
153 | return pos; /* XRUN */ | ||
154 | if (runtime->tstamp_mode & SNDRV_PCM_TSTAMP_MMAP) | ||
155 | snd_timestamp_now((snd_timestamp_t*)&runtime->status->tstamp, runtime->tstamp_timespec); | ||
156 | #ifdef CONFIG_SND_DEBUG | ||
157 | if (pos >= runtime->buffer_size) { | ||
158 | snd_printk(KERN_ERR "BUG: stream = %i, pos = 0x%lx, buffer size = 0x%lx, period size = 0x%lx\n", substream->stream, pos, runtime->buffer_size, runtime->period_size); | ||
159 | } else | ||
160 | #endif | ||
161 | snd_runtime_check(pos < runtime->buffer_size, return 0); | ||
162 | pos -= pos % runtime->min_align; | ||
163 | return pos; | ||
164 | } | ||
165 | |||
166 | static inline int snd_pcm_update_hw_ptr_post(snd_pcm_substream_t *substream, | ||
167 | snd_pcm_runtime_t *runtime) | ||
168 | { | ||
169 | snd_pcm_uframes_t avail; | ||
170 | |||
171 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | ||
172 | avail = snd_pcm_playback_avail(runtime); | ||
173 | else | ||
174 | avail = snd_pcm_capture_avail(runtime); | ||
175 | if (avail > runtime->avail_max) | ||
176 | runtime->avail_max = avail; | ||
177 | if (avail >= runtime->stop_threshold) { | ||
178 | if (substream->runtime->status->state == SNDRV_PCM_STATE_DRAINING) | ||
179 | snd_pcm_drain_done(substream); | ||
180 | else | ||
181 | xrun(substream); | ||
182 | return -EPIPE; | ||
183 | } | ||
184 | if (avail >= runtime->control->avail_min) | ||
185 | wake_up(&runtime->sleep); | ||
186 | return 0; | ||
187 | } | ||
188 | |||
189 | static inline int snd_pcm_update_hw_ptr_interrupt(snd_pcm_substream_t *substream) | ||
190 | { | ||
191 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
192 | snd_pcm_uframes_t pos; | ||
193 | snd_pcm_uframes_t new_hw_ptr, hw_ptr_interrupt; | ||
194 | snd_pcm_sframes_t delta; | ||
195 | |||
196 | pos = snd_pcm_update_hw_ptr_pos(substream, runtime); | ||
197 | if (pos == SNDRV_PCM_POS_XRUN) { | ||
198 | xrun(substream); | ||
199 | return -EPIPE; | ||
200 | } | ||
201 | if (runtime->period_size == runtime->buffer_size) | ||
202 | goto __next_buf; | ||
203 | new_hw_ptr = runtime->hw_ptr_base + pos; | ||
204 | hw_ptr_interrupt = runtime->hw_ptr_interrupt + runtime->period_size; | ||
205 | |||
206 | delta = hw_ptr_interrupt - new_hw_ptr; | ||
207 | if (delta > 0) { | ||
208 | if ((snd_pcm_uframes_t)delta < runtime->buffer_size / 2) { | ||
209 | #ifdef CONFIG_SND_DEBUG | ||
210 | if (runtime->periods > 1 && substream->pstr->xrun_debug) { | ||
211 | snd_printd(KERN_ERR "Unexpected hw_pointer value [1] (stream = %i, delta: -%ld, max jitter = %ld): wrong interrupt acknowledge?\n", substream->stream, (long) delta, runtime->buffer_size / 2); | ||
212 | if (substream->pstr->xrun_debug > 1) | ||
213 | dump_stack(); | ||
214 | } | ||
215 | #endif | ||
216 | return 0; | ||
217 | } | ||
218 | __next_buf: | ||
219 | runtime->hw_ptr_base += runtime->buffer_size; | ||
220 | if (runtime->hw_ptr_base == runtime->boundary) | ||
221 | runtime->hw_ptr_base = 0; | ||
222 | new_hw_ptr = runtime->hw_ptr_base + pos; | ||
223 | } | ||
224 | |||
225 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && | ||
226 | runtime->silence_size > 0) | ||
227 | snd_pcm_playback_silence(substream, new_hw_ptr); | ||
228 | |||
229 | runtime->status->hw_ptr = new_hw_ptr; | ||
230 | runtime->hw_ptr_interrupt = new_hw_ptr - new_hw_ptr % runtime->period_size; | ||
231 | |||
232 | return snd_pcm_update_hw_ptr_post(substream, runtime); | ||
233 | } | ||
234 | |||
235 | /* CAUTION: call it with irq disabled */ | ||
236 | int snd_pcm_update_hw_ptr(snd_pcm_substream_t *substream) | ||
237 | { | ||
238 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
239 | snd_pcm_uframes_t pos; | ||
240 | snd_pcm_uframes_t old_hw_ptr, new_hw_ptr; | ||
241 | snd_pcm_sframes_t delta; | ||
242 | |||
243 | old_hw_ptr = runtime->status->hw_ptr; | ||
244 | pos = snd_pcm_update_hw_ptr_pos(substream, runtime); | ||
245 | if (pos == SNDRV_PCM_POS_XRUN) { | ||
246 | xrun(substream); | ||
247 | return -EPIPE; | ||
248 | } | ||
249 | new_hw_ptr = runtime->hw_ptr_base + pos; | ||
250 | |||
251 | delta = old_hw_ptr - new_hw_ptr; | ||
252 | if (delta > 0) { | ||
253 | if ((snd_pcm_uframes_t)delta < runtime->buffer_size / 2) { | ||
254 | #ifdef CONFIG_SND_DEBUG | ||
255 | if (runtime->periods > 2 && substream->pstr->xrun_debug) { | ||
256 | snd_printd(KERN_ERR "Unexpected hw_pointer value [2] (stream = %i, delta: -%ld, max jitter = %ld): wrong interrupt acknowledge?\n", substream->stream, (long) delta, runtime->buffer_size / 2); | ||
257 | if (substream->pstr->xrun_debug > 1) | ||
258 | dump_stack(); | ||
259 | } | ||
260 | #endif | ||
261 | return 0; | ||
262 | } | ||
263 | runtime->hw_ptr_base += runtime->buffer_size; | ||
264 | if (runtime->hw_ptr_base == runtime->boundary) | ||
265 | runtime->hw_ptr_base = 0; | ||
266 | new_hw_ptr = runtime->hw_ptr_base + pos; | ||
267 | } | ||
268 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && | ||
269 | runtime->silence_size > 0) | ||
270 | snd_pcm_playback_silence(substream, new_hw_ptr); | ||
271 | |||
272 | runtime->status->hw_ptr = new_hw_ptr; | ||
273 | |||
274 | return snd_pcm_update_hw_ptr_post(substream, runtime); | ||
275 | } | ||
276 | |||
277 | /** | ||
278 | * snd_pcm_set_ops - set the PCM operators | ||
279 | * @pcm: the pcm instance | ||
280 | * @direction: stream direction, SNDRV_PCM_STREAM_XXX | ||
281 | * @ops: the operator table | ||
282 | * | ||
283 | * Sets the given PCM operators to the pcm instance. | ||
284 | */ | ||
285 | void snd_pcm_set_ops(snd_pcm_t *pcm, int direction, snd_pcm_ops_t *ops) | ||
286 | { | ||
287 | snd_pcm_str_t *stream = &pcm->streams[direction]; | ||
288 | snd_pcm_substream_t *substream; | ||
289 | |||
290 | for (substream = stream->substream; substream != NULL; substream = substream->next) | ||
291 | substream->ops = ops; | ||
292 | } | ||
293 | |||
294 | |||
295 | /** | ||
296 | * snd_pcm_sync - set the PCM sync id | ||
297 | * @substream: the pcm substream | ||
298 | * | ||
299 | * Sets the PCM sync identifier for the card. | ||
300 | */ | ||
301 | void snd_pcm_set_sync(snd_pcm_substream_t * substream) | ||
302 | { | ||
303 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
304 | |||
305 | runtime->sync.id32[0] = substream->pcm->card->number; | ||
306 | runtime->sync.id32[1] = -1; | ||
307 | runtime->sync.id32[2] = -1; | ||
308 | runtime->sync.id32[3] = -1; | ||
309 | } | ||
310 | |||
311 | /* | ||
312 | * Standard ioctl routine | ||
313 | */ | ||
314 | |||
315 | /* Code taken from alsa-lib */ | ||
316 | #define assert(a) snd_assert((a), return -EINVAL) | ||
317 | |||
318 | static inline unsigned int div32(unsigned int a, unsigned int b, | ||
319 | unsigned int *r) | ||
320 | { | ||
321 | if (b == 0) { | ||
322 | *r = 0; | ||
323 | return UINT_MAX; | ||
324 | } | ||
325 | *r = a % b; | ||
326 | return a / b; | ||
327 | } | ||
328 | |||
329 | static inline unsigned int div_down(unsigned int a, unsigned int b) | ||
330 | { | ||
331 | if (b == 0) | ||
332 | return UINT_MAX; | ||
333 | return a / b; | ||
334 | } | ||
335 | |||
336 | static inline unsigned int div_up(unsigned int a, unsigned int b) | ||
337 | { | ||
338 | unsigned int r; | ||
339 | unsigned int q; | ||
340 | if (b == 0) | ||
341 | return UINT_MAX; | ||
342 | q = div32(a, b, &r); | ||
343 | if (r) | ||
344 | ++q; | ||
345 | return q; | ||
346 | } | ||
347 | |||
348 | static inline unsigned int mul(unsigned int a, unsigned int b) | ||
349 | { | ||
350 | if (a == 0) | ||
351 | return 0; | ||
352 | if (div_down(UINT_MAX, a) < b) | ||
353 | return UINT_MAX; | ||
354 | return a * b; | ||
355 | } | ||
356 | |||
357 | static inline unsigned int muldiv32(unsigned int a, unsigned int b, | ||
358 | unsigned int c, unsigned int *r) | ||
359 | { | ||
360 | u_int64_t n = (u_int64_t) a * b; | ||
361 | if (c == 0) { | ||
362 | snd_assert(n > 0, ); | ||
363 | *r = 0; | ||
364 | return UINT_MAX; | ||
365 | } | ||
366 | div64_32(&n, c, r); | ||
367 | if (n >= UINT_MAX) { | ||
368 | *r = 0; | ||
369 | return UINT_MAX; | ||
370 | } | ||
371 | return n; | ||
372 | } | ||
373 | |||
374 | static int snd_interval_refine_min(snd_interval_t *i, unsigned int min, int openmin) | ||
375 | { | ||
376 | int changed = 0; | ||
377 | assert(!snd_interval_empty(i)); | ||
378 | if (i->min < min) { | ||
379 | i->min = min; | ||
380 | i->openmin = openmin; | ||
381 | changed = 1; | ||
382 | } else if (i->min == min && !i->openmin && openmin) { | ||
383 | i->openmin = 1; | ||
384 | changed = 1; | ||
385 | } | ||
386 | if (i->integer) { | ||
387 | if (i->openmin) { | ||
388 | i->min++; | ||
389 | i->openmin = 0; | ||
390 | } | ||
391 | } | ||
392 | if (snd_interval_checkempty(i)) { | ||
393 | snd_interval_none(i); | ||
394 | return -EINVAL; | ||
395 | } | ||
396 | return changed; | ||
397 | } | ||
398 | |||
399 | static int snd_interval_refine_max(snd_interval_t *i, unsigned int max, int openmax) | ||
400 | { | ||
401 | int changed = 0; | ||
402 | assert(!snd_interval_empty(i)); | ||
403 | if (i->max > max) { | ||
404 | i->max = max; | ||
405 | i->openmax = openmax; | ||
406 | changed = 1; | ||
407 | } else if (i->max == max && !i->openmax && openmax) { | ||
408 | i->openmax = 1; | ||
409 | changed = 1; | ||
410 | } | ||
411 | if (i->integer) { | ||
412 | if (i->openmax) { | ||
413 | i->max--; | ||
414 | i->openmax = 0; | ||
415 | } | ||
416 | } | ||
417 | if (snd_interval_checkempty(i)) { | ||
418 | snd_interval_none(i); | ||
419 | return -EINVAL; | ||
420 | } | ||
421 | return changed; | ||
422 | } | ||
423 | |||
424 | /** | ||
425 | * snd_interval_refine - refine the interval value of configurator | ||
426 | * @i: the interval value to refine | ||
427 | * @v: the interval value to refer to | ||
428 | * | ||
429 | * Refines the interval value with the reference value. | ||
430 | * The interval is changed to the range satisfying both intervals. | ||
431 | * The interval status (min, max, integer, etc.) are evaluated. | ||
432 | * | ||
433 | * Returns non-zero if the value is changed, zero if not changed. | ||
434 | */ | ||
435 | int snd_interval_refine(snd_interval_t *i, const snd_interval_t *v) | ||
436 | { | ||
437 | int changed = 0; | ||
438 | assert(!snd_interval_empty(i)); | ||
439 | if (i->min < v->min) { | ||
440 | i->min = v->min; | ||
441 | i->openmin = v->openmin; | ||
442 | changed = 1; | ||
443 | } else if (i->min == v->min && !i->openmin && v->openmin) { | ||
444 | i->openmin = 1; | ||
445 | changed = 1; | ||
446 | } | ||
447 | if (i->max > v->max) { | ||
448 | i->max = v->max; | ||
449 | i->openmax = v->openmax; | ||
450 | changed = 1; | ||
451 | } else if (i->max == v->max && !i->openmax && v->openmax) { | ||
452 | i->openmax = 1; | ||
453 | changed = 1; | ||
454 | } | ||
455 | if (!i->integer && v->integer) { | ||
456 | i->integer = 1; | ||
457 | changed = 1; | ||
458 | } | ||
459 | if (i->integer) { | ||
460 | if (i->openmin) { | ||
461 | i->min++; | ||
462 | i->openmin = 0; | ||
463 | } | ||
464 | if (i->openmax) { | ||
465 | i->max--; | ||
466 | i->openmax = 0; | ||
467 | } | ||
468 | } else if (!i->openmin && !i->openmax && i->min == i->max) | ||
469 | i->integer = 1; | ||
470 | if (snd_interval_checkempty(i)) { | ||
471 | snd_interval_none(i); | ||
472 | return -EINVAL; | ||
473 | } | ||
474 | return changed; | ||
475 | } | ||
476 | |||
477 | static int snd_interval_refine_first(snd_interval_t *i) | ||
478 | { | ||
479 | assert(!snd_interval_empty(i)); | ||
480 | if (snd_interval_single(i)) | ||
481 | return 0; | ||
482 | i->max = i->min; | ||
483 | i->openmax = i->openmin; | ||
484 | if (i->openmax) | ||
485 | i->max++; | ||
486 | return 1; | ||
487 | } | ||
488 | |||
489 | static int snd_interval_refine_last(snd_interval_t *i) | ||
490 | { | ||
491 | assert(!snd_interval_empty(i)); | ||
492 | if (snd_interval_single(i)) | ||
493 | return 0; | ||
494 | i->min = i->max; | ||
495 | i->openmin = i->openmax; | ||
496 | if (i->openmin) | ||
497 | i->min--; | ||
498 | return 1; | ||
499 | } | ||
500 | |||
501 | static int snd_interval_refine_set(snd_interval_t *i, unsigned int val) | ||
502 | { | ||
503 | snd_interval_t t; | ||
504 | t.empty = 0; | ||
505 | t.min = t.max = val; | ||
506 | t.openmin = t.openmax = 0; | ||
507 | t.integer = 1; | ||
508 | return snd_interval_refine(i, &t); | ||
509 | } | ||
510 | |||
511 | void snd_interval_mul(const snd_interval_t *a, const snd_interval_t *b, snd_interval_t *c) | ||
512 | { | ||
513 | if (a->empty || b->empty) { | ||
514 | snd_interval_none(c); | ||
515 | return; | ||
516 | } | ||
517 | c->empty = 0; | ||
518 | c->min = mul(a->min, b->min); | ||
519 | c->openmin = (a->openmin || b->openmin); | ||
520 | c->max = mul(a->max, b->max); | ||
521 | c->openmax = (a->openmax || b->openmax); | ||
522 | c->integer = (a->integer && b->integer); | ||
523 | } | ||
524 | |||
525 | /** | ||
526 | * snd_interval_div - refine the interval value with division | ||
527 | * | ||
528 | * c = a / b | ||
529 | * | ||
530 | * Returns non-zero if the value is changed, zero if not changed. | ||
531 | */ | ||
532 | void snd_interval_div(const snd_interval_t *a, const snd_interval_t *b, snd_interval_t *c) | ||
533 | { | ||
534 | unsigned int r; | ||
535 | if (a->empty || b->empty) { | ||
536 | snd_interval_none(c); | ||
537 | return; | ||
538 | } | ||
539 | c->empty = 0; | ||
540 | c->min = div32(a->min, b->max, &r); | ||
541 | c->openmin = (r || a->openmin || b->openmax); | ||
542 | if (b->min > 0) { | ||
543 | c->max = div32(a->max, b->min, &r); | ||
544 | if (r) { | ||
545 | c->max++; | ||
546 | c->openmax = 1; | ||
547 | } else | ||
548 | c->openmax = (a->openmax || b->openmin); | ||
549 | } else { | ||
550 | c->max = UINT_MAX; | ||
551 | c->openmax = 0; | ||
552 | } | ||
553 | c->integer = 0; | ||
554 | } | ||
555 | |||
556 | /** | ||
557 | * snd_interval_muldivk - refine the interval value | ||
558 | * | ||
559 | * c = a * b / k | ||
560 | * | ||
561 | * Returns non-zero if the value is changed, zero if not changed. | ||
562 | */ | ||
563 | void snd_interval_muldivk(const snd_interval_t *a, const snd_interval_t *b, | ||
564 | unsigned int k, snd_interval_t *c) | ||
565 | { | ||
566 | unsigned int r; | ||
567 | if (a->empty || b->empty) { | ||
568 | snd_interval_none(c); | ||
569 | return; | ||
570 | } | ||
571 | c->empty = 0; | ||
572 | c->min = muldiv32(a->min, b->min, k, &r); | ||
573 | c->openmin = (r || a->openmin || b->openmin); | ||
574 | c->max = muldiv32(a->max, b->max, k, &r); | ||
575 | if (r) { | ||
576 | c->max++; | ||
577 | c->openmax = 1; | ||
578 | } else | ||
579 | c->openmax = (a->openmax || b->openmax); | ||
580 | c->integer = 0; | ||
581 | } | ||
582 | |||
583 | /** | ||
584 | * snd_interval_mulkdiv - refine the interval value | ||
585 | * | ||
586 | * c = a * k / b | ||
587 | * | ||
588 | * Returns non-zero if the value is changed, zero if not changed. | ||
589 | */ | ||
590 | void snd_interval_mulkdiv(const snd_interval_t *a, unsigned int k, | ||
591 | const snd_interval_t *b, snd_interval_t *c) | ||
592 | { | ||
593 | unsigned int r; | ||
594 | if (a->empty || b->empty) { | ||
595 | snd_interval_none(c); | ||
596 | return; | ||
597 | } | ||
598 | c->empty = 0; | ||
599 | c->min = muldiv32(a->min, k, b->max, &r); | ||
600 | c->openmin = (r || a->openmin || b->openmax); | ||
601 | if (b->min > 0) { | ||
602 | c->max = muldiv32(a->max, k, b->min, &r); | ||
603 | if (r) { | ||
604 | c->max++; | ||
605 | c->openmax = 1; | ||
606 | } else | ||
607 | c->openmax = (a->openmax || b->openmin); | ||
608 | } else { | ||
609 | c->max = UINT_MAX; | ||
610 | c->openmax = 0; | ||
611 | } | ||
612 | c->integer = 0; | ||
613 | } | ||
614 | |||
615 | #undef assert | ||
616 | /* ---- */ | ||
617 | |||
618 | |||
619 | /** | ||
620 | * snd_interval_ratnum - refine the interval value | ||
621 | * | ||
622 | * Returns non-zero if the value is changed, zero if not changed. | ||
623 | */ | ||
624 | int snd_interval_ratnum(snd_interval_t *i, | ||
625 | unsigned int rats_count, ratnum_t *rats, | ||
626 | unsigned int *nump, unsigned int *denp) | ||
627 | { | ||
628 | unsigned int best_num, best_diff, best_den; | ||
629 | unsigned int k; | ||
630 | snd_interval_t t; | ||
631 | int err; | ||
632 | |||
633 | best_num = best_den = best_diff = 0; | ||
634 | for (k = 0; k < rats_count; ++k) { | ||
635 | unsigned int num = rats[k].num; | ||
636 | unsigned int den; | ||
637 | unsigned int q = i->min; | ||
638 | int diff; | ||
639 | if (q == 0) | ||
640 | q = 1; | ||
641 | den = div_down(num, q); | ||
642 | if (den < rats[k].den_min) | ||
643 | continue; | ||
644 | if (den > rats[k].den_max) | ||
645 | den = rats[k].den_max; | ||
646 | else { | ||
647 | unsigned int r; | ||
648 | r = (den - rats[k].den_min) % rats[k].den_step; | ||
649 | if (r != 0) | ||
650 | den -= r; | ||
651 | } | ||
652 | diff = num - q * den; | ||
653 | if (best_num == 0 || | ||
654 | diff * best_den < best_diff * den) { | ||
655 | best_diff = diff; | ||
656 | best_den = den; | ||
657 | best_num = num; | ||
658 | } | ||
659 | } | ||
660 | if (best_den == 0) { | ||
661 | i->empty = 1; | ||
662 | return -EINVAL; | ||
663 | } | ||
664 | t.min = div_down(best_num, best_den); | ||
665 | t.openmin = !!(best_num % best_den); | ||
666 | |||
667 | best_num = best_den = best_diff = 0; | ||
668 | for (k = 0; k < rats_count; ++k) { | ||
669 | unsigned int num = rats[k].num; | ||
670 | unsigned int den; | ||
671 | unsigned int q = i->max; | ||
672 | int diff; | ||
673 | if (q == 0) { | ||
674 | i->empty = 1; | ||
675 | return -EINVAL; | ||
676 | } | ||
677 | den = div_up(num, q); | ||
678 | if (den > rats[k].den_max) | ||
679 | continue; | ||
680 | if (den < rats[k].den_min) | ||
681 | den = rats[k].den_min; | ||
682 | else { | ||
683 | unsigned int r; | ||
684 | r = (den - rats[k].den_min) % rats[k].den_step; | ||
685 | if (r != 0) | ||
686 | den += rats[k].den_step - r; | ||
687 | } | ||
688 | diff = q * den - num; | ||
689 | if (best_num == 0 || | ||
690 | diff * best_den < best_diff * den) { | ||
691 | best_diff = diff; | ||
692 | best_den = den; | ||
693 | best_num = num; | ||
694 | } | ||
695 | } | ||
696 | if (best_den == 0) { | ||
697 | i->empty = 1; | ||
698 | return -EINVAL; | ||
699 | } | ||
700 | t.max = div_up(best_num, best_den); | ||
701 | t.openmax = !!(best_num % best_den); | ||
702 | t.integer = 0; | ||
703 | err = snd_interval_refine(i, &t); | ||
704 | if (err < 0) | ||
705 | return err; | ||
706 | |||
707 | if (snd_interval_single(i)) { | ||
708 | if (nump) | ||
709 | *nump = best_num; | ||
710 | if (denp) | ||
711 | *denp = best_den; | ||
712 | } | ||
713 | return err; | ||
714 | } | ||
715 | |||
716 | /** | ||
717 | * snd_interval_ratden - refine the interval value | ||
718 | * | ||
719 | * Returns non-zero if the value is changed, zero if not changed. | ||
720 | */ | ||
721 | static int snd_interval_ratden(snd_interval_t *i, | ||
722 | unsigned int rats_count, ratden_t *rats, | ||
723 | unsigned int *nump, unsigned int *denp) | ||
724 | { | ||
725 | unsigned int best_num, best_diff, best_den; | ||
726 | unsigned int k; | ||
727 | snd_interval_t t; | ||
728 | int err; | ||
729 | |||
730 | best_num = best_den = best_diff = 0; | ||
731 | for (k = 0; k < rats_count; ++k) { | ||
732 | unsigned int num; | ||
733 | unsigned int den = rats[k].den; | ||
734 | unsigned int q = i->min; | ||
735 | int diff; | ||
736 | num = mul(q, den); | ||
737 | if (num > rats[k].num_max) | ||
738 | continue; | ||
739 | if (num < rats[k].num_min) | ||
740 | num = rats[k].num_max; | ||
741 | else { | ||
742 | unsigned int r; | ||
743 | r = (num - rats[k].num_min) % rats[k].num_step; | ||
744 | if (r != 0) | ||
745 | num += rats[k].num_step - r; | ||
746 | } | ||
747 | diff = num - q * den; | ||
748 | if (best_num == 0 || | ||
749 | diff * best_den < best_diff * den) { | ||
750 | best_diff = diff; | ||
751 | best_den = den; | ||
752 | best_num = num; | ||
753 | } | ||
754 | } | ||
755 | if (best_den == 0) { | ||
756 | i->empty = 1; | ||
757 | return -EINVAL; | ||
758 | } | ||
759 | t.min = div_down(best_num, best_den); | ||
760 | t.openmin = !!(best_num % best_den); | ||
761 | |||
762 | best_num = best_den = best_diff = 0; | ||
763 | for (k = 0; k < rats_count; ++k) { | ||
764 | unsigned int num; | ||
765 | unsigned int den = rats[k].den; | ||
766 | unsigned int q = i->max; | ||
767 | int diff; | ||
768 | num = mul(q, den); | ||
769 | if (num < rats[k].num_min) | ||
770 | continue; | ||
771 | if (num > rats[k].num_max) | ||
772 | num = rats[k].num_max; | ||
773 | else { | ||
774 | unsigned int r; | ||
775 | r = (num - rats[k].num_min) % rats[k].num_step; | ||
776 | if (r != 0) | ||
777 | num -= r; | ||
778 | } | ||
779 | diff = q * den - num; | ||
780 | if (best_num == 0 || | ||
781 | diff * best_den < best_diff * den) { | ||
782 | best_diff = diff; | ||
783 | best_den = den; | ||
784 | best_num = num; | ||
785 | } | ||
786 | } | ||
787 | if (best_den == 0) { | ||
788 | i->empty = 1; | ||
789 | return -EINVAL; | ||
790 | } | ||
791 | t.max = div_up(best_num, best_den); | ||
792 | t.openmax = !!(best_num % best_den); | ||
793 | t.integer = 0; | ||
794 | err = snd_interval_refine(i, &t); | ||
795 | if (err < 0) | ||
796 | return err; | ||
797 | |||
798 | if (snd_interval_single(i)) { | ||
799 | if (nump) | ||
800 | *nump = best_num; | ||
801 | if (denp) | ||
802 | *denp = best_den; | ||
803 | } | ||
804 | return err; | ||
805 | } | ||
806 | |||
807 | /** | ||
808 | * snd_interval_list - refine the interval value from the list | ||
809 | * @i: the interval value to refine | ||
810 | * @count: the number of elements in the list | ||
811 | * @list: the value list | ||
812 | * @mask: the bit-mask to evaluate | ||
813 | * | ||
814 | * Refines the interval value from the list. | ||
815 | * When mask is non-zero, only the elements corresponding to bit 1 are | ||
816 | * evaluated. | ||
817 | * | ||
818 | * Returns non-zero if the value is changed, zero if not changed. | ||
819 | */ | ||
820 | int snd_interval_list(snd_interval_t *i, unsigned int count, unsigned int *list, unsigned int mask) | ||
821 | { | ||
822 | unsigned int k; | ||
823 | int changed = 0; | ||
824 | for (k = 0; k < count; k++) { | ||
825 | if (mask && !(mask & (1 << k))) | ||
826 | continue; | ||
827 | if (i->min == list[k] && !i->openmin) | ||
828 | goto _l1; | ||
829 | if (i->min < list[k]) { | ||
830 | i->min = list[k]; | ||
831 | i->openmin = 0; | ||
832 | changed = 1; | ||
833 | goto _l1; | ||
834 | } | ||
835 | } | ||
836 | i->empty = 1; | ||
837 | return -EINVAL; | ||
838 | _l1: | ||
839 | for (k = count; k-- > 0;) { | ||
840 | if (mask && !(mask & (1 << k))) | ||
841 | continue; | ||
842 | if (i->max == list[k] && !i->openmax) | ||
843 | goto _l2; | ||
844 | if (i->max > list[k]) { | ||
845 | i->max = list[k]; | ||
846 | i->openmax = 0; | ||
847 | changed = 1; | ||
848 | goto _l2; | ||
849 | } | ||
850 | } | ||
851 | i->empty = 1; | ||
852 | return -EINVAL; | ||
853 | _l2: | ||
854 | if (snd_interval_checkempty(i)) { | ||
855 | i->empty = 1; | ||
856 | return -EINVAL; | ||
857 | } | ||
858 | return changed; | ||
859 | } | ||
860 | |||
861 | static int snd_interval_step(snd_interval_t *i, unsigned int min, unsigned int step) | ||
862 | { | ||
863 | unsigned int n; | ||
864 | int changed = 0; | ||
865 | n = (i->min - min) % step; | ||
866 | if (n != 0 || i->openmin) { | ||
867 | i->min += step - n; | ||
868 | changed = 1; | ||
869 | } | ||
870 | n = (i->max - min) % step; | ||
871 | if (n != 0 || i->openmax) { | ||
872 | i->max -= n; | ||
873 | changed = 1; | ||
874 | } | ||
875 | if (snd_interval_checkempty(i)) { | ||
876 | i->empty = 1; | ||
877 | return -EINVAL; | ||
878 | } | ||
879 | return changed; | ||
880 | } | ||
881 | |||
882 | /* Info constraints helpers */ | ||
883 | |||
884 | /** | ||
885 | * snd_pcm_hw_rule_add - add the hw-constraint rule | ||
886 | * @runtime: the pcm runtime instance | ||
887 | * @cond: condition bits | ||
888 | * @var: the variable to evaluate | ||
889 | * @func: the evaluation function | ||
890 | * @private: the private data pointer passed to function | ||
891 | * @dep: the dependent variables | ||
892 | * | ||
893 | * Returns zero if successful, or a negative error code on failure. | ||
894 | */ | ||
895 | int snd_pcm_hw_rule_add(snd_pcm_runtime_t *runtime, unsigned int cond, | ||
896 | int var, | ||
897 | snd_pcm_hw_rule_func_t func, void *private, | ||
898 | int dep, ...) | ||
899 | { | ||
900 | snd_pcm_hw_constraints_t *constrs = &runtime->hw_constraints; | ||
901 | snd_pcm_hw_rule_t *c; | ||
902 | unsigned int k; | ||
903 | va_list args; | ||
904 | va_start(args, dep); | ||
905 | if (constrs->rules_num >= constrs->rules_all) { | ||
906 | snd_pcm_hw_rule_t *new; | ||
907 | unsigned int new_rules = constrs->rules_all + 16; | ||
908 | new = kcalloc(new_rules, sizeof(*c), GFP_KERNEL); | ||
909 | if (!new) | ||
910 | return -ENOMEM; | ||
911 | if (constrs->rules) { | ||
912 | memcpy(new, constrs->rules, | ||
913 | constrs->rules_num * sizeof(*c)); | ||
914 | kfree(constrs->rules); | ||
915 | } | ||
916 | constrs->rules = new; | ||
917 | constrs->rules_all = new_rules; | ||
918 | } | ||
919 | c = &constrs->rules[constrs->rules_num]; | ||
920 | c->cond = cond; | ||
921 | c->func = func; | ||
922 | c->var = var; | ||
923 | c->private = private; | ||
924 | k = 0; | ||
925 | while (1) { | ||
926 | snd_assert(k < ARRAY_SIZE(c->deps), return -EINVAL); | ||
927 | c->deps[k++] = dep; | ||
928 | if (dep < 0) | ||
929 | break; | ||
930 | dep = va_arg(args, int); | ||
931 | } | ||
932 | constrs->rules_num++; | ||
933 | va_end(args); | ||
934 | return 0; | ||
935 | } | ||
936 | |||
937 | /** | ||
938 | * snd_pcm_hw_constraint_mask | ||
939 | */ | ||
940 | int snd_pcm_hw_constraint_mask(snd_pcm_runtime_t *runtime, snd_pcm_hw_param_t var, | ||
941 | u_int32_t mask) | ||
942 | { | ||
943 | snd_pcm_hw_constraints_t *constrs = &runtime->hw_constraints; | ||
944 | snd_mask_t *maskp = constrs_mask(constrs, var); | ||
945 | *maskp->bits &= mask; | ||
946 | memset(maskp->bits + 1, 0, (SNDRV_MASK_MAX-32) / 8); /* clear rest */ | ||
947 | if (*maskp->bits == 0) | ||
948 | return -EINVAL; | ||
949 | return 0; | ||
950 | } | ||
951 | |||
952 | /** | ||
953 | * snd_pcm_hw_constraint_mask64 | ||
954 | */ | ||
955 | int snd_pcm_hw_constraint_mask64(snd_pcm_runtime_t *runtime, snd_pcm_hw_param_t var, | ||
956 | u_int64_t mask) | ||
957 | { | ||
958 | snd_pcm_hw_constraints_t *constrs = &runtime->hw_constraints; | ||
959 | snd_mask_t *maskp = constrs_mask(constrs, var); | ||
960 | maskp->bits[0] &= (u_int32_t)mask; | ||
961 | maskp->bits[1] &= (u_int32_t)(mask >> 32); | ||
962 | memset(maskp->bits + 2, 0, (SNDRV_MASK_MAX-64) / 8); /* clear rest */ | ||
963 | if (! maskp->bits[0] && ! maskp->bits[1]) | ||
964 | return -EINVAL; | ||
965 | return 0; | ||
966 | } | ||
967 | |||
968 | /** | ||
969 | * snd_pcm_hw_constraint_integer | ||
970 | */ | ||
971 | int snd_pcm_hw_constraint_integer(snd_pcm_runtime_t *runtime, snd_pcm_hw_param_t var) | ||
972 | { | ||
973 | snd_pcm_hw_constraints_t *constrs = &runtime->hw_constraints; | ||
974 | return snd_interval_setinteger(constrs_interval(constrs, var)); | ||
975 | } | ||
976 | |||
977 | /** | ||
978 | * snd_pcm_hw_constraint_minmax | ||
979 | */ | ||
980 | int snd_pcm_hw_constraint_minmax(snd_pcm_runtime_t *runtime, snd_pcm_hw_param_t var, | ||
981 | unsigned int min, unsigned int max) | ||
982 | { | ||
983 | snd_pcm_hw_constraints_t *constrs = &runtime->hw_constraints; | ||
984 | snd_interval_t t; | ||
985 | t.min = min; | ||
986 | t.max = max; | ||
987 | t.openmin = t.openmax = 0; | ||
988 | t.integer = 0; | ||
989 | return snd_interval_refine(constrs_interval(constrs, var), &t); | ||
990 | } | ||
991 | |||
992 | static int snd_pcm_hw_rule_list(snd_pcm_hw_params_t *params, | ||
993 | snd_pcm_hw_rule_t *rule) | ||
994 | { | ||
995 | snd_pcm_hw_constraint_list_t *list = rule->private; | ||
996 | return snd_interval_list(hw_param_interval(params, rule->var), list->count, list->list, list->mask); | ||
997 | } | ||
998 | |||
999 | |||
1000 | /** | ||
1001 | * snd_pcm_hw_constraint_list | ||
1002 | */ | ||
1003 | int snd_pcm_hw_constraint_list(snd_pcm_runtime_t *runtime, | ||
1004 | unsigned int cond, | ||
1005 | snd_pcm_hw_param_t var, | ||
1006 | snd_pcm_hw_constraint_list_t *l) | ||
1007 | { | ||
1008 | return snd_pcm_hw_rule_add(runtime, cond, var, | ||
1009 | snd_pcm_hw_rule_list, l, | ||
1010 | var, -1); | ||
1011 | } | ||
1012 | |||
1013 | static int snd_pcm_hw_rule_ratnums(snd_pcm_hw_params_t *params, | ||
1014 | snd_pcm_hw_rule_t *rule) | ||
1015 | { | ||
1016 | snd_pcm_hw_constraint_ratnums_t *r = rule->private; | ||
1017 | unsigned int num = 0, den = 0; | ||
1018 | int err; | ||
1019 | err = snd_interval_ratnum(hw_param_interval(params, rule->var), | ||
1020 | r->nrats, r->rats, &num, &den); | ||
1021 | if (err >= 0 && den && rule->var == SNDRV_PCM_HW_PARAM_RATE) { | ||
1022 | params->rate_num = num; | ||
1023 | params->rate_den = den; | ||
1024 | } | ||
1025 | return err; | ||
1026 | } | ||
1027 | |||
1028 | /** | ||
1029 | * snd_pcm_hw_constraint_ratnums | ||
1030 | */ | ||
1031 | int snd_pcm_hw_constraint_ratnums(snd_pcm_runtime_t *runtime, | ||
1032 | unsigned int cond, | ||
1033 | snd_pcm_hw_param_t var, | ||
1034 | snd_pcm_hw_constraint_ratnums_t *r) | ||
1035 | { | ||
1036 | return snd_pcm_hw_rule_add(runtime, cond, var, | ||
1037 | snd_pcm_hw_rule_ratnums, r, | ||
1038 | var, -1); | ||
1039 | } | ||
1040 | |||
1041 | static int snd_pcm_hw_rule_ratdens(snd_pcm_hw_params_t *params, | ||
1042 | snd_pcm_hw_rule_t *rule) | ||
1043 | { | ||
1044 | snd_pcm_hw_constraint_ratdens_t *r = rule->private; | ||
1045 | unsigned int num = 0, den = 0; | ||
1046 | int err = snd_interval_ratden(hw_param_interval(params, rule->var), | ||
1047 | r->nrats, r->rats, &num, &den); | ||
1048 | if (err >= 0 && den && rule->var == SNDRV_PCM_HW_PARAM_RATE) { | ||
1049 | params->rate_num = num; | ||
1050 | params->rate_den = den; | ||
1051 | } | ||
1052 | return err; | ||
1053 | } | ||
1054 | |||
1055 | /** | ||
1056 | * snd_pcm_hw_constraint_ratdens | ||
1057 | */ | ||
1058 | int snd_pcm_hw_constraint_ratdens(snd_pcm_runtime_t *runtime, | ||
1059 | unsigned int cond, | ||
1060 | snd_pcm_hw_param_t var, | ||
1061 | snd_pcm_hw_constraint_ratdens_t *r) | ||
1062 | { | ||
1063 | return snd_pcm_hw_rule_add(runtime, cond, var, | ||
1064 | snd_pcm_hw_rule_ratdens, r, | ||
1065 | var, -1); | ||
1066 | } | ||
1067 | |||
1068 | static int snd_pcm_hw_rule_msbits(snd_pcm_hw_params_t *params, | ||
1069 | snd_pcm_hw_rule_t *rule) | ||
1070 | { | ||
1071 | unsigned int l = (unsigned long) rule->private; | ||
1072 | int width = l & 0xffff; | ||
1073 | unsigned int msbits = l >> 16; | ||
1074 | snd_interval_t *i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS); | ||
1075 | if (snd_interval_single(i) && snd_interval_value(i) == width) | ||
1076 | params->msbits = msbits; | ||
1077 | return 0; | ||
1078 | } | ||
1079 | |||
1080 | /** | ||
1081 | * snd_pcm_hw_constraint_msbits | ||
1082 | */ | ||
1083 | int snd_pcm_hw_constraint_msbits(snd_pcm_runtime_t *runtime, | ||
1084 | unsigned int cond, | ||
1085 | unsigned int width, | ||
1086 | unsigned int msbits) | ||
1087 | { | ||
1088 | unsigned long l = (msbits << 16) | width; | ||
1089 | return snd_pcm_hw_rule_add(runtime, cond, -1, | ||
1090 | snd_pcm_hw_rule_msbits, | ||
1091 | (void*) l, | ||
1092 | SNDRV_PCM_HW_PARAM_SAMPLE_BITS, -1); | ||
1093 | } | ||
1094 | |||
1095 | static int snd_pcm_hw_rule_step(snd_pcm_hw_params_t *params, | ||
1096 | snd_pcm_hw_rule_t *rule) | ||
1097 | { | ||
1098 | unsigned long step = (unsigned long) rule->private; | ||
1099 | return snd_interval_step(hw_param_interval(params, rule->var), 0, step); | ||
1100 | } | ||
1101 | |||
1102 | /** | ||
1103 | * snd_pcm_hw_constraint_step | ||
1104 | */ | ||
1105 | int snd_pcm_hw_constraint_step(snd_pcm_runtime_t *runtime, | ||
1106 | unsigned int cond, | ||
1107 | snd_pcm_hw_param_t var, | ||
1108 | unsigned long step) | ||
1109 | { | ||
1110 | return snd_pcm_hw_rule_add(runtime, cond, var, | ||
1111 | snd_pcm_hw_rule_step, (void *) step, | ||
1112 | var, -1); | ||
1113 | } | ||
1114 | |||
1115 | static int snd_pcm_hw_rule_pow2(snd_pcm_hw_params_t *params, snd_pcm_hw_rule_t *rule) | ||
1116 | { | ||
1117 | static int pow2_sizes[] = { | ||
1118 | 1<<0, 1<<1, 1<<2, 1<<3, 1<<4, 1<<5, 1<<6, 1<<7, | ||
1119 | 1<<8, 1<<9, 1<<10, 1<<11, 1<<12, 1<<13, 1<<14, 1<<15, | ||
1120 | 1<<16, 1<<17, 1<<18, 1<<19, 1<<20, 1<<21, 1<<22, 1<<23, | ||
1121 | 1<<24, 1<<25, 1<<26, 1<<27, 1<<28, 1<<29, 1<<30 | ||
1122 | }; | ||
1123 | return snd_interval_list(hw_param_interval(params, rule->var), | ||
1124 | ARRAY_SIZE(pow2_sizes), pow2_sizes, 0); | ||
1125 | } | ||
1126 | |||
1127 | /** | ||
1128 | * snd_pcm_hw_constraint_pow2 | ||
1129 | */ | ||
1130 | int snd_pcm_hw_constraint_pow2(snd_pcm_runtime_t *runtime, | ||
1131 | unsigned int cond, | ||
1132 | snd_pcm_hw_param_t var) | ||
1133 | { | ||
1134 | return snd_pcm_hw_rule_add(runtime, cond, var, | ||
1135 | snd_pcm_hw_rule_pow2, NULL, | ||
1136 | var, -1); | ||
1137 | } | ||
1138 | |||
1139 | /* To use the same code we have in alsa-lib */ | ||
1140 | #define snd_pcm_t snd_pcm_substream_t | ||
1141 | #define assert(i) snd_assert((i), return -EINVAL) | ||
1142 | #ifndef INT_MIN | ||
1143 | #define INT_MIN ((int)((unsigned int)INT_MAX+1)) | ||
1144 | #endif | ||
1145 | |||
1146 | void _snd_pcm_hw_param_any(snd_pcm_hw_params_t *params, snd_pcm_hw_param_t var) | ||
1147 | { | ||
1148 | if (hw_is_mask(var)) { | ||
1149 | snd_mask_any(hw_param_mask(params, var)); | ||
1150 | params->cmask |= 1 << var; | ||
1151 | params->rmask |= 1 << var; | ||
1152 | return; | ||
1153 | } | ||
1154 | if (hw_is_interval(var)) { | ||
1155 | snd_interval_any(hw_param_interval(params, var)); | ||
1156 | params->cmask |= 1 << var; | ||
1157 | params->rmask |= 1 << var; | ||
1158 | return; | ||
1159 | } | ||
1160 | snd_BUG(); | ||
1161 | } | ||
1162 | |||
1163 | /** | ||
1164 | * snd_pcm_hw_param_any | ||
1165 | */ | ||
1166 | int snd_pcm_hw_param_any(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, | ||
1167 | snd_pcm_hw_param_t var) | ||
1168 | { | ||
1169 | _snd_pcm_hw_param_any(params, var); | ||
1170 | return snd_pcm_hw_refine(pcm, params); | ||
1171 | } | ||
1172 | |||
1173 | void _snd_pcm_hw_params_any(snd_pcm_hw_params_t *params) | ||
1174 | { | ||
1175 | unsigned int k; | ||
1176 | memset(params, 0, sizeof(*params)); | ||
1177 | for (k = SNDRV_PCM_HW_PARAM_FIRST_MASK; k <= SNDRV_PCM_HW_PARAM_LAST_MASK; k++) | ||
1178 | _snd_pcm_hw_param_any(params, k); | ||
1179 | for (k = SNDRV_PCM_HW_PARAM_FIRST_INTERVAL; k <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; k++) | ||
1180 | _snd_pcm_hw_param_any(params, k); | ||
1181 | params->info = ~0U; | ||
1182 | } | ||
1183 | |||
1184 | /** | ||
1185 | * snd_pcm_hw_params_any | ||
1186 | * | ||
1187 | * Fill PARAMS with full configuration space boundaries | ||
1188 | */ | ||
1189 | int snd_pcm_hw_params_any(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) | ||
1190 | { | ||
1191 | _snd_pcm_hw_params_any(params); | ||
1192 | return snd_pcm_hw_refine(pcm, params); | ||
1193 | } | ||
1194 | |||
1195 | /** | ||
1196 | * snd_pcm_hw_param_value | ||
1197 | * | ||
1198 | * Return the value for field PAR if it's fixed in configuration space | ||
1199 | * defined by PARAMS. Return -EINVAL otherwise | ||
1200 | */ | ||
1201 | int snd_pcm_hw_param_value(const snd_pcm_hw_params_t *params, | ||
1202 | snd_pcm_hw_param_t var, int *dir) | ||
1203 | { | ||
1204 | if (hw_is_mask(var)) { | ||
1205 | const snd_mask_t *mask = hw_param_mask_c(params, var); | ||
1206 | if (!snd_mask_single(mask)) | ||
1207 | return -EINVAL; | ||
1208 | if (dir) | ||
1209 | *dir = 0; | ||
1210 | return snd_mask_value(mask); | ||
1211 | } | ||
1212 | if (hw_is_interval(var)) { | ||
1213 | const snd_interval_t *i = hw_param_interval_c(params, var); | ||
1214 | if (!snd_interval_single(i)) | ||
1215 | return -EINVAL; | ||
1216 | if (dir) | ||
1217 | *dir = i->openmin; | ||
1218 | return snd_interval_value(i); | ||
1219 | } | ||
1220 | assert(0); | ||
1221 | return -EINVAL; | ||
1222 | } | ||
1223 | |||
1224 | /** | ||
1225 | * snd_pcm_hw_param_value_min | ||
1226 | * | ||
1227 | * Return the minimum value for field PAR. | ||
1228 | */ | ||
1229 | unsigned int snd_pcm_hw_param_value_min(const snd_pcm_hw_params_t *params, | ||
1230 | snd_pcm_hw_param_t var, int *dir) | ||
1231 | { | ||
1232 | if (hw_is_mask(var)) { | ||
1233 | if (dir) | ||
1234 | *dir = 0; | ||
1235 | return snd_mask_min(hw_param_mask_c(params, var)); | ||
1236 | } | ||
1237 | if (hw_is_interval(var)) { | ||
1238 | const snd_interval_t *i = hw_param_interval_c(params, var); | ||
1239 | if (dir) | ||
1240 | *dir = i->openmin; | ||
1241 | return snd_interval_min(i); | ||
1242 | } | ||
1243 | assert(0); | ||
1244 | return -EINVAL; | ||
1245 | } | ||
1246 | |||
1247 | /** | ||
1248 | * snd_pcm_hw_param_value_max | ||
1249 | * | ||
1250 | * Return the maximum value for field PAR. | ||
1251 | */ | ||
1252 | unsigned int snd_pcm_hw_param_value_max(const snd_pcm_hw_params_t *params, | ||
1253 | snd_pcm_hw_param_t var, int *dir) | ||
1254 | { | ||
1255 | if (hw_is_mask(var)) { | ||
1256 | if (dir) | ||
1257 | *dir = 0; | ||
1258 | return snd_mask_max(hw_param_mask_c(params, var)); | ||
1259 | } | ||
1260 | if (hw_is_interval(var)) { | ||
1261 | const snd_interval_t *i = hw_param_interval_c(params, var); | ||
1262 | if (dir) | ||
1263 | *dir = - (int) i->openmax; | ||
1264 | return snd_interval_max(i); | ||
1265 | } | ||
1266 | assert(0); | ||
1267 | return -EINVAL; | ||
1268 | } | ||
1269 | |||
1270 | void _snd_pcm_hw_param_setempty(snd_pcm_hw_params_t *params, | ||
1271 | snd_pcm_hw_param_t var) | ||
1272 | { | ||
1273 | if (hw_is_mask(var)) { | ||
1274 | snd_mask_none(hw_param_mask(params, var)); | ||
1275 | params->cmask |= 1 << var; | ||
1276 | params->rmask |= 1 << var; | ||
1277 | } else if (hw_is_interval(var)) { | ||
1278 | snd_interval_none(hw_param_interval(params, var)); | ||
1279 | params->cmask |= 1 << var; | ||
1280 | params->rmask |= 1 << var; | ||
1281 | } else { | ||
1282 | snd_BUG(); | ||
1283 | } | ||
1284 | } | ||
1285 | |||
1286 | int _snd_pcm_hw_param_setinteger(snd_pcm_hw_params_t *params, | ||
1287 | snd_pcm_hw_param_t var) | ||
1288 | { | ||
1289 | int changed; | ||
1290 | assert(hw_is_interval(var)); | ||
1291 | changed = snd_interval_setinteger(hw_param_interval(params, var)); | ||
1292 | if (changed) { | ||
1293 | params->cmask |= 1 << var; | ||
1294 | params->rmask |= 1 << var; | ||
1295 | } | ||
1296 | return changed; | ||
1297 | } | ||
1298 | |||
1299 | /** | ||
1300 | * snd_pcm_hw_param_setinteger | ||
1301 | * | ||
1302 | * Inside configuration space defined by PARAMS remove from PAR all | ||
1303 | * non integer values. Reduce configuration space accordingly. | ||
1304 | * Return -EINVAL if the configuration space is empty | ||
1305 | */ | ||
1306 | int snd_pcm_hw_param_setinteger(snd_pcm_t *pcm, | ||
1307 | snd_pcm_hw_params_t *params, | ||
1308 | snd_pcm_hw_param_t var) | ||
1309 | { | ||
1310 | int changed = _snd_pcm_hw_param_setinteger(params, var); | ||
1311 | if (changed < 0) | ||
1312 | return changed; | ||
1313 | if (params->rmask) { | ||
1314 | int err = snd_pcm_hw_refine(pcm, params); | ||
1315 | if (err < 0) | ||
1316 | return err; | ||
1317 | } | ||
1318 | return 0; | ||
1319 | } | ||
1320 | |||
1321 | int _snd_pcm_hw_param_first(snd_pcm_hw_params_t *params, | ||
1322 | snd_pcm_hw_param_t var) | ||
1323 | { | ||
1324 | int changed; | ||
1325 | if (hw_is_mask(var)) | ||
1326 | changed = snd_mask_refine_first(hw_param_mask(params, var)); | ||
1327 | else if (hw_is_interval(var)) | ||
1328 | changed = snd_interval_refine_first(hw_param_interval(params, var)); | ||
1329 | else { | ||
1330 | assert(0); | ||
1331 | return -EINVAL; | ||
1332 | } | ||
1333 | if (changed) { | ||
1334 | params->cmask |= 1 << var; | ||
1335 | params->rmask |= 1 << var; | ||
1336 | } | ||
1337 | return changed; | ||
1338 | } | ||
1339 | |||
1340 | |||
1341 | /** | ||
1342 | * snd_pcm_hw_param_first | ||
1343 | * | ||
1344 | * Inside configuration space defined by PARAMS remove from PAR all | ||
1345 | * values > minimum. Reduce configuration space accordingly. | ||
1346 | * Return the minimum. | ||
1347 | */ | ||
1348 | int snd_pcm_hw_param_first(snd_pcm_t *pcm, | ||
1349 | snd_pcm_hw_params_t *params, | ||
1350 | snd_pcm_hw_param_t var, int *dir) | ||
1351 | { | ||
1352 | int changed = _snd_pcm_hw_param_first(params, var); | ||
1353 | if (changed < 0) | ||
1354 | return changed; | ||
1355 | if (params->rmask) { | ||
1356 | int err = snd_pcm_hw_refine(pcm, params); | ||
1357 | assert(err >= 0); | ||
1358 | } | ||
1359 | return snd_pcm_hw_param_value(params, var, dir); | ||
1360 | } | ||
1361 | |||
1362 | int _snd_pcm_hw_param_last(snd_pcm_hw_params_t *params, | ||
1363 | snd_pcm_hw_param_t var) | ||
1364 | { | ||
1365 | int changed; | ||
1366 | if (hw_is_mask(var)) | ||
1367 | changed = snd_mask_refine_last(hw_param_mask(params, var)); | ||
1368 | else if (hw_is_interval(var)) | ||
1369 | changed = snd_interval_refine_last(hw_param_interval(params, var)); | ||
1370 | else { | ||
1371 | assert(0); | ||
1372 | return -EINVAL; | ||
1373 | } | ||
1374 | if (changed) { | ||
1375 | params->cmask |= 1 << var; | ||
1376 | params->rmask |= 1 << var; | ||
1377 | } | ||
1378 | return changed; | ||
1379 | } | ||
1380 | |||
1381 | |||
1382 | /** | ||
1383 | * snd_pcm_hw_param_last | ||
1384 | * | ||
1385 | * Inside configuration space defined by PARAMS remove from PAR all | ||
1386 | * values < maximum. Reduce configuration space accordingly. | ||
1387 | * Return the maximum. | ||
1388 | */ | ||
1389 | int snd_pcm_hw_param_last(snd_pcm_t *pcm, | ||
1390 | snd_pcm_hw_params_t *params, | ||
1391 | snd_pcm_hw_param_t var, int *dir) | ||
1392 | { | ||
1393 | int changed = _snd_pcm_hw_param_last(params, var); | ||
1394 | if (changed < 0) | ||
1395 | return changed; | ||
1396 | if (params->rmask) { | ||
1397 | int err = snd_pcm_hw_refine(pcm, params); | ||
1398 | assert(err >= 0); | ||
1399 | } | ||
1400 | return snd_pcm_hw_param_value(params, var, dir); | ||
1401 | } | ||
1402 | |||
1403 | int _snd_pcm_hw_param_min(snd_pcm_hw_params_t *params, | ||
1404 | snd_pcm_hw_param_t var, unsigned int val, int dir) | ||
1405 | { | ||
1406 | int changed; | ||
1407 | int open = 0; | ||
1408 | if (dir) { | ||
1409 | if (dir > 0) { | ||
1410 | open = 1; | ||
1411 | } else if (dir < 0) { | ||
1412 | if (val > 0) { | ||
1413 | open = 1; | ||
1414 | val--; | ||
1415 | } | ||
1416 | } | ||
1417 | } | ||
1418 | if (hw_is_mask(var)) | ||
1419 | changed = snd_mask_refine_min(hw_param_mask(params, var), val + !!open); | ||
1420 | else if (hw_is_interval(var)) | ||
1421 | changed = snd_interval_refine_min(hw_param_interval(params, var), val, open); | ||
1422 | else { | ||
1423 | assert(0); | ||
1424 | return -EINVAL; | ||
1425 | } | ||
1426 | if (changed) { | ||
1427 | params->cmask |= 1 << var; | ||
1428 | params->rmask |= 1 << var; | ||
1429 | } | ||
1430 | return changed; | ||
1431 | } | ||
1432 | |||
1433 | /** | ||
1434 | * snd_pcm_hw_param_min | ||
1435 | * | ||
1436 | * Inside configuration space defined by PARAMS remove from PAR all | ||
1437 | * values < VAL. Reduce configuration space accordingly. | ||
1438 | * Return new minimum or -EINVAL if the configuration space is empty | ||
1439 | */ | ||
1440 | int snd_pcm_hw_param_min(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, | ||
1441 | snd_pcm_hw_param_t var, unsigned int val, int *dir) | ||
1442 | { | ||
1443 | int changed = _snd_pcm_hw_param_min(params, var, val, dir ? *dir : 0); | ||
1444 | if (changed < 0) | ||
1445 | return changed; | ||
1446 | if (params->rmask) { | ||
1447 | int err = snd_pcm_hw_refine(pcm, params); | ||
1448 | if (err < 0) | ||
1449 | return err; | ||
1450 | } | ||
1451 | return snd_pcm_hw_param_value_min(params, var, dir); | ||
1452 | } | ||
1453 | |||
1454 | int _snd_pcm_hw_param_max(snd_pcm_hw_params_t *params, | ||
1455 | snd_pcm_hw_param_t var, unsigned int val, int dir) | ||
1456 | { | ||
1457 | int changed; | ||
1458 | int open = 0; | ||
1459 | if (dir) { | ||
1460 | if (dir < 0) { | ||
1461 | open = 1; | ||
1462 | } else if (dir > 0) { | ||
1463 | open = 1; | ||
1464 | val++; | ||
1465 | } | ||
1466 | } | ||
1467 | if (hw_is_mask(var)) { | ||
1468 | if (val == 0 && open) { | ||
1469 | snd_mask_none(hw_param_mask(params, var)); | ||
1470 | changed = -EINVAL; | ||
1471 | } else | ||
1472 | changed = snd_mask_refine_max(hw_param_mask(params, var), val - !!open); | ||
1473 | } else if (hw_is_interval(var)) | ||
1474 | changed = snd_interval_refine_max(hw_param_interval(params, var), val, open); | ||
1475 | else { | ||
1476 | assert(0); | ||
1477 | return -EINVAL; | ||
1478 | } | ||
1479 | if (changed) { | ||
1480 | params->cmask |= 1 << var; | ||
1481 | params->rmask |= 1 << var; | ||
1482 | } | ||
1483 | return changed; | ||
1484 | } | ||
1485 | |||
1486 | /** | ||
1487 | * snd_pcm_hw_param_max | ||
1488 | * | ||
1489 | * Inside configuration space defined by PARAMS remove from PAR all | ||
1490 | * values >= VAL + 1. Reduce configuration space accordingly. | ||
1491 | * Return new maximum or -EINVAL if the configuration space is empty | ||
1492 | */ | ||
1493 | int snd_pcm_hw_param_max(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, | ||
1494 | snd_pcm_hw_param_t var, unsigned int val, int *dir) | ||
1495 | { | ||
1496 | int changed = _snd_pcm_hw_param_max(params, var, val, dir ? *dir : 0); | ||
1497 | if (changed < 0) | ||
1498 | return changed; | ||
1499 | if (params->rmask) { | ||
1500 | int err = snd_pcm_hw_refine(pcm, params); | ||
1501 | if (err < 0) | ||
1502 | return err; | ||
1503 | } | ||
1504 | return snd_pcm_hw_param_value_max(params, var, dir); | ||
1505 | } | ||
1506 | |||
1507 | int _snd_pcm_hw_param_set(snd_pcm_hw_params_t *params, | ||
1508 | snd_pcm_hw_param_t var, unsigned int val, int dir) | ||
1509 | { | ||
1510 | int changed; | ||
1511 | if (hw_is_mask(var)) { | ||
1512 | snd_mask_t *m = hw_param_mask(params, var); | ||
1513 | if (val == 0 && dir < 0) { | ||
1514 | changed = -EINVAL; | ||
1515 | snd_mask_none(m); | ||
1516 | } else { | ||
1517 | if (dir > 0) | ||
1518 | val++; | ||
1519 | else if (dir < 0) | ||
1520 | val--; | ||
1521 | changed = snd_mask_refine_set(hw_param_mask(params, var), val); | ||
1522 | } | ||
1523 | } else if (hw_is_interval(var)) { | ||
1524 | snd_interval_t *i = hw_param_interval(params, var); | ||
1525 | if (val == 0 && dir < 0) { | ||
1526 | changed = -EINVAL; | ||
1527 | snd_interval_none(i); | ||
1528 | } else if (dir == 0) | ||
1529 | changed = snd_interval_refine_set(i, val); | ||
1530 | else { | ||
1531 | snd_interval_t t; | ||
1532 | t.openmin = 1; | ||
1533 | t.openmax = 1; | ||
1534 | t.empty = 0; | ||
1535 | t.integer = 0; | ||
1536 | if (dir < 0) { | ||
1537 | t.min = val - 1; | ||
1538 | t.max = val; | ||
1539 | } else { | ||
1540 | t.min = val; | ||
1541 | t.max = val+1; | ||
1542 | } | ||
1543 | changed = snd_interval_refine(i, &t); | ||
1544 | } | ||
1545 | } else { | ||
1546 | assert(0); | ||
1547 | return -EINVAL; | ||
1548 | } | ||
1549 | if (changed) { | ||
1550 | params->cmask |= 1 << var; | ||
1551 | params->rmask |= 1 << var; | ||
1552 | } | ||
1553 | return changed; | ||
1554 | } | ||
1555 | |||
1556 | /** | ||
1557 | * snd_pcm_hw_param_set | ||
1558 | * | ||
1559 | * Inside configuration space defined by PARAMS remove from PAR all | ||
1560 | * values != VAL. Reduce configuration space accordingly. | ||
1561 | * Return VAL or -EINVAL if the configuration space is empty | ||
1562 | */ | ||
1563 | int snd_pcm_hw_param_set(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, | ||
1564 | snd_pcm_hw_param_t var, unsigned int val, int dir) | ||
1565 | { | ||
1566 | int changed = _snd_pcm_hw_param_set(params, var, val, dir); | ||
1567 | if (changed < 0) | ||
1568 | return changed; | ||
1569 | if (params->rmask) { | ||
1570 | int err = snd_pcm_hw_refine(pcm, params); | ||
1571 | if (err < 0) | ||
1572 | return err; | ||
1573 | } | ||
1574 | return snd_pcm_hw_param_value(params, var, NULL); | ||
1575 | } | ||
1576 | |||
1577 | int _snd_pcm_hw_param_mask(snd_pcm_hw_params_t *params, | ||
1578 | snd_pcm_hw_param_t var, const snd_mask_t *val) | ||
1579 | { | ||
1580 | int changed; | ||
1581 | assert(hw_is_mask(var)); | ||
1582 | changed = snd_mask_refine(hw_param_mask(params, var), val); | ||
1583 | if (changed) { | ||
1584 | params->cmask |= 1 << var; | ||
1585 | params->rmask |= 1 << var; | ||
1586 | } | ||
1587 | return changed; | ||
1588 | } | ||
1589 | |||
1590 | /** | ||
1591 | * snd_pcm_hw_param_mask | ||
1592 | * | ||
1593 | * Inside configuration space defined by PARAMS remove from PAR all values | ||
1594 | * not contained in MASK. Reduce configuration space accordingly. | ||
1595 | * This function can be called only for SNDRV_PCM_HW_PARAM_ACCESS, | ||
1596 | * SNDRV_PCM_HW_PARAM_FORMAT, SNDRV_PCM_HW_PARAM_SUBFORMAT. | ||
1597 | * Return 0 on success or -EINVAL | ||
1598 | * if the configuration space is empty | ||
1599 | */ | ||
1600 | int snd_pcm_hw_param_mask(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, | ||
1601 | snd_pcm_hw_param_t var, const snd_mask_t *val) | ||
1602 | { | ||
1603 | int changed = _snd_pcm_hw_param_mask(params, var, val); | ||
1604 | if (changed < 0) | ||
1605 | return changed; | ||
1606 | if (params->rmask) { | ||
1607 | int err = snd_pcm_hw_refine(pcm, params); | ||
1608 | if (err < 0) | ||
1609 | return err; | ||
1610 | } | ||
1611 | return 0; | ||
1612 | } | ||
1613 | |||
1614 | static int boundary_sub(int a, int adir, | ||
1615 | int b, int bdir, | ||
1616 | int *c, int *cdir) | ||
1617 | { | ||
1618 | adir = adir < 0 ? -1 : (adir > 0 ? 1 : 0); | ||
1619 | bdir = bdir < 0 ? -1 : (bdir > 0 ? 1 : 0); | ||
1620 | *c = a - b; | ||
1621 | *cdir = adir - bdir; | ||
1622 | if (*cdir == -2) { | ||
1623 | assert(*c > INT_MIN); | ||
1624 | (*c)--; | ||
1625 | } else if (*cdir == 2) { | ||
1626 | assert(*c < INT_MAX); | ||
1627 | (*c)++; | ||
1628 | } | ||
1629 | return 0; | ||
1630 | } | ||
1631 | |||
1632 | static int boundary_lt(unsigned int a, int adir, | ||
1633 | unsigned int b, int bdir) | ||
1634 | { | ||
1635 | assert(a > 0 || adir >= 0); | ||
1636 | assert(b > 0 || bdir >= 0); | ||
1637 | if (adir < 0) { | ||
1638 | a--; | ||
1639 | adir = 1; | ||
1640 | } else if (adir > 0) | ||
1641 | adir = 1; | ||
1642 | if (bdir < 0) { | ||
1643 | b--; | ||
1644 | bdir = 1; | ||
1645 | } else if (bdir > 0) | ||
1646 | bdir = 1; | ||
1647 | return a < b || (a == b && adir < bdir); | ||
1648 | } | ||
1649 | |||
1650 | /* Return 1 if min is nearer to best than max */ | ||
1651 | static int boundary_nearer(int min, int mindir, | ||
1652 | int best, int bestdir, | ||
1653 | int max, int maxdir) | ||
1654 | { | ||
1655 | int dmin, dmindir; | ||
1656 | int dmax, dmaxdir; | ||
1657 | boundary_sub(best, bestdir, min, mindir, &dmin, &dmindir); | ||
1658 | boundary_sub(max, maxdir, best, bestdir, &dmax, &dmaxdir); | ||
1659 | return boundary_lt(dmin, dmindir, dmax, dmaxdir); | ||
1660 | } | ||
1661 | |||
1662 | /** | ||
1663 | * snd_pcm_hw_param_near | ||
1664 | * | ||
1665 | * Inside configuration space defined by PARAMS set PAR to the available value | ||
1666 | * nearest to VAL. Reduce configuration space accordingly. | ||
1667 | * This function cannot be called for SNDRV_PCM_HW_PARAM_ACCESS, | ||
1668 | * SNDRV_PCM_HW_PARAM_FORMAT, SNDRV_PCM_HW_PARAM_SUBFORMAT. | ||
1669 | * Return the value found. | ||
1670 | */ | ||
1671 | int snd_pcm_hw_param_near(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, | ||
1672 | snd_pcm_hw_param_t var, unsigned int best, int *dir) | ||
1673 | { | ||
1674 | snd_pcm_hw_params_t *save = NULL; | ||
1675 | int v; | ||
1676 | unsigned int saved_min; | ||
1677 | int last = 0; | ||
1678 | int min, max; | ||
1679 | int mindir, maxdir; | ||
1680 | int valdir = dir ? *dir : 0; | ||
1681 | /* FIXME */ | ||
1682 | if (best > INT_MAX) | ||
1683 | best = INT_MAX; | ||
1684 | min = max = best; | ||
1685 | mindir = maxdir = valdir; | ||
1686 | if (maxdir > 0) | ||
1687 | maxdir = 0; | ||
1688 | else if (maxdir == 0) | ||
1689 | maxdir = -1; | ||
1690 | else { | ||
1691 | maxdir = 1; | ||
1692 | max--; | ||
1693 | } | ||
1694 | save = kmalloc(sizeof(*save), GFP_KERNEL); | ||
1695 | if (save == NULL) | ||
1696 | return -ENOMEM; | ||
1697 | *save = *params; | ||
1698 | saved_min = min; | ||
1699 | min = snd_pcm_hw_param_min(pcm, params, var, min, &mindir); | ||
1700 | if (min >= 0) { | ||
1701 | snd_pcm_hw_params_t *params1; | ||
1702 | if (max < 0) | ||
1703 | goto _end; | ||
1704 | if ((unsigned int)min == saved_min && mindir == valdir) | ||
1705 | goto _end; | ||
1706 | params1 = kmalloc(sizeof(*params1), GFP_KERNEL); | ||
1707 | if (params1 == NULL) { | ||
1708 | kfree(save); | ||
1709 | return -ENOMEM; | ||
1710 | } | ||
1711 | *params1 = *save; | ||
1712 | max = snd_pcm_hw_param_max(pcm, params1, var, max, &maxdir); | ||
1713 | if (max < 0) { | ||
1714 | kfree(params1); | ||
1715 | goto _end; | ||
1716 | } | ||
1717 | if (boundary_nearer(max, maxdir, best, valdir, min, mindir)) { | ||
1718 | *params = *params1; | ||
1719 | last = 1; | ||
1720 | } | ||
1721 | kfree(params1); | ||
1722 | } else { | ||
1723 | *params = *save; | ||
1724 | max = snd_pcm_hw_param_max(pcm, params, var, max, &maxdir); | ||
1725 | assert(max >= 0); | ||
1726 | last = 1; | ||
1727 | } | ||
1728 | _end: | ||
1729 | kfree(save); | ||
1730 | if (last) | ||
1731 | v = snd_pcm_hw_param_last(pcm, params, var, dir); | ||
1732 | else | ||
1733 | v = snd_pcm_hw_param_first(pcm, params, var, dir); | ||
1734 | assert(v >= 0); | ||
1735 | return v; | ||
1736 | } | ||
1737 | |||
1738 | /** | ||
1739 | * snd_pcm_hw_param_choose | ||
1740 | * | ||
1741 | * Choose one configuration from configuration space defined by PARAMS | ||
1742 | * The configuration chosen is that obtained fixing in this order: | ||
1743 | * first access, first format, first subformat, min channels, | ||
1744 | * min rate, min period time, max buffer size, min tick time | ||
1745 | */ | ||
1746 | int snd_pcm_hw_params_choose(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) | ||
1747 | { | ||
1748 | int err; | ||
1749 | |||
1750 | err = snd_pcm_hw_param_first(pcm, params, SNDRV_PCM_HW_PARAM_ACCESS, NULL); | ||
1751 | assert(err >= 0); | ||
1752 | |||
1753 | err = snd_pcm_hw_param_first(pcm, params, SNDRV_PCM_HW_PARAM_FORMAT, NULL); | ||
1754 | assert(err >= 0); | ||
1755 | |||
1756 | err = snd_pcm_hw_param_first(pcm, params, SNDRV_PCM_HW_PARAM_SUBFORMAT, NULL); | ||
1757 | assert(err >= 0); | ||
1758 | |||
1759 | err = snd_pcm_hw_param_first(pcm, params, SNDRV_PCM_HW_PARAM_CHANNELS, NULL); | ||
1760 | assert(err >= 0); | ||
1761 | |||
1762 | err = snd_pcm_hw_param_first(pcm, params, SNDRV_PCM_HW_PARAM_RATE, NULL); | ||
1763 | assert(err >= 0); | ||
1764 | |||
1765 | err = snd_pcm_hw_param_first(pcm, params, SNDRV_PCM_HW_PARAM_PERIOD_TIME, NULL); | ||
1766 | assert(err >= 0); | ||
1767 | |||
1768 | err = snd_pcm_hw_param_last(pcm, params, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, NULL); | ||
1769 | assert(err >= 0); | ||
1770 | |||
1771 | err = snd_pcm_hw_param_first(pcm, params, SNDRV_PCM_HW_PARAM_TICK_TIME, NULL); | ||
1772 | assert(err >= 0); | ||
1773 | |||
1774 | return 0; | ||
1775 | } | ||
1776 | |||
1777 | #undef snd_pcm_t | ||
1778 | #undef assert | ||
1779 | |||
1780 | static int snd_pcm_lib_ioctl_reset(snd_pcm_substream_t *substream, | ||
1781 | void *arg) | ||
1782 | { | ||
1783 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
1784 | unsigned long flags; | ||
1785 | snd_pcm_stream_lock_irqsave(substream, flags); | ||
1786 | if (snd_pcm_running(substream) && | ||
1787 | snd_pcm_update_hw_ptr(substream) >= 0) | ||
1788 | runtime->status->hw_ptr %= runtime->buffer_size; | ||
1789 | else | ||
1790 | runtime->status->hw_ptr = 0; | ||
1791 | snd_pcm_stream_unlock_irqrestore(substream, flags); | ||
1792 | return 0; | ||
1793 | } | ||
1794 | |||
1795 | static int snd_pcm_lib_ioctl_channel_info(snd_pcm_substream_t *substream, | ||
1796 | void *arg) | ||
1797 | { | ||
1798 | snd_pcm_channel_info_t *info = arg; | ||
1799 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
1800 | int width; | ||
1801 | if (!(runtime->info & SNDRV_PCM_INFO_MMAP)) { | ||
1802 | info->offset = -1; | ||
1803 | return 0; | ||
1804 | } | ||
1805 | width = snd_pcm_format_physical_width(runtime->format); | ||
1806 | if (width < 0) | ||
1807 | return width; | ||
1808 | info->offset = 0; | ||
1809 | switch (runtime->access) { | ||
1810 | case SNDRV_PCM_ACCESS_MMAP_INTERLEAVED: | ||
1811 | case SNDRV_PCM_ACCESS_RW_INTERLEAVED: | ||
1812 | info->first = info->channel * width; | ||
1813 | info->step = runtime->channels * width; | ||
1814 | break; | ||
1815 | case SNDRV_PCM_ACCESS_MMAP_NONINTERLEAVED: | ||
1816 | case SNDRV_PCM_ACCESS_RW_NONINTERLEAVED: | ||
1817 | { | ||
1818 | size_t size = runtime->dma_bytes / runtime->channels; | ||
1819 | info->first = info->channel * size * 8; | ||
1820 | info->step = width; | ||
1821 | break; | ||
1822 | } | ||
1823 | default: | ||
1824 | snd_BUG(); | ||
1825 | break; | ||
1826 | } | ||
1827 | return 0; | ||
1828 | } | ||
1829 | |||
1830 | /** | ||
1831 | * snd_pcm_lib_ioctl - a generic PCM ioctl callback | ||
1832 | * @substream: the pcm substream instance | ||
1833 | * @cmd: ioctl command | ||
1834 | * @arg: ioctl argument | ||
1835 | * | ||
1836 | * Processes the generic ioctl commands for PCM. | ||
1837 | * Can be passed as the ioctl callback for PCM ops. | ||
1838 | * | ||
1839 | * Returns zero if successful, or a negative error code on failure. | ||
1840 | */ | ||
1841 | int snd_pcm_lib_ioctl(snd_pcm_substream_t *substream, | ||
1842 | unsigned int cmd, void *arg) | ||
1843 | { | ||
1844 | switch (cmd) { | ||
1845 | case SNDRV_PCM_IOCTL1_INFO: | ||
1846 | return 0; | ||
1847 | case SNDRV_PCM_IOCTL1_RESET: | ||
1848 | return snd_pcm_lib_ioctl_reset(substream, arg); | ||
1849 | case SNDRV_PCM_IOCTL1_CHANNEL_INFO: | ||
1850 | return snd_pcm_lib_ioctl_channel_info(substream, arg); | ||
1851 | } | ||
1852 | return -ENXIO; | ||
1853 | } | ||
1854 | |||
1855 | /* | ||
1856 | * Conditions | ||
1857 | */ | ||
1858 | |||
1859 | static void snd_pcm_system_tick_set(snd_pcm_substream_t *substream, | ||
1860 | unsigned long ticks) | ||
1861 | { | ||
1862 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
1863 | if (ticks == 0) | ||
1864 | del_timer(&runtime->tick_timer); | ||
1865 | else { | ||
1866 | ticks += (1000000 / HZ) - 1; | ||
1867 | ticks /= (1000000 / HZ); | ||
1868 | mod_timer(&runtime->tick_timer, jiffies + ticks); | ||
1869 | } | ||
1870 | } | ||
1871 | |||
1872 | /* Temporary alias */ | ||
1873 | void snd_pcm_tick_set(snd_pcm_substream_t *substream, unsigned long ticks) | ||
1874 | { | ||
1875 | snd_pcm_system_tick_set(substream, ticks); | ||
1876 | } | ||
1877 | |||
1878 | void snd_pcm_tick_prepare(snd_pcm_substream_t *substream) | ||
1879 | { | ||
1880 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
1881 | snd_pcm_uframes_t frames = ULONG_MAX; | ||
1882 | snd_pcm_uframes_t avail, dist; | ||
1883 | unsigned int ticks; | ||
1884 | u_int64_t n; | ||
1885 | u_int32_t r; | ||
1886 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | ||
1887 | if (runtime->silence_size >= runtime->boundary) { | ||
1888 | frames = 1; | ||
1889 | } else if (runtime->silence_size > 0 && | ||
1890 | runtime->silence_filled < runtime->buffer_size) { | ||
1891 | snd_pcm_sframes_t noise_dist; | ||
1892 | noise_dist = snd_pcm_playback_hw_avail(runtime) + runtime->silence_filled; | ||
1893 | snd_assert(noise_dist <= (snd_pcm_sframes_t)runtime->silence_threshold, ); | ||
1894 | frames = noise_dist - runtime->silence_threshold; | ||
1895 | } | ||
1896 | avail = snd_pcm_playback_avail(runtime); | ||
1897 | } else { | ||
1898 | avail = snd_pcm_capture_avail(runtime); | ||
1899 | } | ||
1900 | if (avail < runtime->control->avail_min) { | ||
1901 | snd_pcm_sframes_t n = runtime->control->avail_min - avail; | ||
1902 | if (n > 0 && frames > (snd_pcm_uframes_t)n) | ||
1903 | frames = n; | ||
1904 | } | ||
1905 | if (avail < runtime->buffer_size) { | ||
1906 | snd_pcm_sframes_t n = runtime->buffer_size - avail; | ||
1907 | if (n > 0 && frames > (snd_pcm_uframes_t)n) | ||
1908 | frames = n; | ||
1909 | } | ||
1910 | if (frames == ULONG_MAX) { | ||
1911 | snd_pcm_tick_set(substream, 0); | ||
1912 | return; | ||
1913 | } | ||
1914 | dist = runtime->status->hw_ptr - runtime->hw_ptr_base; | ||
1915 | /* Distance to next interrupt */ | ||
1916 | dist = runtime->period_size - dist % runtime->period_size; | ||
1917 | if (dist <= frames) { | ||
1918 | snd_pcm_tick_set(substream, 0); | ||
1919 | return; | ||
1920 | } | ||
1921 | /* the base time is us */ | ||
1922 | n = frames; | ||
1923 | n *= 1000000; | ||
1924 | div64_32(&n, runtime->tick_time * runtime->rate, &r); | ||
1925 | ticks = n + (r > 0 ? 1 : 0); | ||
1926 | if (ticks < runtime->sleep_min) | ||
1927 | ticks = runtime->sleep_min; | ||
1928 | snd_pcm_tick_set(substream, (unsigned long) ticks); | ||
1929 | } | ||
1930 | |||
1931 | void snd_pcm_tick_elapsed(snd_pcm_substream_t *substream) | ||
1932 | { | ||
1933 | snd_pcm_runtime_t *runtime; | ||
1934 | unsigned long flags; | ||
1935 | |||
1936 | snd_assert(substream != NULL, return); | ||
1937 | runtime = substream->runtime; | ||
1938 | snd_assert(runtime != NULL, return); | ||
1939 | |||
1940 | snd_pcm_stream_lock_irqsave(substream, flags); | ||
1941 | if (!snd_pcm_running(substream) || | ||
1942 | snd_pcm_update_hw_ptr(substream) < 0) | ||
1943 | goto _end; | ||
1944 | if (runtime->sleep_min) | ||
1945 | snd_pcm_tick_prepare(substream); | ||
1946 | _end: | ||
1947 | snd_pcm_stream_unlock_irqrestore(substream, flags); | ||
1948 | } | ||
1949 | |||
1950 | /** | ||
1951 | * snd_pcm_period_elapsed - update the pcm status for the next period | ||
1952 | * @substream: the pcm substream instance | ||
1953 | * | ||
1954 | * This function is called from the interrupt handler when the | ||
1955 | * PCM has processed the period size. It will update the current | ||
1956 | * pointer, set up the tick, wake up sleepers, etc. | ||
1957 | * | ||
1958 | * Even if more than one periods have elapsed since the last call, you | ||
1959 | * have to call this only once. | ||
1960 | */ | ||
1961 | void snd_pcm_period_elapsed(snd_pcm_substream_t *substream) | ||
1962 | { | ||
1963 | snd_pcm_runtime_t *runtime; | ||
1964 | unsigned long flags; | ||
1965 | |||
1966 | snd_assert(substream != NULL, return); | ||
1967 | runtime = substream->runtime; | ||
1968 | snd_assert(runtime != NULL, return); | ||
1969 | |||
1970 | if (runtime->transfer_ack_begin) | ||
1971 | runtime->transfer_ack_begin(substream); | ||
1972 | |||
1973 | snd_pcm_stream_lock_irqsave(substream, flags); | ||
1974 | if (!snd_pcm_running(substream) || | ||
1975 | snd_pcm_update_hw_ptr_interrupt(substream) < 0) | ||
1976 | goto _end; | ||
1977 | |||
1978 | if (substream->timer_running) | ||
1979 | snd_timer_interrupt(substream->timer, 1); | ||
1980 | if (runtime->sleep_min) | ||
1981 | snd_pcm_tick_prepare(substream); | ||
1982 | _end: | ||
1983 | snd_pcm_stream_unlock_irqrestore(substream, flags); | ||
1984 | if (runtime->transfer_ack_end) | ||
1985 | runtime->transfer_ack_end(substream); | ||
1986 | kill_fasync(&runtime->fasync, SIGIO, POLL_IN); | ||
1987 | } | ||
1988 | |||
1989 | static int snd_pcm_lib_write_transfer(snd_pcm_substream_t *substream, | ||
1990 | unsigned int hwoff, | ||
1991 | unsigned long data, unsigned int off, | ||
1992 | snd_pcm_uframes_t frames) | ||
1993 | { | ||
1994 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
1995 | int err; | ||
1996 | char __user *buf = (char __user *) data + frames_to_bytes(runtime, off); | ||
1997 | if (substream->ops->copy) { | ||
1998 | if ((err = substream->ops->copy(substream, -1, hwoff, buf, frames)) < 0) | ||
1999 | return err; | ||
2000 | } else { | ||
2001 | char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, hwoff); | ||
2002 | snd_assert(runtime->dma_area, return -EFAULT); | ||
2003 | if (copy_from_user(hwbuf, buf, frames_to_bytes(runtime, frames))) | ||
2004 | return -EFAULT; | ||
2005 | } | ||
2006 | return 0; | ||
2007 | } | ||
2008 | |||
2009 | typedef int (*transfer_f)(snd_pcm_substream_t *substream, unsigned int hwoff, | ||
2010 | unsigned long data, unsigned int off, | ||
2011 | snd_pcm_uframes_t size); | ||
2012 | |||
2013 | static snd_pcm_sframes_t snd_pcm_lib_write1(snd_pcm_substream_t *substream, | ||
2014 | unsigned long data, | ||
2015 | snd_pcm_uframes_t size, | ||
2016 | int nonblock, | ||
2017 | transfer_f transfer) | ||
2018 | { | ||
2019 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
2020 | snd_pcm_uframes_t xfer = 0; | ||
2021 | snd_pcm_uframes_t offset = 0; | ||
2022 | int err = 0; | ||
2023 | |||
2024 | if (size == 0) | ||
2025 | return 0; | ||
2026 | if (size > runtime->xfer_align) | ||
2027 | size -= size % runtime->xfer_align; | ||
2028 | |||
2029 | snd_pcm_stream_lock_irq(substream); | ||
2030 | switch (runtime->status->state) { | ||
2031 | case SNDRV_PCM_STATE_PREPARED: | ||
2032 | case SNDRV_PCM_STATE_RUNNING: | ||
2033 | case SNDRV_PCM_STATE_PAUSED: | ||
2034 | break; | ||
2035 | case SNDRV_PCM_STATE_XRUN: | ||
2036 | err = -EPIPE; | ||
2037 | goto _end_unlock; | ||
2038 | case SNDRV_PCM_STATE_SUSPENDED: | ||
2039 | err = -ESTRPIPE; | ||
2040 | goto _end_unlock; | ||
2041 | default: | ||
2042 | err = -EBADFD; | ||
2043 | goto _end_unlock; | ||
2044 | } | ||
2045 | |||
2046 | while (size > 0) { | ||
2047 | snd_pcm_uframes_t frames, appl_ptr, appl_ofs; | ||
2048 | snd_pcm_uframes_t avail; | ||
2049 | snd_pcm_uframes_t cont; | ||
2050 | if (runtime->sleep_min == 0 && runtime->status->state == SNDRV_PCM_STATE_RUNNING) | ||
2051 | snd_pcm_update_hw_ptr(substream); | ||
2052 | avail = snd_pcm_playback_avail(runtime); | ||
2053 | if (((avail < runtime->control->avail_min && size > avail) || | ||
2054 | (size >= runtime->xfer_align && avail < runtime->xfer_align))) { | ||
2055 | wait_queue_t wait; | ||
2056 | enum { READY, SIGNALED, ERROR, SUSPENDED, EXPIRED } state; | ||
2057 | long tout; | ||
2058 | |||
2059 | if (nonblock) { | ||
2060 | err = -EAGAIN; | ||
2061 | goto _end_unlock; | ||
2062 | } | ||
2063 | |||
2064 | init_waitqueue_entry(&wait, current); | ||
2065 | add_wait_queue(&runtime->sleep, &wait); | ||
2066 | while (1) { | ||
2067 | if (signal_pending(current)) { | ||
2068 | state = SIGNALED; | ||
2069 | break; | ||
2070 | } | ||
2071 | set_current_state(TASK_INTERRUPTIBLE); | ||
2072 | snd_pcm_stream_unlock_irq(substream); | ||
2073 | tout = schedule_timeout(10 * HZ); | ||
2074 | snd_pcm_stream_lock_irq(substream); | ||
2075 | if (tout == 0) { | ||
2076 | if (runtime->status->state != SNDRV_PCM_STATE_PREPARED && | ||
2077 | runtime->status->state != SNDRV_PCM_STATE_PAUSED) { | ||
2078 | state = runtime->status->state == SNDRV_PCM_STATE_SUSPENDED ? SUSPENDED : EXPIRED; | ||
2079 | break; | ||
2080 | } | ||
2081 | } | ||
2082 | switch (runtime->status->state) { | ||
2083 | case SNDRV_PCM_STATE_XRUN: | ||
2084 | case SNDRV_PCM_STATE_DRAINING: | ||
2085 | state = ERROR; | ||
2086 | goto _end_loop; | ||
2087 | case SNDRV_PCM_STATE_SUSPENDED: | ||
2088 | state = SUSPENDED; | ||
2089 | goto _end_loop; | ||
2090 | default: | ||
2091 | break; | ||
2092 | } | ||
2093 | avail = snd_pcm_playback_avail(runtime); | ||
2094 | if (avail >= runtime->control->avail_min) { | ||
2095 | state = READY; | ||
2096 | break; | ||
2097 | } | ||
2098 | } | ||
2099 | _end_loop: | ||
2100 | remove_wait_queue(&runtime->sleep, &wait); | ||
2101 | |||
2102 | switch (state) { | ||
2103 | case ERROR: | ||
2104 | err = -EPIPE; | ||
2105 | goto _end_unlock; | ||
2106 | case SUSPENDED: | ||
2107 | err = -ESTRPIPE; | ||
2108 | goto _end_unlock; | ||
2109 | case SIGNALED: | ||
2110 | err = -ERESTARTSYS; | ||
2111 | goto _end_unlock; | ||
2112 | case EXPIRED: | ||
2113 | snd_printd("playback write error (DMA or IRQ trouble?)\n"); | ||
2114 | err = -EIO; | ||
2115 | goto _end_unlock; | ||
2116 | default: | ||
2117 | break; | ||
2118 | } | ||
2119 | } | ||
2120 | if (avail > runtime->xfer_align) | ||
2121 | avail -= avail % runtime->xfer_align; | ||
2122 | frames = size > avail ? avail : size; | ||
2123 | cont = runtime->buffer_size - runtime->control->appl_ptr % runtime->buffer_size; | ||
2124 | if (frames > cont) | ||
2125 | frames = cont; | ||
2126 | snd_assert(frames != 0, snd_pcm_stream_unlock_irq(substream); return -EINVAL); | ||
2127 | appl_ptr = runtime->control->appl_ptr; | ||
2128 | appl_ofs = appl_ptr % runtime->buffer_size; | ||
2129 | snd_pcm_stream_unlock_irq(substream); | ||
2130 | if ((err = transfer(substream, appl_ofs, data, offset, frames)) < 0) | ||
2131 | goto _end; | ||
2132 | snd_pcm_stream_lock_irq(substream); | ||
2133 | switch (runtime->status->state) { | ||
2134 | case SNDRV_PCM_STATE_XRUN: | ||
2135 | err = -EPIPE; | ||
2136 | goto _end_unlock; | ||
2137 | case SNDRV_PCM_STATE_SUSPENDED: | ||
2138 | err = -ESTRPIPE; | ||
2139 | goto _end_unlock; | ||
2140 | default: | ||
2141 | break; | ||
2142 | } | ||
2143 | appl_ptr += frames; | ||
2144 | if (appl_ptr >= runtime->boundary) | ||
2145 | appl_ptr -= runtime->boundary; | ||
2146 | runtime->control->appl_ptr = appl_ptr; | ||
2147 | if (substream->ops->ack) | ||
2148 | substream->ops->ack(substream); | ||
2149 | |||
2150 | offset += frames; | ||
2151 | size -= frames; | ||
2152 | xfer += frames; | ||
2153 | if (runtime->status->state == SNDRV_PCM_STATE_PREPARED && | ||
2154 | snd_pcm_playback_hw_avail(runtime) >= (snd_pcm_sframes_t)runtime->start_threshold) { | ||
2155 | err = snd_pcm_start(substream); | ||
2156 | if (err < 0) | ||
2157 | goto _end_unlock; | ||
2158 | } | ||
2159 | if (runtime->sleep_min && | ||
2160 | runtime->status->state == SNDRV_PCM_STATE_RUNNING) | ||
2161 | snd_pcm_tick_prepare(substream); | ||
2162 | } | ||
2163 | _end_unlock: | ||
2164 | snd_pcm_stream_unlock_irq(substream); | ||
2165 | _end: | ||
2166 | return xfer > 0 ? (snd_pcm_sframes_t)xfer : err; | ||
2167 | } | ||
2168 | |||
2169 | snd_pcm_sframes_t snd_pcm_lib_write(snd_pcm_substream_t *substream, const void __user *buf, snd_pcm_uframes_t size) | ||
2170 | { | ||
2171 | snd_pcm_runtime_t *runtime; | ||
2172 | int nonblock; | ||
2173 | |||
2174 | snd_assert(substream != NULL, return -ENXIO); | ||
2175 | runtime = substream->runtime; | ||
2176 | snd_assert(runtime != NULL, return -ENXIO); | ||
2177 | snd_assert(substream->ops->copy != NULL || runtime->dma_area != NULL, return -EINVAL); | ||
2178 | if (runtime->status->state == SNDRV_PCM_STATE_OPEN) | ||
2179 | return -EBADFD; | ||
2180 | |||
2181 | snd_assert(substream->ffile != NULL, return -ENXIO); | ||
2182 | nonblock = !!(substream->ffile->f_flags & O_NONBLOCK); | ||
2183 | #if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE) | ||
2184 | if (substream->oss.oss) { | ||
2185 | snd_pcm_oss_setup_t *setup = substream->oss.setup; | ||
2186 | if (setup != NULL) { | ||
2187 | if (setup->nonblock) | ||
2188 | nonblock = 1; | ||
2189 | else if (setup->block) | ||
2190 | nonblock = 0; | ||
2191 | } | ||
2192 | } | ||
2193 | #endif | ||
2194 | |||
2195 | if (runtime->access != SNDRV_PCM_ACCESS_RW_INTERLEAVED && | ||
2196 | runtime->channels > 1) | ||
2197 | return -EINVAL; | ||
2198 | return snd_pcm_lib_write1(substream, (unsigned long)buf, size, nonblock, | ||
2199 | snd_pcm_lib_write_transfer); | ||
2200 | } | ||
2201 | |||
2202 | static int snd_pcm_lib_writev_transfer(snd_pcm_substream_t *substream, | ||
2203 | unsigned int hwoff, | ||
2204 | unsigned long data, unsigned int off, | ||
2205 | snd_pcm_uframes_t frames) | ||
2206 | { | ||
2207 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
2208 | int err; | ||
2209 | void __user **bufs = (void __user **)data; | ||
2210 | int channels = runtime->channels; | ||
2211 | int c; | ||
2212 | if (substream->ops->copy) { | ||
2213 | snd_assert(substream->ops->silence != NULL, return -EINVAL); | ||
2214 | for (c = 0; c < channels; ++c, ++bufs) { | ||
2215 | if (*bufs == NULL) { | ||
2216 | if ((err = substream->ops->silence(substream, c, hwoff, frames)) < 0) | ||
2217 | return err; | ||
2218 | } else { | ||
2219 | char __user *buf = *bufs + samples_to_bytes(runtime, off); | ||
2220 | if ((err = substream->ops->copy(substream, c, hwoff, buf, frames)) < 0) | ||
2221 | return err; | ||
2222 | } | ||
2223 | } | ||
2224 | } else { | ||
2225 | /* default transfer behaviour */ | ||
2226 | size_t dma_csize = runtime->dma_bytes / channels; | ||
2227 | snd_assert(runtime->dma_area, return -EFAULT); | ||
2228 | for (c = 0; c < channels; ++c, ++bufs) { | ||
2229 | char *hwbuf = runtime->dma_area + (c * dma_csize) + samples_to_bytes(runtime, hwoff); | ||
2230 | if (*bufs == NULL) { | ||
2231 | snd_pcm_format_set_silence(runtime->format, hwbuf, frames); | ||
2232 | } else { | ||
2233 | char __user *buf = *bufs + samples_to_bytes(runtime, off); | ||
2234 | if (copy_from_user(hwbuf, buf, samples_to_bytes(runtime, frames))) | ||
2235 | return -EFAULT; | ||
2236 | } | ||
2237 | } | ||
2238 | } | ||
2239 | return 0; | ||
2240 | } | ||
2241 | |||
2242 | snd_pcm_sframes_t snd_pcm_lib_writev(snd_pcm_substream_t *substream, | ||
2243 | void __user **bufs, | ||
2244 | snd_pcm_uframes_t frames) | ||
2245 | { | ||
2246 | snd_pcm_runtime_t *runtime; | ||
2247 | int nonblock; | ||
2248 | |||
2249 | snd_assert(substream != NULL, return -ENXIO); | ||
2250 | runtime = substream->runtime; | ||
2251 | snd_assert(runtime != NULL, return -ENXIO); | ||
2252 | snd_assert(substream->ops->copy != NULL || runtime->dma_area != NULL, return -EINVAL); | ||
2253 | if (runtime->status->state == SNDRV_PCM_STATE_OPEN) | ||
2254 | return -EBADFD; | ||
2255 | |||
2256 | snd_assert(substream->ffile != NULL, return -ENXIO); | ||
2257 | nonblock = !!(substream->ffile->f_flags & O_NONBLOCK); | ||
2258 | #if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE) | ||
2259 | if (substream->oss.oss) { | ||
2260 | snd_pcm_oss_setup_t *setup = substream->oss.setup; | ||
2261 | if (setup != NULL) { | ||
2262 | if (setup->nonblock) | ||
2263 | nonblock = 1; | ||
2264 | else if (setup->block) | ||
2265 | nonblock = 0; | ||
2266 | } | ||
2267 | } | ||
2268 | #endif | ||
2269 | |||
2270 | if (runtime->access != SNDRV_PCM_ACCESS_RW_NONINTERLEAVED) | ||
2271 | return -EINVAL; | ||
2272 | return snd_pcm_lib_write1(substream, (unsigned long)bufs, frames, | ||
2273 | nonblock, snd_pcm_lib_writev_transfer); | ||
2274 | } | ||
2275 | |||
2276 | static int snd_pcm_lib_read_transfer(snd_pcm_substream_t *substream, | ||
2277 | unsigned int hwoff, | ||
2278 | unsigned long data, unsigned int off, | ||
2279 | snd_pcm_uframes_t frames) | ||
2280 | { | ||
2281 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
2282 | int err; | ||
2283 | char __user *buf = (char __user *) data + frames_to_bytes(runtime, off); | ||
2284 | if (substream->ops->copy) { | ||
2285 | if ((err = substream->ops->copy(substream, -1, hwoff, buf, frames)) < 0) | ||
2286 | return err; | ||
2287 | } else { | ||
2288 | char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, hwoff); | ||
2289 | snd_assert(runtime->dma_area, return -EFAULT); | ||
2290 | if (copy_to_user(buf, hwbuf, frames_to_bytes(runtime, frames))) | ||
2291 | return -EFAULT; | ||
2292 | } | ||
2293 | return 0; | ||
2294 | } | ||
2295 | |||
2296 | static snd_pcm_sframes_t snd_pcm_lib_read1(snd_pcm_substream_t *substream, | ||
2297 | unsigned long data, | ||
2298 | snd_pcm_uframes_t size, | ||
2299 | int nonblock, | ||
2300 | transfer_f transfer) | ||
2301 | { | ||
2302 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
2303 | snd_pcm_uframes_t xfer = 0; | ||
2304 | snd_pcm_uframes_t offset = 0; | ||
2305 | int err = 0; | ||
2306 | |||
2307 | if (size == 0) | ||
2308 | return 0; | ||
2309 | if (size > runtime->xfer_align) | ||
2310 | size -= size % runtime->xfer_align; | ||
2311 | |||
2312 | snd_pcm_stream_lock_irq(substream); | ||
2313 | switch (runtime->status->state) { | ||
2314 | case SNDRV_PCM_STATE_PREPARED: | ||
2315 | if (size >= runtime->start_threshold) { | ||
2316 | err = snd_pcm_start(substream); | ||
2317 | if (err < 0) | ||
2318 | goto _end_unlock; | ||
2319 | } | ||
2320 | break; | ||
2321 | case SNDRV_PCM_STATE_DRAINING: | ||
2322 | case SNDRV_PCM_STATE_RUNNING: | ||
2323 | case SNDRV_PCM_STATE_PAUSED: | ||
2324 | break; | ||
2325 | case SNDRV_PCM_STATE_XRUN: | ||
2326 | err = -EPIPE; | ||
2327 | goto _end_unlock; | ||
2328 | case SNDRV_PCM_STATE_SUSPENDED: | ||
2329 | err = -ESTRPIPE; | ||
2330 | goto _end_unlock; | ||
2331 | default: | ||
2332 | err = -EBADFD; | ||
2333 | goto _end_unlock; | ||
2334 | } | ||
2335 | |||
2336 | while (size > 0) { | ||
2337 | snd_pcm_uframes_t frames, appl_ptr, appl_ofs; | ||
2338 | snd_pcm_uframes_t avail; | ||
2339 | snd_pcm_uframes_t cont; | ||
2340 | if (runtime->sleep_min == 0 && runtime->status->state == SNDRV_PCM_STATE_RUNNING) | ||
2341 | snd_pcm_update_hw_ptr(substream); | ||
2342 | __draining: | ||
2343 | avail = snd_pcm_capture_avail(runtime); | ||
2344 | if (runtime->status->state == SNDRV_PCM_STATE_DRAINING) { | ||
2345 | if (avail < runtime->xfer_align) { | ||
2346 | err = -EPIPE; | ||
2347 | goto _end_unlock; | ||
2348 | } | ||
2349 | } else if ((avail < runtime->control->avail_min && size > avail) || | ||
2350 | (size >= runtime->xfer_align && avail < runtime->xfer_align)) { | ||
2351 | wait_queue_t wait; | ||
2352 | enum { READY, SIGNALED, ERROR, SUSPENDED, EXPIRED } state; | ||
2353 | long tout; | ||
2354 | |||
2355 | if (nonblock) { | ||
2356 | err = -EAGAIN; | ||
2357 | goto _end_unlock; | ||
2358 | } | ||
2359 | |||
2360 | init_waitqueue_entry(&wait, current); | ||
2361 | add_wait_queue(&runtime->sleep, &wait); | ||
2362 | while (1) { | ||
2363 | if (signal_pending(current)) { | ||
2364 | state = SIGNALED; | ||
2365 | break; | ||
2366 | } | ||
2367 | set_current_state(TASK_INTERRUPTIBLE); | ||
2368 | snd_pcm_stream_unlock_irq(substream); | ||
2369 | tout = schedule_timeout(10 * HZ); | ||
2370 | snd_pcm_stream_lock_irq(substream); | ||
2371 | if (tout == 0) { | ||
2372 | if (runtime->status->state != SNDRV_PCM_STATE_PREPARED && | ||
2373 | runtime->status->state != SNDRV_PCM_STATE_PAUSED) { | ||
2374 | state = runtime->status->state == SNDRV_PCM_STATE_SUSPENDED ? SUSPENDED : EXPIRED; | ||
2375 | break; | ||
2376 | } | ||
2377 | } | ||
2378 | switch (runtime->status->state) { | ||
2379 | case SNDRV_PCM_STATE_XRUN: | ||
2380 | state = ERROR; | ||
2381 | goto _end_loop; | ||
2382 | case SNDRV_PCM_STATE_SUSPENDED: | ||
2383 | state = SUSPENDED; | ||
2384 | goto _end_loop; | ||
2385 | case SNDRV_PCM_STATE_DRAINING: | ||
2386 | goto __draining; | ||
2387 | default: | ||
2388 | break; | ||
2389 | } | ||
2390 | avail = snd_pcm_capture_avail(runtime); | ||
2391 | if (avail >= runtime->control->avail_min) { | ||
2392 | state = READY; | ||
2393 | break; | ||
2394 | } | ||
2395 | } | ||
2396 | _end_loop: | ||
2397 | remove_wait_queue(&runtime->sleep, &wait); | ||
2398 | |||
2399 | switch (state) { | ||
2400 | case ERROR: | ||
2401 | err = -EPIPE; | ||
2402 | goto _end_unlock; | ||
2403 | case SUSPENDED: | ||
2404 | err = -ESTRPIPE; | ||
2405 | goto _end_unlock; | ||
2406 | case SIGNALED: | ||
2407 | err = -ERESTARTSYS; | ||
2408 | goto _end_unlock; | ||
2409 | case EXPIRED: | ||
2410 | snd_printd("capture read error (DMA or IRQ trouble?)\n"); | ||
2411 | err = -EIO; | ||
2412 | goto _end_unlock; | ||
2413 | default: | ||
2414 | break; | ||
2415 | } | ||
2416 | } | ||
2417 | if (avail > runtime->xfer_align) | ||
2418 | avail -= avail % runtime->xfer_align; | ||
2419 | frames = size > avail ? avail : size; | ||
2420 | cont = runtime->buffer_size - runtime->control->appl_ptr % runtime->buffer_size; | ||
2421 | if (frames > cont) | ||
2422 | frames = cont; | ||
2423 | snd_assert(frames != 0, snd_pcm_stream_unlock_irq(substream); return -EINVAL); | ||
2424 | appl_ptr = runtime->control->appl_ptr; | ||
2425 | appl_ofs = appl_ptr % runtime->buffer_size; | ||
2426 | snd_pcm_stream_unlock_irq(substream); | ||
2427 | if ((err = transfer(substream, appl_ofs, data, offset, frames)) < 0) | ||
2428 | goto _end; | ||
2429 | snd_pcm_stream_lock_irq(substream); | ||
2430 | switch (runtime->status->state) { | ||
2431 | case SNDRV_PCM_STATE_XRUN: | ||
2432 | err = -EPIPE; | ||
2433 | goto _end_unlock; | ||
2434 | case SNDRV_PCM_STATE_SUSPENDED: | ||
2435 | err = -ESTRPIPE; | ||
2436 | goto _end_unlock; | ||
2437 | default: | ||
2438 | break; | ||
2439 | } | ||
2440 | appl_ptr += frames; | ||
2441 | if (appl_ptr >= runtime->boundary) | ||
2442 | appl_ptr -= runtime->boundary; | ||
2443 | runtime->control->appl_ptr = appl_ptr; | ||
2444 | if (substream->ops->ack) | ||
2445 | substream->ops->ack(substream); | ||
2446 | |||
2447 | offset += frames; | ||
2448 | size -= frames; | ||
2449 | xfer += frames; | ||
2450 | if (runtime->sleep_min && | ||
2451 | runtime->status->state == SNDRV_PCM_STATE_RUNNING) | ||
2452 | snd_pcm_tick_prepare(substream); | ||
2453 | } | ||
2454 | _end_unlock: | ||
2455 | snd_pcm_stream_unlock_irq(substream); | ||
2456 | _end: | ||
2457 | return xfer > 0 ? (snd_pcm_sframes_t)xfer : err; | ||
2458 | } | ||
2459 | |||
2460 | snd_pcm_sframes_t snd_pcm_lib_read(snd_pcm_substream_t *substream, void __user *buf, snd_pcm_uframes_t size) | ||
2461 | { | ||
2462 | snd_pcm_runtime_t *runtime; | ||
2463 | int nonblock; | ||
2464 | |||
2465 | snd_assert(substream != NULL, return -ENXIO); | ||
2466 | runtime = substream->runtime; | ||
2467 | snd_assert(runtime != NULL, return -ENXIO); | ||
2468 | snd_assert(substream->ops->copy != NULL || runtime->dma_area != NULL, return -EINVAL); | ||
2469 | if (runtime->status->state == SNDRV_PCM_STATE_OPEN) | ||
2470 | return -EBADFD; | ||
2471 | |||
2472 | snd_assert(substream->ffile != NULL, return -ENXIO); | ||
2473 | nonblock = !!(substream->ffile->f_flags & O_NONBLOCK); | ||
2474 | #if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE) | ||
2475 | if (substream->oss.oss) { | ||
2476 | snd_pcm_oss_setup_t *setup = substream->oss.setup; | ||
2477 | if (setup != NULL) { | ||
2478 | if (setup->nonblock) | ||
2479 | nonblock = 1; | ||
2480 | else if (setup->block) | ||
2481 | nonblock = 0; | ||
2482 | } | ||
2483 | } | ||
2484 | #endif | ||
2485 | if (runtime->access != SNDRV_PCM_ACCESS_RW_INTERLEAVED) | ||
2486 | return -EINVAL; | ||
2487 | return snd_pcm_lib_read1(substream, (unsigned long)buf, size, nonblock, snd_pcm_lib_read_transfer); | ||
2488 | } | ||
2489 | |||
2490 | static int snd_pcm_lib_readv_transfer(snd_pcm_substream_t *substream, | ||
2491 | unsigned int hwoff, | ||
2492 | unsigned long data, unsigned int off, | ||
2493 | snd_pcm_uframes_t frames) | ||
2494 | { | ||
2495 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
2496 | int err; | ||
2497 | void __user **bufs = (void __user **)data; | ||
2498 | int channels = runtime->channels; | ||
2499 | int c; | ||
2500 | if (substream->ops->copy) { | ||
2501 | for (c = 0; c < channels; ++c, ++bufs) { | ||
2502 | char __user *buf; | ||
2503 | if (*bufs == NULL) | ||
2504 | continue; | ||
2505 | buf = *bufs + samples_to_bytes(runtime, off); | ||
2506 | if ((err = substream->ops->copy(substream, c, hwoff, buf, frames)) < 0) | ||
2507 | return err; | ||
2508 | } | ||
2509 | } else { | ||
2510 | snd_pcm_uframes_t dma_csize = runtime->dma_bytes / channels; | ||
2511 | snd_assert(runtime->dma_area, return -EFAULT); | ||
2512 | for (c = 0; c < channels; ++c, ++bufs) { | ||
2513 | char *hwbuf; | ||
2514 | char __user *buf; | ||
2515 | if (*bufs == NULL) | ||
2516 | continue; | ||
2517 | |||
2518 | hwbuf = runtime->dma_area + (c * dma_csize) + samples_to_bytes(runtime, hwoff); | ||
2519 | buf = *bufs + samples_to_bytes(runtime, off); | ||
2520 | if (copy_to_user(buf, hwbuf, samples_to_bytes(runtime, frames))) | ||
2521 | return -EFAULT; | ||
2522 | } | ||
2523 | } | ||
2524 | return 0; | ||
2525 | } | ||
2526 | |||
2527 | snd_pcm_sframes_t snd_pcm_lib_readv(snd_pcm_substream_t *substream, | ||
2528 | void __user **bufs, | ||
2529 | snd_pcm_uframes_t frames) | ||
2530 | { | ||
2531 | snd_pcm_runtime_t *runtime; | ||
2532 | int nonblock; | ||
2533 | |||
2534 | snd_assert(substream != NULL, return -ENXIO); | ||
2535 | runtime = substream->runtime; | ||
2536 | snd_assert(runtime != NULL, return -ENXIO); | ||
2537 | snd_assert(substream->ops->copy != NULL || runtime->dma_area != NULL, return -EINVAL); | ||
2538 | if (runtime->status->state == SNDRV_PCM_STATE_OPEN) | ||
2539 | return -EBADFD; | ||
2540 | |||
2541 | snd_assert(substream->ffile != NULL, return -ENXIO); | ||
2542 | nonblock = !!(substream->ffile->f_flags & O_NONBLOCK); | ||
2543 | #if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE) | ||
2544 | if (substream->oss.oss) { | ||
2545 | snd_pcm_oss_setup_t *setup = substream->oss.setup; | ||
2546 | if (setup != NULL) { | ||
2547 | if (setup->nonblock) | ||
2548 | nonblock = 1; | ||
2549 | else if (setup->block) | ||
2550 | nonblock = 0; | ||
2551 | } | ||
2552 | } | ||
2553 | #endif | ||
2554 | |||
2555 | if (runtime->access != SNDRV_PCM_ACCESS_RW_NONINTERLEAVED) | ||
2556 | return -EINVAL; | ||
2557 | return snd_pcm_lib_read1(substream, (unsigned long)bufs, frames, nonblock, snd_pcm_lib_readv_transfer); | ||
2558 | } | ||
2559 | |||
2560 | /* | ||
2561 | * Exported symbols | ||
2562 | */ | ||
2563 | |||
2564 | EXPORT_SYMBOL(snd_interval_refine); | ||
2565 | EXPORT_SYMBOL(snd_interval_list); | ||
2566 | EXPORT_SYMBOL(snd_interval_ratnum); | ||
2567 | EXPORT_SYMBOL(snd_interval_muldivk); | ||
2568 | EXPORT_SYMBOL(snd_interval_mulkdiv); | ||
2569 | EXPORT_SYMBOL(snd_interval_div); | ||
2570 | EXPORT_SYMBOL(_snd_pcm_hw_params_any); | ||
2571 | EXPORT_SYMBOL(_snd_pcm_hw_param_min); | ||
2572 | EXPORT_SYMBOL(_snd_pcm_hw_param_set); | ||
2573 | EXPORT_SYMBOL(_snd_pcm_hw_param_setempty); | ||
2574 | EXPORT_SYMBOL(_snd_pcm_hw_param_setinteger); | ||
2575 | EXPORT_SYMBOL(snd_pcm_hw_param_value_min); | ||
2576 | EXPORT_SYMBOL(snd_pcm_hw_param_value_max); | ||
2577 | EXPORT_SYMBOL(snd_pcm_hw_param_mask); | ||
2578 | EXPORT_SYMBOL(snd_pcm_hw_param_first); | ||
2579 | EXPORT_SYMBOL(snd_pcm_hw_param_last); | ||
2580 | EXPORT_SYMBOL(snd_pcm_hw_param_near); | ||
2581 | EXPORT_SYMBOL(snd_pcm_hw_param_set); | ||
2582 | EXPORT_SYMBOL(snd_pcm_hw_refine); | ||
2583 | EXPORT_SYMBOL(snd_pcm_hw_params); | ||
2584 | EXPORT_SYMBOL(snd_pcm_hw_constraints_init); | ||
2585 | EXPORT_SYMBOL(snd_pcm_hw_constraints_complete); | ||
2586 | EXPORT_SYMBOL(snd_pcm_hw_constraint_list); | ||
2587 | EXPORT_SYMBOL(snd_pcm_hw_constraint_step); | ||
2588 | EXPORT_SYMBOL(snd_pcm_hw_constraint_ratnums); | ||
2589 | EXPORT_SYMBOL(snd_pcm_hw_constraint_ratdens); | ||
2590 | EXPORT_SYMBOL(snd_pcm_hw_constraint_msbits); | ||
2591 | EXPORT_SYMBOL(snd_pcm_hw_constraint_minmax); | ||
2592 | EXPORT_SYMBOL(snd_pcm_hw_constraint_integer); | ||
2593 | EXPORT_SYMBOL(snd_pcm_hw_constraint_pow2); | ||
2594 | EXPORT_SYMBOL(snd_pcm_hw_rule_add); | ||
2595 | EXPORT_SYMBOL(snd_pcm_set_ops); | ||
2596 | EXPORT_SYMBOL(snd_pcm_set_sync); | ||
2597 | EXPORT_SYMBOL(snd_pcm_lib_ioctl); | ||
2598 | EXPORT_SYMBOL(snd_pcm_stop); | ||
2599 | EXPORT_SYMBOL(snd_pcm_period_elapsed); | ||
2600 | EXPORT_SYMBOL(snd_pcm_lib_write); | ||
2601 | EXPORT_SYMBOL(snd_pcm_lib_read); | ||
2602 | EXPORT_SYMBOL(snd_pcm_lib_writev); | ||
2603 | EXPORT_SYMBOL(snd_pcm_lib_readv); | ||
2604 | EXPORT_SYMBOL(snd_pcm_lib_buffer_bytes); | ||
2605 | EXPORT_SYMBOL(snd_pcm_lib_period_bytes); | ||
2606 | /* pcm_memory.c */ | ||
2607 | EXPORT_SYMBOL(snd_pcm_lib_preallocate_free_for_all); | ||
2608 | EXPORT_SYMBOL(snd_pcm_lib_preallocate_pages); | ||
2609 | EXPORT_SYMBOL(snd_pcm_lib_preallocate_pages_for_all); | ||
2610 | EXPORT_SYMBOL(snd_pcm_sgbuf_ops_page); | ||
2611 | EXPORT_SYMBOL(snd_pcm_lib_malloc_pages); | ||
2612 | EXPORT_SYMBOL(snd_pcm_lib_free_pages); | ||
diff --git a/sound/core/pcm_memory.c b/sound/core/pcm_memory.c new file mode 100644 index 000000000000..f1d5f7a6ee0c --- /dev/null +++ b/sound/core/pcm_memory.c | |||
@@ -0,0 +1,363 @@ | |||
1 | /* | ||
2 | * Digital Audio (PCM) abstract layer | ||
3 | * Copyright (c) by Jaroslav Kysela <perex@suse.cz> | ||
4 | * | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #include <sound/driver.h> | ||
23 | #include <asm/io.h> | ||
24 | #include <linux/time.h> | ||
25 | #include <linux/init.h> | ||
26 | #include <linux/moduleparam.h> | ||
27 | #include <sound/core.h> | ||
28 | #include <sound/pcm.h> | ||
29 | #include <sound/info.h> | ||
30 | #include <sound/initval.h> | ||
31 | |||
32 | static int preallocate_dma = 1; | ||
33 | module_param(preallocate_dma, int, 0444); | ||
34 | MODULE_PARM_DESC(preallocate_dma, "Preallocate DMA memory when the PCM devices are initialized."); | ||
35 | |||
36 | static int maximum_substreams = 4; | ||
37 | module_param(maximum_substreams, int, 0444); | ||
38 | MODULE_PARM_DESC(maximum_substreams, "Maximum substreams with preallocated DMA memory."); | ||
39 | |||
40 | static const size_t snd_minimum_buffer = 16384; | ||
41 | |||
42 | |||
43 | /* | ||
44 | * try to allocate as the large pages as possible. | ||
45 | * stores the resultant memory size in *res_size. | ||
46 | * | ||
47 | * the minimum size is snd_minimum_buffer. it should be power of 2. | ||
48 | */ | ||
49 | static int preallocate_pcm_pages(snd_pcm_substream_t *substream, size_t size) | ||
50 | { | ||
51 | struct snd_dma_buffer *dmab = &substream->dma_buffer; | ||
52 | int err; | ||
53 | |||
54 | snd_assert(size > 0, return -EINVAL); | ||
55 | |||
56 | /* already reserved? */ | ||
57 | if (snd_dma_get_reserved_buf(dmab, substream->dma_buf_id) > 0) { | ||
58 | if (dmab->bytes >= size) | ||
59 | return 0; /* yes */ | ||
60 | /* no, free the reserved block */ | ||
61 | snd_dma_free_pages(dmab); | ||
62 | dmab->bytes = 0; | ||
63 | } | ||
64 | |||
65 | do { | ||
66 | if ((err = snd_dma_alloc_pages(dmab->dev.type, dmab->dev.dev, | ||
67 | size, dmab)) < 0) { | ||
68 | if (err != -ENOMEM) | ||
69 | return err; /* fatal error */ | ||
70 | } else | ||
71 | return 0; | ||
72 | size >>= 1; | ||
73 | } while (size >= snd_minimum_buffer); | ||
74 | dmab->bytes = 0; /* tell error */ | ||
75 | return 0; | ||
76 | } | ||
77 | |||
78 | /* | ||
79 | * release the preallocated buffer if not yet done. | ||
80 | */ | ||
81 | static void snd_pcm_lib_preallocate_dma_free(snd_pcm_substream_t *substream) | ||
82 | { | ||
83 | if (substream->dma_buffer.area == NULL) | ||
84 | return; | ||
85 | if (substream->dma_buf_id) | ||
86 | snd_dma_reserve_buf(&substream->dma_buffer, substream->dma_buf_id); | ||
87 | else | ||
88 | snd_dma_free_pages(&substream->dma_buffer); | ||
89 | substream->dma_buffer.area = NULL; | ||
90 | } | ||
91 | |||
92 | /** | ||
93 | * snd_pcm_lib_preallocate_free - release the preallocated buffer of the specified substream. | ||
94 | * @substream: the pcm substream instance | ||
95 | * | ||
96 | * Releases the pre-allocated buffer of the given substream. | ||
97 | * | ||
98 | * Returns zero if successful, or a negative error code on failure. | ||
99 | */ | ||
100 | int snd_pcm_lib_preallocate_free(snd_pcm_substream_t *substream) | ||
101 | { | ||
102 | snd_pcm_lib_preallocate_dma_free(substream); | ||
103 | if (substream->proc_prealloc_entry) { | ||
104 | snd_info_unregister(substream->proc_prealloc_entry); | ||
105 | substream->proc_prealloc_entry = NULL; | ||
106 | } | ||
107 | return 0; | ||
108 | } | ||
109 | |||
110 | /** | ||
111 | * snd_pcm_lib_preallocate_free_for_all - release all pre-allocated buffers on the pcm | ||
112 | * @pcm: the pcm instance | ||
113 | * | ||
114 | * Releases all the pre-allocated buffers on the given pcm. | ||
115 | * | ||
116 | * Returns zero if successful, or a negative error code on failure. | ||
117 | */ | ||
118 | int snd_pcm_lib_preallocate_free_for_all(snd_pcm_t *pcm) | ||
119 | { | ||
120 | snd_pcm_substream_t *substream; | ||
121 | int stream; | ||
122 | |||
123 | for (stream = 0; stream < 2; stream++) | ||
124 | for (substream = pcm->streams[stream].substream; substream; substream = substream->next) | ||
125 | snd_pcm_lib_preallocate_free(substream); | ||
126 | return 0; | ||
127 | } | ||
128 | |||
129 | /* | ||
130 | * read callback for prealloc proc file | ||
131 | * | ||
132 | * prints the current allocated size in kB. | ||
133 | */ | ||
134 | static void snd_pcm_lib_preallocate_proc_read(snd_info_entry_t *entry, | ||
135 | snd_info_buffer_t *buffer) | ||
136 | { | ||
137 | snd_pcm_substream_t *substream = (snd_pcm_substream_t *)entry->private_data; | ||
138 | snd_iprintf(buffer, "%lu\n", (unsigned long) substream->dma_buffer.bytes / 1024); | ||
139 | } | ||
140 | |||
141 | /* | ||
142 | * write callback for prealloc proc file | ||
143 | * | ||
144 | * accepts the preallocation size in kB. | ||
145 | */ | ||
146 | static void snd_pcm_lib_preallocate_proc_write(snd_info_entry_t *entry, | ||
147 | snd_info_buffer_t *buffer) | ||
148 | { | ||
149 | snd_pcm_substream_t *substream = (snd_pcm_substream_t *)entry->private_data; | ||
150 | char line[64], str[64]; | ||
151 | size_t size; | ||
152 | struct snd_dma_buffer new_dmab; | ||
153 | |||
154 | if (substream->runtime) { | ||
155 | buffer->error = -EBUSY; | ||
156 | return; | ||
157 | } | ||
158 | if (!snd_info_get_line(buffer, line, sizeof(line))) { | ||
159 | snd_info_get_str(str, line, sizeof(str)); | ||
160 | size = simple_strtoul(str, NULL, 10) * 1024; | ||
161 | if ((size != 0 && size < 8192) || size > substream->dma_max) { | ||
162 | buffer->error = -EINVAL; | ||
163 | return; | ||
164 | } | ||
165 | if (substream->dma_buffer.bytes == size) | ||
166 | return; | ||
167 | memset(&new_dmab, 0, sizeof(new_dmab)); | ||
168 | new_dmab.dev = substream->dma_buffer.dev; | ||
169 | if (size > 0) { | ||
170 | if (snd_dma_alloc_pages(substream->dma_buffer.dev.type, | ||
171 | substream->dma_buffer.dev.dev, | ||
172 | size, &new_dmab) < 0) { | ||
173 | buffer->error = -ENOMEM; | ||
174 | return; | ||
175 | } | ||
176 | substream->buffer_bytes_max = size; | ||
177 | } else { | ||
178 | substream->buffer_bytes_max = UINT_MAX; | ||
179 | } | ||
180 | if (substream->dma_buffer.area) | ||
181 | snd_dma_free_pages(&substream->dma_buffer); | ||
182 | substream->dma_buffer = new_dmab; | ||
183 | } else { | ||
184 | buffer->error = -EINVAL; | ||
185 | } | ||
186 | } | ||
187 | |||
188 | /* | ||
189 | * pre-allocate the buffer and create a proc file for the substream | ||
190 | */ | ||
191 | static int snd_pcm_lib_preallocate_pages1(snd_pcm_substream_t *substream, | ||
192 | size_t size, size_t max) | ||
193 | { | ||
194 | snd_info_entry_t *entry; | ||
195 | |||
196 | if (size > 0 && preallocate_dma && substream->number < maximum_substreams) | ||
197 | preallocate_pcm_pages(substream, size); | ||
198 | |||
199 | if (substream->dma_buffer.bytes > 0) | ||
200 | substream->buffer_bytes_max = substream->dma_buffer.bytes; | ||
201 | substream->dma_max = max; | ||
202 | if ((entry = snd_info_create_card_entry(substream->pcm->card, "prealloc", substream->proc_root)) != NULL) { | ||
203 | entry->c.text.read_size = 64; | ||
204 | entry->c.text.read = snd_pcm_lib_preallocate_proc_read; | ||
205 | entry->c.text.write_size = 64; | ||
206 | entry->c.text.write = snd_pcm_lib_preallocate_proc_write; | ||
207 | entry->private_data = substream; | ||
208 | if (snd_info_register(entry) < 0) { | ||
209 | snd_info_free_entry(entry); | ||
210 | entry = NULL; | ||
211 | } | ||
212 | } | ||
213 | substream->proc_prealloc_entry = entry; | ||
214 | return 0; | ||
215 | } | ||
216 | |||
217 | |||
218 | /** | ||
219 | * snd_pcm_lib_preallocate_pages - pre-allocation for the given DMA type | ||
220 | * @substream: the pcm substream instance | ||
221 | * @type: DMA type (SNDRV_DMA_TYPE_*) | ||
222 | * @data: DMA type dependant data | ||
223 | * @size: the requested pre-allocation size in bytes | ||
224 | * @max: the max. allowed pre-allocation size | ||
225 | * | ||
226 | * Do pre-allocation for the given DMA buffer type. | ||
227 | * | ||
228 | * When substream->dma_buf_id is set, the function tries to look for | ||
229 | * the reserved buffer, and the buffer is not freed but reserved at | ||
230 | * destruction time. The dma_buf_id must be unique for all systems | ||
231 | * (in the same DMA buffer type) e.g. using snd_dma_pci_buf_id(). | ||
232 | * | ||
233 | * Returns zero if successful, or a negative error code on failure. | ||
234 | */ | ||
235 | int snd_pcm_lib_preallocate_pages(snd_pcm_substream_t *substream, | ||
236 | int type, struct device *data, | ||
237 | size_t size, size_t max) | ||
238 | { | ||
239 | substream->dma_buffer.dev.type = type; | ||
240 | substream->dma_buffer.dev.dev = data; | ||
241 | return snd_pcm_lib_preallocate_pages1(substream, size, max); | ||
242 | } | ||
243 | |||
244 | /** | ||
245 | * snd_pcm_lib_preallocate_pages_for_all - pre-allocation for continous memory type (all substreams) | ||
246 | * @substream: the pcm substream instance | ||
247 | * @type: DMA type (SNDRV_DMA_TYPE_*) | ||
248 | * @data: DMA type dependant data | ||
249 | * @size: the requested pre-allocation size in bytes | ||
250 | * @max: the max. allowed pre-allocation size | ||
251 | * | ||
252 | * Do pre-allocation to all substreams of the given pcm for the | ||
253 | * specified DMA type. | ||
254 | * | ||
255 | * Returns zero if successful, or a negative error code on failure. | ||
256 | */ | ||
257 | int snd_pcm_lib_preallocate_pages_for_all(snd_pcm_t *pcm, | ||
258 | int type, void *data, | ||
259 | size_t size, size_t max) | ||
260 | { | ||
261 | snd_pcm_substream_t *substream; | ||
262 | int stream, err; | ||
263 | |||
264 | for (stream = 0; stream < 2; stream++) | ||
265 | for (substream = pcm->streams[stream].substream; substream; substream = substream->next) | ||
266 | if ((err = snd_pcm_lib_preallocate_pages(substream, type, data, size, max)) < 0) | ||
267 | return err; | ||
268 | return 0; | ||
269 | } | ||
270 | |||
271 | /** | ||
272 | * snd_pcm_sgbuf_ops_page - get the page struct at the given offset | ||
273 | * @substream: the pcm substream instance | ||
274 | * @offset: the buffer offset | ||
275 | * | ||
276 | * Returns the page struct at the given buffer offset. | ||
277 | * Used as the page callback of PCM ops. | ||
278 | */ | ||
279 | struct page *snd_pcm_sgbuf_ops_page(snd_pcm_substream_t *substream, unsigned long offset) | ||
280 | { | ||
281 | struct snd_sg_buf *sgbuf = snd_pcm_substream_sgbuf(substream); | ||
282 | |||
283 | unsigned int idx = offset >> PAGE_SHIFT; | ||
284 | if (idx >= (unsigned int)sgbuf->pages) | ||
285 | return NULL; | ||
286 | return sgbuf->page_table[idx]; | ||
287 | } | ||
288 | |||
289 | /** | ||
290 | * snd_pcm_lib_malloc_pages - allocate the DMA buffer | ||
291 | * @substream: the substream to allocate the DMA buffer to | ||
292 | * @size: the requested buffer size in bytes | ||
293 | * | ||
294 | * Allocates the DMA buffer on the BUS type given earlier to | ||
295 | * snd_pcm_lib_preallocate_xxx_pages(). | ||
296 | * | ||
297 | * Returns 1 if the buffer is changed, 0 if not changed, or a negative | ||
298 | * code on failure. | ||
299 | */ | ||
300 | int snd_pcm_lib_malloc_pages(snd_pcm_substream_t *substream, size_t size) | ||
301 | { | ||
302 | snd_pcm_runtime_t *runtime; | ||
303 | struct snd_dma_buffer *dmab = NULL; | ||
304 | |||
305 | snd_assert(substream->dma_buffer.dev.type != SNDRV_DMA_TYPE_UNKNOWN, return -EINVAL); | ||
306 | snd_assert(substream != NULL, return -EINVAL); | ||
307 | runtime = substream->runtime; | ||
308 | snd_assert(runtime != NULL, return -EINVAL); | ||
309 | |||
310 | if (runtime->dma_buffer_p) { | ||
311 | /* perphaps, we might free the large DMA memory region | ||
312 | to save some space here, but the actual solution | ||
313 | costs us less time */ | ||
314 | if (runtime->dma_buffer_p->bytes >= size) { | ||
315 | runtime->dma_bytes = size; | ||
316 | return 0; /* ok, do not change */ | ||
317 | } | ||
318 | snd_pcm_lib_free_pages(substream); | ||
319 | } | ||
320 | if (substream->dma_buffer.area != NULL && substream->dma_buffer.bytes >= size) { | ||
321 | dmab = &substream->dma_buffer; /* use the pre-allocated buffer */ | ||
322 | } else { | ||
323 | dmab = kcalloc(1, sizeof(*dmab), GFP_KERNEL); | ||
324 | if (! dmab) | ||
325 | return -ENOMEM; | ||
326 | dmab->dev = substream->dma_buffer.dev; | ||
327 | if (snd_dma_alloc_pages(substream->dma_buffer.dev.type, | ||
328 | substream->dma_buffer.dev.dev, | ||
329 | size, dmab) < 0) { | ||
330 | kfree(dmab); | ||
331 | return -ENOMEM; | ||
332 | } | ||
333 | } | ||
334 | snd_pcm_set_runtime_buffer(substream, dmab); | ||
335 | runtime->dma_bytes = size; | ||
336 | return 1; /* area was changed */ | ||
337 | } | ||
338 | |||
339 | /** | ||
340 | * snd_pcm_lib_free_pages - release the allocated DMA buffer. | ||
341 | * @substream: the substream to release the DMA buffer | ||
342 | * | ||
343 | * Releases the DMA buffer allocated via snd_pcm_lib_malloc_pages(). | ||
344 | * | ||
345 | * Returns zero if successful, or a negative error code on failure. | ||
346 | */ | ||
347 | int snd_pcm_lib_free_pages(snd_pcm_substream_t *substream) | ||
348 | { | ||
349 | snd_pcm_runtime_t *runtime; | ||
350 | |||
351 | snd_assert(substream != NULL, return -EINVAL); | ||
352 | runtime = substream->runtime; | ||
353 | snd_assert(runtime != NULL, return -EINVAL); | ||
354 | if (runtime->dma_area == NULL) | ||
355 | return 0; | ||
356 | if (runtime->dma_buffer_p != &substream->dma_buffer) { | ||
357 | /* it's a newly allocated buffer. release it now. */ | ||
358 | snd_dma_free_pages(runtime->dma_buffer_p); | ||
359 | kfree(runtime->dma_buffer_p); | ||
360 | } | ||
361 | snd_pcm_set_runtime_buffer(substream, NULL); | ||
362 | return 0; | ||
363 | } | ||
diff --git a/sound/core/pcm_misc.c b/sound/core/pcm_misc.c new file mode 100644 index 000000000000..422b8db14154 --- /dev/null +++ b/sound/core/pcm_misc.c | |||
@@ -0,0 +1,481 @@ | |||
1 | /* | ||
2 | * PCM Interface - misc routines | ||
3 | * Copyright (c) 1998 by Jaroslav Kysela <perex@suse.cz> | ||
4 | * | ||
5 | * | ||
6 | * This library is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU Library General Public License as | ||
8 | * published by the Free Software Foundation; either version 2 of | ||
9 | * the License, or (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU Library General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU Library General Public | ||
17 | * License along with this library; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #include <sound/driver.h> | ||
23 | #include <linux/time.h> | ||
24 | #include <sound/core.h> | ||
25 | #include <sound/pcm.h> | ||
26 | #define SND_PCM_FORMAT_UNKNOWN (-1) | ||
27 | |||
28 | /* NOTE: "signed" prefix must be given below since the default char is | ||
29 | * unsigned on some architectures! | ||
30 | */ | ||
31 | struct pcm_format_data { | ||
32 | unsigned char width; /* bit width */ | ||
33 | unsigned char phys; /* physical bit width */ | ||
34 | signed char le; /* 0 = big-endian, 1 = little-endian, -1 = others */ | ||
35 | signed char signd; /* 0 = unsigned, 1 = signed, -1 = others */ | ||
36 | unsigned char silence[8]; /* silence data to fill */ | ||
37 | }; | ||
38 | |||
39 | static struct pcm_format_data pcm_formats[SNDRV_PCM_FORMAT_LAST+1] = { | ||
40 | [SNDRV_PCM_FORMAT_S8] = { | ||
41 | .width = 8, .phys = 8, .le = -1, .signd = 1, | ||
42 | .silence = {}, | ||
43 | }, | ||
44 | [SNDRV_PCM_FORMAT_U8] = { | ||
45 | .width = 8, .phys = 8, .le = -1, .signd = 0, | ||
46 | .silence = { 0x80 }, | ||
47 | }, | ||
48 | [SNDRV_PCM_FORMAT_S16_LE] = { | ||
49 | .width = 16, .phys = 16, .le = 1, .signd = 1, | ||
50 | .silence = {}, | ||
51 | }, | ||
52 | [SNDRV_PCM_FORMAT_S16_BE] = { | ||
53 | .width = 16, .phys = 16, .le = 0, .signd = 1, | ||
54 | .silence = {}, | ||
55 | }, | ||
56 | [SNDRV_PCM_FORMAT_U16_LE] = { | ||
57 | .width = 16, .phys = 16, .le = 1, .signd = 0, | ||
58 | .silence = { 0x00, 0x80 }, | ||
59 | }, | ||
60 | [SNDRV_PCM_FORMAT_U16_BE] = { | ||
61 | .width = 16, .phys = 16, .le = 0, .signd = 0, | ||
62 | .silence = { 0x80, 0x00 }, | ||
63 | }, | ||
64 | [SNDRV_PCM_FORMAT_S24_LE] = { | ||
65 | .width = 24, .phys = 32, .le = 1, .signd = 1, | ||
66 | .silence = {}, | ||
67 | }, | ||
68 | [SNDRV_PCM_FORMAT_S24_BE] = { | ||
69 | .width = 24, .phys = 32, .le = 0, .signd = 1, | ||
70 | .silence = {}, | ||
71 | }, | ||
72 | [SNDRV_PCM_FORMAT_U24_LE] = { | ||
73 | .width = 24, .phys = 32, .le = 1, .signd = 0, | ||
74 | .silence = { 0x00, 0x00, 0x80 }, | ||
75 | }, | ||
76 | [SNDRV_PCM_FORMAT_U24_BE] = { | ||
77 | .width = 24, .phys = 32, .le = 0, .signd = 0, | ||
78 | .silence = { 0x80, 0x00, 0x00 }, | ||
79 | }, | ||
80 | [SNDRV_PCM_FORMAT_S32_LE] = { | ||
81 | .width = 32, .phys = 32, .le = 1, .signd = 1, | ||
82 | .silence = {}, | ||
83 | }, | ||
84 | [SNDRV_PCM_FORMAT_S32_BE] = { | ||
85 | .width = 32, .phys = 32, .le = 0, .signd = 1, | ||
86 | .silence = {}, | ||
87 | }, | ||
88 | [SNDRV_PCM_FORMAT_U32_LE] = { | ||
89 | .width = 32, .phys = 32, .le = 1, .signd = 0, | ||
90 | .silence = { 0x00, 0x00, 0x00, 0x80 }, | ||
91 | }, | ||
92 | [SNDRV_PCM_FORMAT_U32_BE] = { | ||
93 | .width = 32, .phys = 32, .le = 0, .signd = 0, | ||
94 | .silence = { 0x80, 0x00, 0x00, 0x00 }, | ||
95 | }, | ||
96 | [SNDRV_PCM_FORMAT_FLOAT_LE] = { | ||
97 | .width = 32, .phys = 32, .le = 1, .signd = -1, | ||
98 | .silence = {}, | ||
99 | }, | ||
100 | [SNDRV_PCM_FORMAT_FLOAT_BE] = { | ||
101 | .width = 32, .phys = 32, .le = 0, .signd = -1, | ||
102 | .silence = {}, | ||
103 | }, | ||
104 | [SNDRV_PCM_FORMAT_FLOAT64_LE] = { | ||
105 | .width = 64, .phys = 64, .le = 1, .signd = -1, | ||
106 | .silence = {}, | ||
107 | }, | ||
108 | [SNDRV_PCM_FORMAT_FLOAT64_BE] = { | ||
109 | .width = 64, .phys = 64, .le = 0, .signd = -1, | ||
110 | .silence = {}, | ||
111 | }, | ||
112 | [SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE] = { | ||
113 | .width = 32, .phys = 32, .le = 1, .signd = -1, | ||
114 | .silence = {}, | ||
115 | }, | ||
116 | [SNDRV_PCM_FORMAT_IEC958_SUBFRAME_BE] = { | ||
117 | .width = 32, .phys = 32, .le = 0, .signd = -1, | ||
118 | .silence = {}, | ||
119 | }, | ||
120 | [SNDRV_PCM_FORMAT_MU_LAW] = { | ||
121 | .width = 8, .phys = 8, .le = -1, .signd = -1, | ||
122 | .silence = { 0x7f }, | ||
123 | }, | ||
124 | [SNDRV_PCM_FORMAT_A_LAW] = { | ||
125 | .width = 8, .phys = 8, .le = -1, .signd = -1, | ||
126 | .silence = { 0x55 }, | ||
127 | }, | ||
128 | [SNDRV_PCM_FORMAT_IMA_ADPCM] = { | ||
129 | .width = 4, .phys = 4, .le = -1, .signd = -1, | ||
130 | .silence = {}, | ||
131 | }, | ||
132 | /* FIXME: the following three formats are not defined properly yet */ | ||
133 | [SNDRV_PCM_FORMAT_MPEG] = { | ||
134 | .le = -1, .signd = -1, | ||
135 | }, | ||
136 | [SNDRV_PCM_FORMAT_GSM] = { | ||
137 | .le = -1, .signd = -1, | ||
138 | }, | ||
139 | [SNDRV_PCM_FORMAT_SPECIAL] = { | ||
140 | .le = -1, .signd = -1, | ||
141 | }, | ||
142 | [SNDRV_PCM_FORMAT_S24_3LE] = { | ||
143 | .width = 24, .phys = 24, .le = 1, .signd = 1, | ||
144 | .silence = {}, | ||
145 | }, | ||
146 | [SNDRV_PCM_FORMAT_S24_3BE] = { | ||
147 | .width = 24, .phys = 24, .le = 0, .signd = 1, | ||
148 | .silence = {}, | ||
149 | }, | ||
150 | [SNDRV_PCM_FORMAT_U24_3LE] = { | ||
151 | .width = 24, .phys = 24, .le = 1, .signd = 0, | ||
152 | .silence = { 0x00, 0x00, 0x80 }, | ||
153 | }, | ||
154 | [SNDRV_PCM_FORMAT_U24_3BE] = { | ||
155 | .width = 24, .phys = 24, .le = 0, .signd = 0, | ||
156 | .silence = { 0x80, 0x00, 0x00 }, | ||
157 | }, | ||
158 | [SNDRV_PCM_FORMAT_S20_3LE] = { | ||
159 | .width = 20, .phys = 24, .le = 1, .signd = 1, | ||
160 | .silence = {}, | ||
161 | }, | ||
162 | [SNDRV_PCM_FORMAT_S20_3BE] = { | ||
163 | .width = 20, .phys = 24, .le = 0, .signd = 1, | ||
164 | .silence = {}, | ||
165 | }, | ||
166 | [SNDRV_PCM_FORMAT_U20_3LE] = { | ||
167 | .width = 20, .phys = 24, .le = 1, .signd = 0, | ||
168 | .silence = { 0x00, 0x00, 0x08 }, | ||
169 | }, | ||
170 | [SNDRV_PCM_FORMAT_U20_3BE] = { | ||
171 | .width = 20, .phys = 24, .le = 0, .signd = 0, | ||
172 | .silence = { 0x08, 0x00, 0x00 }, | ||
173 | }, | ||
174 | [SNDRV_PCM_FORMAT_S18_3LE] = { | ||
175 | .width = 18, .phys = 24, .le = 1, .signd = 1, | ||
176 | .silence = {}, | ||
177 | }, | ||
178 | [SNDRV_PCM_FORMAT_S18_3BE] = { | ||
179 | .width = 18, .phys = 24, .le = 0, .signd = 1, | ||
180 | .silence = {}, | ||
181 | }, | ||
182 | [SNDRV_PCM_FORMAT_U18_3LE] = { | ||
183 | .width = 18, .phys = 24, .le = 1, .signd = 0, | ||
184 | .silence = { 0x00, 0x00, 0x02 }, | ||
185 | }, | ||
186 | [SNDRV_PCM_FORMAT_U18_3BE] = { | ||
187 | .width = 18, .phys = 24, .le = 0, .signd = 0, | ||
188 | .silence = { 0x02, 0x00, 0x00 }, | ||
189 | }, | ||
190 | }; | ||
191 | |||
192 | |||
193 | /** | ||
194 | * snd_pcm_format_signed - Check the PCM format is signed linear | ||
195 | * @format: the format to check | ||
196 | * | ||
197 | * Returns 1 if the given PCM format is signed linear, 0 if unsigned | ||
198 | * linear, and a negative error code for non-linear formats. | ||
199 | */ | ||
200 | int snd_pcm_format_signed(snd_pcm_format_t format) | ||
201 | { | ||
202 | int val; | ||
203 | if (format < 0 || format > SNDRV_PCM_FORMAT_LAST) | ||
204 | return -EINVAL; | ||
205 | if ((val = pcm_formats[format].signd) < 0) | ||
206 | return -EINVAL; | ||
207 | return val; | ||
208 | } | ||
209 | |||
210 | /** | ||
211 | * snd_pcm_format_unsigned - Check the PCM format is unsigned linear | ||
212 | * @format: the format to check | ||
213 | * | ||
214 | * Returns 1 if the given PCM format is unsigned linear, 0 if signed | ||
215 | * linear, and a negative error code for non-linear formats. | ||
216 | */ | ||
217 | int snd_pcm_format_unsigned(snd_pcm_format_t format) | ||
218 | { | ||
219 | int val; | ||
220 | |||
221 | val = snd_pcm_format_signed(format); | ||
222 | if (val < 0) | ||
223 | return val; | ||
224 | return !val; | ||
225 | } | ||
226 | |||
227 | /** | ||
228 | * snd_pcm_format_linear - Check the PCM format is linear | ||
229 | * @format: the format to check | ||
230 | * | ||
231 | * Returns 1 if the given PCM format is linear, 0 if not. | ||
232 | */ | ||
233 | int snd_pcm_format_linear(snd_pcm_format_t format) | ||
234 | { | ||
235 | return snd_pcm_format_signed(format) >= 0; | ||
236 | } | ||
237 | |||
238 | /** | ||
239 | * snd_pcm_format_little_endian - Check the PCM format is little-endian | ||
240 | * @format: the format to check | ||
241 | * | ||
242 | * Returns 1 if the given PCM format is little-endian, 0 if | ||
243 | * big-endian, or a negative error code if endian not specified. | ||
244 | */ | ||
245 | int snd_pcm_format_little_endian(snd_pcm_format_t format) | ||
246 | { | ||
247 | int val; | ||
248 | if (format < 0 || format > SNDRV_PCM_FORMAT_LAST) | ||
249 | return -EINVAL; | ||
250 | if ((val = pcm_formats[format].le) < 0) | ||
251 | return -EINVAL; | ||
252 | return val; | ||
253 | } | ||
254 | |||
255 | /** | ||
256 | * snd_pcm_format_big_endian - Check the PCM format is big-endian | ||
257 | * @format: the format to check | ||
258 | * | ||
259 | * Returns 1 if the given PCM format is big-endian, 0 if | ||
260 | * little-endian, or a negative error code if endian not specified. | ||
261 | */ | ||
262 | int snd_pcm_format_big_endian(snd_pcm_format_t format) | ||
263 | { | ||
264 | int val; | ||
265 | |||
266 | val = snd_pcm_format_little_endian(format); | ||
267 | if (val < 0) | ||
268 | return val; | ||
269 | return !val; | ||
270 | } | ||
271 | |||
272 | /** | ||
273 | * snd_pcm_format_cpu_endian - Check the PCM format is CPU-endian | ||
274 | * @format: the format to check | ||
275 | * | ||
276 | * Returns 1 if the given PCM format is CPU-endian, 0 if | ||
277 | * opposite, or a negative error code if endian not specified. | ||
278 | */ | ||
279 | int snd_pcm_format_cpu_endian(snd_pcm_format_t format) | ||
280 | { | ||
281 | #ifdef SNDRV_LITTLE_ENDIAN | ||
282 | return snd_pcm_format_little_endian(format); | ||
283 | #else | ||
284 | return snd_pcm_format_big_endian(format); | ||
285 | #endif | ||
286 | } | ||
287 | |||
288 | /** | ||
289 | * snd_pcm_format_width - return the bit-width of the format | ||
290 | * @format: the format to check | ||
291 | * | ||
292 | * Returns the bit-width of the format, or a negative error code | ||
293 | * if unknown format. | ||
294 | */ | ||
295 | int snd_pcm_format_width(snd_pcm_format_t format) | ||
296 | { | ||
297 | int val; | ||
298 | if (format < 0 || format > SNDRV_PCM_FORMAT_LAST) | ||
299 | return -EINVAL; | ||
300 | if ((val = pcm_formats[format].width) == 0) | ||
301 | return -EINVAL; | ||
302 | return val; | ||
303 | } | ||
304 | |||
305 | /** | ||
306 | * snd_pcm_format_physical_width - return the physical bit-width of the format | ||
307 | * @format: the format to check | ||
308 | * | ||
309 | * Returns the physical bit-width of the format, or a negative error code | ||
310 | * if unknown format. | ||
311 | */ | ||
312 | int snd_pcm_format_physical_width(snd_pcm_format_t format) | ||
313 | { | ||
314 | int val; | ||
315 | if (format < 0 || format > SNDRV_PCM_FORMAT_LAST) | ||
316 | return -EINVAL; | ||
317 | if ((val = pcm_formats[format].phys) == 0) | ||
318 | return -EINVAL; | ||
319 | return val; | ||
320 | } | ||
321 | |||
322 | /** | ||
323 | * snd_pcm_format_size - return the byte size of samples on the given format | ||
324 | * @format: the format to check | ||
325 | * | ||
326 | * Returns the byte size of the given samples for the format, or a | ||
327 | * negative error code if unknown format. | ||
328 | */ | ||
329 | ssize_t snd_pcm_format_size(snd_pcm_format_t format, size_t samples) | ||
330 | { | ||
331 | int phys_width = snd_pcm_format_physical_width(format); | ||
332 | if (phys_width < 0) | ||
333 | return -EINVAL; | ||
334 | return samples * phys_width / 8; | ||
335 | } | ||
336 | |||
337 | /** | ||
338 | * snd_pcm_format_silence_64 - return the silent data in 8 bytes array | ||
339 | * @format: the format to check | ||
340 | * | ||
341 | * Returns the format pattern to fill or NULL if error. | ||
342 | */ | ||
343 | const unsigned char *snd_pcm_format_silence_64(snd_pcm_format_t format) | ||
344 | { | ||
345 | if (format < 0 || format > SNDRV_PCM_FORMAT_LAST) | ||
346 | return NULL; | ||
347 | if (! pcm_formats[format].phys) | ||
348 | return NULL; | ||
349 | return pcm_formats[format].silence; | ||
350 | } | ||
351 | |||
352 | /** | ||
353 | * snd_pcm_format_set_silence - set the silence data on the buffer | ||
354 | * @format: the PCM format | ||
355 | * @data: the buffer pointer | ||
356 | * @samples: the number of samples to set silence | ||
357 | * | ||
358 | * Sets the silence data on the buffer for the given samples. | ||
359 | * | ||
360 | * Returns zero if successful, or a negative error code on failure. | ||
361 | */ | ||
362 | int snd_pcm_format_set_silence(snd_pcm_format_t format, void *data, unsigned int samples) | ||
363 | { | ||
364 | int width; | ||
365 | unsigned char *dst, *pat; | ||
366 | |||
367 | if (format < 0 || format > SNDRV_PCM_FORMAT_LAST) | ||
368 | return -EINVAL; | ||
369 | if (samples == 0) | ||
370 | return 0; | ||
371 | width = pcm_formats[format].phys; /* physical width */ | ||
372 | pat = pcm_formats[format].silence; | ||
373 | if (! width) | ||
374 | return -EINVAL; | ||
375 | /* signed or 1 byte data */ | ||
376 | if (pcm_formats[format].signd == 1 || width <= 8) { | ||
377 | unsigned int bytes = samples * width / 8; | ||
378 | memset(data, *pat, bytes); | ||
379 | return 0; | ||
380 | } | ||
381 | /* non-zero samples, fill using a loop */ | ||
382 | width /= 8; | ||
383 | dst = data; | ||
384 | #if 0 | ||
385 | while (samples--) { | ||
386 | memcpy(dst, pat, width); | ||
387 | dst += width; | ||
388 | } | ||
389 | #else | ||
390 | /* a bit optimization for constant width */ | ||
391 | switch (width) { | ||
392 | case 2: | ||
393 | while (samples--) { | ||
394 | memcpy(dst, pat, 2); | ||
395 | dst += 2; | ||
396 | } | ||
397 | break; | ||
398 | case 3: | ||
399 | while (samples--) { | ||
400 | memcpy(dst, pat, 3); | ||
401 | dst += 3; | ||
402 | } | ||
403 | break; | ||
404 | case 4: | ||
405 | while (samples--) { | ||
406 | memcpy(dst, pat, 4); | ||
407 | dst += 4; | ||
408 | } | ||
409 | break; | ||
410 | case 8: | ||
411 | while (samples--) { | ||
412 | memcpy(dst, pat, 8); | ||
413 | dst += 8; | ||
414 | } | ||
415 | break; | ||
416 | } | ||
417 | #endif | ||
418 | return 0; | ||
419 | } | ||
420 | |||
421 | /* [width][unsigned][bigendian] */ | ||
422 | static int linear_formats[4][2][2] = { | ||
423 | {{ SNDRV_PCM_FORMAT_S8, SNDRV_PCM_FORMAT_S8}, | ||
424 | { SNDRV_PCM_FORMAT_U8, SNDRV_PCM_FORMAT_U8}}, | ||
425 | {{SNDRV_PCM_FORMAT_S16_LE, SNDRV_PCM_FORMAT_S16_BE}, | ||
426 | {SNDRV_PCM_FORMAT_U16_LE, SNDRV_PCM_FORMAT_U16_BE}}, | ||
427 | {{SNDRV_PCM_FORMAT_S24_LE, SNDRV_PCM_FORMAT_S24_BE}, | ||
428 | {SNDRV_PCM_FORMAT_U24_LE, SNDRV_PCM_FORMAT_U24_BE}}, | ||
429 | {{SNDRV_PCM_FORMAT_S32_LE, SNDRV_PCM_FORMAT_S32_BE}, | ||
430 | {SNDRV_PCM_FORMAT_U32_LE, SNDRV_PCM_FORMAT_U32_BE}} | ||
431 | }; | ||
432 | |||
433 | /** | ||
434 | * snd_pcm_build_linear_format - return the suitable linear format for the given condition | ||
435 | * @width: the bit-width | ||
436 | * @unsignd: 1 if unsigned, 0 if signed. | ||
437 | * @big_endian: 1 if big-endian, 0 if little-endian | ||
438 | * | ||
439 | * Returns the suitable linear format for the given condition. | ||
440 | */ | ||
441 | snd_pcm_format_t snd_pcm_build_linear_format(int width, int unsignd, int big_endian) | ||
442 | { | ||
443 | if (width & 7) | ||
444 | return SND_PCM_FORMAT_UNKNOWN; | ||
445 | width = (width / 8) - 1; | ||
446 | if (width < 0 || width >= 4) | ||
447 | return SND_PCM_FORMAT_UNKNOWN; | ||
448 | return linear_formats[width][!!unsignd][!!big_endian]; | ||
449 | } | ||
450 | |||
451 | /** | ||
452 | * snd_pcm_limit_hw_rates - determine rate_min/rate_max fields | ||
453 | * @runtime: the runtime instance | ||
454 | * | ||
455 | * Determines the rate_min and rate_max fields from the rates bits of | ||
456 | * the given runtime->hw. | ||
457 | * | ||
458 | * Returns zero if successful. | ||
459 | */ | ||
460 | int snd_pcm_limit_hw_rates(snd_pcm_runtime_t *runtime) | ||
461 | { | ||
462 | static unsigned rates[] = { | ||
463 | /* ATTENTION: these values depend on the definition in pcm.h! */ | ||
464 | 5512, 8000, 11025, 16000, 22050, 32000, 44100, 48000, | ||
465 | 64000, 88200, 96000, 176400, 192000 | ||
466 | }; | ||
467 | int i; | ||
468 | for (i = 0; i < (int)ARRAY_SIZE(rates); i++) { | ||
469 | if (runtime->hw.rates & (1 << i)) { | ||
470 | runtime->hw.rate_min = rates[i]; | ||
471 | break; | ||
472 | } | ||
473 | } | ||
474 | for (i = (int)ARRAY_SIZE(rates) - 1; i >= 0; i--) { | ||
475 | if (runtime->hw.rates & (1 << i)) { | ||
476 | runtime->hw.rate_max = rates[i]; | ||
477 | break; | ||
478 | } | ||
479 | } | ||
480 | return 0; | ||
481 | } | ||
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c new file mode 100644 index 000000000000..cad9bbde9986 --- /dev/null +++ b/sound/core/pcm_native.c | |||
@@ -0,0 +1,3364 @@ | |||
1 | /* | ||
2 | * Digital Audio (PCM) abstract layer | ||
3 | * Copyright (c) by Jaroslav Kysela <perex@suse.cz> | ||
4 | * | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #include <sound/driver.h> | ||
23 | #include <linux/mm.h> | ||
24 | #include <linux/smp_lock.h> | ||
25 | #include <linux/file.h> | ||
26 | #include <linux/slab.h> | ||
27 | #include <linux/time.h> | ||
28 | #include <linux/uio.h> | ||
29 | #include <sound/core.h> | ||
30 | #include <sound/control.h> | ||
31 | #include <sound/info.h> | ||
32 | #include <sound/pcm.h> | ||
33 | #include <sound/pcm_params.h> | ||
34 | #include <sound/timer.h> | ||
35 | #include <sound/minors.h> | ||
36 | #include <asm/io.h> | ||
37 | |||
38 | /* | ||
39 | * Compatibility | ||
40 | */ | ||
41 | |||
42 | struct sndrv_pcm_hw_params_old { | ||
43 | unsigned int flags; | ||
44 | unsigned int masks[SNDRV_PCM_HW_PARAM_SUBFORMAT - | ||
45 | SNDRV_PCM_HW_PARAM_ACCESS + 1]; | ||
46 | struct sndrv_interval intervals[SNDRV_PCM_HW_PARAM_TICK_TIME - | ||
47 | SNDRV_PCM_HW_PARAM_SAMPLE_BITS + 1]; | ||
48 | unsigned int rmask; | ||
49 | unsigned int cmask; | ||
50 | unsigned int info; | ||
51 | unsigned int msbits; | ||
52 | unsigned int rate_num; | ||
53 | unsigned int rate_den; | ||
54 | sndrv_pcm_uframes_t fifo_size; | ||
55 | unsigned char reserved[64]; | ||
56 | }; | ||
57 | |||
58 | #define SNDRV_PCM_IOCTL_HW_REFINE_OLD _IOWR('A', 0x10, struct sndrv_pcm_hw_params_old) | ||
59 | #define SNDRV_PCM_IOCTL_HW_PARAMS_OLD _IOWR('A', 0x11, struct sndrv_pcm_hw_params_old) | ||
60 | |||
61 | static int snd_pcm_hw_refine_old_user(snd_pcm_substream_t * substream, struct sndrv_pcm_hw_params_old __user * _oparams); | ||
62 | static int snd_pcm_hw_params_old_user(snd_pcm_substream_t * substream, struct sndrv_pcm_hw_params_old __user * _oparams); | ||
63 | |||
64 | /* | ||
65 | * | ||
66 | */ | ||
67 | |||
68 | DEFINE_RWLOCK(snd_pcm_link_rwlock); | ||
69 | static DECLARE_RWSEM(snd_pcm_link_rwsem); | ||
70 | |||
71 | |||
72 | static inline mm_segment_t snd_enter_user(void) | ||
73 | { | ||
74 | mm_segment_t fs = get_fs(); | ||
75 | set_fs(get_ds()); | ||
76 | return fs; | ||
77 | } | ||
78 | |||
79 | static inline void snd_leave_user(mm_segment_t fs) | ||
80 | { | ||
81 | set_fs(fs); | ||
82 | } | ||
83 | |||
84 | |||
85 | |||
86 | int snd_pcm_info(snd_pcm_substream_t * substream, snd_pcm_info_t *info) | ||
87 | { | ||
88 | snd_pcm_runtime_t * runtime; | ||
89 | snd_pcm_t *pcm = substream->pcm; | ||
90 | snd_pcm_str_t *pstr = substream->pstr; | ||
91 | |||
92 | snd_assert(substream != NULL, return -ENXIO); | ||
93 | memset(info, 0, sizeof(*info)); | ||
94 | info->card = pcm->card->number; | ||
95 | info->device = pcm->device; | ||
96 | info->stream = substream->stream; | ||
97 | info->subdevice = substream->number; | ||
98 | strlcpy(info->id, pcm->id, sizeof(info->id)); | ||
99 | strlcpy(info->name, pcm->name, sizeof(info->name)); | ||
100 | info->dev_class = pcm->dev_class; | ||
101 | info->dev_subclass = pcm->dev_subclass; | ||
102 | info->subdevices_count = pstr->substream_count; | ||
103 | info->subdevices_avail = pstr->substream_count - pstr->substream_opened; | ||
104 | strlcpy(info->subname, substream->name, sizeof(info->subname)); | ||
105 | runtime = substream->runtime; | ||
106 | /* AB: FIXME!!! This is definitely nonsense */ | ||
107 | if (runtime) { | ||
108 | info->sync = runtime->sync; | ||
109 | substream->ops->ioctl(substream, SNDRV_PCM_IOCTL1_INFO, info); | ||
110 | } | ||
111 | return 0; | ||
112 | } | ||
113 | |||
114 | int snd_pcm_info_user(snd_pcm_substream_t * substream, snd_pcm_info_t __user * _info) | ||
115 | { | ||
116 | snd_pcm_info_t *info; | ||
117 | int err; | ||
118 | |||
119 | info = kmalloc(sizeof(*info), GFP_KERNEL); | ||
120 | if (! info) | ||
121 | return -ENOMEM; | ||
122 | err = snd_pcm_info(substream, info); | ||
123 | if (err >= 0) { | ||
124 | if (copy_to_user(_info, info, sizeof(*info))) | ||
125 | err = -EFAULT; | ||
126 | } | ||
127 | kfree(info); | ||
128 | return err; | ||
129 | } | ||
130 | |||
131 | #undef RULES_DEBUG | ||
132 | |||
133 | #ifdef RULES_DEBUG | ||
134 | #define HW_PARAM(v) [SNDRV_PCM_HW_PARAM_##v] = #v | ||
135 | char *snd_pcm_hw_param_names[] = { | ||
136 | HW_PARAM(ACCESS), | ||
137 | HW_PARAM(FORMAT), | ||
138 | HW_PARAM(SUBFORMAT), | ||
139 | HW_PARAM(SAMPLE_BITS), | ||
140 | HW_PARAM(FRAME_BITS), | ||
141 | HW_PARAM(CHANNELS), | ||
142 | HW_PARAM(RATE), | ||
143 | HW_PARAM(PERIOD_TIME), | ||
144 | HW_PARAM(PERIOD_SIZE), | ||
145 | HW_PARAM(PERIOD_BYTES), | ||
146 | HW_PARAM(PERIODS), | ||
147 | HW_PARAM(BUFFER_TIME), | ||
148 | HW_PARAM(BUFFER_SIZE), | ||
149 | HW_PARAM(BUFFER_BYTES), | ||
150 | HW_PARAM(TICK_TIME), | ||
151 | }; | ||
152 | #endif | ||
153 | |||
154 | int snd_pcm_hw_refine(snd_pcm_substream_t *substream, | ||
155 | snd_pcm_hw_params_t *params) | ||
156 | { | ||
157 | unsigned int k; | ||
158 | snd_pcm_hardware_t *hw; | ||
159 | snd_interval_t *i = NULL; | ||
160 | snd_mask_t *m = NULL; | ||
161 | snd_pcm_hw_constraints_t *constrs = &substream->runtime->hw_constraints; | ||
162 | unsigned int rstamps[constrs->rules_num]; | ||
163 | unsigned int vstamps[SNDRV_PCM_HW_PARAM_LAST_INTERVAL + 1]; | ||
164 | unsigned int stamp = 2; | ||
165 | int changed, again; | ||
166 | |||
167 | params->info = 0; | ||
168 | params->fifo_size = 0; | ||
169 | if (params->rmask & (1 << SNDRV_PCM_HW_PARAM_SAMPLE_BITS)) | ||
170 | params->msbits = 0; | ||
171 | if (params->rmask & (1 << SNDRV_PCM_HW_PARAM_RATE)) { | ||
172 | params->rate_num = 0; | ||
173 | params->rate_den = 0; | ||
174 | } | ||
175 | |||
176 | for (k = SNDRV_PCM_HW_PARAM_FIRST_MASK; k <= SNDRV_PCM_HW_PARAM_LAST_MASK; k++) { | ||
177 | m = hw_param_mask(params, k); | ||
178 | if (snd_mask_empty(m)) | ||
179 | return -EINVAL; | ||
180 | if (!(params->rmask & (1 << k))) | ||
181 | continue; | ||
182 | #ifdef RULES_DEBUG | ||
183 | printk("%s = ", snd_pcm_hw_param_names[k]); | ||
184 | printk("%04x%04x%04x%04x -> ", m->bits[3], m->bits[2], m->bits[1], m->bits[0]); | ||
185 | #endif | ||
186 | changed = snd_mask_refine(m, constrs_mask(constrs, k)); | ||
187 | #ifdef RULES_DEBUG | ||
188 | printk("%04x%04x%04x%04x\n", m->bits[3], m->bits[2], m->bits[1], m->bits[0]); | ||
189 | #endif | ||
190 | if (changed) | ||
191 | params->cmask |= 1 << k; | ||
192 | if (changed < 0) | ||
193 | return changed; | ||
194 | } | ||
195 | |||
196 | for (k = SNDRV_PCM_HW_PARAM_FIRST_INTERVAL; k <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; k++) { | ||
197 | i = hw_param_interval(params, k); | ||
198 | if (snd_interval_empty(i)) | ||
199 | return -EINVAL; | ||
200 | if (!(params->rmask & (1 << k))) | ||
201 | continue; | ||
202 | #ifdef RULES_DEBUG | ||
203 | printk("%s = ", snd_pcm_hw_param_names[k]); | ||
204 | if (i->empty) | ||
205 | printk("empty"); | ||
206 | else | ||
207 | printk("%c%u %u%c", | ||
208 | i->openmin ? '(' : '[', i->min, | ||
209 | i->max, i->openmax ? ')' : ']'); | ||
210 | printk(" -> "); | ||
211 | #endif | ||
212 | changed = snd_interval_refine(i, constrs_interval(constrs, k)); | ||
213 | #ifdef RULES_DEBUG | ||
214 | if (i->empty) | ||
215 | printk("empty\n"); | ||
216 | else | ||
217 | printk("%c%u %u%c\n", | ||
218 | i->openmin ? '(' : '[', i->min, | ||
219 | i->max, i->openmax ? ')' : ']'); | ||
220 | #endif | ||
221 | if (changed) | ||
222 | params->cmask |= 1 << k; | ||
223 | if (changed < 0) | ||
224 | return changed; | ||
225 | } | ||
226 | |||
227 | for (k = 0; k < constrs->rules_num; k++) | ||
228 | rstamps[k] = 0; | ||
229 | for (k = 0; k <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; k++) | ||
230 | vstamps[k] = (params->rmask & (1 << k)) ? 1 : 0; | ||
231 | do { | ||
232 | again = 0; | ||
233 | for (k = 0; k < constrs->rules_num; k++) { | ||
234 | snd_pcm_hw_rule_t *r = &constrs->rules[k]; | ||
235 | unsigned int d; | ||
236 | int doit = 0; | ||
237 | if (r->cond && !(r->cond & params->flags)) | ||
238 | continue; | ||
239 | for (d = 0; r->deps[d] >= 0; d++) { | ||
240 | if (vstamps[r->deps[d]] > rstamps[k]) { | ||
241 | doit = 1; | ||
242 | break; | ||
243 | } | ||
244 | } | ||
245 | if (!doit) | ||
246 | continue; | ||
247 | #ifdef RULES_DEBUG | ||
248 | printk("Rule %d [%p]: ", k, r->func); | ||
249 | if (r->var >= 0) { | ||
250 | printk("%s = ", snd_pcm_hw_param_names[r->var]); | ||
251 | if (hw_is_mask(r->var)) { | ||
252 | m = hw_param_mask(params, r->var); | ||
253 | printk("%x", *m->bits); | ||
254 | } else { | ||
255 | i = hw_param_interval(params, r->var); | ||
256 | if (i->empty) | ||
257 | printk("empty"); | ||
258 | else | ||
259 | printk("%c%u %u%c", | ||
260 | i->openmin ? '(' : '[', i->min, | ||
261 | i->max, i->openmax ? ')' : ']'); | ||
262 | } | ||
263 | } | ||
264 | #endif | ||
265 | changed = r->func(params, r); | ||
266 | #ifdef RULES_DEBUG | ||
267 | if (r->var >= 0) { | ||
268 | printk(" -> "); | ||
269 | if (hw_is_mask(r->var)) | ||
270 | printk("%x", *m->bits); | ||
271 | else { | ||
272 | if (i->empty) | ||
273 | printk("empty"); | ||
274 | else | ||
275 | printk("%c%u %u%c", | ||
276 | i->openmin ? '(' : '[', i->min, | ||
277 | i->max, i->openmax ? ')' : ']'); | ||
278 | } | ||
279 | } | ||
280 | printk("\n"); | ||
281 | #endif | ||
282 | rstamps[k] = stamp; | ||
283 | if (changed && r->var >= 0) { | ||
284 | params->cmask |= (1 << r->var); | ||
285 | vstamps[r->var] = stamp; | ||
286 | again = 1; | ||
287 | } | ||
288 | if (changed < 0) | ||
289 | return changed; | ||
290 | stamp++; | ||
291 | } | ||
292 | } while (again); | ||
293 | if (!params->msbits) { | ||
294 | i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS); | ||
295 | if (snd_interval_single(i)) | ||
296 | params->msbits = snd_interval_value(i); | ||
297 | } | ||
298 | |||
299 | if (!params->rate_den) { | ||
300 | i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); | ||
301 | if (snd_interval_single(i)) { | ||
302 | params->rate_num = snd_interval_value(i); | ||
303 | params->rate_den = 1; | ||
304 | } | ||
305 | } | ||
306 | |||
307 | hw = &substream->runtime->hw; | ||
308 | if (!params->info) | ||
309 | params->info = hw->info; | ||
310 | if (!params->fifo_size) | ||
311 | params->fifo_size = hw->fifo_size; | ||
312 | params->rmask = 0; | ||
313 | return 0; | ||
314 | } | ||
315 | |||
316 | static int snd_pcm_hw_refine_user(snd_pcm_substream_t * substream, snd_pcm_hw_params_t __user * _params) | ||
317 | { | ||
318 | snd_pcm_hw_params_t *params; | ||
319 | int err; | ||
320 | |||
321 | params = kmalloc(sizeof(*params), GFP_KERNEL); | ||
322 | if (!params) { | ||
323 | err = -ENOMEM; | ||
324 | goto out; | ||
325 | } | ||
326 | if (copy_from_user(params, _params, sizeof(*params))) { | ||
327 | err = -EFAULT; | ||
328 | goto out; | ||
329 | } | ||
330 | err = snd_pcm_hw_refine(substream, params); | ||
331 | if (copy_to_user(_params, params, sizeof(*params))) { | ||
332 | if (!err) | ||
333 | err = -EFAULT; | ||
334 | } | ||
335 | out: | ||
336 | kfree(params); | ||
337 | return err; | ||
338 | } | ||
339 | |||
340 | int snd_pcm_hw_params(snd_pcm_substream_t *substream, | ||
341 | snd_pcm_hw_params_t *params) | ||
342 | { | ||
343 | snd_pcm_runtime_t *runtime; | ||
344 | int err; | ||
345 | unsigned int bits; | ||
346 | snd_pcm_uframes_t frames; | ||
347 | |||
348 | snd_assert(substream != NULL, return -ENXIO); | ||
349 | runtime = substream->runtime; | ||
350 | snd_assert(runtime != NULL, return -ENXIO); | ||
351 | snd_pcm_stream_lock_irq(substream); | ||
352 | switch (runtime->status->state) { | ||
353 | case SNDRV_PCM_STATE_OPEN: | ||
354 | case SNDRV_PCM_STATE_SETUP: | ||
355 | case SNDRV_PCM_STATE_PREPARED: | ||
356 | break; | ||
357 | default: | ||
358 | snd_pcm_stream_unlock_irq(substream); | ||
359 | return -EBADFD; | ||
360 | } | ||
361 | snd_pcm_stream_unlock_irq(substream); | ||
362 | #if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE) | ||
363 | if (!substream->oss.oss) | ||
364 | #endif | ||
365 | if (atomic_read(&runtime->mmap_count)) | ||
366 | return -EBADFD; | ||
367 | |||
368 | params->rmask = ~0U; | ||
369 | err = snd_pcm_hw_refine(substream, params); | ||
370 | if (err < 0) | ||
371 | goto _error; | ||
372 | |||
373 | err = snd_pcm_hw_params_choose(substream, params); | ||
374 | if (err < 0) | ||
375 | goto _error; | ||
376 | |||
377 | if (substream->ops->hw_params != NULL) { | ||
378 | err = substream->ops->hw_params(substream, params); | ||
379 | if (err < 0) | ||
380 | goto _error; | ||
381 | } | ||
382 | |||
383 | runtime->access = params_access(params); | ||
384 | runtime->format = params_format(params); | ||
385 | runtime->subformat = params_subformat(params); | ||
386 | runtime->channels = params_channels(params); | ||
387 | runtime->rate = params_rate(params); | ||
388 | runtime->period_size = params_period_size(params); | ||
389 | runtime->periods = params_periods(params); | ||
390 | runtime->buffer_size = params_buffer_size(params); | ||
391 | runtime->tick_time = params_tick_time(params); | ||
392 | runtime->info = params->info; | ||
393 | runtime->rate_num = params->rate_num; | ||
394 | runtime->rate_den = params->rate_den; | ||
395 | |||
396 | bits = snd_pcm_format_physical_width(runtime->format); | ||
397 | runtime->sample_bits = bits; | ||
398 | bits *= runtime->channels; | ||
399 | runtime->frame_bits = bits; | ||
400 | frames = 1; | ||
401 | while (bits % 8 != 0) { | ||
402 | bits *= 2; | ||
403 | frames *= 2; | ||
404 | } | ||
405 | runtime->byte_align = bits / 8; | ||
406 | runtime->min_align = frames; | ||
407 | |||
408 | /* Default sw params */ | ||
409 | runtime->tstamp_mode = SNDRV_PCM_TSTAMP_NONE; | ||
410 | runtime->period_step = 1; | ||
411 | runtime->sleep_min = 0; | ||
412 | runtime->control->avail_min = runtime->period_size; | ||
413 | runtime->xfer_align = runtime->period_size; | ||
414 | runtime->start_threshold = 1; | ||
415 | runtime->stop_threshold = runtime->buffer_size; | ||
416 | runtime->silence_threshold = 0; | ||
417 | runtime->silence_size = 0; | ||
418 | runtime->boundary = runtime->buffer_size; | ||
419 | while (runtime->boundary * 2 <= LONG_MAX - runtime->buffer_size) | ||
420 | runtime->boundary *= 2; | ||
421 | |||
422 | snd_pcm_timer_resolution_change(substream); | ||
423 | runtime->status->state = SNDRV_PCM_STATE_SETUP; | ||
424 | return 0; | ||
425 | _error: | ||
426 | /* hardware might be unuseable from this time, | ||
427 | so we force application to retry to set | ||
428 | the correct hardware parameter settings */ | ||
429 | runtime->status->state = SNDRV_PCM_STATE_OPEN; | ||
430 | if (substream->ops->hw_free != NULL) | ||
431 | substream->ops->hw_free(substream); | ||
432 | return err; | ||
433 | } | ||
434 | |||
435 | static int snd_pcm_hw_params_user(snd_pcm_substream_t * substream, snd_pcm_hw_params_t __user * _params) | ||
436 | { | ||
437 | snd_pcm_hw_params_t *params; | ||
438 | int err; | ||
439 | |||
440 | params = kmalloc(sizeof(*params), GFP_KERNEL); | ||
441 | if (!params) { | ||
442 | err = -ENOMEM; | ||
443 | goto out; | ||
444 | } | ||
445 | if (copy_from_user(params, _params, sizeof(*params))) { | ||
446 | err = -EFAULT; | ||
447 | goto out; | ||
448 | } | ||
449 | err = snd_pcm_hw_params(substream, params); | ||
450 | if (copy_to_user(_params, params, sizeof(*params))) { | ||
451 | if (!err) | ||
452 | err = -EFAULT; | ||
453 | } | ||
454 | out: | ||
455 | kfree(params); | ||
456 | return err; | ||
457 | } | ||
458 | |||
459 | static int snd_pcm_hw_free(snd_pcm_substream_t * substream) | ||
460 | { | ||
461 | snd_pcm_runtime_t *runtime; | ||
462 | int result = 0; | ||
463 | |||
464 | snd_assert(substream != NULL, return -ENXIO); | ||
465 | runtime = substream->runtime; | ||
466 | snd_assert(runtime != NULL, return -ENXIO); | ||
467 | snd_pcm_stream_lock_irq(substream); | ||
468 | switch (runtime->status->state) { | ||
469 | case SNDRV_PCM_STATE_SETUP: | ||
470 | case SNDRV_PCM_STATE_PREPARED: | ||
471 | break; | ||
472 | default: | ||
473 | snd_pcm_stream_unlock_irq(substream); | ||
474 | return -EBADFD; | ||
475 | } | ||
476 | snd_pcm_stream_unlock_irq(substream); | ||
477 | if (atomic_read(&runtime->mmap_count)) | ||
478 | return -EBADFD; | ||
479 | if (substream->ops->hw_free) | ||
480 | result = substream->ops->hw_free(substream); | ||
481 | runtime->status->state = SNDRV_PCM_STATE_OPEN; | ||
482 | return result; | ||
483 | } | ||
484 | |||
485 | static int snd_pcm_sw_params(snd_pcm_substream_t * substream, snd_pcm_sw_params_t *params) | ||
486 | { | ||
487 | snd_pcm_runtime_t *runtime; | ||
488 | |||
489 | snd_assert(substream != NULL, return -ENXIO); | ||
490 | runtime = substream->runtime; | ||
491 | snd_assert(runtime != NULL, return -ENXIO); | ||
492 | snd_pcm_stream_lock_irq(substream); | ||
493 | if (runtime->status->state == SNDRV_PCM_STATE_OPEN) { | ||
494 | snd_pcm_stream_unlock_irq(substream); | ||
495 | return -EBADFD; | ||
496 | } | ||
497 | snd_pcm_stream_unlock_irq(substream); | ||
498 | |||
499 | if (params->tstamp_mode > SNDRV_PCM_TSTAMP_LAST) | ||
500 | return -EINVAL; | ||
501 | if (params->avail_min == 0) | ||
502 | return -EINVAL; | ||
503 | if (params->xfer_align == 0 || | ||
504 | params->xfer_align % runtime->min_align != 0) | ||
505 | return -EINVAL; | ||
506 | if (params->silence_size >= runtime->boundary) { | ||
507 | if (params->silence_threshold != 0) | ||
508 | return -EINVAL; | ||
509 | } else { | ||
510 | if (params->silence_size > params->silence_threshold) | ||
511 | return -EINVAL; | ||
512 | if (params->silence_threshold > runtime->buffer_size) | ||
513 | return -EINVAL; | ||
514 | } | ||
515 | snd_pcm_stream_lock_irq(substream); | ||
516 | runtime->tstamp_mode = params->tstamp_mode; | ||
517 | runtime->sleep_min = params->sleep_min; | ||
518 | runtime->period_step = params->period_step; | ||
519 | runtime->control->avail_min = params->avail_min; | ||
520 | runtime->start_threshold = params->start_threshold; | ||
521 | runtime->stop_threshold = params->stop_threshold; | ||
522 | runtime->silence_threshold = params->silence_threshold; | ||
523 | runtime->silence_size = params->silence_size; | ||
524 | runtime->xfer_align = params->xfer_align; | ||
525 | params->boundary = runtime->boundary; | ||
526 | if (snd_pcm_running(substream)) { | ||
527 | if (runtime->sleep_min) | ||
528 | snd_pcm_tick_prepare(substream); | ||
529 | else | ||
530 | snd_pcm_tick_set(substream, 0); | ||
531 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && | ||
532 | runtime->silence_size > 0) | ||
533 | snd_pcm_playback_silence(substream, ULONG_MAX); | ||
534 | wake_up(&runtime->sleep); | ||
535 | } | ||
536 | snd_pcm_stream_unlock_irq(substream); | ||
537 | return 0; | ||
538 | } | ||
539 | |||
540 | static int snd_pcm_sw_params_user(snd_pcm_substream_t * substream, snd_pcm_sw_params_t __user * _params) | ||
541 | { | ||
542 | snd_pcm_sw_params_t params; | ||
543 | int err; | ||
544 | if (copy_from_user(¶ms, _params, sizeof(params))) | ||
545 | return -EFAULT; | ||
546 | err = snd_pcm_sw_params(substream, ¶ms); | ||
547 | if (copy_to_user(_params, ¶ms, sizeof(params))) | ||
548 | return -EFAULT; | ||
549 | return err; | ||
550 | } | ||
551 | |||
552 | int snd_pcm_status(snd_pcm_substream_t *substream, | ||
553 | snd_pcm_status_t *status) | ||
554 | { | ||
555 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
556 | |||
557 | snd_pcm_stream_lock_irq(substream); | ||
558 | status->state = runtime->status->state; | ||
559 | status->suspended_state = runtime->status->suspended_state; | ||
560 | if (status->state == SNDRV_PCM_STATE_OPEN) | ||
561 | goto _end; | ||
562 | status->trigger_tstamp = runtime->trigger_tstamp; | ||
563 | if (snd_pcm_running(substream)) { | ||
564 | snd_pcm_update_hw_ptr(substream); | ||
565 | if (runtime->tstamp_mode & SNDRV_PCM_TSTAMP_MMAP) | ||
566 | status->tstamp = runtime->status->tstamp; | ||
567 | else | ||
568 | snd_timestamp_now(&status->tstamp, runtime->tstamp_timespec); | ||
569 | } else | ||
570 | snd_timestamp_now(&status->tstamp, runtime->tstamp_timespec); | ||
571 | status->appl_ptr = runtime->control->appl_ptr; | ||
572 | status->hw_ptr = runtime->status->hw_ptr; | ||
573 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | ||
574 | status->avail = snd_pcm_playback_avail(runtime); | ||
575 | if (runtime->status->state == SNDRV_PCM_STATE_RUNNING || | ||
576 | runtime->status->state == SNDRV_PCM_STATE_DRAINING) | ||
577 | status->delay = runtime->buffer_size - status->avail; | ||
578 | else | ||
579 | status->delay = 0; | ||
580 | } else { | ||
581 | status->avail = snd_pcm_capture_avail(runtime); | ||
582 | if (runtime->status->state == SNDRV_PCM_STATE_RUNNING) | ||
583 | status->delay = status->avail; | ||
584 | else | ||
585 | status->delay = 0; | ||
586 | } | ||
587 | status->avail_max = runtime->avail_max; | ||
588 | status->overrange = runtime->overrange; | ||
589 | runtime->avail_max = 0; | ||
590 | runtime->overrange = 0; | ||
591 | _end: | ||
592 | snd_pcm_stream_unlock_irq(substream); | ||
593 | return 0; | ||
594 | } | ||
595 | |||
596 | static int snd_pcm_status_user(snd_pcm_substream_t * substream, snd_pcm_status_t __user * _status) | ||
597 | { | ||
598 | snd_pcm_status_t status; | ||
599 | snd_pcm_runtime_t *runtime; | ||
600 | int res; | ||
601 | |||
602 | snd_assert(substream != NULL, return -ENXIO); | ||
603 | runtime = substream->runtime; | ||
604 | memset(&status, 0, sizeof(status)); | ||
605 | res = snd_pcm_status(substream, &status); | ||
606 | if (res < 0) | ||
607 | return res; | ||
608 | if (copy_to_user(_status, &status, sizeof(status))) | ||
609 | return -EFAULT; | ||
610 | return 0; | ||
611 | } | ||
612 | |||
613 | static int snd_pcm_channel_info(snd_pcm_substream_t * substream, snd_pcm_channel_info_t * info) | ||
614 | { | ||
615 | snd_pcm_runtime_t *runtime; | ||
616 | unsigned int channel; | ||
617 | |||
618 | snd_assert(substream != NULL, return -ENXIO); | ||
619 | channel = info->channel; | ||
620 | runtime = substream->runtime; | ||
621 | snd_pcm_stream_lock_irq(substream); | ||
622 | if (runtime->status->state == SNDRV_PCM_STATE_OPEN) { | ||
623 | snd_pcm_stream_unlock_irq(substream); | ||
624 | return -EBADFD; | ||
625 | } | ||
626 | snd_pcm_stream_unlock_irq(substream); | ||
627 | if (channel >= runtime->channels) | ||
628 | return -EINVAL; | ||
629 | memset(info, 0, sizeof(*info)); | ||
630 | info->channel = channel; | ||
631 | return substream->ops->ioctl(substream, SNDRV_PCM_IOCTL1_CHANNEL_INFO, info); | ||
632 | } | ||
633 | |||
634 | static int snd_pcm_channel_info_user(snd_pcm_substream_t * substream, snd_pcm_channel_info_t __user * _info) | ||
635 | { | ||
636 | snd_pcm_channel_info_t info; | ||
637 | int res; | ||
638 | |||
639 | if (copy_from_user(&info, _info, sizeof(info))) | ||
640 | return -EFAULT; | ||
641 | res = snd_pcm_channel_info(substream, &info); | ||
642 | if (res < 0) | ||
643 | return res; | ||
644 | if (copy_to_user(_info, &info, sizeof(info))) | ||
645 | return -EFAULT; | ||
646 | return 0; | ||
647 | } | ||
648 | |||
649 | static void snd_pcm_trigger_tstamp(snd_pcm_substream_t *substream) | ||
650 | { | ||
651 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
652 | if (runtime->trigger_master == NULL) | ||
653 | return; | ||
654 | if (runtime->trigger_master == substream) { | ||
655 | snd_timestamp_now(&runtime->trigger_tstamp, runtime->tstamp_timespec); | ||
656 | } else { | ||
657 | snd_pcm_trigger_tstamp(runtime->trigger_master); | ||
658 | runtime->trigger_tstamp = runtime->trigger_master->runtime->trigger_tstamp; | ||
659 | } | ||
660 | runtime->trigger_master = NULL; | ||
661 | } | ||
662 | |||
663 | struct action_ops { | ||
664 | int (*pre_action)(snd_pcm_substream_t *substream, int state); | ||
665 | int (*do_action)(snd_pcm_substream_t *substream, int state); | ||
666 | void (*undo_action)(snd_pcm_substream_t *substream, int state); | ||
667 | void (*post_action)(snd_pcm_substream_t *substream, int state); | ||
668 | }; | ||
669 | |||
670 | /* | ||
671 | * this functions is core for handling of linked stream | ||
672 | * Note: the stream state might be changed also on failure | ||
673 | * Note2: call with calling stream lock + link lock | ||
674 | */ | ||
675 | static int snd_pcm_action_group(struct action_ops *ops, | ||
676 | snd_pcm_substream_t *substream, | ||
677 | int state, int do_lock) | ||
678 | { | ||
679 | struct list_head *pos; | ||
680 | snd_pcm_substream_t *s = NULL; | ||
681 | snd_pcm_substream_t *s1; | ||
682 | int res = 0; | ||
683 | |||
684 | snd_pcm_group_for_each(pos, substream) { | ||
685 | s = snd_pcm_group_substream_entry(pos); | ||
686 | if (do_lock && s != substream) | ||
687 | spin_lock(&s->self_group.lock); | ||
688 | res = ops->pre_action(s, state); | ||
689 | if (res < 0) | ||
690 | goto _unlock; | ||
691 | } | ||
692 | snd_pcm_group_for_each(pos, substream) { | ||
693 | s = snd_pcm_group_substream_entry(pos); | ||
694 | res = ops->do_action(s, state); | ||
695 | if (res < 0) { | ||
696 | if (ops->undo_action) { | ||
697 | snd_pcm_group_for_each(pos, substream) { | ||
698 | s1 = snd_pcm_group_substream_entry(pos); | ||
699 | if (s1 == s) /* failed stream */ | ||
700 | break; | ||
701 | ops->undo_action(s1, state); | ||
702 | } | ||
703 | } | ||
704 | s = NULL; /* unlock all */ | ||
705 | goto _unlock; | ||
706 | } | ||
707 | } | ||
708 | snd_pcm_group_for_each(pos, substream) { | ||
709 | s = snd_pcm_group_substream_entry(pos); | ||
710 | ops->post_action(s, state); | ||
711 | } | ||
712 | _unlock: | ||
713 | if (do_lock) { | ||
714 | /* unlock streams */ | ||
715 | snd_pcm_group_for_each(pos, substream) { | ||
716 | s1 = snd_pcm_group_substream_entry(pos); | ||
717 | if (s1 != substream) | ||
718 | spin_unlock(&s1->self_group.lock); | ||
719 | if (s1 == s) /* end */ | ||
720 | break; | ||
721 | } | ||
722 | } | ||
723 | return res; | ||
724 | } | ||
725 | |||
726 | /* | ||
727 | * Note: call with stream lock | ||
728 | */ | ||
729 | static int snd_pcm_action_single(struct action_ops *ops, | ||
730 | snd_pcm_substream_t *substream, | ||
731 | int state) | ||
732 | { | ||
733 | int res; | ||
734 | |||
735 | res = ops->pre_action(substream, state); | ||
736 | if (res < 0) | ||
737 | return res; | ||
738 | res = ops->do_action(substream, state); | ||
739 | if (res == 0) | ||
740 | ops->post_action(substream, state); | ||
741 | else if (ops->undo_action) | ||
742 | ops->undo_action(substream, state); | ||
743 | return res; | ||
744 | } | ||
745 | |||
746 | /* | ||
747 | * Note: call with stream lock | ||
748 | */ | ||
749 | static int snd_pcm_action(struct action_ops *ops, | ||
750 | snd_pcm_substream_t *substream, | ||
751 | int state) | ||
752 | { | ||
753 | int res; | ||
754 | |||
755 | if (snd_pcm_stream_linked(substream)) { | ||
756 | if (!spin_trylock(&substream->group->lock)) { | ||
757 | spin_unlock(&substream->self_group.lock); | ||
758 | spin_lock(&substream->group->lock); | ||
759 | spin_lock(&substream->self_group.lock); | ||
760 | } | ||
761 | res = snd_pcm_action_group(ops, substream, state, 1); | ||
762 | spin_unlock(&substream->group->lock); | ||
763 | } else { | ||
764 | res = snd_pcm_action_single(ops, substream, state); | ||
765 | } | ||
766 | return res; | ||
767 | } | ||
768 | |||
769 | /* | ||
770 | * Note: don't use any locks before | ||
771 | */ | ||
772 | static int snd_pcm_action_lock_irq(struct action_ops *ops, | ||
773 | snd_pcm_substream_t *substream, | ||
774 | int state) | ||
775 | { | ||
776 | int res; | ||
777 | |||
778 | read_lock_irq(&snd_pcm_link_rwlock); | ||
779 | if (snd_pcm_stream_linked(substream)) { | ||
780 | spin_lock(&substream->group->lock); | ||
781 | spin_lock(&substream->self_group.lock); | ||
782 | res = snd_pcm_action_group(ops, substream, state, 1); | ||
783 | spin_unlock(&substream->self_group.lock); | ||
784 | spin_unlock(&substream->group->lock); | ||
785 | } else { | ||
786 | spin_lock(&substream->self_group.lock); | ||
787 | res = snd_pcm_action_single(ops, substream, state); | ||
788 | spin_unlock(&substream->self_group.lock); | ||
789 | } | ||
790 | read_unlock_irq(&snd_pcm_link_rwlock); | ||
791 | return res; | ||
792 | } | ||
793 | |||
794 | /* | ||
795 | */ | ||
796 | static int snd_pcm_action_nonatomic(struct action_ops *ops, | ||
797 | snd_pcm_substream_t *substream, | ||
798 | int state) | ||
799 | { | ||
800 | int res; | ||
801 | |||
802 | down_read(&snd_pcm_link_rwsem); | ||
803 | if (snd_pcm_stream_linked(substream)) | ||
804 | res = snd_pcm_action_group(ops, substream, state, 0); | ||
805 | else | ||
806 | res = snd_pcm_action_single(ops, substream, state); | ||
807 | up_read(&snd_pcm_link_rwsem); | ||
808 | return res; | ||
809 | } | ||
810 | |||
811 | /* | ||
812 | * start callbacks | ||
813 | */ | ||
814 | static int snd_pcm_pre_start(snd_pcm_substream_t *substream, int state) | ||
815 | { | ||
816 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
817 | if (runtime->status->state != SNDRV_PCM_STATE_PREPARED) | ||
818 | return -EBADFD; | ||
819 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && | ||
820 | !snd_pcm_playback_data(substream)) | ||
821 | return -EPIPE; | ||
822 | runtime->trigger_master = substream; | ||
823 | return 0; | ||
824 | } | ||
825 | |||
826 | static int snd_pcm_do_start(snd_pcm_substream_t *substream, int state) | ||
827 | { | ||
828 | if (substream->runtime->trigger_master != substream) | ||
829 | return 0; | ||
830 | return substream->ops->trigger(substream, SNDRV_PCM_TRIGGER_START); | ||
831 | } | ||
832 | |||
833 | static void snd_pcm_undo_start(snd_pcm_substream_t *substream, int state) | ||
834 | { | ||
835 | if (substream->runtime->trigger_master == substream) | ||
836 | substream->ops->trigger(substream, SNDRV_PCM_TRIGGER_STOP); | ||
837 | } | ||
838 | |||
839 | static void snd_pcm_post_start(snd_pcm_substream_t *substream, int state) | ||
840 | { | ||
841 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
842 | snd_pcm_trigger_tstamp(substream); | ||
843 | runtime->status->state = state; | ||
844 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && | ||
845 | runtime->silence_size > 0) | ||
846 | snd_pcm_playback_silence(substream, ULONG_MAX); | ||
847 | if (runtime->sleep_min) | ||
848 | snd_pcm_tick_prepare(substream); | ||
849 | if (substream->timer) | ||
850 | snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MSTART, &runtime->trigger_tstamp); | ||
851 | } | ||
852 | |||
853 | static struct action_ops snd_pcm_action_start = { | ||
854 | .pre_action = snd_pcm_pre_start, | ||
855 | .do_action = snd_pcm_do_start, | ||
856 | .undo_action = snd_pcm_undo_start, | ||
857 | .post_action = snd_pcm_post_start | ||
858 | }; | ||
859 | |||
860 | /** | ||
861 | * snd_pcm_start | ||
862 | * | ||
863 | * Start all linked streams. | ||
864 | */ | ||
865 | int snd_pcm_start(snd_pcm_substream_t *substream) | ||
866 | { | ||
867 | return snd_pcm_action(&snd_pcm_action_start, substream, SNDRV_PCM_STATE_RUNNING); | ||
868 | } | ||
869 | |||
870 | /* | ||
871 | * stop callbacks | ||
872 | */ | ||
873 | static int snd_pcm_pre_stop(snd_pcm_substream_t *substream, int state) | ||
874 | { | ||
875 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
876 | if (runtime->status->state == SNDRV_PCM_STATE_OPEN) | ||
877 | return -EBADFD; | ||
878 | runtime->trigger_master = substream; | ||
879 | return 0; | ||
880 | } | ||
881 | |||
882 | static int snd_pcm_do_stop(snd_pcm_substream_t *substream, int state) | ||
883 | { | ||
884 | if (substream->runtime->trigger_master == substream && | ||
885 | snd_pcm_running(substream)) | ||
886 | substream->ops->trigger(substream, SNDRV_PCM_TRIGGER_STOP); | ||
887 | return 0; /* unconditonally stop all substreams */ | ||
888 | } | ||
889 | |||
890 | static void snd_pcm_post_stop(snd_pcm_substream_t *substream, int state) | ||
891 | { | ||
892 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
893 | if (runtime->status->state != state) { | ||
894 | snd_pcm_trigger_tstamp(substream); | ||
895 | if (substream->timer) | ||
896 | snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MSTOP, &runtime->trigger_tstamp); | ||
897 | runtime->status->state = state; | ||
898 | snd_pcm_tick_set(substream, 0); | ||
899 | } | ||
900 | wake_up(&runtime->sleep); | ||
901 | } | ||
902 | |||
903 | static struct action_ops snd_pcm_action_stop = { | ||
904 | .pre_action = snd_pcm_pre_stop, | ||
905 | .do_action = snd_pcm_do_stop, | ||
906 | .post_action = snd_pcm_post_stop | ||
907 | }; | ||
908 | |||
909 | /** | ||
910 | * snd_pcm_stop | ||
911 | * | ||
912 | * Try to stop all running streams in the substream group. | ||
913 | * The state of each stream is changed to the given value after that unconditionally. | ||
914 | */ | ||
915 | int snd_pcm_stop(snd_pcm_substream_t *substream, int state) | ||
916 | { | ||
917 | return snd_pcm_action(&snd_pcm_action_stop, substream, state); | ||
918 | } | ||
919 | |||
920 | /** | ||
921 | * snd_pcm_drain_done | ||
922 | * | ||
923 | * Stop the DMA only when the given stream is playback. | ||
924 | * The state is changed to SETUP. | ||
925 | * Unlike snd_pcm_stop(), this affects only the given stream. | ||
926 | */ | ||
927 | int snd_pcm_drain_done(snd_pcm_substream_t *substream) | ||
928 | { | ||
929 | return snd_pcm_action_single(&snd_pcm_action_stop, substream, SNDRV_PCM_STATE_SETUP); | ||
930 | } | ||
931 | |||
932 | /* | ||
933 | * pause callbacks | ||
934 | */ | ||
935 | static int snd_pcm_pre_pause(snd_pcm_substream_t *substream, int push) | ||
936 | { | ||
937 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
938 | if (!(runtime->info & SNDRV_PCM_INFO_PAUSE)) | ||
939 | return -ENOSYS; | ||
940 | if (push) { | ||
941 | if (runtime->status->state != SNDRV_PCM_STATE_RUNNING) | ||
942 | return -EBADFD; | ||
943 | } else if (runtime->status->state != SNDRV_PCM_STATE_PAUSED) | ||
944 | return -EBADFD; | ||
945 | runtime->trigger_master = substream; | ||
946 | return 0; | ||
947 | } | ||
948 | |||
949 | static int snd_pcm_do_pause(snd_pcm_substream_t *substream, int push) | ||
950 | { | ||
951 | if (substream->runtime->trigger_master != substream) | ||
952 | return 0; | ||
953 | return substream->ops->trigger(substream, | ||
954 | push ? SNDRV_PCM_TRIGGER_PAUSE_PUSH : | ||
955 | SNDRV_PCM_TRIGGER_PAUSE_RELEASE); | ||
956 | } | ||
957 | |||
958 | static void snd_pcm_undo_pause(snd_pcm_substream_t *substream, int push) | ||
959 | { | ||
960 | if (substream->runtime->trigger_master == substream) | ||
961 | substream->ops->trigger(substream, | ||
962 | push ? SNDRV_PCM_TRIGGER_PAUSE_RELEASE : | ||
963 | SNDRV_PCM_TRIGGER_PAUSE_PUSH); | ||
964 | } | ||
965 | |||
966 | static void snd_pcm_post_pause(snd_pcm_substream_t *substream, int push) | ||
967 | { | ||
968 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
969 | snd_pcm_trigger_tstamp(substream); | ||
970 | if (push) { | ||
971 | runtime->status->state = SNDRV_PCM_STATE_PAUSED; | ||
972 | if (substream->timer) | ||
973 | snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MPAUSE, &runtime->trigger_tstamp); | ||
974 | snd_pcm_tick_set(substream, 0); | ||
975 | wake_up(&runtime->sleep); | ||
976 | } else { | ||
977 | runtime->status->state = SNDRV_PCM_STATE_RUNNING; | ||
978 | if (runtime->sleep_min) | ||
979 | snd_pcm_tick_prepare(substream); | ||
980 | if (substream->timer) | ||
981 | snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MCONTINUE, &runtime->trigger_tstamp); | ||
982 | } | ||
983 | } | ||
984 | |||
985 | static struct action_ops snd_pcm_action_pause = { | ||
986 | .pre_action = snd_pcm_pre_pause, | ||
987 | .do_action = snd_pcm_do_pause, | ||
988 | .undo_action = snd_pcm_undo_pause, | ||
989 | .post_action = snd_pcm_post_pause | ||
990 | }; | ||
991 | |||
992 | /* | ||
993 | * Push/release the pause for all linked streams. | ||
994 | */ | ||
995 | static int snd_pcm_pause(snd_pcm_substream_t *substream, int push) | ||
996 | { | ||
997 | return snd_pcm_action(&snd_pcm_action_pause, substream, push); | ||
998 | } | ||
999 | |||
1000 | #ifdef CONFIG_PM | ||
1001 | /* suspend */ | ||
1002 | |||
1003 | static int snd_pcm_pre_suspend(snd_pcm_substream_t *substream, int state) | ||
1004 | { | ||
1005 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
1006 | if (runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) | ||
1007 | return -EBUSY; | ||
1008 | runtime->trigger_master = substream; | ||
1009 | return 0; | ||
1010 | } | ||
1011 | |||
1012 | static int snd_pcm_do_suspend(snd_pcm_substream_t *substream, int state) | ||
1013 | { | ||
1014 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
1015 | if (runtime->trigger_master != substream) | ||
1016 | return 0; | ||
1017 | if (! snd_pcm_running(substream)) | ||
1018 | return 0; | ||
1019 | substream->ops->trigger(substream, SNDRV_PCM_TRIGGER_SUSPEND); | ||
1020 | return 0; /* suspend unconditionally */ | ||
1021 | } | ||
1022 | |||
1023 | static void snd_pcm_post_suspend(snd_pcm_substream_t *substream, int state) | ||
1024 | { | ||
1025 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
1026 | snd_pcm_trigger_tstamp(substream); | ||
1027 | if (substream->timer) | ||
1028 | snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MPAUSE, &runtime->trigger_tstamp); | ||
1029 | runtime->status->suspended_state = runtime->status->state; | ||
1030 | runtime->status->state = SNDRV_PCM_STATE_SUSPENDED; | ||
1031 | snd_pcm_tick_set(substream, 0); | ||
1032 | wake_up(&runtime->sleep); | ||
1033 | } | ||
1034 | |||
1035 | static struct action_ops snd_pcm_action_suspend = { | ||
1036 | .pre_action = snd_pcm_pre_suspend, | ||
1037 | .do_action = snd_pcm_do_suspend, | ||
1038 | .post_action = snd_pcm_post_suspend | ||
1039 | }; | ||
1040 | |||
1041 | /** | ||
1042 | * snd_pcm_suspend | ||
1043 | * | ||
1044 | * Trigger SUSPEND to all linked streams. | ||
1045 | * After this call, all streams are changed to SUSPENDED state. | ||
1046 | */ | ||
1047 | int snd_pcm_suspend(snd_pcm_substream_t *substream) | ||
1048 | { | ||
1049 | int err; | ||
1050 | unsigned long flags; | ||
1051 | |||
1052 | snd_pcm_stream_lock_irqsave(substream, flags); | ||
1053 | err = snd_pcm_action(&snd_pcm_action_suspend, substream, 0); | ||
1054 | snd_pcm_stream_unlock_irqrestore(substream, flags); | ||
1055 | return err; | ||
1056 | } | ||
1057 | |||
1058 | /** | ||
1059 | * snd_pcm_suspend_all | ||
1060 | * | ||
1061 | * Trigger SUSPEND to all substreams in the given pcm. | ||
1062 | * After this call, all streams are changed to SUSPENDED state. | ||
1063 | */ | ||
1064 | int snd_pcm_suspend_all(snd_pcm_t *pcm) | ||
1065 | { | ||
1066 | snd_pcm_substream_t *substream; | ||
1067 | int stream, err = 0; | ||
1068 | |||
1069 | for (stream = 0; stream < 2; stream++) { | ||
1070 | for (substream = pcm->streams[stream].substream; substream; substream = substream->next) { | ||
1071 | /* FIXME: the open/close code should lock this as well */ | ||
1072 | if (substream->runtime == NULL) | ||
1073 | continue; | ||
1074 | err = snd_pcm_suspend(substream); | ||
1075 | if (err < 0 && err != -EBUSY) | ||
1076 | return err; | ||
1077 | } | ||
1078 | } | ||
1079 | return 0; | ||
1080 | } | ||
1081 | |||
1082 | /* resume */ | ||
1083 | |||
1084 | static int snd_pcm_pre_resume(snd_pcm_substream_t *substream, int state) | ||
1085 | { | ||
1086 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
1087 | if (!(runtime->info & SNDRV_PCM_INFO_RESUME)) | ||
1088 | return -ENOSYS; | ||
1089 | runtime->trigger_master = substream; | ||
1090 | return 0; | ||
1091 | } | ||
1092 | |||
1093 | static int snd_pcm_do_resume(snd_pcm_substream_t *substream, int state) | ||
1094 | { | ||
1095 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
1096 | if (runtime->trigger_master != substream) | ||
1097 | return 0; | ||
1098 | /* DMA not running previously? */ | ||
1099 | if (runtime->status->suspended_state != SNDRV_PCM_STATE_RUNNING && | ||
1100 | (runtime->status->suspended_state != SNDRV_PCM_STATE_DRAINING || | ||
1101 | substream->stream != SNDRV_PCM_STREAM_PLAYBACK)) | ||
1102 | return 0; | ||
1103 | return substream->ops->trigger(substream, SNDRV_PCM_TRIGGER_RESUME); | ||
1104 | } | ||
1105 | |||
1106 | static void snd_pcm_undo_resume(snd_pcm_substream_t *substream, int state) | ||
1107 | { | ||
1108 | if (substream->runtime->trigger_master == substream && | ||
1109 | snd_pcm_running(substream)) | ||
1110 | substream->ops->trigger(substream, SNDRV_PCM_TRIGGER_SUSPEND); | ||
1111 | } | ||
1112 | |||
1113 | static void snd_pcm_post_resume(snd_pcm_substream_t *substream, int state) | ||
1114 | { | ||
1115 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
1116 | snd_pcm_trigger_tstamp(substream); | ||
1117 | if (substream->timer) | ||
1118 | snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MCONTINUE, &runtime->trigger_tstamp); | ||
1119 | runtime->status->state = runtime->status->suspended_state; | ||
1120 | if (runtime->sleep_min) | ||
1121 | snd_pcm_tick_prepare(substream); | ||
1122 | } | ||
1123 | |||
1124 | static struct action_ops snd_pcm_action_resume = { | ||
1125 | .pre_action = snd_pcm_pre_resume, | ||
1126 | .do_action = snd_pcm_do_resume, | ||
1127 | .undo_action = snd_pcm_undo_resume, | ||
1128 | .post_action = snd_pcm_post_resume | ||
1129 | }; | ||
1130 | |||
1131 | static int snd_pcm_resume(snd_pcm_substream_t *substream) | ||
1132 | { | ||
1133 | snd_card_t *card = substream->pcm->card; | ||
1134 | int res; | ||
1135 | |||
1136 | snd_power_lock(card); | ||
1137 | if ((res = snd_power_wait(card, SNDRV_CTL_POWER_D0, substream->ffile)) >= 0) | ||
1138 | res = snd_pcm_action_lock_irq(&snd_pcm_action_resume, substream, 0); | ||
1139 | snd_power_unlock(card); | ||
1140 | return res; | ||
1141 | } | ||
1142 | |||
1143 | #else | ||
1144 | |||
1145 | static int snd_pcm_resume(snd_pcm_substream_t *substream) | ||
1146 | { | ||
1147 | return -ENOSYS; | ||
1148 | } | ||
1149 | |||
1150 | #endif /* CONFIG_PM */ | ||
1151 | |||
1152 | /* | ||
1153 | * xrun ioctl | ||
1154 | * | ||
1155 | * Change the RUNNING stream(s) to XRUN state. | ||
1156 | */ | ||
1157 | static int snd_pcm_xrun(snd_pcm_substream_t *substream) | ||
1158 | { | ||
1159 | snd_card_t *card = substream->pcm->card; | ||
1160 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
1161 | int result; | ||
1162 | |||
1163 | snd_power_lock(card); | ||
1164 | if (runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) { | ||
1165 | result = snd_power_wait(card, SNDRV_CTL_POWER_D0, substream->ffile); | ||
1166 | if (result < 0) | ||
1167 | goto _unlock; | ||
1168 | } | ||
1169 | |||
1170 | snd_pcm_stream_lock_irq(substream); | ||
1171 | switch (runtime->status->state) { | ||
1172 | case SNDRV_PCM_STATE_XRUN: | ||
1173 | result = 0; /* already there */ | ||
1174 | break; | ||
1175 | case SNDRV_PCM_STATE_RUNNING: | ||
1176 | result = snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN); | ||
1177 | break; | ||
1178 | default: | ||
1179 | result = -EBADFD; | ||
1180 | } | ||
1181 | snd_pcm_stream_unlock_irq(substream); | ||
1182 | _unlock: | ||
1183 | snd_power_unlock(card); | ||
1184 | return result; | ||
1185 | } | ||
1186 | |||
1187 | /* | ||
1188 | * reset ioctl | ||
1189 | */ | ||
1190 | static int snd_pcm_pre_reset(snd_pcm_substream_t * substream, int state) | ||
1191 | { | ||
1192 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
1193 | switch (runtime->status->state) { | ||
1194 | case SNDRV_PCM_STATE_RUNNING: | ||
1195 | case SNDRV_PCM_STATE_PREPARED: | ||
1196 | case SNDRV_PCM_STATE_PAUSED: | ||
1197 | case SNDRV_PCM_STATE_SUSPENDED: | ||
1198 | return 0; | ||
1199 | default: | ||
1200 | return -EBADFD; | ||
1201 | } | ||
1202 | } | ||
1203 | |||
1204 | static int snd_pcm_do_reset(snd_pcm_substream_t * substream, int state) | ||
1205 | { | ||
1206 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
1207 | int err = substream->ops->ioctl(substream, SNDRV_PCM_IOCTL1_RESET, NULL); | ||
1208 | if (err < 0) | ||
1209 | return err; | ||
1210 | // snd_assert(runtime->status->hw_ptr < runtime->buffer_size, ); | ||
1211 | runtime->hw_ptr_base = 0; | ||
1212 | runtime->hw_ptr_interrupt = runtime->status->hw_ptr - runtime->status->hw_ptr % runtime->period_size; | ||
1213 | runtime->silence_start = runtime->status->hw_ptr; | ||
1214 | runtime->silence_filled = 0; | ||
1215 | return 0; | ||
1216 | } | ||
1217 | |||
1218 | static void snd_pcm_post_reset(snd_pcm_substream_t * substream, int state) | ||
1219 | { | ||
1220 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
1221 | runtime->control->appl_ptr = runtime->status->hw_ptr; | ||
1222 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && | ||
1223 | runtime->silence_size > 0) | ||
1224 | snd_pcm_playback_silence(substream, ULONG_MAX); | ||
1225 | } | ||
1226 | |||
1227 | static struct action_ops snd_pcm_action_reset = { | ||
1228 | .pre_action = snd_pcm_pre_reset, | ||
1229 | .do_action = snd_pcm_do_reset, | ||
1230 | .post_action = snd_pcm_post_reset | ||
1231 | }; | ||
1232 | |||
1233 | static int snd_pcm_reset(snd_pcm_substream_t *substream) | ||
1234 | { | ||
1235 | return snd_pcm_action_nonatomic(&snd_pcm_action_reset, substream, 0); | ||
1236 | } | ||
1237 | |||
1238 | /* | ||
1239 | * prepare ioctl | ||
1240 | */ | ||
1241 | static int snd_pcm_pre_prepare(snd_pcm_substream_t * substream, int state) | ||
1242 | { | ||
1243 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
1244 | if (runtime->status->state == SNDRV_PCM_STATE_OPEN) | ||
1245 | return -EBADFD; | ||
1246 | if (snd_pcm_running(substream)) | ||
1247 | return -EBUSY; | ||
1248 | return 0; | ||
1249 | } | ||
1250 | |||
1251 | static int snd_pcm_do_prepare(snd_pcm_substream_t * substream, int state) | ||
1252 | { | ||
1253 | int err; | ||
1254 | err = substream->ops->prepare(substream); | ||
1255 | if (err < 0) | ||
1256 | return err; | ||
1257 | return snd_pcm_do_reset(substream, 0); | ||
1258 | } | ||
1259 | |||
1260 | static void snd_pcm_post_prepare(snd_pcm_substream_t * substream, int state) | ||
1261 | { | ||
1262 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
1263 | runtime->control->appl_ptr = runtime->status->hw_ptr; | ||
1264 | runtime->status->state = SNDRV_PCM_STATE_PREPARED; | ||
1265 | } | ||
1266 | |||
1267 | static struct action_ops snd_pcm_action_prepare = { | ||
1268 | .pre_action = snd_pcm_pre_prepare, | ||
1269 | .do_action = snd_pcm_do_prepare, | ||
1270 | .post_action = snd_pcm_post_prepare | ||
1271 | }; | ||
1272 | |||
1273 | /** | ||
1274 | * snd_pcm_prepare | ||
1275 | */ | ||
1276 | int snd_pcm_prepare(snd_pcm_substream_t *substream) | ||
1277 | { | ||
1278 | int res; | ||
1279 | snd_card_t *card = substream->pcm->card; | ||
1280 | |||
1281 | snd_power_lock(card); | ||
1282 | if ((res = snd_power_wait(card, SNDRV_CTL_POWER_D0, substream->ffile)) >= 0) | ||
1283 | res = snd_pcm_action_nonatomic(&snd_pcm_action_prepare, substream, 0); | ||
1284 | snd_power_unlock(card); | ||
1285 | return res; | ||
1286 | } | ||
1287 | |||
1288 | /* | ||
1289 | * drain ioctl | ||
1290 | */ | ||
1291 | |||
1292 | static int snd_pcm_pre_drain_init(snd_pcm_substream_t * substream, int state) | ||
1293 | { | ||
1294 | if (substream->ffile->f_flags & O_NONBLOCK) | ||
1295 | return -EAGAIN; | ||
1296 | substream->runtime->trigger_master = substream; | ||
1297 | return 0; | ||
1298 | } | ||
1299 | |||
1300 | static int snd_pcm_do_drain_init(snd_pcm_substream_t * substream, int state) | ||
1301 | { | ||
1302 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
1303 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | ||
1304 | switch (runtime->status->state) { | ||
1305 | case SNDRV_PCM_STATE_PREPARED: | ||
1306 | /* start playback stream if possible */ | ||
1307 | if (! snd_pcm_playback_empty(substream)) { | ||
1308 | snd_pcm_do_start(substream, SNDRV_PCM_STATE_DRAINING); | ||
1309 | snd_pcm_post_start(substream, SNDRV_PCM_STATE_DRAINING); | ||
1310 | } | ||
1311 | break; | ||
1312 | case SNDRV_PCM_STATE_RUNNING: | ||
1313 | runtime->status->state = SNDRV_PCM_STATE_DRAINING; | ||
1314 | break; | ||
1315 | default: | ||
1316 | break; | ||
1317 | } | ||
1318 | } else { | ||
1319 | /* stop running stream */ | ||
1320 | if (runtime->status->state == SNDRV_PCM_STATE_RUNNING) { | ||
1321 | int state = snd_pcm_capture_avail(runtime) > 0 ? | ||
1322 | SNDRV_PCM_STATE_DRAINING : SNDRV_PCM_STATE_SETUP; | ||
1323 | snd_pcm_do_stop(substream, state); | ||
1324 | snd_pcm_post_stop(substream, state); | ||
1325 | } | ||
1326 | } | ||
1327 | return 0; | ||
1328 | } | ||
1329 | |||
1330 | static void snd_pcm_post_drain_init(snd_pcm_substream_t * substream, int state) | ||
1331 | { | ||
1332 | } | ||
1333 | |||
1334 | static struct action_ops snd_pcm_action_drain_init = { | ||
1335 | .pre_action = snd_pcm_pre_drain_init, | ||
1336 | .do_action = snd_pcm_do_drain_init, | ||
1337 | .post_action = snd_pcm_post_drain_init | ||
1338 | }; | ||
1339 | |||
1340 | struct drain_rec { | ||
1341 | snd_pcm_substream_t *substream; | ||
1342 | wait_queue_t wait; | ||
1343 | snd_pcm_uframes_t stop_threshold; | ||
1344 | }; | ||
1345 | |||
1346 | static int snd_pcm_drop(snd_pcm_substream_t *substream); | ||
1347 | |||
1348 | /* | ||
1349 | * Drain the stream(s). | ||
1350 | * When the substream is linked, sync until the draining of all playback streams | ||
1351 | * is finished. | ||
1352 | * After this call, all streams are supposed to be either SETUP or DRAINING | ||
1353 | * (capture only) state. | ||
1354 | */ | ||
1355 | static int snd_pcm_drain(snd_pcm_substream_t *substream) | ||
1356 | { | ||
1357 | snd_card_t *card; | ||
1358 | snd_pcm_runtime_t *runtime; | ||
1359 | struct list_head *pos; | ||
1360 | int result = 0; | ||
1361 | int i, num_drecs; | ||
1362 | struct drain_rec *drec, drec_tmp, *d; | ||
1363 | |||
1364 | snd_assert(substream != NULL, return -ENXIO); | ||
1365 | card = substream->pcm->card; | ||
1366 | runtime = substream->runtime; | ||
1367 | |||
1368 | if (runtime->status->state == SNDRV_PCM_STATE_OPEN) | ||
1369 | return -EBADFD; | ||
1370 | |||
1371 | down_read(&snd_pcm_link_rwsem); | ||
1372 | snd_power_lock(card); | ||
1373 | if (runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) { | ||
1374 | result = snd_power_wait(card, SNDRV_CTL_POWER_D0, substream->ffile); | ||
1375 | if (result < 0) | ||
1376 | goto _unlock; | ||
1377 | } | ||
1378 | |||
1379 | /* allocate temporary record for drain sync */ | ||
1380 | if (snd_pcm_stream_linked(substream)) { | ||
1381 | drec = kmalloc(substream->group->count * sizeof(*drec), GFP_KERNEL); | ||
1382 | if (! drec) { | ||
1383 | result = -ENOMEM; | ||
1384 | goto _unlock; | ||
1385 | } | ||
1386 | } else | ||
1387 | drec = &drec_tmp; | ||
1388 | |||
1389 | snd_pcm_stream_lock_irq(substream); | ||
1390 | /* resume pause */ | ||
1391 | if (runtime->status->state == SNDRV_PCM_STATE_PAUSED) | ||
1392 | snd_pcm_pause(substream, 0); | ||
1393 | |||
1394 | /* pre-start/stop - all running streams are changed to DRAINING state */ | ||
1395 | result = snd_pcm_action(&snd_pcm_action_drain_init, substream, 0); | ||
1396 | if (result < 0) | ||
1397 | goto _end; | ||
1398 | |||
1399 | /* check streams with PLAYBACK & DRAINING */ | ||
1400 | num_drecs = 0; | ||
1401 | snd_pcm_group_for_each(pos, substream) { | ||
1402 | snd_pcm_substream_t *s = snd_pcm_group_substream_entry(pos); | ||
1403 | runtime = s->runtime; | ||
1404 | if (runtime->status->state != SNDRV_PCM_STATE_DRAINING) { | ||
1405 | runtime->status->state = SNDRV_PCM_STATE_SETUP; | ||
1406 | continue; | ||
1407 | } | ||
1408 | if (s->stream == SNDRV_PCM_STREAM_PLAYBACK) { | ||
1409 | d = &drec[num_drecs++]; | ||
1410 | d->substream = s; | ||
1411 | init_waitqueue_entry(&d->wait, current); | ||
1412 | add_wait_queue(&runtime->sleep, &d->wait); | ||
1413 | /* stop_threshold fixup to avoid endless loop when | ||
1414 | * stop_threshold > buffer_size | ||
1415 | */ | ||
1416 | d->stop_threshold = runtime->stop_threshold; | ||
1417 | if (runtime->stop_threshold > runtime->buffer_size) | ||
1418 | runtime->stop_threshold = runtime->buffer_size; | ||
1419 | } | ||
1420 | } | ||
1421 | |||
1422 | if (! num_drecs) | ||
1423 | goto _end; | ||
1424 | |||
1425 | for (;;) { | ||
1426 | long tout; | ||
1427 | if (signal_pending(current)) { | ||
1428 | result = -ERESTARTSYS; | ||
1429 | break; | ||
1430 | } | ||
1431 | set_current_state(TASK_INTERRUPTIBLE); | ||
1432 | snd_pcm_stream_unlock_irq(substream); | ||
1433 | snd_power_unlock(card); | ||
1434 | tout = schedule_timeout(10 * HZ); | ||
1435 | snd_power_lock(card); | ||
1436 | snd_pcm_stream_lock_irq(substream); | ||
1437 | if (tout == 0) { | ||
1438 | if (substream->runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) | ||
1439 | result = -ESTRPIPE; | ||
1440 | else { | ||
1441 | snd_printd("playback drain error (DMA or IRQ trouble?)\n"); | ||
1442 | snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP); | ||
1443 | result = -EIO; | ||
1444 | } | ||
1445 | break; | ||
1446 | } | ||
1447 | /* all finished? */ | ||
1448 | for (i = 0; i < num_drecs; i++) { | ||
1449 | runtime = drec[i].substream->runtime; | ||
1450 | if (runtime->status->state == SNDRV_PCM_STATE_DRAINING) | ||
1451 | break; | ||
1452 | } | ||
1453 | if (i == num_drecs) | ||
1454 | break; | ||
1455 | } | ||
1456 | for (i = 0; i < num_drecs; i++) { | ||
1457 | d = &drec[i]; | ||
1458 | runtime = d->substream->runtime; | ||
1459 | remove_wait_queue(&runtime->sleep, &d->wait); | ||
1460 | runtime->stop_threshold = d->stop_threshold; | ||
1461 | } | ||
1462 | |||
1463 | _end: | ||
1464 | snd_pcm_stream_unlock_irq(substream); | ||
1465 | if (drec != &drec_tmp) | ||
1466 | kfree(drec); | ||
1467 | _unlock: | ||
1468 | snd_power_unlock(card); | ||
1469 | up_read(&snd_pcm_link_rwsem); | ||
1470 | |||
1471 | return result; | ||
1472 | } | ||
1473 | |||
1474 | /* | ||
1475 | * drop ioctl | ||
1476 | * | ||
1477 | * Immediately put all linked substreams into SETUP state. | ||
1478 | */ | ||
1479 | static int snd_pcm_drop(snd_pcm_substream_t *substream) | ||
1480 | { | ||
1481 | snd_pcm_runtime_t *runtime; | ||
1482 | snd_card_t *card; | ||
1483 | int result = 0; | ||
1484 | |||
1485 | snd_assert(substream != NULL, return -ENXIO); | ||
1486 | runtime = substream->runtime; | ||
1487 | card = substream->pcm->card; | ||
1488 | |||
1489 | if (runtime->status->state == SNDRV_PCM_STATE_OPEN) | ||
1490 | return -EBADFD; | ||
1491 | |||
1492 | snd_power_lock(card); | ||
1493 | if (runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) { | ||
1494 | result = snd_power_wait(card, SNDRV_CTL_POWER_D0, substream->ffile); | ||
1495 | if (result < 0) | ||
1496 | goto _unlock; | ||
1497 | } | ||
1498 | |||
1499 | snd_pcm_stream_lock_irq(substream); | ||
1500 | /* resume pause */ | ||
1501 | if (runtime->status->state == SNDRV_PCM_STATE_PAUSED) | ||
1502 | snd_pcm_pause(substream, 0); | ||
1503 | |||
1504 | snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP); | ||
1505 | /* runtime->control->appl_ptr = runtime->status->hw_ptr; */ | ||
1506 | snd_pcm_stream_unlock_irq(substream); | ||
1507 | _unlock: | ||
1508 | snd_power_unlock(card); | ||
1509 | return result; | ||
1510 | } | ||
1511 | |||
1512 | |||
1513 | /* WARNING: Don't forget to fput back the file */ | ||
1514 | extern int snd_major; | ||
1515 | static struct file *snd_pcm_file_fd(int fd) | ||
1516 | { | ||
1517 | struct file *file; | ||
1518 | struct inode *inode; | ||
1519 | unsigned short minor; | ||
1520 | file = fget(fd); | ||
1521 | if (!file) | ||
1522 | return NULL; | ||
1523 | inode = file->f_dentry->d_inode; | ||
1524 | if (!S_ISCHR(inode->i_mode) || | ||
1525 | imajor(inode) != snd_major) { | ||
1526 | fput(file); | ||
1527 | return NULL; | ||
1528 | } | ||
1529 | minor = iminor(inode); | ||
1530 | if (minor >= 256 || | ||
1531 | minor % SNDRV_MINOR_DEVICES < SNDRV_MINOR_PCM_PLAYBACK) { | ||
1532 | fput(file); | ||
1533 | return NULL; | ||
1534 | } | ||
1535 | return file; | ||
1536 | } | ||
1537 | |||
1538 | /* | ||
1539 | * PCM link handling | ||
1540 | */ | ||
1541 | static int snd_pcm_link(snd_pcm_substream_t *substream, int fd) | ||
1542 | { | ||
1543 | int res = 0; | ||
1544 | struct file *file; | ||
1545 | snd_pcm_file_t *pcm_file; | ||
1546 | snd_pcm_substream_t *substream1; | ||
1547 | |||
1548 | file = snd_pcm_file_fd(fd); | ||
1549 | if (!file) | ||
1550 | return -EBADFD; | ||
1551 | pcm_file = file->private_data; | ||
1552 | substream1 = pcm_file->substream; | ||
1553 | down_write(&snd_pcm_link_rwsem); | ||
1554 | write_lock_irq(&snd_pcm_link_rwlock); | ||
1555 | if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN || | ||
1556 | substream->runtime->status->state != substream1->runtime->status->state) { | ||
1557 | res = -EBADFD; | ||
1558 | goto _end; | ||
1559 | } | ||
1560 | if (snd_pcm_stream_linked(substream1)) { | ||
1561 | res = -EALREADY; | ||
1562 | goto _end; | ||
1563 | } | ||
1564 | if (!snd_pcm_stream_linked(substream)) { | ||
1565 | substream->group = kmalloc(sizeof(snd_pcm_group_t), GFP_ATOMIC); | ||
1566 | if (substream->group == NULL) { | ||
1567 | res = -ENOMEM; | ||
1568 | goto _end; | ||
1569 | } | ||
1570 | spin_lock_init(&substream->group->lock); | ||
1571 | INIT_LIST_HEAD(&substream->group->substreams); | ||
1572 | list_add_tail(&substream->link_list, &substream->group->substreams); | ||
1573 | substream->group->count = 1; | ||
1574 | } | ||
1575 | list_add_tail(&substream1->link_list, &substream->group->substreams); | ||
1576 | substream->group->count++; | ||
1577 | substream1->group = substream->group; | ||
1578 | _end: | ||
1579 | write_unlock_irq(&snd_pcm_link_rwlock); | ||
1580 | up_write(&snd_pcm_link_rwsem); | ||
1581 | fput(file); | ||
1582 | return res; | ||
1583 | } | ||
1584 | |||
1585 | static void relink_to_local(snd_pcm_substream_t *substream) | ||
1586 | { | ||
1587 | substream->group = &substream->self_group; | ||
1588 | INIT_LIST_HEAD(&substream->self_group.substreams); | ||
1589 | list_add_tail(&substream->link_list, &substream->self_group.substreams); | ||
1590 | } | ||
1591 | |||
1592 | static int snd_pcm_unlink(snd_pcm_substream_t *substream) | ||
1593 | { | ||
1594 | struct list_head *pos; | ||
1595 | int res = 0; | ||
1596 | |||
1597 | down_write(&snd_pcm_link_rwsem); | ||
1598 | write_lock_irq(&snd_pcm_link_rwlock); | ||
1599 | if (!snd_pcm_stream_linked(substream)) { | ||
1600 | res = -EALREADY; | ||
1601 | goto _end; | ||
1602 | } | ||
1603 | list_del(&substream->link_list); | ||
1604 | substream->group->count--; | ||
1605 | if (substream->group->count == 1) { /* detach the last stream, too */ | ||
1606 | snd_pcm_group_for_each(pos, substream) { | ||
1607 | relink_to_local(snd_pcm_group_substream_entry(pos)); | ||
1608 | break; | ||
1609 | } | ||
1610 | kfree(substream->group); | ||
1611 | } | ||
1612 | relink_to_local(substream); | ||
1613 | _end: | ||
1614 | write_unlock_irq(&snd_pcm_link_rwlock); | ||
1615 | up_write(&snd_pcm_link_rwsem); | ||
1616 | return res; | ||
1617 | } | ||
1618 | |||
1619 | /* | ||
1620 | * hw configurator | ||
1621 | */ | ||
1622 | static int snd_pcm_hw_rule_mul(snd_pcm_hw_params_t *params, | ||
1623 | snd_pcm_hw_rule_t *rule) | ||
1624 | { | ||
1625 | snd_interval_t t; | ||
1626 | snd_interval_mul(hw_param_interval_c(params, rule->deps[0]), | ||
1627 | hw_param_interval_c(params, rule->deps[1]), &t); | ||
1628 | return snd_interval_refine(hw_param_interval(params, rule->var), &t); | ||
1629 | } | ||
1630 | |||
1631 | static int snd_pcm_hw_rule_div(snd_pcm_hw_params_t *params, | ||
1632 | snd_pcm_hw_rule_t *rule) | ||
1633 | { | ||
1634 | snd_interval_t t; | ||
1635 | snd_interval_div(hw_param_interval_c(params, rule->deps[0]), | ||
1636 | hw_param_interval_c(params, rule->deps[1]), &t); | ||
1637 | return snd_interval_refine(hw_param_interval(params, rule->var), &t); | ||
1638 | } | ||
1639 | |||
1640 | static int snd_pcm_hw_rule_muldivk(snd_pcm_hw_params_t *params, | ||
1641 | snd_pcm_hw_rule_t *rule) | ||
1642 | { | ||
1643 | snd_interval_t t; | ||
1644 | snd_interval_muldivk(hw_param_interval_c(params, rule->deps[0]), | ||
1645 | hw_param_interval_c(params, rule->deps[1]), | ||
1646 | (unsigned long) rule->private, &t); | ||
1647 | return snd_interval_refine(hw_param_interval(params, rule->var), &t); | ||
1648 | } | ||
1649 | |||
1650 | static int snd_pcm_hw_rule_mulkdiv(snd_pcm_hw_params_t *params, | ||
1651 | snd_pcm_hw_rule_t *rule) | ||
1652 | { | ||
1653 | snd_interval_t t; | ||
1654 | snd_interval_mulkdiv(hw_param_interval_c(params, rule->deps[0]), | ||
1655 | (unsigned long) rule->private, | ||
1656 | hw_param_interval_c(params, rule->deps[1]), &t); | ||
1657 | return snd_interval_refine(hw_param_interval(params, rule->var), &t); | ||
1658 | } | ||
1659 | |||
1660 | static int snd_pcm_hw_rule_format(snd_pcm_hw_params_t *params, | ||
1661 | snd_pcm_hw_rule_t *rule) | ||
1662 | { | ||
1663 | unsigned int k; | ||
1664 | snd_interval_t *i = hw_param_interval(params, rule->deps[0]); | ||
1665 | snd_mask_t m; | ||
1666 | snd_mask_t *mask = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); | ||
1667 | snd_mask_any(&m); | ||
1668 | for (k = 0; k <= SNDRV_PCM_FORMAT_LAST; ++k) { | ||
1669 | int bits; | ||
1670 | if (! snd_mask_test(mask, k)) | ||
1671 | continue; | ||
1672 | bits = snd_pcm_format_physical_width(k); | ||
1673 | if (bits <= 0) | ||
1674 | continue; /* ignore invalid formats */ | ||
1675 | if ((unsigned)bits < i->min || (unsigned)bits > i->max) | ||
1676 | snd_mask_reset(&m, k); | ||
1677 | } | ||
1678 | return snd_mask_refine(mask, &m); | ||
1679 | } | ||
1680 | |||
1681 | static int snd_pcm_hw_rule_sample_bits(snd_pcm_hw_params_t *params, | ||
1682 | snd_pcm_hw_rule_t *rule) | ||
1683 | { | ||
1684 | snd_interval_t t; | ||
1685 | unsigned int k; | ||
1686 | t.min = UINT_MAX; | ||
1687 | t.max = 0; | ||
1688 | t.openmin = 0; | ||
1689 | t.openmax = 0; | ||
1690 | for (k = 0; k <= SNDRV_PCM_FORMAT_LAST; ++k) { | ||
1691 | int bits; | ||
1692 | if (! snd_mask_test(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), k)) | ||
1693 | continue; | ||
1694 | bits = snd_pcm_format_physical_width(k); | ||
1695 | if (bits <= 0) | ||
1696 | continue; /* ignore invalid formats */ | ||
1697 | if (t.min > (unsigned)bits) | ||
1698 | t.min = bits; | ||
1699 | if (t.max < (unsigned)bits) | ||
1700 | t.max = bits; | ||
1701 | } | ||
1702 | t.integer = 1; | ||
1703 | return snd_interval_refine(hw_param_interval(params, rule->var), &t); | ||
1704 | } | ||
1705 | |||
1706 | #if SNDRV_PCM_RATE_5512 != 1 << 0 || SNDRV_PCM_RATE_192000 != 1 << 12 | ||
1707 | #error "Change this table" | ||
1708 | #endif | ||
1709 | |||
1710 | static unsigned int rates[] = { 5512, 8000, 11025, 16000, 22050, 32000, 44100, | ||
1711 | 48000, 64000, 88200, 96000, 176400, 192000 }; | ||
1712 | |||
1713 | static int snd_pcm_hw_rule_rate(snd_pcm_hw_params_t *params, | ||
1714 | snd_pcm_hw_rule_t *rule) | ||
1715 | { | ||
1716 | snd_pcm_hardware_t *hw = rule->private; | ||
1717 | return snd_interval_list(hw_param_interval(params, rule->var), | ||
1718 | ARRAY_SIZE(rates), rates, hw->rates); | ||
1719 | } | ||
1720 | |||
1721 | static int snd_pcm_hw_rule_buffer_bytes_max(snd_pcm_hw_params_t *params, | ||
1722 | snd_pcm_hw_rule_t *rule) | ||
1723 | { | ||
1724 | snd_interval_t t; | ||
1725 | snd_pcm_substream_t *substream = rule->private; | ||
1726 | t.min = 0; | ||
1727 | t.max = substream->buffer_bytes_max; | ||
1728 | t.openmin = 0; | ||
1729 | t.openmax = 0; | ||
1730 | t.integer = 1; | ||
1731 | return snd_interval_refine(hw_param_interval(params, rule->var), &t); | ||
1732 | } | ||
1733 | |||
1734 | int snd_pcm_hw_constraints_init(snd_pcm_substream_t *substream) | ||
1735 | { | ||
1736 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
1737 | snd_pcm_hw_constraints_t *constrs = &runtime->hw_constraints; | ||
1738 | int k, err; | ||
1739 | |||
1740 | for (k = SNDRV_PCM_HW_PARAM_FIRST_MASK; k <= SNDRV_PCM_HW_PARAM_LAST_MASK; k++) { | ||
1741 | snd_mask_any(constrs_mask(constrs, k)); | ||
1742 | } | ||
1743 | |||
1744 | for (k = SNDRV_PCM_HW_PARAM_FIRST_INTERVAL; k <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; k++) { | ||
1745 | snd_interval_any(constrs_interval(constrs, k)); | ||
1746 | } | ||
1747 | |||
1748 | snd_interval_setinteger(constrs_interval(constrs, SNDRV_PCM_HW_PARAM_CHANNELS)); | ||
1749 | snd_interval_setinteger(constrs_interval(constrs, SNDRV_PCM_HW_PARAM_BUFFER_SIZE)); | ||
1750 | snd_interval_setinteger(constrs_interval(constrs, SNDRV_PCM_HW_PARAM_BUFFER_BYTES)); | ||
1751 | snd_interval_setinteger(constrs_interval(constrs, SNDRV_PCM_HW_PARAM_SAMPLE_BITS)); | ||
1752 | snd_interval_setinteger(constrs_interval(constrs, SNDRV_PCM_HW_PARAM_FRAME_BITS)); | ||
1753 | |||
1754 | err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FORMAT, | ||
1755 | snd_pcm_hw_rule_format, NULL, | ||
1756 | SNDRV_PCM_HW_PARAM_SAMPLE_BITS, -1); | ||
1757 | if (err < 0) | ||
1758 | return err; | ||
1759 | err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_SAMPLE_BITS, | ||
1760 | snd_pcm_hw_rule_sample_bits, NULL, | ||
1761 | SNDRV_PCM_HW_PARAM_FORMAT, | ||
1762 | SNDRV_PCM_HW_PARAM_SAMPLE_BITS, -1); | ||
1763 | if (err < 0) | ||
1764 | return err; | ||
1765 | err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_SAMPLE_BITS, | ||
1766 | snd_pcm_hw_rule_div, NULL, | ||
1767 | SNDRV_PCM_HW_PARAM_FRAME_BITS, SNDRV_PCM_HW_PARAM_CHANNELS, -1); | ||
1768 | if (err < 0) | ||
1769 | return err; | ||
1770 | err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FRAME_BITS, | ||
1771 | snd_pcm_hw_rule_mul, NULL, | ||
1772 | SNDRV_PCM_HW_PARAM_SAMPLE_BITS, SNDRV_PCM_HW_PARAM_CHANNELS, -1); | ||
1773 | if (err < 0) | ||
1774 | return err; | ||
1775 | err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FRAME_BITS, | ||
1776 | snd_pcm_hw_rule_mulkdiv, (void*) 8, | ||
1777 | SNDRV_PCM_HW_PARAM_PERIOD_BYTES, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, -1); | ||
1778 | if (err < 0) | ||
1779 | return err; | ||
1780 | err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FRAME_BITS, | ||
1781 | snd_pcm_hw_rule_mulkdiv, (void*) 8, | ||
1782 | SNDRV_PCM_HW_PARAM_BUFFER_BYTES, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, -1); | ||
1783 | if (err < 0) | ||
1784 | return err; | ||
1785 | err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, | ||
1786 | snd_pcm_hw_rule_div, NULL, | ||
1787 | SNDRV_PCM_HW_PARAM_FRAME_BITS, SNDRV_PCM_HW_PARAM_SAMPLE_BITS, -1); | ||
1788 | if (err < 0) | ||
1789 | return err; | ||
1790 | err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, | ||
1791 | snd_pcm_hw_rule_mulkdiv, (void*) 1000000, | ||
1792 | SNDRV_PCM_HW_PARAM_PERIOD_SIZE, SNDRV_PCM_HW_PARAM_PERIOD_TIME, -1); | ||
1793 | if (err < 0) | ||
1794 | return err; | ||
1795 | err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, | ||
1796 | snd_pcm_hw_rule_mulkdiv, (void*) 1000000, | ||
1797 | SNDRV_PCM_HW_PARAM_BUFFER_SIZE, SNDRV_PCM_HW_PARAM_BUFFER_TIME, -1); | ||
1798 | if (err < 0) | ||
1799 | return err; | ||
1800 | err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIODS, | ||
1801 | snd_pcm_hw_rule_div, NULL, | ||
1802 | SNDRV_PCM_HW_PARAM_BUFFER_SIZE, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, -1); | ||
1803 | if (err < 0) | ||
1804 | return err; | ||
1805 | err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, | ||
1806 | snd_pcm_hw_rule_div, NULL, | ||
1807 | SNDRV_PCM_HW_PARAM_BUFFER_SIZE, SNDRV_PCM_HW_PARAM_PERIODS, -1); | ||
1808 | if (err < 0) | ||
1809 | return err; | ||
1810 | err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, | ||
1811 | snd_pcm_hw_rule_mulkdiv, (void*) 8, | ||
1812 | SNDRV_PCM_HW_PARAM_PERIOD_BYTES, SNDRV_PCM_HW_PARAM_FRAME_BITS, -1); | ||
1813 | if (err < 0) | ||
1814 | return err; | ||
1815 | err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, | ||
1816 | snd_pcm_hw_rule_muldivk, (void*) 1000000, | ||
1817 | SNDRV_PCM_HW_PARAM_PERIOD_TIME, SNDRV_PCM_HW_PARAM_RATE, -1); | ||
1818 | if (err < 0) | ||
1819 | return err; | ||
1820 | err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, | ||
1821 | snd_pcm_hw_rule_mul, NULL, | ||
1822 | SNDRV_PCM_HW_PARAM_PERIOD_SIZE, SNDRV_PCM_HW_PARAM_PERIODS, -1); | ||
1823 | if (err < 0) | ||
1824 | return err; | ||
1825 | err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, | ||
1826 | snd_pcm_hw_rule_mulkdiv, (void*) 8, | ||
1827 | SNDRV_PCM_HW_PARAM_BUFFER_BYTES, SNDRV_PCM_HW_PARAM_FRAME_BITS, -1); | ||
1828 | if (err < 0) | ||
1829 | return err; | ||
1830 | err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, | ||
1831 | snd_pcm_hw_rule_muldivk, (void*) 1000000, | ||
1832 | SNDRV_PCM_HW_PARAM_BUFFER_TIME, SNDRV_PCM_HW_PARAM_RATE, -1); | ||
1833 | if (err < 0) | ||
1834 | return err; | ||
1835 | err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, | ||
1836 | snd_pcm_hw_rule_muldivk, (void*) 8, | ||
1837 | SNDRV_PCM_HW_PARAM_PERIOD_SIZE, SNDRV_PCM_HW_PARAM_FRAME_BITS, -1); | ||
1838 | if (err < 0) | ||
1839 | return err; | ||
1840 | err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, | ||
1841 | snd_pcm_hw_rule_muldivk, (void*) 8, | ||
1842 | SNDRV_PCM_HW_PARAM_BUFFER_SIZE, SNDRV_PCM_HW_PARAM_FRAME_BITS, -1); | ||
1843 | if (err < 0) | ||
1844 | return err; | ||
1845 | err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_TIME, | ||
1846 | snd_pcm_hw_rule_mulkdiv, (void*) 1000000, | ||
1847 | SNDRV_PCM_HW_PARAM_PERIOD_SIZE, SNDRV_PCM_HW_PARAM_RATE, -1); | ||
1848 | if (err < 0) | ||
1849 | return err; | ||
1850 | err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_TIME, | ||
1851 | snd_pcm_hw_rule_mulkdiv, (void*) 1000000, | ||
1852 | SNDRV_PCM_HW_PARAM_BUFFER_SIZE, SNDRV_PCM_HW_PARAM_RATE, -1); | ||
1853 | if (err < 0) | ||
1854 | return err; | ||
1855 | return 0; | ||
1856 | } | ||
1857 | |||
1858 | int snd_pcm_hw_constraints_complete(snd_pcm_substream_t *substream) | ||
1859 | { | ||
1860 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
1861 | snd_pcm_hardware_t *hw = &runtime->hw; | ||
1862 | int err; | ||
1863 | unsigned int mask = 0; | ||
1864 | |||
1865 | if (hw->info & SNDRV_PCM_INFO_INTERLEAVED) | ||
1866 | mask |= 1 << SNDRV_PCM_ACCESS_RW_INTERLEAVED; | ||
1867 | if (hw->info & SNDRV_PCM_INFO_NONINTERLEAVED) | ||
1868 | mask |= 1 << SNDRV_PCM_ACCESS_RW_NONINTERLEAVED; | ||
1869 | if (hw->info & SNDRV_PCM_INFO_MMAP) { | ||
1870 | if (hw->info & SNDRV_PCM_INFO_INTERLEAVED) | ||
1871 | mask |= 1 << SNDRV_PCM_ACCESS_MMAP_INTERLEAVED; | ||
1872 | if (hw->info & SNDRV_PCM_INFO_NONINTERLEAVED) | ||
1873 | mask |= 1 << SNDRV_PCM_ACCESS_MMAP_NONINTERLEAVED; | ||
1874 | if (hw->info & SNDRV_PCM_INFO_COMPLEX) | ||
1875 | mask |= 1 << SNDRV_PCM_ACCESS_MMAP_COMPLEX; | ||
1876 | } | ||
1877 | err = snd_pcm_hw_constraint_mask(runtime, SNDRV_PCM_HW_PARAM_ACCESS, mask); | ||
1878 | snd_assert(err >= 0, return -EINVAL); | ||
1879 | |||
1880 | err = snd_pcm_hw_constraint_mask64(runtime, SNDRV_PCM_HW_PARAM_FORMAT, hw->formats); | ||
1881 | snd_assert(err >= 0, return -EINVAL); | ||
1882 | |||
1883 | err = snd_pcm_hw_constraint_mask(runtime, SNDRV_PCM_HW_PARAM_SUBFORMAT, 1 << SNDRV_PCM_SUBFORMAT_STD); | ||
1884 | snd_assert(err >= 0, return -EINVAL); | ||
1885 | |||
1886 | err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_CHANNELS, | ||
1887 | hw->channels_min, hw->channels_max); | ||
1888 | snd_assert(err >= 0, return -EINVAL); | ||
1889 | |||
1890 | err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_RATE, | ||
1891 | hw->rate_min, hw->rate_max); | ||
1892 | snd_assert(err >= 0, return -EINVAL); | ||
1893 | |||
1894 | err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, | ||
1895 | hw->period_bytes_min, hw->period_bytes_max); | ||
1896 | snd_assert(err >= 0, return -EINVAL); | ||
1897 | |||
1898 | err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIODS, | ||
1899 | hw->periods_min, hw->periods_max); | ||
1900 | snd_assert(err >= 0, return -EINVAL); | ||
1901 | |||
1902 | err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, | ||
1903 | hw->period_bytes_min, hw->buffer_bytes_max); | ||
1904 | snd_assert(err >= 0, return -EINVAL); | ||
1905 | |||
1906 | err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, | ||
1907 | snd_pcm_hw_rule_buffer_bytes_max, substream, | ||
1908 | SNDRV_PCM_HW_PARAM_BUFFER_BYTES, -1); | ||
1909 | if (err < 0) | ||
1910 | return err; | ||
1911 | |||
1912 | /* FIXME: remove */ | ||
1913 | if (runtime->dma_bytes) { | ||
1914 | err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 0, runtime->dma_bytes); | ||
1915 | snd_assert(err >= 0, return -EINVAL); | ||
1916 | } | ||
1917 | |||
1918 | if (!(hw->rates & (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS))) { | ||
1919 | err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, | ||
1920 | snd_pcm_hw_rule_rate, hw, | ||
1921 | SNDRV_PCM_HW_PARAM_RATE, -1); | ||
1922 | if (err < 0) | ||
1923 | return err; | ||
1924 | } | ||
1925 | |||
1926 | /* FIXME: this belong to lowlevel */ | ||
1927 | snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_TICK_TIME, | ||
1928 | 1000000 / HZ, 1000000 / HZ); | ||
1929 | snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIOD_SIZE); | ||
1930 | |||
1931 | return 0; | ||
1932 | } | ||
1933 | |||
1934 | static void snd_pcm_add_file(snd_pcm_str_t *str, | ||
1935 | snd_pcm_file_t *pcm_file) | ||
1936 | { | ||
1937 | pcm_file->next = str->files; | ||
1938 | str->files = pcm_file; | ||
1939 | } | ||
1940 | |||
1941 | static void snd_pcm_remove_file(snd_pcm_str_t *str, | ||
1942 | snd_pcm_file_t *pcm_file) | ||
1943 | { | ||
1944 | snd_pcm_file_t * pcm_file1; | ||
1945 | if (str->files == pcm_file) { | ||
1946 | str->files = pcm_file->next; | ||
1947 | } else { | ||
1948 | pcm_file1 = str->files; | ||
1949 | while (pcm_file1 && pcm_file1->next != pcm_file) | ||
1950 | pcm_file1 = pcm_file1->next; | ||
1951 | if (pcm_file1 != NULL) | ||
1952 | pcm_file1->next = pcm_file->next; | ||
1953 | } | ||
1954 | } | ||
1955 | |||
1956 | static int snd_pcm_release_file(snd_pcm_file_t * pcm_file) | ||
1957 | { | ||
1958 | snd_pcm_substream_t *substream; | ||
1959 | snd_pcm_runtime_t *runtime; | ||
1960 | snd_pcm_str_t * str; | ||
1961 | |||
1962 | snd_assert(pcm_file != NULL, return -ENXIO); | ||
1963 | substream = pcm_file->substream; | ||
1964 | snd_assert(substream != NULL, return -ENXIO); | ||
1965 | runtime = substream->runtime; | ||
1966 | str = substream->pstr; | ||
1967 | snd_pcm_unlink(substream); | ||
1968 | if (substream->open_flag) { | ||
1969 | if (substream->ops->hw_free != NULL) | ||
1970 | substream->ops->hw_free(substream); | ||
1971 | substream->ops->close(substream); | ||
1972 | substream->open_flag = 0; | ||
1973 | } | ||
1974 | substream->ffile = NULL; | ||
1975 | snd_pcm_remove_file(str, pcm_file); | ||
1976 | snd_pcm_release_substream(substream); | ||
1977 | kfree(pcm_file); | ||
1978 | return 0; | ||
1979 | } | ||
1980 | |||
1981 | static int snd_pcm_open_file(struct file *file, | ||
1982 | snd_pcm_t *pcm, | ||
1983 | int stream, | ||
1984 | snd_pcm_file_t **rpcm_file) | ||
1985 | { | ||
1986 | int err = 0; | ||
1987 | snd_pcm_file_t *pcm_file; | ||
1988 | snd_pcm_substream_t *substream; | ||
1989 | snd_pcm_str_t *str; | ||
1990 | |||
1991 | snd_assert(rpcm_file != NULL, return -EINVAL); | ||
1992 | *rpcm_file = NULL; | ||
1993 | |||
1994 | pcm_file = kcalloc(1, sizeof(*pcm_file), GFP_KERNEL); | ||
1995 | if (pcm_file == NULL) { | ||
1996 | return -ENOMEM; | ||
1997 | } | ||
1998 | |||
1999 | if ((err = snd_pcm_open_substream(pcm, stream, &substream)) < 0) { | ||
2000 | kfree(pcm_file); | ||
2001 | return err; | ||
2002 | } | ||
2003 | |||
2004 | str = substream->pstr; | ||
2005 | substream->file = pcm_file; | ||
2006 | substream->no_mmap_ctrl = 0; | ||
2007 | |||
2008 | pcm_file->substream = substream; | ||
2009 | |||
2010 | snd_pcm_add_file(str, pcm_file); | ||
2011 | |||
2012 | err = snd_pcm_hw_constraints_init(substream); | ||
2013 | if (err < 0) { | ||
2014 | snd_printd("snd_pcm_hw_constraints_init failed\n"); | ||
2015 | snd_pcm_release_file(pcm_file); | ||
2016 | return err; | ||
2017 | } | ||
2018 | |||
2019 | if ((err = substream->ops->open(substream)) < 0) { | ||
2020 | snd_pcm_release_file(pcm_file); | ||
2021 | return err; | ||
2022 | } | ||
2023 | substream->open_flag = 1; | ||
2024 | |||
2025 | err = snd_pcm_hw_constraints_complete(substream); | ||
2026 | if (err < 0) { | ||
2027 | snd_printd("snd_pcm_hw_constraints_complete failed\n"); | ||
2028 | substream->ops->close(substream); | ||
2029 | snd_pcm_release_file(pcm_file); | ||
2030 | return err; | ||
2031 | } | ||
2032 | |||
2033 | substream->ffile = file; | ||
2034 | |||
2035 | file->private_data = pcm_file; | ||
2036 | *rpcm_file = pcm_file; | ||
2037 | return 0; | ||
2038 | } | ||
2039 | |||
2040 | static int snd_pcm_open(struct inode *inode, struct file *file) | ||
2041 | { | ||
2042 | int cardnum = SNDRV_MINOR_CARD(iminor(inode)); | ||
2043 | int device = SNDRV_MINOR_DEVICE(iminor(inode)); | ||
2044 | int err; | ||
2045 | snd_pcm_t *pcm; | ||
2046 | snd_pcm_file_t *pcm_file; | ||
2047 | wait_queue_t wait; | ||
2048 | |||
2049 | snd_runtime_check(device >= SNDRV_MINOR_PCM_PLAYBACK && device < SNDRV_MINOR_DEVICES, return -ENXIO); | ||
2050 | pcm = snd_pcm_devices[(cardnum * SNDRV_PCM_DEVICES) + (device % SNDRV_MINOR_PCMS)]; | ||
2051 | if (pcm == NULL) { | ||
2052 | err = -ENODEV; | ||
2053 | goto __error1; | ||
2054 | } | ||
2055 | err = snd_card_file_add(pcm->card, file); | ||
2056 | if (err < 0) | ||
2057 | goto __error1; | ||
2058 | if (!try_module_get(pcm->card->module)) { | ||
2059 | err = -EFAULT; | ||
2060 | goto __error2; | ||
2061 | } | ||
2062 | init_waitqueue_entry(&wait, current); | ||
2063 | add_wait_queue(&pcm->open_wait, &wait); | ||
2064 | down(&pcm->open_mutex); | ||
2065 | while (1) { | ||
2066 | err = snd_pcm_open_file(file, pcm, device >= SNDRV_MINOR_PCM_CAPTURE ? SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK, &pcm_file); | ||
2067 | if (err >= 0) | ||
2068 | break; | ||
2069 | if (err == -EAGAIN) { | ||
2070 | if (file->f_flags & O_NONBLOCK) { | ||
2071 | err = -EBUSY; | ||
2072 | break; | ||
2073 | } | ||
2074 | } else | ||
2075 | break; | ||
2076 | set_current_state(TASK_INTERRUPTIBLE); | ||
2077 | up(&pcm->open_mutex); | ||
2078 | schedule(); | ||
2079 | down(&pcm->open_mutex); | ||
2080 | if (signal_pending(current)) { | ||
2081 | err = -ERESTARTSYS; | ||
2082 | break; | ||
2083 | } | ||
2084 | } | ||
2085 | remove_wait_queue(&pcm->open_wait, &wait); | ||
2086 | up(&pcm->open_mutex); | ||
2087 | if (err < 0) | ||
2088 | goto __error; | ||
2089 | return err; | ||
2090 | |||
2091 | __error: | ||
2092 | module_put(pcm->card->module); | ||
2093 | __error2: | ||
2094 | snd_card_file_remove(pcm->card, file); | ||
2095 | __error1: | ||
2096 | return err; | ||
2097 | } | ||
2098 | |||
2099 | static int snd_pcm_release(struct inode *inode, struct file *file) | ||
2100 | { | ||
2101 | snd_pcm_t *pcm; | ||
2102 | snd_pcm_substream_t *substream; | ||
2103 | snd_pcm_file_t *pcm_file; | ||
2104 | |||
2105 | pcm_file = file->private_data; | ||
2106 | substream = pcm_file->substream; | ||
2107 | snd_assert(substream != NULL, return -ENXIO); | ||
2108 | snd_assert(!atomic_read(&substream->runtime->mmap_count), ); | ||
2109 | pcm = substream->pcm; | ||
2110 | snd_pcm_drop(substream); | ||
2111 | fasync_helper(-1, file, 0, &substream->runtime->fasync); | ||
2112 | down(&pcm->open_mutex); | ||
2113 | snd_pcm_release_file(pcm_file); | ||
2114 | up(&pcm->open_mutex); | ||
2115 | wake_up(&pcm->open_wait); | ||
2116 | module_put(pcm->card->module); | ||
2117 | snd_card_file_remove(pcm->card, file); | ||
2118 | return 0; | ||
2119 | } | ||
2120 | |||
2121 | static snd_pcm_sframes_t snd_pcm_playback_rewind(snd_pcm_substream_t *substream, snd_pcm_uframes_t frames) | ||
2122 | { | ||
2123 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
2124 | snd_pcm_sframes_t appl_ptr; | ||
2125 | snd_pcm_sframes_t ret; | ||
2126 | snd_pcm_sframes_t hw_avail; | ||
2127 | |||
2128 | if (frames == 0) | ||
2129 | return 0; | ||
2130 | |||
2131 | snd_pcm_stream_lock_irq(substream); | ||
2132 | switch (runtime->status->state) { | ||
2133 | case SNDRV_PCM_STATE_PREPARED: | ||
2134 | break; | ||
2135 | case SNDRV_PCM_STATE_DRAINING: | ||
2136 | case SNDRV_PCM_STATE_RUNNING: | ||
2137 | if (snd_pcm_update_hw_ptr(substream) >= 0) | ||
2138 | break; | ||
2139 | /* Fall through */ | ||
2140 | case SNDRV_PCM_STATE_XRUN: | ||
2141 | ret = -EPIPE; | ||
2142 | goto __end; | ||
2143 | default: | ||
2144 | ret = -EBADFD; | ||
2145 | goto __end; | ||
2146 | } | ||
2147 | |||
2148 | hw_avail = snd_pcm_playback_hw_avail(runtime); | ||
2149 | if (hw_avail <= 0) { | ||
2150 | ret = 0; | ||
2151 | goto __end; | ||
2152 | } | ||
2153 | if (frames > (snd_pcm_uframes_t)hw_avail) | ||
2154 | frames = hw_avail; | ||
2155 | else | ||
2156 | frames -= frames % runtime->xfer_align; | ||
2157 | appl_ptr = runtime->control->appl_ptr - frames; | ||
2158 | if (appl_ptr < 0) | ||
2159 | appl_ptr += runtime->boundary; | ||
2160 | runtime->control->appl_ptr = appl_ptr; | ||
2161 | if (runtime->status->state == SNDRV_PCM_STATE_RUNNING && | ||
2162 | runtime->sleep_min) | ||
2163 | snd_pcm_tick_prepare(substream); | ||
2164 | ret = frames; | ||
2165 | __end: | ||
2166 | snd_pcm_stream_unlock_irq(substream); | ||
2167 | return ret; | ||
2168 | } | ||
2169 | |||
2170 | static snd_pcm_sframes_t snd_pcm_capture_rewind(snd_pcm_substream_t *substream, snd_pcm_uframes_t frames) | ||
2171 | { | ||
2172 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
2173 | snd_pcm_sframes_t appl_ptr; | ||
2174 | snd_pcm_sframes_t ret; | ||
2175 | snd_pcm_sframes_t hw_avail; | ||
2176 | |||
2177 | if (frames == 0) | ||
2178 | return 0; | ||
2179 | |||
2180 | snd_pcm_stream_lock_irq(substream); | ||
2181 | switch (runtime->status->state) { | ||
2182 | case SNDRV_PCM_STATE_PREPARED: | ||
2183 | case SNDRV_PCM_STATE_DRAINING: | ||
2184 | break; | ||
2185 | case SNDRV_PCM_STATE_RUNNING: | ||
2186 | if (snd_pcm_update_hw_ptr(substream) >= 0) | ||
2187 | break; | ||
2188 | /* Fall through */ | ||
2189 | case SNDRV_PCM_STATE_XRUN: | ||
2190 | ret = -EPIPE; | ||
2191 | goto __end; | ||
2192 | default: | ||
2193 | ret = -EBADFD; | ||
2194 | goto __end; | ||
2195 | } | ||
2196 | |||
2197 | hw_avail = snd_pcm_capture_hw_avail(runtime); | ||
2198 | if (hw_avail <= 0) { | ||
2199 | ret = 0; | ||
2200 | goto __end; | ||
2201 | } | ||
2202 | if (frames > (snd_pcm_uframes_t)hw_avail) | ||
2203 | frames = hw_avail; | ||
2204 | else | ||
2205 | frames -= frames % runtime->xfer_align; | ||
2206 | appl_ptr = runtime->control->appl_ptr - frames; | ||
2207 | if (appl_ptr < 0) | ||
2208 | appl_ptr += runtime->boundary; | ||
2209 | runtime->control->appl_ptr = appl_ptr; | ||
2210 | if (runtime->status->state == SNDRV_PCM_STATE_RUNNING && | ||
2211 | runtime->sleep_min) | ||
2212 | snd_pcm_tick_prepare(substream); | ||
2213 | ret = frames; | ||
2214 | __end: | ||
2215 | snd_pcm_stream_unlock_irq(substream); | ||
2216 | return ret; | ||
2217 | } | ||
2218 | |||
2219 | static snd_pcm_sframes_t snd_pcm_playback_forward(snd_pcm_substream_t *substream, snd_pcm_uframes_t frames) | ||
2220 | { | ||
2221 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
2222 | snd_pcm_sframes_t appl_ptr; | ||
2223 | snd_pcm_sframes_t ret; | ||
2224 | snd_pcm_sframes_t avail; | ||
2225 | |||
2226 | if (frames == 0) | ||
2227 | return 0; | ||
2228 | |||
2229 | snd_pcm_stream_lock_irq(substream); | ||
2230 | switch (runtime->status->state) { | ||
2231 | case SNDRV_PCM_STATE_PREPARED: | ||
2232 | case SNDRV_PCM_STATE_PAUSED: | ||
2233 | break; | ||
2234 | case SNDRV_PCM_STATE_DRAINING: | ||
2235 | case SNDRV_PCM_STATE_RUNNING: | ||
2236 | if (snd_pcm_update_hw_ptr(substream) >= 0) | ||
2237 | break; | ||
2238 | /* Fall through */ | ||
2239 | case SNDRV_PCM_STATE_XRUN: | ||
2240 | ret = -EPIPE; | ||
2241 | goto __end; | ||
2242 | default: | ||
2243 | ret = -EBADFD; | ||
2244 | goto __end; | ||
2245 | } | ||
2246 | |||
2247 | avail = snd_pcm_playback_avail(runtime); | ||
2248 | if (avail <= 0) { | ||
2249 | ret = 0; | ||
2250 | goto __end; | ||
2251 | } | ||
2252 | if (frames > (snd_pcm_uframes_t)avail) | ||
2253 | frames = avail; | ||
2254 | else | ||
2255 | frames -= frames % runtime->xfer_align; | ||
2256 | appl_ptr = runtime->control->appl_ptr + frames; | ||
2257 | if (appl_ptr >= (snd_pcm_sframes_t)runtime->boundary) | ||
2258 | appl_ptr -= runtime->boundary; | ||
2259 | runtime->control->appl_ptr = appl_ptr; | ||
2260 | if (runtime->status->state == SNDRV_PCM_STATE_RUNNING && | ||
2261 | runtime->sleep_min) | ||
2262 | snd_pcm_tick_prepare(substream); | ||
2263 | ret = frames; | ||
2264 | __end: | ||
2265 | snd_pcm_stream_unlock_irq(substream); | ||
2266 | return ret; | ||
2267 | } | ||
2268 | |||
2269 | static snd_pcm_sframes_t snd_pcm_capture_forward(snd_pcm_substream_t *substream, snd_pcm_uframes_t frames) | ||
2270 | { | ||
2271 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
2272 | snd_pcm_sframes_t appl_ptr; | ||
2273 | snd_pcm_sframes_t ret; | ||
2274 | snd_pcm_sframes_t avail; | ||
2275 | |||
2276 | if (frames == 0) | ||
2277 | return 0; | ||
2278 | |||
2279 | snd_pcm_stream_lock_irq(substream); | ||
2280 | switch (runtime->status->state) { | ||
2281 | case SNDRV_PCM_STATE_PREPARED: | ||
2282 | case SNDRV_PCM_STATE_DRAINING: | ||
2283 | case SNDRV_PCM_STATE_PAUSED: | ||
2284 | break; | ||
2285 | case SNDRV_PCM_STATE_RUNNING: | ||
2286 | if (snd_pcm_update_hw_ptr(substream) >= 0) | ||
2287 | break; | ||
2288 | /* Fall through */ | ||
2289 | case SNDRV_PCM_STATE_XRUN: | ||
2290 | ret = -EPIPE; | ||
2291 | goto __end; | ||
2292 | default: | ||
2293 | ret = -EBADFD; | ||
2294 | goto __end; | ||
2295 | } | ||
2296 | |||
2297 | avail = snd_pcm_capture_avail(runtime); | ||
2298 | if (avail <= 0) { | ||
2299 | ret = 0; | ||
2300 | goto __end; | ||
2301 | } | ||
2302 | if (frames > (snd_pcm_uframes_t)avail) | ||
2303 | frames = avail; | ||
2304 | else | ||
2305 | frames -= frames % runtime->xfer_align; | ||
2306 | appl_ptr = runtime->control->appl_ptr + frames; | ||
2307 | if (appl_ptr >= (snd_pcm_sframes_t)runtime->boundary) | ||
2308 | appl_ptr -= runtime->boundary; | ||
2309 | runtime->control->appl_ptr = appl_ptr; | ||
2310 | if (runtime->status->state == SNDRV_PCM_STATE_RUNNING && | ||
2311 | runtime->sleep_min) | ||
2312 | snd_pcm_tick_prepare(substream); | ||
2313 | ret = frames; | ||
2314 | __end: | ||
2315 | snd_pcm_stream_unlock_irq(substream); | ||
2316 | return ret; | ||
2317 | } | ||
2318 | |||
2319 | static int snd_pcm_hwsync(snd_pcm_substream_t *substream) | ||
2320 | { | ||
2321 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
2322 | int err; | ||
2323 | |||
2324 | snd_pcm_stream_lock_irq(substream); | ||
2325 | switch (runtime->status->state) { | ||
2326 | case SNDRV_PCM_STATE_DRAINING: | ||
2327 | if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) | ||
2328 | goto __badfd; | ||
2329 | case SNDRV_PCM_STATE_RUNNING: | ||
2330 | if ((err = snd_pcm_update_hw_ptr(substream)) < 0) | ||
2331 | break; | ||
2332 | /* Fall through */ | ||
2333 | case SNDRV_PCM_STATE_PREPARED: | ||
2334 | case SNDRV_PCM_STATE_SUSPENDED: | ||
2335 | err = 0; | ||
2336 | break; | ||
2337 | case SNDRV_PCM_STATE_XRUN: | ||
2338 | err = -EPIPE; | ||
2339 | break; | ||
2340 | default: | ||
2341 | __badfd: | ||
2342 | err = -EBADFD; | ||
2343 | break; | ||
2344 | } | ||
2345 | snd_pcm_stream_unlock_irq(substream); | ||
2346 | return err; | ||
2347 | } | ||
2348 | |||
2349 | static int snd_pcm_delay(snd_pcm_substream_t *substream, snd_pcm_sframes_t __user *res) | ||
2350 | { | ||
2351 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
2352 | int err; | ||
2353 | snd_pcm_sframes_t n = 0; | ||
2354 | |||
2355 | snd_pcm_stream_lock_irq(substream); | ||
2356 | switch (runtime->status->state) { | ||
2357 | case SNDRV_PCM_STATE_DRAINING: | ||
2358 | if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) | ||
2359 | goto __badfd; | ||
2360 | case SNDRV_PCM_STATE_RUNNING: | ||
2361 | if ((err = snd_pcm_update_hw_ptr(substream)) < 0) | ||
2362 | break; | ||
2363 | /* Fall through */ | ||
2364 | case SNDRV_PCM_STATE_PREPARED: | ||
2365 | case SNDRV_PCM_STATE_SUSPENDED: | ||
2366 | err = 0; | ||
2367 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | ||
2368 | n = snd_pcm_playback_hw_avail(runtime); | ||
2369 | else | ||
2370 | n = snd_pcm_capture_avail(runtime); | ||
2371 | break; | ||
2372 | case SNDRV_PCM_STATE_XRUN: | ||
2373 | err = -EPIPE; | ||
2374 | break; | ||
2375 | default: | ||
2376 | __badfd: | ||
2377 | err = -EBADFD; | ||
2378 | break; | ||
2379 | } | ||
2380 | snd_pcm_stream_unlock_irq(substream); | ||
2381 | if (!err) | ||
2382 | if (put_user(n, res)) | ||
2383 | err = -EFAULT; | ||
2384 | return err; | ||
2385 | } | ||
2386 | |||
2387 | static int snd_pcm_sync_ptr(snd_pcm_substream_t *substream, struct sndrv_pcm_sync_ptr __user *_sync_ptr) | ||
2388 | { | ||
2389 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
2390 | struct sndrv_pcm_sync_ptr sync_ptr; | ||
2391 | volatile struct sndrv_pcm_mmap_status *status; | ||
2392 | volatile struct sndrv_pcm_mmap_control *control; | ||
2393 | int err; | ||
2394 | |||
2395 | memset(&sync_ptr, 0, sizeof(sync_ptr)); | ||
2396 | if (get_user(sync_ptr.flags, (unsigned __user *)&(_sync_ptr->flags))) | ||
2397 | return -EFAULT; | ||
2398 | if (copy_from_user(&sync_ptr.c.control, &(_sync_ptr->c.control), sizeof(struct sndrv_pcm_mmap_control))) | ||
2399 | return -EFAULT; | ||
2400 | status = runtime->status; | ||
2401 | control = runtime->control; | ||
2402 | if (sync_ptr.flags & SNDRV_PCM_SYNC_PTR_HWSYNC) { | ||
2403 | err = snd_pcm_hwsync(substream); | ||
2404 | if (err < 0) | ||
2405 | return err; | ||
2406 | } | ||
2407 | snd_pcm_stream_lock_irq(substream); | ||
2408 | if (!(sync_ptr.flags & SNDRV_PCM_SYNC_PTR_APPL)) | ||
2409 | control->appl_ptr = sync_ptr.c.control.appl_ptr; | ||
2410 | else | ||
2411 | sync_ptr.c.control.appl_ptr = control->appl_ptr; | ||
2412 | if (!(sync_ptr.flags & SNDRV_PCM_SYNC_PTR_AVAIL_MIN)) | ||
2413 | control->avail_min = sync_ptr.c.control.avail_min; | ||
2414 | else | ||
2415 | sync_ptr.c.control.avail_min = control->avail_min; | ||
2416 | sync_ptr.s.status.state = status->state; | ||
2417 | sync_ptr.s.status.hw_ptr = status->hw_ptr; | ||
2418 | sync_ptr.s.status.tstamp = status->tstamp; | ||
2419 | sync_ptr.s.status.suspended_state = status->suspended_state; | ||
2420 | snd_pcm_stream_unlock_irq(substream); | ||
2421 | if (copy_to_user(_sync_ptr, &sync_ptr, sizeof(sync_ptr))) | ||
2422 | return -EFAULT; | ||
2423 | return 0; | ||
2424 | } | ||
2425 | |||
2426 | static int snd_pcm_playback_ioctl1(snd_pcm_substream_t *substream, | ||
2427 | unsigned int cmd, void __user *arg); | ||
2428 | static int snd_pcm_capture_ioctl1(snd_pcm_substream_t *substream, | ||
2429 | unsigned int cmd, void __user *arg); | ||
2430 | |||
2431 | static int snd_pcm_common_ioctl1(snd_pcm_substream_t *substream, | ||
2432 | unsigned int cmd, void __user *arg) | ||
2433 | { | ||
2434 | snd_assert(substream != NULL, return -ENXIO); | ||
2435 | |||
2436 | switch (cmd) { | ||
2437 | case SNDRV_PCM_IOCTL_PVERSION: | ||
2438 | return put_user(SNDRV_PCM_VERSION, (int __user *)arg) ? -EFAULT : 0; | ||
2439 | case SNDRV_PCM_IOCTL_INFO: | ||
2440 | return snd_pcm_info_user(substream, arg); | ||
2441 | case SNDRV_PCM_IOCTL_TSTAMP: | ||
2442 | { | ||
2443 | int xarg; | ||
2444 | if (get_user(xarg, (int __user *)arg)) | ||
2445 | return -EFAULT; | ||
2446 | substream->runtime->tstamp_timespec = xarg ? 1 : 0; | ||
2447 | return 0; | ||
2448 | } | ||
2449 | case SNDRV_PCM_IOCTL_HW_REFINE: | ||
2450 | return snd_pcm_hw_refine_user(substream, arg); | ||
2451 | case SNDRV_PCM_IOCTL_HW_PARAMS: | ||
2452 | return snd_pcm_hw_params_user(substream, arg); | ||
2453 | case SNDRV_PCM_IOCTL_HW_FREE: | ||
2454 | return snd_pcm_hw_free(substream); | ||
2455 | case SNDRV_PCM_IOCTL_SW_PARAMS: | ||
2456 | return snd_pcm_sw_params_user(substream, arg); | ||
2457 | case SNDRV_PCM_IOCTL_STATUS: | ||
2458 | return snd_pcm_status_user(substream, arg); | ||
2459 | case SNDRV_PCM_IOCTL_CHANNEL_INFO: | ||
2460 | return snd_pcm_channel_info_user(substream, arg); | ||
2461 | case SNDRV_PCM_IOCTL_PREPARE: | ||
2462 | return snd_pcm_prepare(substream); | ||
2463 | case SNDRV_PCM_IOCTL_RESET: | ||
2464 | return snd_pcm_reset(substream); | ||
2465 | case SNDRV_PCM_IOCTL_START: | ||
2466 | return snd_pcm_action_lock_irq(&snd_pcm_action_start, substream, SNDRV_PCM_STATE_RUNNING); | ||
2467 | case SNDRV_PCM_IOCTL_LINK: | ||
2468 | return snd_pcm_link(substream, (int)(unsigned long) arg); | ||
2469 | case SNDRV_PCM_IOCTL_UNLINK: | ||
2470 | return snd_pcm_unlink(substream); | ||
2471 | case SNDRV_PCM_IOCTL_RESUME: | ||
2472 | return snd_pcm_resume(substream); | ||
2473 | case SNDRV_PCM_IOCTL_XRUN: | ||
2474 | return snd_pcm_xrun(substream); | ||
2475 | case SNDRV_PCM_IOCTL_HWSYNC: | ||
2476 | return snd_pcm_hwsync(substream); | ||
2477 | case SNDRV_PCM_IOCTL_DELAY: | ||
2478 | return snd_pcm_delay(substream, arg); | ||
2479 | case SNDRV_PCM_IOCTL_SYNC_PTR: | ||
2480 | return snd_pcm_sync_ptr(substream, arg); | ||
2481 | case SNDRV_PCM_IOCTL_HW_REFINE_OLD: | ||
2482 | return snd_pcm_hw_refine_old_user(substream, arg); | ||
2483 | case SNDRV_PCM_IOCTL_HW_PARAMS_OLD: | ||
2484 | return snd_pcm_hw_params_old_user(substream, arg); | ||
2485 | case SNDRV_PCM_IOCTL_DRAIN: | ||
2486 | return snd_pcm_drain(substream); | ||
2487 | case SNDRV_PCM_IOCTL_DROP: | ||
2488 | return snd_pcm_drop(substream); | ||
2489 | } | ||
2490 | snd_printd("unknown ioctl = 0x%x\n", cmd); | ||
2491 | return -ENOTTY; | ||
2492 | } | ||
2493 | |||
2494 | static int snd_pcm_playback_ioctl1(snd_pcm_substream_t *substream, | ||
2495 | unsigned int cmd, void __user *arg) | ||
2496 | { | ||
2497 | snd_assert(substream != NULL, return -ENXIO); | ||
2498 | snd_assert(substream->stream == SNDRV_PCM_STREAM_PLAYBACK, return -EINVAL); | ||
2499 | switch (cmd) { | ||
2500 | case SNDRV_PCM_IOCTL_WRITEI_FRAMES: | ||
2501 | { | ||
2502 | snd_xferi_t xferi; | ||
2503 | snd_xferi_t __user *_xferi = arg; | ||
2504 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
2505 | snd_pcm_sframes_t result; | ||
2506 | if (runtime->status->state == SNDRV_PCM_STATE_OPEN) | ||
2507 | return -EBADFD; | ||
2508 | if (put_user(0, &_xferi->result)) | ||
2509 | return -EFAULT; | ||
2510 | if (copy_from_user(&xferi, _xferi, sizeof(xferi))) | ||
2511 | return -EFAULT; | ||
2512 | result = snd_pcm_lib_write(substream, xferi.buf, xferi.frames); | ||
2513 | __put_user(result, &_xferi->result); | ||
2514 | return result < 0 ? result : 0; | ||
2515 | } | ||
2516 | case SNDRV_PCM_IOCTL_WRITEN_FRAMES: | ||
2517 | { | ||
2518 | snd_xfern_t xfern; | ||
2519 | snd_xfern_t __user *_xfern = arg; | ||
2520 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
2521 | void __user **bufs; | ||
2522 | snd_pcm_sframes_t result; | ||
2523 | if (runtime->status->state == SNDRV_PCM_STATE_OPEN) | ||
2524 | return -EBADFD; | ||
2525 | if (runtime->channels > 128) | ||
2526 | return -EINVAL; | ||
2527 | if (put_user(0, &_xfern->result)) | ||
2528 | return -EFAULT; | ||
2529 | if (copy_from_user(&xfern, _xfern, sizeof(xfern))) | ||
2530 | return -EFAULT; | ||
2531 | bufs = kmalloc(sizeof(void *) * runtime->channels, GFP_KERNEL); | ||
2532 | if (bufs == NULL) | ||
2533 | return -ENOMEM; | ||
2534 | if (copy_from_user(bufs, xfern.bufs, sizeof(void *) * runtime->channels)) { | ||
2535 | kfree(bufs); | ||
2536 | return -EFAULT; | ||
2537 | } | ||
2538 | result = snd_pcm_lib_writev(substream, bufs, xfern.frames); | ||
2539 | kfree(bufs); | ||
2540 | __put_user(result, &_xfern->result); | ||
2541 | return result < 0 ? result : 0; | ||
2542 | } | ||
2543 | case SNDRV_PCM_IOCTL_REWIND: | ||
2544 | { | ||
2545 | snd_pcm_uframes_t frames; | ||
2546 | snd_pcm_uframes_t __user *_frames = arg; | ||
2547 | snd_pcm_sframes_t result; | ||
2548 | if (get_user(frames, _frames)) | ||
2549 | return -EFAULT; | ||
2550 | if (put_user(0, _frames)) | ||
2551 | return -EFAULT; | ||
2552 | result = snd_pcm_playback_rewind(substream, frames); | ||
2553 | __put_user(result, _frames); | ||
2554 | return result < 0 ? result : 0; | ||
2555 | } | ||
2556 | case SNDRV_PCM_IOCTL_FORWARD: | ||
2557 | { | ||
2558 | snd_pcm_uframes_t frames; | ||
2559 | snd_pcm_uframes_t __user *_frames = arg; | ||
2560 | snd_pcm_sframes_t result; | ||
2561 | if (get_user(frames, _frames)) | ||
2562 | return -EFAULT; | ||
2563 | if (put_user(0, _frames)) | ||
2564 | return -EFAULT; | ||
2565 | result = snd_pcm_playback_forward(substream, frames); | ||
2566 | __put_user(result, _frames); | ||
2567 | return result < 0 ? result : 0; | ||
2568 | } | ||
2569 | case SNDRV_PCM_IOCTL_PAUSE: | ||
2570 | { | ||
2571 | int res; | ||
2572 | snd_pcm_stream_lock_irq(substream); | ||
2573 | res = snd_pcm_pause(substream, (int)(unsigned long)arg); | ||
2574 | snd_pcm_stream_unlock_irq(substream); | ||
2575 | return res; | ||
2576 | } | ||
2577 | } | ||
2578 | return snd_pcm_common_ioctl1(substream, cmd, arg); | ||
2579 | } | ||
2580 | |||
2581 | static int snd_pcm_capture_ioctl1(snd_pcm_substream_t *substream, | ||
2582 | unsigned int cmd, void __user *arg) | ||
2583 | { | ||
2584 | snd_assert(substream != NULL, return -ENXIO); | ||
2585 | snd_assert(substream->stream == SNDRV_PCM_STREAM_CAPTURE, return -EINVAL); | ||
2586 | switch (cmd) { | ||
2587 | case SNDRV_PCM_IOCTL_READI_FRAMES: | ||
2588 | { | ||
2589 | snd_xferi_t xferi; | ||
2590 | snd_xferi_t __user *_xferi = arg; | ||
2591 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
2592 | snd_pcm_sframes_t result; | ||
2593 | if (runtime->status->state == SNDRV_PCM_STATE_OPEN) | ||
2594 | return -EBADFD; | ||
2595 | if (put_user(0, &_xferi->result)) | ||
2596 | return -EFAULT; | ||
2597 | if (copy_from_user(&xferi, _xferi, sizeof(xferi))) | ||
2598 | return -EFAULT; | ||
2599 | result = snd_pcm_lib_read(substream, xferi.buf, xferi.frames); | ||
2600 | __put_user(result, &_xferi->result); | ||
2601 | return result < 0 ? result : 0; | ||
2602 | } | ||
2603 | case SNDRV_PCM_IOCTL_READN_FRAMES: | ||
2604 | { | ||
2605 | snd_xfern_t xfern; | ||
2606 | snd_xfern_t __user *_xfern = arg; | ||
2607 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
2608 | void *bufs; | ||
2609 | snd_pcm_sframes_t result; | ||
2610 | if (runtime->status->state == SNDRV_PCM_STATE_OPEN) | ||
2611 | return -EBADFD; | ||
2612 | if (runtime->channels > 128) | ||
2613 | return -EINVAL; | ||
2614 | if (put_user(0, &_xfern->result)) | ||
2615 | return -EFAULT; | ||
2616 | if (copy_from_user(&xfern, _xfern, sizeof(xfern))) | ||
2617 | return -EFAULT; | ||
2618 | bufs = kmalloc(sizeof(void *) * runtime->channels, GFP_KERNEL); | ||
2619 | if (bufs == NULL) | ||
2620 | return -ENOMEM; | ||
2621 | if (copy_from_user(bufs, xfern.bufs, sizeof(void *) * runtime->channels)) { | ||
2622 | kfree(bufs); | ||
2623 | return -EFAULT; | ||
2624 | } | ||
2625 | result = snd_pcm_lib_readv(substream, bufs, xfern.frames); | ||
2626 | kfree(bufs); | ||
2627 | __put_user(result, &_xfern->result); | ||
2628 | return result < 0 ? result : 0; | ||
2629 | } | ||
2630 | case SNDRV_PCM_IOCTL_REWIND: | ||
2631 | { | ||
2632 | snd_pcm_uframes_t frames; | ||
2633 | snd_pcm_uframes_t __user *_frames = arg; | ||
2634 | snd_pcm_sframes_t result; | ||
2635 | if (get_user(frames, _frames)) | ||
2636 | return -EFAULT; | ||
2637 | if (put_user(0, _frames)) | ||
2638 | return -EFAULT; | ||
2639 | result = snd_pcm_capture_rewind(substream, frames); | ||
2640 | __put_user(result, _frames); | ||
2641 | return result < 0 ? result : 0; | ||
2642 | } | ||
2643 | case SNDRV_PCM_IOCTL_FORWARD: | ||
2644 | { | ||
2645 | snd_pcm_uframes_t frames; | ||
2646 | snd_pcm_uframes_t __user *_frames = arg; | ||
2647 | snd_pcm_sframes_t result; | ||
2648 | if (get_user(frames, _frames)) | ||
2649 | return -EFAULT; | ||
2650 | if (put_user(0, _frames)) | ||
2651 | return -EFAULT; | ||
2652 | result = snd_pcm_capture_forward(substream, frames); | ||
2653 | __put_user(result, _frames); | ||
2654 | return result < 0 ? result : 0; | ||
2655 | } | ||
2656 | } | ||
2657 | return snd_pcm_common_ioctl1(substream, cmd, arg); | ||
2658 | } | ||
2659 | |||
2660 | static long snd_pcm_playback_ioctl(struct file *file, unsigned int cmd, unsigned long arg) | ||
2661 | { | ||
2662 | snd_pcm_file_t *pcm_file; | ||
2663 | |||
2664 | pcm_file = file->private_data; | ||
2665 | |||
2666 | if (((cmd >> 8) & 0xff) != 'A') | ||
2667 | return -ENOTTY; | ||
2668 | |||
2669 | return snd_pcm_playback_ioctl1(pcm_file->substream, cmd, (void __user *)arg); | ||
2670 | } | ||
2671 | |||
2672 | static long snd_pcm_capture_ioctl(struct file *file, unsigned int cmd, unsigned long arg) | ||
2673 | { | ||
2674 | snd_pcm_file_t *pcm_file; | ||
2675 | |||
2676 | pcm_file = file->private_data; | ||
2677 | |||
2678 | if (((cmd >> 8) & 0xff) != 'A') | ||
2679 | return -ENOTTY; | ||
2680 | |||
2681 | return snd_pcm_capture_ioctl1(pcm_file->substream, cmd, (void __user *)arg); | ||
2682 | } | ||
2683 | |||
2684 | int snd_pcm_kernel_playback_ioctl(snd_pcm_substream_t *substream, | ||
2685 | unsigned int cmd, void *arg) | ||
2686 | { | ||
2687 | mm_segment_t fs; | ||
2688 | int result; | ||
2689 | |||
2690 | fs = snd_enter_user(); | ||
2691 | result = snd_pcm_playback_ioctl1(substream, cmd, (void __user *)arg); | ||
2692 | snd_leave_user(fs); | ||
2693 | return result; | ||
2694 | } | ||
2695 | |||
2696 | int snd_pcm_kernel_capture_ioctl(snd_pcm_substream_t *substream, | ||
2697 | unsigned int cmd, void *arg) | ||
2698 | { | ||
2699 | mm_segment_t fs; | ||
2700 | int result; | ||
2701 | |||
2702 | fs = snd_enter_user(); | ||
2703 | result = snd_pcm_capture_ioctl1(substream, cmd, (void __user *)arg); | ||
2704 | snd_leave_user(fs); | ||
2705 | return result; | ||
2706 | } | ||
2707 | |||
2708 | int snd_pcm_kernel_ioctl(snd_pcm_substream_t *substream, | ||
2709 | unsigned int cmd, void *arg) | ||
2710 | { | ||
2711 | switch (substream->stream) { | ||
2712 | case SNDRV_PCM_STREAM_PLAYBACK: | ||
2713 | return snd_pcm_kernel_playback_ioctl(substream, cmd, arg); | ||
2714 | case SNDRV_PCM_STREAM_CAPTURE: | ||
2715 | return snd_pcm_kernel_capture_ioctl(substream, cmd, arg); | ||
2716 | default: | ||
2717 | return -EINVAL; | ||
2718 | } | ||
2719 | } | ||
2720 | |||
2721 | static ssize_t snd_pcm_read(struct file *file, char __user *buf, size_t count, loff_t * offset) | ||
2722 | { | ||
2723 | snd_pcm_file_t *pcm_file; | ||
2724 | snd_pcm_substream_t *substream; | ||
2725 | snd_pcm_runtime_t *runtime; | ||
2726 | snd_pcm_sframes_t result; | ||
2727 | |||
2728 | pcm_file = file->private_data; | ||
2729 | substream = pcm_file->substream; | ||
2730 | snd_assert(substream != NULL, return -ENXIO); | ||
2731 | runtime = substream->runtime; | ||
2732 | if (runtime->status->state == SNDRV_PCM_STATE_OPEN) | ||
2733 | return -EBADFD; | ||
2734 | if (!frame_aligned(runtime, count)) | ||
2735 | return -EINVAL; | ||
2736 | count = bytes_to_frames(runtime, count); | ||
2737 | result = snd_pcm_lib_read(substream, buf, count); | ||
2738 | if (result > 0) | ||
2739 | result = frames_to_bytes(runtime, result); | ||
2740 | return result; | ||
2741 | } | ||
2742 | |||
2743 | static ssize_t snd_pcm_write(struct file *file, const char __user *buf, size_t count, loff_t * offset) | ||
2744 | { | ||
2745 | snd_pcm_file_t *pcm_file; | ||
2746 | snd_pcm_substream_t *substream; | ||
2747 | snd_pcm_runtime_t *runtime; | ||
2748 | snd_pcm_sframes_t result; | ||
2749 | |||
2750 | pcm_file = file->private_data; | ||
2751 | substream = pcm_file->substream; | ||
2752 | snd_assert(substream != NULL, result = -ENXIO; goto end); | ||
2753 | runtime = substream->runtime; | ||
2754 | if (runtime->status->state == SNDRV_PCM_STATE_OPEN) { | ||
2755 | result = -EBADFD; | ||
2756 | goto end; | ||
2757 | } | ||
2758 | if (!frame_aligned(runtime, count)) { | ||
2759 | result = -EINVAL; | ||
2760 | goto end; | ||
2761 | } | ||
2762 | count = bytes_to_frames(runtime, count); | ||
2763 | result = snd_pcm_lib_write(substream, buf, count); | ||
2764 | if (result > 0) | ||
2765 | result = frames_to_bytes(runtime, result); | ||
2766 | end: | ||
2767 | return result; | ||
2768 | } | ||
2769 | |||
2770 | static ssize_t snd_pcm_readv(struct file *file, const struct iovec *_vector, | ||
2771 | unsigned long count, loff_t * offset) | ||
2772 | |||
2773 | { | ||
2774 | snd_pcm_file_t *pcm_file; | ||
2775 | snd_pcm_substream_t *substream; | ||
2776 | snd_pcm_runtime_t *runtime; | ||
2777 | snd_pcm_sframes_t result; | ||
2778 | unsigned long i; | ||
2779 | void __user **bufs; | ||
2780 | snd_pcm_uframes_t frames; | ||
2781 | |||
2782 | pcm_file = file->private_data; | ||
2783 | substream = pcm_file->substream; | ||
2784 | snd_assert(substream != NULL, return -ENXIO); | ||
2785 | runtime = substream->runtime; | ||
2786 | if (runtime->status->state == SNDRV_PCM_STATE_OPEN) | ||
2787 | return -EBADFD; | ||
2788 | if (count > 1024 || count != runtime->channels) | ||
2789 | return -EINVAL; | ||
2790 | if (!frame_aligned(runtime, _vector->iov_len)) | ||
2791 | return -EINVAL; | ||
2792 | frames = bytes_to_samples(runtime, _vector->iov_len); | ||
2793 | bufs = kmalloc(sizeof(void *) * count, GFP_KERNEL); | ||
2794 | if (bufs == NULL) | ||
2795 | return -ENOMEM; | ||
2796 | for (i = 0; i < count; ++i) | ||
2797 | bufs[i] = _vector[i].iov_base; | ||
2798 | result = snd_pcm_lib_readv(substream, bufs, frames); | ||
2799 | if (result > 0) | ||
2800 | result = frames_to_bytes(runtime, result); | ||
2801 | kfree(bufs); | ||
2802 | return result; | ||
2803 | } | ||
2804 | |||
2805 | static ssize_t snd_pcm_writev(struct file *file, const struct iovec *_vector, | ||
2806 | unsigned long count, loff_t * offset) | ||
2807 | { | ||
2808 | snd_pcm_file_t *pcm_file; | ||
2809 | snd_pcm_substream_t *substream; | ||
2810 | snd_pcm_runtime_t *runtime; | ||
2811 | snd_pcm_sframes_t result; | ||
2812 | unsigned long i; | ||
2813 | void __user **bufs; | ||
2814 | snd_pcm_uframes_t frames; | ||
2815 | |||
2816 | pcm_file = file->private_data; | ||
2817 | substream = pcm_file->substream; | ||
2818 | snd_assert(substream != NULL, result = -ENXIO; goto end); | ||
2819 | runtime = substream->runtime; | ||
2820 | if (runtime->status->state == SNDRV_PCM_STATE_OPEN) { | ||
2821 | result = -EBADFD; | ||
2822 | goto end; | ||
2823 | } | ||
2824 | if (count > 128 || count != runtime->channels || | ||
2825 | !frame_aligned(runtime, _vector->iov_len)) { | ||
2826 | result = -EINVAL; | ||
2827 | goto end; | ||
2828 | } | ||
2829 | frames = bytes_to_samples(runtime, _vector->iov_len); | ||
2830 | bufs = kmalloc(sizeof(void *) * count, GFP_KERNEL); | ||
2831 | if (bufs == NULL) | ||
2832 | return -ENOMEM; | ||
2833 | for (i = 0; i < count; ++i) | ||
2834 | bufs[i] = _vector[i].iov_base; | ||
2835 | result = snd_pcm_lib_writev(substream, bufs, frames); | ||
2836 | if (result > 0) | ||
2837 | result = frames_to_bytes(runtime, result); | ||
2838 | kfree(bufs); | ||
2839 | end: | ||
2840 | return result; | ||
2841 | } | ||
2842 | |||
2843 | static unsigned int snd_pcm_playback_poll(struct file *file, poll_table * wait) | ||
2844 | { | ||
2845 | snd_pcm_file_t *pcm_file; | ||
2846 | snd_pcm_substream_t *substream; | ||
2847 | snd_pcm_runtime_t *runtime; | ||
2848 | unsigned int mask; | ||
2849 | snd_pcm_uframes_t avail; | ||
2850 | |||
2851 | pcm_file = file->private_data; | ||
2852 | |||
2853 | substream = pcm_file->substream; | ||
2854 | snd_assert(substream != NULL, return -ENXIO); | ||
2855 | runtime = substream->runtime; | ||
2856 | |||
2857 | poll_wait(file, &runtime->sleep, wait); | ||
2858 | |||
2859 | snd_pcm_stream_lock_irq(substream); | ||
2860 | avail = snd_pcm_playback_avail(runtime); | ||
2861 | switch (runtime->status->state) { | ||
2862 | case SNDRV_PCM_STATE_RUNNING: | ||
2863 | case SNDRV_PCM_STATE_PREPARED: | ||
2864 | case SNDRV_PCM_STATE_PAUSED: | ||
2865 | if (avail >= runtime->control->avail_min) { | ||
2866 | mask = POLLOUT | POLLWRNORM; | ||
2867 | break; | ||
2868 | } | ||
2869 | /* Fall through */ | ||
2870 | case SNDRV_PCM_STATE_DRAINING: | ||
2871 | mask = 0; | ||
2872 | break; | ||
2873 | default: | ||
2874 | mask = POLLOUT | POLLWRNORM | POLLERR; | ||
2875 | break; | ||
2876 | } | ||
2877 | snd_pcm_stream_unlock_irq(substream); | ||
2878 | return mask; | ||
2879 | } | ||
2880 | |||
2881 | static unsigned int snd_pcm_capture_poll(struct file *file, poll_table * wait) | ||
2882 | { | ||
2883 | snd_pcm_file_t *pcm_file; | ||
2884 | snd_pcm_substream_t *substream; | ||
2885 | snd_pcm_runtime_t *runtime; | ||
2886 | unsigned int mask; | ||
2887 | snd_pcm_uframes_t avail; | ||
2888 | |||
2889 | pcm_file = file->private_data; | ||
2890 | |||
2891 | substream = pcm_file->substream; | ||
2892 | snd_assert(substream != NULL, return -ENXIO); | ||
2893 | runtime = substream->runtime; | ||
2894 | |||
2895 | poll_wait(file, &runtime->sleep, wait); | ||
2896 | |||
2897 | snd_pcm_stream_lock_irq(substream); | ||
2898 | avail = snd_pcm_capture_avail(runtime); | ||
2899 | switch (runtime->status->state) { | ||
2900 | case SNDRV_PCM_STATE_RUNNING: | ||
2901 | case SNDRV_PCM_STATE_PREPARED: | ||
2902 | case SNDRV_PCM_STATE_PAUSED: | ||
2903 | if (avail >= runtime->control->avail_min) { | ||
2904 | mask = POLLIN | POLLRDNORM; | ||
2905 | break; | ||
2906 | } | ||
2907 | mask = 0; | ||
2908 | break; | ||
2909 | case SNDRV_PCM_STATE_DRAINING: | ||
2910 | if (avail > 0) { | ||
2911 | mask = POLLIN | POLLRDNORM; | ||
2912 | break; | ||
2913 | } | ||
2914 | /* Fall through */ | ||
2915 | default: | ||
2916 | mask = POLLIN | POLLRDNORM | POLLERR; | ||
2917 | break; | ||
2918 | } | ||
2919 | snd_pcm_stream_unlock_irq(substream); | ||
2920 | return mask; | ||
2921 | } | ||
2922 | |||
2923 | /* | ||
2924 | * mmap support | ||
2925 | */ | ||
2926 | |||
2927 | /* | ||
2928 | * Only on coherent architectures, we can mmap the status and the control records | ||
2929 | * for effcient data transfer. On others, we have to use HWSYNC ioctl... | ||
2930 | */ | ||
2931 | #if defined(CONFIG_X86) || defined(CONFIG_PPC) || defined(CONFIG_ALPHA) | ||
2932 | /* | ||
2933 | * mmap status record | ||
2934 | */ | ||
2935 | static struct page * snd_pcm_mmap_status_nopage(struct vm_area_struct *area, unsigned long address, int *type) | ||
2936 | { | ||
2937 | snd_pcm_substream_t *substream = (snd_pcm_substream_t *)area->vm_private_data; | ||
2938 | snd_pcm_runtime_t *runtime; | ||
2939 | struct page * page; | ||
2940 | |||
2941 | if (substream == NULL) | ||
2942 | return NOPAGE_OOM; | ||
2943 | runtime = substream->runtime; | ||
2944 | page = virt_to_page(runtime->status); | ||
2945 | if (!PageReserved(page)) | ||
2946 | get_page(page); | ||
2947 | if (type) | ||
2948 | *type = VM_FAULT_MINOR; | ||
2949 | return page; | ||
2950 | } | ||
2951 | |||
2952 | static struct vm_operations_struct snd_pcm_vm_ops_status = | ||
2953 | { | ||
2954 | .nopage = snd_pcm_mmap_status_nopage, | ||
2955 | }; | ||
2956 | |||
2957 | static int snd_pcm_mmap_status(snd_pcm_substream_t *substream, struct file *file, | ||
2958 | struct vm_area_struct *area) | ||
2959 | { | ||
2960 | snd_pcm_runtime_t *runtime; | ||
2961 | long size; | ||
2962 | if (!(area->vm_flags & VM_READ)) | ||
2963 | return -EINVAL; | ||
2964 | runtime = substream->runtime; | ||
2965 | snd_assert(runtime != NULL, return -EAGAIN); | ||
2966 | size = area->vm_end - area->vm_start; | ||
2967 | if (size != PAGE_ALIGN(sizeof(snd_pcm_mmap_status_t))) | ||
2968 | return -EINVAL; | ||
2969 | area->vm_ops = &snd_pcm_vm_ops_status; | ||
2970 | area->vm_private_data = substream; | ||
2971 | area->vm_flags |= VM_RESERVED; | ||
2972 | return 0; | ||
2973 | } | ||
2974 | |||
2975 | /* | ||
2976 | * mmap control record | ||
2977 | */ | ||
2978 | static struct page * snd_pcm_mmap_control_nopage(struct vm_area_struct *area, unsigned long address, int *type) | ||
2979 | { | ||
2980 | snd_pcm_substream_t *substream = (snd_pcm_substream_t *)area->vm_private_data; | ||
2981 | snd_pcm_runtime_t *runtime; | ||
2982 | struct page * page; | ||
2983 | |||
2984 | if (substream == NULL) | ||
2985 | return NOPAGE_OOM; | ||
2986 | runtime = substream->runtime; | ||
2987 | page = virt_to_page(runtime->control); | ||
2988 | if (!PageReserved(page)) | ||
2989 | get_page(page); | ||
2990 | if (type) | ||
2991 | *type = VM_FAULT_MINOR; | ||
2992 | return page; | ||
2993 | } | ||
2994 | |||
2995 | static struct vm_operations_struct snd_pcm_vm_ops_control = | ||
2996 | { | ||
2997 | .nopage = snd_pcm_mmap_control_nopage, | ||
2998 | }; | ||
2999 | |||
3000 | static int snd_pcm_mmap_control(snd_pcm_substream_t *substream, struct file *file, | ||
3001 | struct vm_area_struct *area) | ||
3002 | { | ||
3003 | snd_pcm_runtime_t *runtime; | ||
3004 | long size; | ||
3005 | if (!(area->vm_flags & VM_READ)) | ||
3006 | return -EINVAL; | ||
3007 | runtime = substream->runtime; | ||
3008 | snd_assert(runtime != NULL, return -EAGAIN); | ||
3009 | size = area->vm_end - area->vm_start; | ||
3010 | if (size != PAGE_ALIGN(sizeof(snd_pcm_mmap_control_t))) | ||
3011 | return -EINVAL; | ||
3012 | area->vm_ops = &snd_pcm_vm_ops_control; | ||
3013 | area->vm_private_data = substream; | ||
3014 | area->vm_flags |= VM_RESERVED; | ||
3015 | return 0; | ||
3016 | } | ||
3017 | #else /* ! coherent mmap */ | ||
3018 | /* | ||
3019 | * don't support mmap for status and control records. | ||
3020 | */ | ||
3021 | static int snd_pcm_mmap_status(snd_pcm_substream_t *substream, struct file *file, | ||
3022 | struct vm_area_struct *area) | ||
3023 | { | ||
3024 | return -ENXIO; | ||
3025 | } | ||
3026 | static int snd_pcm_mmap_control(snd_pcm_substream_t *substream, struct file *file, | ||
3027 | struct vm_area_struct *area) | ||
3028 | { | ||
3029 | return -ENXIO; | ||
3030 | } | ||
3031 | #endif /* coherent mmap */ | ||
3032 | |||
3033 | /* | ||
3034 | * nopage callback for mmapping a RAM page | ||
3035 | */ | ||
3036 | static struct page *snd_pcm_mmap_data_nopage(struct vm_area_struct *area, unsigned long address, int *type) | ||
3037 | { | ||
3038 | snd_pcm_substream_t *substream = (snd_pcm_substream_t *)area->vm_private_data; | ||
3039 | snd_pcm_runtime_t *runtime; | ||
3040 | unsigned long offset; | ||
3041 | struct page * page; | ||
3042 | void *vaddr; | ||
3043 | size_t dma_bytes; | ||
3044 | |||
3045 | if (substream == NULL) | ||
3046 | return NOPAGE_OOM; | ||
3047 | runtime = substream->runtime; | ||
3048 | offset = area->vm_pgoff << PAGE_SHIFT; | ||
3049 | offset += address - area->vm_start; | ||
3050 | snd_assert((offset % PAGE_SIZE) == 0, return NOPAGE_OOM); | ||
3051 | dma_bytes = PAGE_ALIGN(runtime->dma_bytes); | ||
3052 | if (offset > dma_bytes - PAGE_SIZE) | ||
3053 | return NOPAGE_SIGBUS; | ||
3054 | if (substream->ops->page) { | ||
3055 | page = substream->ops->page(substream, offset); | ||
3056 | if (! page) | ||
3057 | return NOPAGE_OOM; | ||
3058 | } else { | ||
3059 | vaddr = runtime->dma_area + offset; | ||
3060 | page = virt_to_page(vaddr); | ||
3061 | } | ||
3062 | if (!PageReserved(page)) | ||
3063 | get_page(page); | ||
3064 | if (type) | ||
3065 | *type = VM_FAULT_MINOR; | ||
3066 | return page; | ||
3067 | } | ||
3068 | |||
3069 | static struct vm_operations_struct snd_pcm_vm_ops_data = | ||
3070 | { | ||
3071 | .open = snd_pcm_mmap_data_open, | ||
3072 | .close = snd_pcm_mmap_data_close, | ||
3073 | .nopage = snd_pcm_mmap_data_nopage, | ||
3074 | }; | ||
3075 | |||
3076 | /* | ||
3077 | * mmap the DMA buffer on RAM | ||
3078 | */ | ||
3079 | static int snd_pcm_default_mmap(snd_pcm_substream_t *substream, struct vm_area_struct *area) | ||
3080 | { | ||
3081 | area->vm_ops = &snd_pcm_vm_ops_data; | ||
3082 | area->vm_private_data = substream; | ||
3083 | area->vm_flags |= VM_RESERVED; | ||
3084 | atomic_inc(&substream->runtime->mmap_count); | ||
3085 | return 0; | ||
3086 | } | ||
3087 | |||
3088 | /* | ||
3089 | * mmap the DMA buffer on I/O memory area | ||
3090 | */ | ||
3091 | #if SNDRV_PCM_INFO_MMAP_IOMEM | ||
3092 | static struct vm_operations_struct snd_pcm_vm_ops_data_mmio = | ||
3093 | { | ||
3094 | .open = snd_pcm_mmap_data_open, | ||
3095 | .close = snd_pcm_mmap_data_close, | ||
3096 | }; | ||
3097 | |||
3098 | int snd_pcm_lib_mmap_iomem(snd_pcm_substream_t *substream, struct vm_area_struct *area) | ||
3099 | { | ||
3100 | long size; | ||
3101 | unsigned long offset; | ||
3102 | |||
3103 | #ifdef pgprot_noncached | ||
3104 | area->vm_page_prot = pgprot_noncached(area->vm_page_prot); | ||
3105 | #endif | ||
3106 | area->vm_ops = &snd_pcm_vm_ops_data_mmio; | ||
3107 | area->vm_private_data = substream; | ||
3108 | area->vm_flags |= VM_IO; | ||
3109 | size = area->vm_end - area->vm_start; | ||
3110 | offset = area->vm_pgoff << PAGE_SHIFT; | ||
3111 | if (io_remap_pfn_range(area, area->vm_start, | ||
3112 | (substream->runtime->dma_addr + offset) >> PAGE_SHIFT, | ||
3113 | size, area->vm_page_prot)) | ||
3114 | return -EAGAIN; | ||
3115 | atomic_inc(&substream->runtime->mmap_count); | ||
3116 | return 0; | ||
3117 | } | ||
3118 | #endif /* SNDRV_PCM_INFO_MMAP */ | ||
3119 | |||
3120 | /* | ||
3121 | * mmap DMA buffer | ||
3122 | */ | ||
3123 | int snd_pcm_mmap_data(snd_pcm_substream_t *substream, struct file *file, | ||
3124 | struct vm_area_struct *area) | ||
3125 | { | ||
3126 | snd_pcm_runtime_t *runtime; | ||
3127 | long size; | ||
3128 | unsigned long offset; | ||
3129 | size_t dma_bytes; | ||
3130 | |||
3131 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | ||
3132 | if (!(area->vm_flags & (VM_WRITE|VM_READ))) | ||
3133 | return -EINVAL; | ||
3134 | } else { | ||
3135 | if (!(area->vm_flags & VM_READ)) | ||
3136 | return -EINVAL; | ||
3137 | } | ||
3138 | runtime = substream->runtime; | ||
3139 | snd_assert(runtime != NULL, return -EAGAIN); | ||
3140 | if (runtime->status->state == SNDRV_PCM_STATE_OPEN) | ||
3141 | return -EBADFD; | ||
3142 | if (!(runtime->info & SNDRV_PCM_INFO_MMAP)) | ||
3143 | return -ENXIO; | ||
3144 | if (runtime->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED || | ||
3145 | runtime->access == SNDRV_PCM_ACCESS_RW_NONINTERLEAVED) | ||
3146 | return -EINVAL; | ||
3147 | size = area->vm_end - area->vm_start; | ||
3148 | offset = area->vm_pgoff << PAGE_SHIFT; | ||
3149 | dma_bytes = PAGE_ALIGN(runtime->dma_bytes); | ||
3150 | if ((size_t)size > dma_bytes) | ||
3151 | return -EINVAL; | ||
3152 | if (offset > dma_bytes - size) | ||
3153 | return -EINVAL; | ||
3154 | |||
3155 | if (substream->ops->mmap) | ||
3156 | return substream->ops->mmap(substream, area); | ||
3157 | else | ||
3158 | return snd_pcm_default_mmap(substream, area); | ||
3159 | } | ||
3160 | |||
3161 | static int snd_pcm_mmap(struct file *file, struct vm_area_struct *area) | ||
3162 | { | ||
3163 | snd_pcm_file_t * pcm_file; | ||
3164 | snd_pcm_substream_t *substream; | ||
3165 | unsigned long offset; | ||
3166 | |||
3167 | pcm_file = file->private_data; | ||
3168 | substream = pcm_file->substream; | ||
3169 | snd_assert(substream != NULL, return -ENXIO); | ||
3170 | |||
3171 | offset = area->vm_pgoff << PAGE_SHIFT; | ||
3172 | switch (offset) { | ||
3173 | case SNDRV_PCM_MMAP_OFFSET_STATUS: | ||
3174 | if (substream->no_mmap_ctrl) | ||
3175 | return -ENXIO; | ||
3176 | return snd_pcm_mmap_status(substream, file, area); | ||
3177 | case SNDRV_PCM_MMAP_OFFSET_CONTROL: | ||
3178 | if (substream->no_mmap_ctrl) | ||
3179 | return -ENXIO; | ||
3180 | return snd_pcm_mmap_control(substream, file, area); | ||
3181 | default: | ||
3182 | return snd_pcm_mmap_data(substream, file, area); | ||
3183 | } | ||
3184 | return 0; | ||
3185 | } | ||
3186 | |||
3187 | static int snd_pcm_fasync(int fd, struct file * file, int on) | ||
3188 | { | ||
3189 | snd_pcm_file_t * pcm_file; | ||
3190 | snd_pcm_substream_t *substream; | ||
3191 | snd_pcm_runtime_t *runtime; | ||
3192 | int err; | ||
3193 | |||
3194 | pcm_file = file->private_data; | ||
3195 | substream = pcm_file->substream; | ||
3196 | snd_assert(substream != NULL, return -ENXIO); | ||
3197 | runtime = substream->runtime; | ||
3198 | |||
3199 | err = fasync_helper(fd, file, on, &runtime->fasync); | ||
3200 | if (err < 0) | ||
3201 | return err; | ||
3202 | return 0; | ||
3203 | } | ||
3204 | |||
3205 | /* | ||
3206 | * ioctl32 compat | ||
3207 | */ | ||
3208 | #ifdef CONFIG_COMPAT | ||
3209 | #include "pcm_compat.c" | ||
3210 | #else | ||
3211 | #define snd_pcm_ioctl_compat NULL | ||
3212 | #endif | ||
3213 | |||
3214 | /* | ||
3215 | * To be removed helpers to keep binary compatibility | ||
3216 | */ | ||
3217 | |||
3218 | #define __OLD_TO_NEW_MASK(x) ((x&7)|((x&0x07fffff8)<<5)) | ||
3219 | #define __NEW_TO_OLD_MASK(x) ((x&7)|((x&0xffffff00)>>5)) | ||
3220 | |||
3221 | static void snd_pcm_hw_convert_from_old_params(snd_pcm_hw_params_t *params, struct sndrv_pcm_hw_params_old *oparams) | ||
3222 | { | ||
3223 | unsigned int i; | ||
3224 | |||
3225 | memset(params, 0, sizeof(*params)); | ||
3226 | params->flags = oparams->flags; | ||
3227 | for (i = 0; i < ARRAY_SIZE(oparams->masks); i++) | ||
3228 | params->masks[i].bits[0] = oparams->masks[i]; | ||
3229 | memcpy(params->intervals, oparams->intervals, sizeof(oparams->intervals)); | ||
3230 | params->rmask = __OLD_TO_NEW_MASK(oparams->rmask); | ||
3231 | params->cmask = __OLD_TO_NEW_MASK(oparams->cmask); | ||
3232 | params->info = oparams->info; | ||
3233 | params->msbits = oparams->msbits; | ||
3234 | params->rate_num = oparams->rate_num; | ||
3235 | params->rate_den = oparams->rate_den; | ||
3236 | params->fifo_size = oparams->fifo_size; | ||
3237 | } | ||
3238 | |||
3239 | static void snd_pcm_hw_convert_to_old_params(struct sndrv_pcm_hw_params_old *oparams, snd_pcm_hw_params_t *params) | ||
3240 | { | ||
3241 | unsigned int i; | ||
3242 | |||
3243 | memset(oparams, 0, sizeof(*oparams)); | ||
3244 | oparams->flags = params->flags; | ||
3245 | for (i = 0; i < ARRAY_SIZE(oparams->masks); i++) | ||
3246 | oparams->masks[i] = params->masks[i].bits[0]; | ||
3247 | memcpy(oparams->intervals, params->intervals, sizeof(oparams->intervals)); | ||
3248 | oparams->rmask = __NEW_TO_OLD_MASK(params->rmask); | ||
3249 | oparams->cmask = __NEW_TO_OLD_MASK(params->cmask); | ||
3250 | oparams->info = params->info; | ||
3251 | oparams->msbits = params->msbits; | ||
3252 | oparams->rate_num = params->rate_num; | ||
3253 | oparams->rate_den = params->rate_den; | ||
3254 | oparams->fifo_size = params->fifo_size; | ||
3255 | } | ||
3256 | |||
3257 | static int snd_pcm_hw_refine_old_user(snd_pcm_substream_t * substream, struct sndrv_pcm_hw_params_old __user * _oparams) | ||
3258 | { | ||
3259 | snd_pcm_hw_params_t *params; | ||
3260 | struct sndrv_pcm_hw_params_old *oparams = NULL; | ||
3261 | int err; | ||
3262 | |||
3263 | params = kmalloc(sizeof(*params), GFP_KERNEL); | ||
3264 | if (!params) { | ||
3265 | err = -ENOMEM; | ||
3266 | goto out; | ||
3267 | } | ||
3268 | oparams = kmalloc(sizeof(*oparams), GFP_KERNEL); | ||
3269 | if (!oparams) { | ||
3270 | err = -ENOMEM; | ||
3271 | goto out; | ||
3272 | } | ||
3273 | |||
3274 | if (copy_from_user(oparams, _oparams, sizeof(*oparams))) { | ||
3275 | err = -EFAULT; | ||
3276 | goto out; | ||
3277 | } | ||
3278 | snd_pcm_hw_convert_from_old_params(params, oparams); | ||
3279 | err = snd_pcm_hw_refine(substream, params); | ||
3280 | snd_pcm_hw_convert_to_old_params(oparams, params); | ||
3281 | if (copy_to_user(_oparams, oparams, sizeof(*oparams))) { | ||
3282 | if (!err) | ||
3283 | err = -EFAULT; | ||
3284 | } | ||
3285 | out: | ||
3286 | kfree(params); | ||
3287 | kfree(oparams); | ||
3288 | return err; | ||
3289 | } | ||
3290 | |||
3291 | static int snd_pcm_hw_params_old_user(snd_pcm_substream_t * substream, struct sndrv_pcm_hw_params_old __user * _oparams) | ||
3292 | { | ||
3293 | snd_pcm_hw_params_t *params; | ||
3294 | struct sndrv_pcm_hw_params_old *oparams = NULL; | ||
3295 | int err; | ||
3296 | |||
3297 | params = kmalloc(sizeof(*params), GFP_KERNEL); | ||
3298 | if (!params) { | ||
3299 | err = -ENOMEM; | ||
3300 | goto out; | ||
3301 | } | ||
3302 | oparams = kmalloc(sizeof(*oparams), GFP_KERNEL); | ||
3303 | if (!oparams) { | ||
3304 | err = -ENOMEM; | ||
3305 | goto out; | ||
3306 | } | ||
3307 | if (copy_from_user(oparams, _oparams, sizeof(*oparams))) { | ||
3308 | err = -EFAULT; | ||
3309 | goto out; | ||
3310 | } | ||
3311 | snd_pcm_hw_convert_from_old_params(params, oparams); | ||
3312 | err = snd_pcm_hw_params(substream, params); | ||
3313 | snd_pcm_hw_convert_to_old_params(oparams, params); | ||
3314 | if (copy_to_user(_oparams, oparams, sizeof(*oparams))) { | ||
3315 | if (!err) | ||
3316 | err = -EFAULT; | ||
3317 | } | ||
3318 | out: | ||
3319 | kfree(params); | ||
3320 | kfree(oparams); | ||
3321 | return err; | ||
3322 | } | ||
3323 | |||
3324 | /* | ||
3325 | * Register section | ||
3326 | */ | ||
3327 | |||
3328 | static struct file_operations snd_pcm_f_ops_playback = { | ||
3329 | .owner = THIS_MODULE, | ||
3330 | .write = snd_pcm_write, | ||
3331 | .writev = snd_pcm_writev, | ||
3332 | .open = snd_pcm_open, | ||
3333 | .release = snd_pcm_release, | ||
3334 | .poll = snd_pcm_playback_poll, | ||
3335 | .unlocked_ioctl = snd_pcm_playback_ioctl, | ||
3336 | .compat_ioctl = snd_pcm_ioctl_compat, | ||
3337 | .mmap = snd_pcm_mmap, | ||
3338 | .fasync = snd_pcm_fasync, | ||
3339 | }; | ||
3340 | |||
3341 | static struct file_operations snd_pcm_f_ops_capture = { | ||
3342 | .owner = THIS_MODULE, | ||
3343 | .read = snd_pcm_read, | ||
3344 | .readv = snd_pcm_readv, | ||
3345 | .open = snd_pcm_open, | ||
3346 | .release = snd_pcm_release, | ||
3347 | .poll = snd_pcm_capture_poll, | ||
3348 | .unlocked_ioctl = snd_pcm_capture_ioctl, | ||
3349 | .compat_ioctl = snd_pcm_ioctl_compat, | ||
3350 | .mmap = snd_pcm_mmap, | ||
3351 | .fasync = snd_pcm_fasync, | ||
3352 | }; | ||
3353 | |||
3354 | snd_minor_t snd_pcm_reg[2] = | ||
3355 | { | ||
3356 | { | ||
3357 | .comment = "digital audio playback", | ||
3358 | .f_ops = &snd_pcm_f_ops_playback, | ||
3359 | }, | ||
3360 | { | ||
3361 | .comment = "digital audio capture", | ||
3362 | .f_ops = &snd_pcm_f_ops_capture, | ||
3363 | } | ||
3364 | }; | ||
diff --git a/sound/core/pcm_timer.c b/sound/core/pcm_timer.c new file mode 100644 index 000000000000..884eaea31fec --- /dev/null +++ b/sound/core/pcm_timer.c | |||
@@ -0,0 +1,161 @@ | |||
1 | /* | ||
2 | * Digital Audio (PCM) abstract layer | ||
3 | * Copyright (c) by Jaroslav Kysela <perex@suse.cz> | ||
4 | * | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #include <sound/driver.h> | ||
23 | #include <linux/time.h> | ||
24 | #include <sound/core.h> | ||
25 | #include <sound/pcm.h> | ||
26 | #include <sound/timer.h> | ||
27 | |||
28 | /* | ||
29 | * Timer functions | ||
30 | */ | ||
31 | |||
32 | /* Greatest common divisor */ | ||
33 | static unsigned long gcd(unsigned long a, unsigned long b) | ||
34 | { | ||
35 | unsigned long r; | ||
36 | if (a < b) { | ||
37 | r = a; | ||
38 | a = b; | ||
39 | b = r; | ||
40 | } | ||
41 | while ((r = a % b) != 0) { | ||
42 | a = b; | ||
43 | b = r; | ||
44 | } | ||
45 | return b; | ||
46 | } | ||
47 | |||
48 | void snd_pcm_timer_resolution_change(snd_pcm_substream_t *substream) | ||
49 | { | ||
50 | unsigned long rate, mult, fsize, l, post; | ||
51 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
52 | |||
53 | mult = 1000000000; | ||
54 | rate = runtime->rate; | ||
55 | snd_assert(rate != 0, return); | ||
56 | l = gcd(mult, rate); | ||
57 | mult /= l; | ||
58 | rate /= l; | ||
59 | fsize = runtime->period_size; | ||
60 | snd_assert(fsize != 0, return); | ||
61 | l = gcd(rate, fsize); | ||
62 | rate /= l; | ||
63 | fsize /= l; | ||
64 | post = 1; | ||
65 | while ((mult * fsize) / fsize != mult) { | ||
66 | mult /= 2; | ||
67 | post *= 2; | ||
68 | } | ||
69 | if (rate == 0) { | ||
70 | snd_printk(KERN_ERR "pcm timer resolution out of range (rate = %u, period_size = %lu)\n", runtime->rate, runtime->period_size); | ||
71 | runtime->timer_resolution = -1; | ||
72 | return; | ||
73 | } | ||
74 | runtime->timer_resolution = (mult * fsize / rate) * post; | ||
75 | } | ||
76 | |||
77 | static unsigned long snd_pcm_timer_resolution(snd_timer_t * timer) | ||
78 | { | ||
79 | snd_pcm_substream_t * substream; | ||
80 | |||
81 | substream = timer->private_data; | ||
82 | return substream->runtime ? substream->runtime->timer_resolution : 0; | ||
83 | } | ||
84 | |||
85 | static int snd_pcm_timer_start(snd_timer_t * timer) | ||
86 | { | ||
87 | unsigned long flags; | ||
88 | snd_pcm_substream_t * substream; | ||
89 | |||
90 | substream = snd_timer_chip(timer); | ||
91 | spin_lock_irqsave(&substream->timer_lock, flags); | ||
92 | substream->timer_running = 1; | ||
93 | spin_unlock_irqrestore(&substream->timer_lock, flags); | ||
94 | return 0; | ||
95 | } | ||
96 | |||
97 | static int snd_pcm_timer_stop(snd_timer_t * timer) | ||
98 | { | ||
99 | unsigned long flags; | ||
100 | snd_pcm_substream_t * substream; | ||
101 | |||
102 | substream = snd_timer_chip(timer); | ||
103 | spin_lock_irqsave(&substream->timer_lock, flags); | ||
104 | substream->timer_running = 0; | ||
105 | spin_unlock_irqrestore(&substream->timer_lock, flags); | ||
106 | return 0; | ||
107 | } | ||
108 | |||
109 | static struct _snd_timer_hardware snd_pcm_timer = | ||
110 | { | ||
111 | .flags = SNDRV_TIMER_HW_AUTO | SNDRV_TIMER_HW_SLAVE, | ||
112 | .resolution = 0, | ||
113 | .ticks = 1, | ||
114 | .c_resolution = snd_pcm_timer_resolution, | ||
115 | .start = snd_pcm_timer_start, | ||
116 | .stop = snd_pcm_timer_stop, | ||
117 | }; | ||
118 | |||
119 | /* | ||
120 | * Init functions | ||
121 | */ | ||
122 | |||
123 | static void snd_pcm_timer_free(snd_timer_t *timer) | ||
124 | { | ||
125 | snd_pcm_substream_t *substream = timer->private_data; | ||
126 | substream->timer = NULL; | ||
127 | } | ||
128 | |||
129 | void snd_pcm_timer_init(snd_pcm_substream_t *substream) | ||
130 | { | ||
131 | snd_timer_id_t tid; | ||
132 | snd_timer_t *timer; | ||
133 | |||
134 | tid.dev_sclass = SNDRV_TIMER_SCLASS_NONE; | ||
135 | tid.dev_class = SNDRV_TIMER_CLASS_PCM; | ||
136 | tid.card = substream->pcm->card->number; | ||
137 | tid.device = substream->pcm->device; | ||
138 | tid.subdevice = (substream->number << 1) | (substream->stream & 1); | ||
139 | if (snd_timer_new(substream->pcm->card, "PCM", &tid, &timer) < 0) | ||
140 | return; | ||
141 | sprintf(timer->name, "PCM %s %i-%i-%i", | ||
142 | substream->stream == SNDRV_PCM_STREAM_CAPTURE ? | ||
143 | "capture" : "playback", | ||
144 | tid.card, tid.device, tid.subdevice); | ||
145 | timer->hw = snd_pcm_timer; | ||
146 | if (snd_device_register(timer->card, timer) < 0) { | ||
147 | snd_device_free(timer->card, timer); | ||
148 | return; | ||
149 | } | ||
150 | timer->private_data = substream; | ||
151 | timer->private_free = snd_pcm_timer_free; | ||
152 | substream->timer = timer; | ||
153 | } | ||
154 | |||
155 | void snd_pcm_timer_done(snd_pcm_substream_t *substream) | ||
156 | { | ||
157 | if (substream->timer) { | ||
158 | snd_device_free(substream->pcm->card, substream->timer); | ||
159 | substream->timer = NULL; | ||
160 | } | ||
161 | } | ||
diff --git a/sound/core/rawmidi.c b/sound/core/rawmidi.c new file mode 100644 index 000000000000..edba4118271c --- /dev/null +++ b/sound/core/rawmidi.c | |||
@@ -0,0 +1,1680 @@ | |||
1 | /* | ||
2 | * Abstract layer for MIDI v1.0 stream | ||
3 | * Copyright (c) by Jaroslav Kysela <perex@suse.cz> | ||
4 | * | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #include <sound/driver.h> | ||
23 | #include <sound/core.h> | ||
24 | #include <linux/major.h> | ||
25 | #include <linux/init.h> | ||
26 | #include <linux/smp_lock.h> | ||
27 | #include <linux/sched.h> | ||
28 | #include <linux/slab.h> | ||
29 | #include <linux/time.h> | ||
30 | #include <linux/wait.h> | ||
31 | #include <linux/moduleparam.h> | ||
32 | #include <linux/delay.h> | ||
33 | #include <linux/wait.h> | ||
34 | #include <sound/rawmidi.h> | ||
35 | #include <sound/info.h> | ||
36 | #include <sound/control.h> | ||
37 | #include <sound/minors.h> | ||
38 | #include <sound/initval.h> | ||
39 | |||
40 | MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>"); | ||
41 | MODULE_DESCRIPTION("Midlevel RawMidi code for ALSA."); | ||
42 | MODULE_LICENSE("GPL"); | ||
43 | |||
44 | #ifdef CONFIG_SND_OSSEMUL | ||
45 | static int midi_map[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = 0}; | ||
46 | static int amidi_map[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = 1}; | ||
47 | module_param_array(midi_map, int, NULL, 0444); | ||
48 | MODULE_PARM_DESC(midi_map, "Raw MIDI device number assigned to 1st OSS device."); | ||
49 | module_param_array(amidi_map, int, NULL, 0444); | ||
50 | MODULE_PARM_DESC(amidi_map, "Raw MIDI device number assigned to 2nd OSS device."); | ||
51 | #endif /* CONFIG_SND_OSSEMUL */ | ||
52 | |||
53 | static int snd_rawmidi_free(snd_rawmidi_t *rawmidi); | ||
54 | static int snd_rawmidi_dev_free(snd_device_t *device); | ||
55 | static int snd_rawmidi_dev_register(snd_device_t *device); | ||
56 | static int snd_rawmidi_dev_disconnect(snd_device_t *device); | ||
57 | static int snd_rawmidi_dev_unregister(snd_device_t *device); | ||
58 | |||
59 | static snd_rawmidi_t *snd_rawmidi_devices[SNDRV_CARDS * SNDRV_RAWMIDI_DEVICES]; | ||
60 | |||
61 | static DECLARE_MUTEX(register_mutex); | ||
62 | |||
63 | static inline unsigned short snd_rawmidi_file_flags(struct file *file) | ||
64 | { | ||
65 | switch (file->f_mode & (FMODE_READ | FMODE_WRITE)) { | ||
66 | case FMODE_WRITE: | ||
67 | return SNDRV_RAWMIDI_LFLG_OUTPUT; | ||
68 | case FMODE_READ: | ||
69 | return SNDRV_RAWMIDI_LFLG_INPUT; | ||
70 | default: | ||
71 | return SNDRV_RAWMIDI_LFLG_OPEN; | ||
72 | } | ||
73 | } | ||
74 | |||
75 | static inline int snd_rawmidi_ready(snd_rawmidi_substream_t * substream) | ||
76 | { | ||
77 | snd_rawmidi_runtime_t *runtime = substream->runtime; | ||
78 | return runtime->avail >= runtime->avail_min; | ||
79 | } | ||
80 | |||
81 | static inline int snd_rawmidi_ready_append(snd_rawmidi_substream_t * substream, size_t count) | ||
82 | { | ||
83 | snd_rawmidi_runtime_t *runtime = substream->runtime; | ||
84 | return runtime->avail >= runtime->avail_min && | ||
85 | (!substream->append || runtime->avail >= count); | ||
86 | } | ||
87 | |||
88 | static void snd_rawmidi_input_event_tasklet(unsigned long data) | ||
89 | { | ||
90 | snd_rawmidi_substream_t *substream = (snd_rawmidi_substream_t *)data; | ||
91 | substream->runtime->event(substream); | ||
92 | } | ||
93 | |||
94 | static void snd_rawmidi_output_trigger_tasklet(unsigned long data) | ||
95 | { | ||
96 | snd_rawmidi_substream_t *substream = (snd_rawmidi_substream_t *)data; | ||
97 | substream->ops->trigger(substream, 1); | ||
98 | } | ||
99 | |||
100 | static int snd_rawmidi_runtime_create(snd_rawmidi_substream_t * substream) | ||
101 | { | ||
102 | snd_rawmidi_runtime_t *runtime; | ||
103 | |||
104 | if ((runtime = kcalloc(1, sizeof(*runtime), GFP_KERNEL)) == NULL) | ||
105 | return -ENOMEM; | ||
106 | spin_lock_init(&runtime->lock); | ||
107 | init_waitqueue_head(&runtime->sleep); | ||
108 | if (substream->stream == SNDRV_RAWMIDI_STREAM_INPUT) | ||
109 | tasklet_init(&runtime->tasklet, | ||
110 | snd_rawmidi_input_event_tasklet, | ||
111 | (unsigned long)substream); | ||
112 | else | ||
113 | tasklet_init(&runtime->tasklet, | ||
114 | snd_rawmidi_output_trigger_tasklet, | ||
115 | (unsigned long)substream); | ||
116 | runtime->event = NULL; | ||
117 | runtime->buffer_size = PAGE_SIZE; | ||
118 | runtime->avail_min = 1; | ||
119 | if (substream->stream == SNDRV_RAWMIDI_STREAM_INPUT) | ||
120 | runtime->avail = 0; | ||
121 | else | ||
122 | runtime->avail = runtime->buffer_size; | ||
123 | if ((runtime->buffer = kmalloc(runtime->buffer_size, GFP_KERNEL)) == NULL) { | ||
124 | kfree(runtime); | ||
125 | return -ENOMEM; | ||
126 | } | ||
127 | runtime->appl_ptr = runtime->hw_ptr = 0; | ||
128 | substream->runtime = runtime; | ||
129 | return 0; | ||
130 | } | ||
131 | |||
132 | static int snd_rawmidi_runtime_free(snd_rawmidi_substream_t * substream) | ||
133 | { | ||
134 | snd_rawmidi_runtime_t *runtime = substream->runtime; | ||
135 | |||
136 | kfree(runtime->buffer); | ||
137 | kfree(runtime); | ||
138 | substream->runtime = NULL; | ||
139 | return 0; | ||
140 | } | ||
141 | |||
142 | static inline void snd_rawmidi_output_trigger(snd_rawmidi_substream_t * substream, int up) | ||
143 | { | ||
144 | if (up) { | ||
145 | tasklet_hi_schedule(&substream->runtime->tasklet); | ||
146 | } else { | ||
147 | tasklet_kill(&substream->runtime->tasklet); | ||
148 | substream->ops->trigger(substream, 0); | ||
149 | } | ||
150 | } | ||
151 | |||
152 | static void snd_rawmidi_input_trigger(snd_rawmidi_substream_t * substream, int up) | ||
153 | { | ||
154 | substream->ops->trigger(substream, up); | ||
155 | if (!up && substream->runtime->event) | ||
156 | tasklet_kill(&substream->runtime->tasklet); | ||
157 | } | ||
158 | |||
159 | int snd_rawmidi_drop_output(snd_rawmidi_substream_t * substream) | ||
160 | { | ||
161 | unsigned long flags; | ||
162 | snd_rawmidi_runtime_t *runtime = substream->runtime; | ||
163 | |||
164 | snd_rawmidi_output_trigger(substream, 0); | ||
165 | runtime->drain = 0; | ||
166 | spin_lock_irqsave(&runtime->lock, flags); | ||
167 | runtime->appl_ptr = runtime->hw_ptr = 0; | ||
168 | runtime->avail = runtime->buffer_size; | ||
169 | spin_unlock_irqrestore(&runtime->lock, flags); | ||
170 | return 0; | ||
171 | } | ||
172 | |||
173 | int snd_rawmidi_drain_output(snd_rawmidi_substream_t * substream) | ||
174 | { | ||
175 | int err; | ||
176 | long timeout; | ||
177 | snd_rawmidi_runtime_t *runtime = substream->runtime; | ||
178 | |||
179 | err = 0; | ||
180 | runtime->drain = 1; | ||
181 | timeout = wait_event_interruptible_timeout(runtime->sleep, | ||
182 | (runtime->avail >= runtime->buffer_size), | ||
183 | 10*HZ); | ||
184 | if (signal_pending(current)) | ||
185 | err = -ERESTARTSYS; | ||
186 | if (runtime->avail < runtime->buffer_size && !timeout) { | ||
187 | snd_printk(KERN_WARNING "rawmidi drain error (avail = %li, buffer_size = %li)\n", (long)runtime->avail, (long)runtime->buffer_size); | ||
188 | err = -EIO; | ||
189 | } | ||
190 | runtime->drain = 0; | ||
191 | if (err != -ERESTARTSYS) { | ||
192 | /* we need wait a while to make sure that Tx FIFOs are empty */ | ||
193 | if (substream->ops->drain) | ||
194 | substream->ops->drain(substream); | ||
195 | else | ||
196 | msleep(50); | ||
197 | snd_rawmidi_drop_output(substream); | ||
198 | } | ||
199 | return err; | ||
200 | } | ||
201 | |||
202 | int snd_rawmidi_drain_input(snd_rawmidi_substream_t * substream) | ||
203 | { | ||
204 | unsigned long flags; | ||
205 | snd_rawmidi_runtime_t *runtime = substream->runtime; | ||
206 | |||
207 | snd_rawmidi_input_trigger(substream, 0); | ||
208 | runtime->drain = 0; | ||
209 | spin_lock_irqsave(&runtime->lock, flags); | ||
210 | runtime->appl_ptr = runtime->hw_ptr = 0; | ||
211 | runtime->avail = 0; | ||
212 | spin_unlock_irqrestore(&runtime->lock, flags); | ||
213 | return 0; | ||
214 | } | ||
215 | |||
216 | int snd_rawmidi_kernel_open(int cardnum, int device, int subdevice, | ||
217 | int mode, snd_rawmidi_file_t * rfile) | ||
218 | { | ||
219 | snd_rawmidi_t *rmidi; | ||
220 | struct list_head *list1, *list2; | ||
221 | snd_rawmidi_substream_t *sinput = NULL, *soutput = NULL; | ||
222 | snd_rawmidi_runtime_t *input = NULL, *output = NULL; | ||
223 | int err; | ||
224 | |||
225 | if (rfile) | ||
226 | rfile->input = rfile->output = NULL; | ||
227 | rmidi = snd_rawmidi_devices[(cardnum * SNDRV_RAWMIDI_DEVICES) + device]; | ||
228 | if (rmidi == NULL) { | ||
229 | err = -ENODEV; | ||
230 | goto __error1; | ||
231 | } | ||
232 | if (!try_module_get(rmidi->card->module)) { | ||
233 | err = -EFAULT; | ||
234 | goto __error1; | ||
235 | } | ||
236 | if (!(mode & SNDRV_RAWMIDI_LFLG_NOOPENLOCK)) | ||
237 | down(&rmidi->open_mutex); | ||
238 | if (mode & SNDRV_RAWMIDI_LFLG_INPUT) { | ||
239 | if (!(rmidi->info_flags & SNDRV_RAWMIDI_INFO_INPUT)) { | ||
240 | err = -ENXIO; | ||
241 | goto __error; | ||
242 | } | ||
243 | if (subdevice >= 0 && (unsigned int)subdevice >= rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substream_count) { | ||
244 | err = -ENODEV; | ||
245 | goto __error; | ||
246 | } | ||
247 | if (rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substream_opened >= | ||
248 | rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substream_count) { | ||
249 | err = -EAGAIN; | ||
250 | goto __error; | ||
251 | } | ||
252 | } | ||
253 | if (mode & SNDRV_RAWMIDI_LFLG_OUTPUT) { | ||
254 | if (!(rmidi->info_flags & SNDRV_RAWMIDI_INFO_OUTPUT)) { | ||
255 | err = -ENXIO; | ||
256 | goto __error; | ||
257 | } | ||
258 | if (subdevice >= 0 && (unsigned int)subdevice >= rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substream_count) { | ||
259 | err = -ENODEV; | ||
260 | goto __error; | ||
261 | } | ||
262 | if (rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substream_opened >= | ||
263 | rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substream_count) { | ||
264 | err = -EAGAIN; | ||
265 | goto __error; | ||
266 | } | ||
267 | } | ||
268 | list1 = rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substreams.next; | ||
269 | while (1) { | ||
270 | if (list1 == &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substreams) { | ||
271 | sinput = NULL; | ||
272 | if (mode & SNDRV_RAWMIDI_LFLG_INPUT) { | ||
273 | err = -EAGAIN; | ||
274 | goto __error; | ||
275 | } | ||
276 | break; | ||
277 | } | ||
278 | sinput = list_entry(list1, snd_rawmidi_substream_t, list); | ||
279 | if ((mode & SNDRV_RAWMIDI_LFLG_INPUT) && sinput->opened) | ||
280 | goto __nexti; | ||
281 | if (subdevice < 0 || (subdevice >= 0 && subdevice == sinput->number)) | ||
282 | break; | ||
283 | __nexti: | ||
284 | list1 = list1->next; | ||
285 | } | ||
286 | list2 = rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substreams.next; | ||
287 | while (1) { | ||
288 | if (list2 == &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substreams) { | ||
289 | soutput = NULL; | ||
290 | if (mode & SNDRV_RAWMIDI_LFLG_OUTPUT) { | ||
291 | err = -EAGAIN; | ||
292 | goto __error; | ||
293 | } | ||
294 | break; | ||
295 | } | ||
296 | soutput = list_entry(list2, snd_rawmidi_substream_t, list); | ||
297 | if (mode & SNDRV_RAWMIDI_LFLG_OUTPUT) { | ||
298 | if (mode & SNDRV_RAWMIDI_LFLG_APPEND) { | ||
299 | if (soutput->opened && !soutput->append) | ||
300 | goto __nexto; | ||
301 | } else { | ||
302 | if (soutput->opened) | ||
303 | goto __nexto; | ||
304 | } | ||
305 | } | ||
306 | if (subdevice < 0 || (subdevice >= 0 && subdevice == soutput->number)) | ||
307 | break; | ||
308 | __nexto: | ||
309 | list2 = list2->next; | ||
310 | } | ||
311 | if (mode & SNDRV_RAWMIDI_LFLG_INPUT) { | ||
312 | if ((err = snd_rawmidi_runtime_create(sinput)) < 0) | ||
313 | goto __error; | ||
314 | input = sinput->runtime; | ||
315 | if ((err = sinput->ops->open(sinput)) < 0) | ||
316 | goto __error; | ||
317 | sinput->opened = 1; | ||
318 | rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substream_opened++; | ||
319 | } else { | ||
320 | sinput = NULL; | ||
321 | } | ||
322 | if (mode & SNDRV_RAWMIDI_LFLG_OUTPUT) { | ||
323 | if (soutput->opened) | ||
324 | goto __skip_output; | ||
325 | if ((err = snd_rawmidi_runtime_create(soutput)) < 0) { | ||
326 | if (mode & SNDRV_RAWMIDI_LFLG_INPUT) | ||
327 | sinput->ops->close(sinput); | ||
328 | goto __error; | ||
329 | } | ||
330 | output = soutput->runtime; | ||
331 | if ((err = soutput->ops->open(soutput)) < 0) { | ||
332 | if (mode & SNDRV_RAWMIDI_LFLG_INPUT) | ||
333 | sinput->ops->close(sinput); | ||
334 | goto __error; | ||
335 | } | ||
336 | __skip_output: | ||
337 | soutput->opened = 1; | ||
338 | if (mode & SNDRV_RAWMIDI_LFLG_APPEND) | ||
339 | soutput->append = 1; | ||
340 | if (soutput->use_count++ == 0) | ||
341 | soutput->active_sensing = 1; | ||
342 | rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substream_opened++; | ||
343 | } else { | ||
344 | soutput = NULL; | ||
345 | } | ||
346 | if (!(mode & SNDRV_RAWMIDI_LFLG_NOOPENLOCK)) | ||
347 | up(&rmidi->open_mutex); | ||
348 | if (rfile) { | ||
349 | rfile->rmidi = rmidi; | ||
350 | rfile->input = sinput; | ||
351 | rfile->output = soutput; | ||
352 | } | ||
353 | return 0; | ||
354 | |||
355 | __error: | ||
356 | if (input != NULL) | ||
357 | snd_rawmidi_runtime_free(sinput); | ||
358 | if (output != NULL) | ||
359 | snd_rawmidi_runtime_free(soutput); | ||
360 | module_put(rmidi->card->module); | ||
361 | if (!(mode & SNDRV_RAWMIDI_LFLG_NOOPENLOCK)) | ||
362 | up(&rmidi->open_mutex); | ||
363 | __error1: | ||
364 | return err; | ||
365 | } | ||
366 | |||
367 | static int snd_rawmidi_open(struct inode *inode, struct file *file) | ||
368 | { | ||
369 | int maj = imajor(inode); | ||
370 | int cardnum; | ||
371 | snd_card_t *card; | ||
372 | int device, subdevice; | ||
373 | unsigned short fflags; | ||
374 | int err; | ||
375 | snd_rawmidi_t *rmidi; | ||
376 | snd_rawmidi_file_t *rawmidi_file; | ||
377 | wait_queue_t wait; | ||
378 | struct list_head *list; | ||
379 | snd_ctl_file_t *kctl; | ||
380 | |||
381 | switch (maj) { | ||
382 | case CONFIG_SND_MAJOR: | ||
383 | cardnum = SNDRV_MINOR_CARD(iminor(inode)); | ||
384 | cardnum %= SNDRV_CARDS; | ||
385 | device = SNDRV_MINOR_DEVICE(iminor(inode)) - SNDRV_MINOR_RAWMIDI; | ||
386 | device %= SNDRV_MINOR_RAWMIDIS; | ||
387 | break; | ||
388 | #ifdef CONFIG_SND_OSSEMUL | ||
389 | case SOUND_MAJOR: | ||
390 | cardnum = SNDRV_MINOR_OSS_CARD(iminor(inode)); | ||
391 | cardnum %= SNDRV_CARDS; | ||
392 | device = SNDRV_MINOR_OSS_DEVICE(iminor(inode)) == SNDRV_MINOR_OSS_MIDI ? | ||
393 | midi_map[cardnum] : amidi_map[cardnum]; | ||
394 | break; | ||
395 | #endif | ||
396 | default: | ||
397 | return -ENXIO; | ||
398 | } | ||
399 | |||
400 | rmidi = snd_rawmidi_devices[(cardnum * SNDRV_RAWMIDI_DEVICES) + device]; | ||
401 | if (rmidi == NULL) | ||
402 | return -ENODEV; | ||
403 | #ifdef CONFIG_SND_OSSEMUL | ||
404 | if (maj == SOUND_MAJOR && !rmidi->ossreg) | ||
405 | return -ENXIO; | ||
406 | #endif | ||
407 | if ((file->f_flags & O_APPEND) && !(file->f_flags & O_NONBLOCK)) | ||
408 | return -EINVAL; /* invalid combination */ | ||
409 | card = rmidi->card; | ||
410 | err = snd_card_file_add(card, file); | ||
411 | if (err < 0) | ||
412 | return -ENODEV; | ||
413 | fflags = snd_rawmidi_file_flags(file); | ||
414 | if ((file->f_flags & O_APPEND) || maj != CONFIG_SND_MAJOR) /* OSS emul? */ | ||
415 | fflags |= SNDRV_RAWMIDI_LFLG_APPEND; | ||
416 | fflags |= SNDRV_RAWMIDI_LFLG_NOOPENLOCK; | ||
417 | rawmidi_file = kmalloc(sizeof(*rawmidi_file), GFP_KERNEL); | ||
418 | if (rawmidi_file == NULL) { | ||
419 | snd_card_file_remove(card, file); | ||
420 | return -ENOMEM; | ||
421 | } | ||
422 | init_waitqueue_entry(&wait, current); | ||
423 | add_wait_queue(&rmidi->open_wait, &wait); | ||
424 | down(&rmidi->open_mutex); | ||
425 | while (1) { | ||
426 | subdevice = -1; | ||
427 | down_read(&card->controls_rwsem); | ||
428 | list_for_each(list, &card->ctl_files) { | ||
429 | kctl = snd_ctl_file(list); | ||
430 | if (kctl->pid == current->pid) { | ||
431 | subdevice = kctl->prefer_rawmidi_subdevice; | ||
432 | break; | ||
433 | } | ||
434 | } | ||
435 | up_read(&card->controls_rwsem); | ||
436 | err = snd_rawmidi_kernel_open(cardnum, device, subdevice, fflags, rawmidi_file); | ||
437 | if (err >= 0) | ||
438 | break; | ||
439 | if (err == -EAGAIN) { | ||
440 | if (file->f_flags & O_NONBLOCK) { | ||
441 | err = -EBUSY; | ||
442 | break; | ||
443 | } | ||
444 | } else | ||
445 | break; | ||
446 | set_current_state(TASK_INTERRUPTIBLE); | ||
447 | up(&rmidi->open_mutex); | ||
448 | schedule(); | ||
449 | down(&rmidi->open_mutex); | ||
450 | if (signal_pending(current)) { | ||
451 | err = -ERESTARTSYS; | ||
452 | break; | ||
453 | } | ||
454 | } | ||
455 | #ifdef CONFIG_SND_OSSEMUL | ||
456 | if (rawmidi_file->input && rawmidi_file->input->runtime) | ||
457 | rawmidi_file->input->runtime->oss = (maj == SOUND_MAJOR); | ||
458 | if (rawmidi_file->output && rawmidi_file->output->runtime) | ||
459 | rawmidi_file->output->runtime->oss = (maj == SOUND_MAJOR); | ||
460 | #endif | ||
461 | remove_wait_queue(&rmidi->open_wait, &wait); | ||
462 | if (err >= 0) { | ||
463 | file->private_data = rawmidi_file; | ||
464 | } else { | ||
465 | snd_card_file_remove(card, file); | ||
466 | kfree(rawmidi_file); | ||
467 | } | ||
468 | up(&rmidi->open_mutex); | ||
469 | return err; | ||
470 | } | ||
471 | |||
472 | int snd_rawmidi_kernel_release(snd_rawmidi_file_t * rfile) | ||
473 | { | ||
474 | snd_rawmidi_t *rmidi; | ||
475 | snd_rawmidi_substream_t *substream; | ||
476 | snd_rawmidi_runtime_t *runtime; | ||
477 | |||
478 | snd_assert(rfile != NULL, return -ENXIO); | ||
479 | snd_assert(rfile->input != NULL || rfile->output != NULL, return -ENXIO); | ||
480 | rmidi = rfile->rmidi; | ||
481 | down(&rmidi->open_mutex); | ||
482 | if (rfile->input != NULL) { | ||
483 | substream = rfile->input; | ||
484 | rfile->input = NULL; | ||
485 | runtime = substream->runtime; | ||
486 | snd_rawmidi_input_trigger(substream, 0); | ||
487 | substream->ops->close(substream); | ||
488 | if (runtime->private_free != NULL) | ||
489 | runtime->private_free(substream); | ||
490 | snd_rawmidi_runtime_free(substream); | ||
491 | substream->opened = 0; | ||
492 | rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substream_opened--; | ||
493 | } | ||
494 | if (rfile->output != NULL) { | ||
495 | substream = rfile->output; | ||
496 | rfile->output = NULL; | ||
497 | if (--substream->use_count == 0) { | ||
498 | runtime = substream->runtime; | ||
499 | if (substream->active_sensing) { | ||
500 | unsigned char buf = 0xfe; | ||
501 | /* sending single active sensing message to shut the device up */ | ||
502 | snd_rawmidi_kernel_write(substream, &buf, 1); | ||
503 | } | ||
504 | if (snd_rawmidi_drain_output(substream) == -ERESTARTSYS) | ||
505 | snd_rawmidi_output_trigger(substream, 0); | ||
506 | substream->ops->close(substream); | ||
507 | if (runtime->private_free != NULL) | ||
508 | runtime->private_free(substream); | ||
509 | snd_rawmidi_runtime_free(substream); | ||
510 | substream->opened = 0; | ||
511 | substream->append = 0; | ||
512 | } | ||
513 | rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substream_opened--; | ||
514 | } | ||
515 | up(&rmidi->open_mutex); | ||
516 | module_put(rmidi->card->module); | ||
517 | return 0; | ||
518 | } | ||
519 | |||
520 | static int snd_rawmidi_release(struct inode *inode, struct file *file) | ||
521 | { | ||
522 | snd_rawmidi_file_t *rfile; | ||
523 | snd_rawmidi_t *rmidi; | ||
524 | int err; | ||
525 | |||
526 | rfile = file->private_data; | ||
527 | err = snd_rawmidi_kernel_release(rfile); | ||
528 | rmidi = rfile->rmidi; | ||
529 | wake_up(&rmidi->open_wait); | ||
530 | kfree(rfile); | ||
531 | snd_card_file_remove(rmidi->card, file); | ||
532 | return err; | ||
533 | } | ||
534 | |||
535 | int snd_rawmidi_info(snd_rawmidi_substream_t *substream, snd_rawmidi_info_t *info) | ||
536 | { | ||
537 | snd_rawmidi_t *rmidi; | ||
538 | |||
539 | if (substream == NULL) | ||
540 | return -ENODEV; | ||
541 | rmidi = substream->rmidi; | ||
542 | memset(info, 0, sizeof(*info)); | ||
543 | info->card = rmidi->card->number; | ||
544 | info->device = rmidi->device; | ||
545 | info->subdevice = substream->number; | ||
546 | info->stream = substream->stream; | ||
547 | info->flags = rmidi->info_flags; | ||
548 | strcpy(info->id, rmidi->id); | ||
549 | strcpy(info->name, rmidi->name); | ||
550 | strcpy(info->subname, substream->name); | ||
551 | info->subdevices_count = substream->pstr->substream_count; | ||
552 | info->subdevices_avail = (substream->pstr->substream_count - | ||
553 | substream->pstr->substream_opened); | ||
554 | return 0; | ||
555 | } | ||
556 | |||
557 | static int snd_rawmidi_info_user(snd_rawmidi_substream_t *substream, snd_rawmidi_info_t __user * _info) | ||
558 | { | ||
559 | snd_rawmidi_info_t info; | ||
560 | int err; | ||
561 | if ((err = snd_rawmidi_info(substream, &info)) < 0) | ||
562 | return err; | ||
563 | if (copy_to_user(_info, &info, sizeof(snd_rawmidi_info_t))) | ||
564 | return -EFAULT; | ||
565 | return 0; | ||
566 | } | ||
567 | |||
568 | int snd_rawmidi_info_select(snd_card_t *card, snd_rawmidi_info_t *info) | ||
569 | { | ||
570 | snd_rawmidi_t *rmidi; | ||
571 | snd_rawmidi_str_t *pstr; | ||
572 | snd_rawmidi_substream_t *substream; | ||
573 | struct list_head *list; | ||
574 | if (info->device >= SNDRV_RAWMIDI_DEVICES) | ||
575 | return -ENXIO; | ||
576 | rmidi = snd_rawmidi_devices[card->number * SNDRV_RAWMIDI_DEVICES + info->device]; | ||
577 | if (info->stream < 0 || info->stream > 1) | ||
578 | return -EINVAL; | ||
579 | pstr = &rmidi->streams[info->stream]; | ||
580 | if (pstr->substream_count == 0) | ||
581 | return -ENOENT; | ||
582 | if (info->subdevice >= pstr->substream_count) | ||
583 | return -ENXIO; | ||
584 | list_for_each(list, &pstr->substreams) { | ||
585 | substream = list_entry(list, snd_rawmidi_substream_t, list); | ||
586 | if ((unsigned int)substream->number == info->subdevice) | ||
587 | return snd_rawmidi_info(substream, info); | ||
588 | } | ||
589 | return -ENXIO; | ||
590 | } | ||
591 | |||
592 | static int snd_rawmidi_info_select_user(snd_card_t *card, | ||
593 | snd_rawmidi_info_t __user *_info) | ||
594 | { | ||
595 | int err; | ||
596 | snd_rawmidi_info_t info; | ||
597 | if (get_user(info.device, &_info->device)) | ||
598 | return -EFAULT; | ||
599 | if (get_user(info.stream, &_info->stream)) | ||
600 | return -EFAULT; | ||
601 | if (get_user(info.subdevice, &_info->subdevice)) | ||
602 | return -EFAULT; | ||
603 | if ((err = snd_rawmidi_info_select(card, &info)) < 0) | ||
604 | return err; | ||
605 | if (copy_to_user(_info, &info, sizeof(snd_rawmidi_info_t))) | ||
606 | return -EFAULT; | ||
607 | return 0; | ||
608 | } | ||
609 | |||
610 | int snd_rawmidi_output_params(snd_rawmidi_substream_t * substream, | ||
611 | snd_rawmidi_params_t * params) | ||
612 | { | ||
613 | char *newbuf; | ||
614 | snd_rawmidi_runtime_t *runtime = substream->runtime; | ||
615 | |||
616 | if (substream->append && substream->use_count > 1) | ||
617 | return -EBUSY; | ||
618 | snd_rawmidi_drain_output(substream); | ||
619 | if (params->buffer_size < 32 || params->buffer_size > 1024L * 1024L) { | ||
620 | return -EINVAL; | ||
621 | } | ||
622 | if (params->avail_min < 1 || params->avail_min > params->buffer_size) { | ||
623 | return -EINVAL; | ||
624 | } | ||
625 | if (params->buffer_size != runtime->buffer_size) { | ||
626 | if ((newbuf = (char *) kmalloc(params->buffer_size, GFP_KERNEL)) == NULL) | ||
627 | return -ENOMEM; | ||
628 | kfree(runtime->buffer); | ||
629 | runtime->buffer = newbuf; | ||
630 | runtime->buffer_size = params->buffer_size; | ||
631 | } | ||
632 | runtime->avail_min = params->avail_min; | ||
633 | substream->active_sensing = !params->no_active_sensing; | ||
634 | return 0; | ||
635 | } | ||
636 | |||
637 | int snd_rawmidi_input_params(snd_rawmidi_substream_t * substream, | ||
638 | snd_rawmidi_params_t * params) | ||
639 | { | ||
640 | char *newbuf; | ||
641 | snd_rawmidi_runtime_t *runtime = substream->runtime; | ||
642 | |||
643 | snd_rawmidi_drain_input(substream); | ||
644 | if (params->buffer_size < 32 || params->buffer_size > 1024L * 1024L) { | ||
645 | return -EINVAL; | ||
646 | } | ||
647 | if (params->avail_min < 1 || params->avail_min > params->buffer_size) { | ||
648 | return -EINVAL; | ||
649 | } | ||
650 | if (params->buffer_size != runtime->buffer_size) { | ||
651 | if ((newbuf = (char *) kmalloc(params->buffer_size, GFP_KERNEL)) == NULL) | ||
652 | return -ENOMEM; | ||
653 | kfree(runtime->buffer); | ||
654 | runtime->buffer = newbuf; | ||
655 | runtime->buffer_size = params->buffer_size; | ||
656 | } | ||
657 | runtime->avail_min = params->avail_min; | ||
658 | return 0; | ||
659 | } | ||
660 | |||
661 | static int snd_rawmidi_output_status(snd_rawmidi_substream_t * substream, | ||
662 | snd_rawmidi_status_t * status) | ||
663 | { | ||
664 | snd_rawmidi_runtime_t *runtime = substream->runtime; | ||
665 | |||
666 | memset(status, 0, sizeof(*status)); | ||
667 | status->stream = SNDRV_RAWMIDI_STREAM_OUTPUT; | ||
668 | spin_lock_irq(&runtime->lock); | ||
669 | status->avail = runtime->avail; | ||
670 | spin_unlock_irq(&runtime->lock); | ||
671 | return 0; | ||
672 | } | ||
673 | |||
674 | static int snd_rawmidi_input_status(snd_rawmidi_substream_t * substream, | ||
675 | snd_rawmidi_status_t * status) | ||
676 | { | ||
677 | snd_rawmidi_runtime_t *runtime = substream->runtime; | ||
678 | |||
679 | memset(status, 0, sizeof(*status)); | ||
680 | status->stream = SNDRV_RAWMIDI_STREAM_INPUT; | ||
681 | spin_lock_irq(&runtime->lock); | ||
682 | status->avail = runtime->avail; | ||
683 | status->xruns = runtime->xruns; | ||
684 | runtime->xruns = 0; | ||
685 | spin_unlock_irq(&runtime->lock); | ||
686 | return 0; | ||
687 | } | ||
688 | |||
689 | static long snd_rawmidi_ioctl(struct file *file, unsigned int cmd, unsigned long arg) | ||
690 | { | ||
691 | snd_rawmidi_file_t *rfile; | ||
692 | void __user *argp = (void __user *)arg; | ||
693 | |||
694 | rfile = file->private_data; | ||
695 | if (((cmd >> 8) & 0xff) != 'W') | ||
696 | return -ENOTTY; | ||
697 | switch (cmd) { | ||
698 | case SNDRV_RAWMIDI_IOCTL_PVERSION: | ||
699 | return put_user(SNDRV_RAWMIDI_VERSION, (int __user *)argp) ? -EFAULT : 0; | ||
700 | case SNDRV_RAWMIDI_IOCTL_INFO: | ||
701 | { | ||
702 | snd_rawmidi_stream_t stream; | ||
703 | snd_rawmidi_info_t __user *info = argp; | ||
704 | if (get_user(stream, &info->stream)) | ||
705 | return -EFAULT; | ||
706 | switch (stream) { | ||
707 | case SNDRV_RAWMIDI_STREAM_INPUT: | ||
708 | return snd_rawmidi_info_user(rfile->input, info); | ||
709 | case SNDRV_RAWMIDI_STREAM_OUTPUT: | ||
710 | return snd_rawmidi_info_user(rfile->output, info); | ||
711 | default: | ||
712 | return -EINVAL; | ||
713 | } | ||
714 | } | ||
715 | case SNDRV_RAWMIDI_IOCTL_PARAMS: | ||
716 | { | ||
717 | snd_rawmidi_params_t params; | ||
718 | if (copy_from_user(¶ms, argp, sizeof(snd_rawmidi_params_t))) | ||
719 | return -EFAULT; | ||
720 | switch (params.stream) { | ||
721 | case SNDRV_RAWMIDI_STREAM_OUTPUT: | ||
722 | if (rfile->output == NULL) | ||
723 | return -EINVAL; | ||
724 | return snd_rawmidi_output_params(rfile->output, ¶ms); | ||
725 | case SNDRV_RAWMIDI_STREAM_INPUT: | ||
726 | if (rfile->input == NULL) | ||
727 | return -EINVAL; | ||
728 | return snd_rawmidi_input_params(rfile->input, ¶ms); | ||
729 | default: | ||
730 | return -EINVAL; | ||
731 | } | ||
732 | } | ||
733 | case SNDRV_RAWMIDI_IOCTL_STATUS: | ||
734 | { | ||
735 | int err = 0; | ||
736 | snd_rawmidi_status_t status; | ||
737 | if (copy_from_user(&status, argp, sizeof(snd_rawmidi_status_t))) | ||
738 | return -EFAULT; | ||
739 | switch (status.stream) { | ||
740 | case SNDRV_RAWMIDI_STREAM_OUTPUT: | ||
741 | if (rfile->output == NULL) | ||
742 | return -EINVAL; | ||
743 | err = snd_rawmidi_output_status(rfile->output, &status); | ||
744 | break; | ||
745 | case SNDRV_RAWMIDI_STREAM_INPUT: | ||
746 | if (rfile->input == NULL) | ||
747 | return -EINVAL; | ||
748 | err = snd_rawmidi_input_status(rfile->input, &status); | ||
749 | break; | ||
750 | default: | ||
751 | return -EINVAL; | ||
752 | } | ||
753 | if (err < 0) | ||
754 | return err; | ||
755 | if (copy_to_user(argp, &status, sizeof(snd_rawmidi_status_t))) | ||
756 | return -EFAULT; | ||
757 | return 0; | ||
758 | } | ||
759 | case SNDRV_RAWMIDI_IOCTL_DROP: | ||
760 | { | ||
761 | int val; | ||
762 | if (get_user(val, (int __user *) argp)) | ||
763 | return -EFAULT; | ||
764 | switch (val) { | ||
765 | case SNDRV_RAWMIDI_STREAM_OUTPUT: | ||
766 | if (rfile->output == NULL) | ||
767 | return -EINVAL; | ||
768 | return snd_rawmidi_drop_output(rfile->output); | ||
769 | default: | ||
770 | return -EINVAL; | ||
771 | } | ||
772 | } | ||
773 | case SNDRV_RAWMIDI_IOCTL_DRAIN: | ||
774 | { | ||
775 | int val; | ||
776 | if (get_user(val, (int __user *) argp)) | ||
777 | return -EFAULT; | ||
778 | switch (val) { | ||
779 | case SNDRV_RAWMIDI_STREAM_OUTPUT: | ||
780 | if (rfile->output == NULL) | ||
781 | return -EINVAL; | ||
782 | return snd_rawmidi_drain_output(rfile->output); | ||
783 | case SNDRV_RAWMIDI_STREAM_INPUT: | ||
784 | if (rfile->input == NULL) | ||
785 | return -EINVAL; | ||
786 | return snd_rawmidi_drain_input(rfile->input); | ||
787 | default: | ||
788 | return -EINVAL; | ||
789 | } | ||
790 | } | ||
791 | #ifdef CONFIG_SND_DEBUG | ||
792 | default: | ||
793 | snd_printk(KERN_WARNING "rawmidi: unknown command = 0x%x\n", cmd); | ||
794 | #endif | ||
795 | } | ||
796 | return -ENOTTY; | ||
797 | } | ||
798 | |||
799 | static int snd_rawmidi_control_ioctl(snd_card_t * card, | ||
800 | snd_ctl_file_t * control, | ||
801 | unsigned int cmd, | ||
802 | unsigned long arg) | ||
803 | { | ||
804 | void __user *argp = (void __user *)arg; | ||
805 | unsigned int tmp; | ||
806 | |||
807 | tmp = card->number * SNDRV_RAWMIDI_DEVICES; | ||
808 | switch (cmd) { | ||
809 | case SNDRV_CTL_IOCTL_RAWMIDI_NEXT_DEVICE: | ||
810 | { | ||
811 | int device; | ||
812 | |||
813 | if (get_user(device, (int __user *)argp)) | ||
814 | return -EFAULT; | ||
815 | device = device < 0 ? 0 : device + 1; | ||
816 | while (device < SNDRV_RAWMIDI_DEVICES) { | ||
817 | if (snd_rawmidi_devices[tmp + device]) | ||
818 | break; | ||
819 | device++; | ||
820 | } | ||
821 | if (device == SNDRV_RAWMIDI_DEVICES) | ||
822 | device = -1; | ||
823 | if (put_user(device, (int __user *)argp)) | ||
824 | return -EFAULT; | ||
825 | return 0; | ||
826 | } | ||
827 | case SNDRV_CTL_IOCTL_RAWMIDI_PREFER_SUBDEVICE: | ||
828 | { | ||
829 | int val; | ||
830 | |||
831 | if (get_user(val, (int __user *)argp)) | ||
832 | return -EFAULT; | ||
833 | control->prefer_rawmidi_subdevice = val; | ||
834 | return 0; | ||
835 | } | ||
836 | case SNDRV_CTL_IOCTL_RAWMIDI_INFO: | ||
837 | return snd_rawmidi_info_select_user(card, argp); | ||
838 | } | ||
839 | return -ENOIOCTLCMD; | ||
840 | } | ||
841 | |||
842 | /** | ||
843 | * snd_rawmidi_receive - receive the input data from the device | ||
844 | * @substream: the rawmidi substream | ||
845 | * @buffer: the buffer pointer | ||
846 | * @count: the data size to read | ||
847 | * | ||
848 | * Reads the data from the internal buffer. | ||
849 | * | ||
850 | * Returns the size of read data, or a negative error code on failure. | ||
851 | */ | ||
852 | int snd_rawmidi_receive(snd_rawmidi_substream_t * substream, const unsigned char *buffer, int count) | ||
853 | { | ||
854 | unsigned long flags; | ||
855 | int result = 0, count1; | ||
856 | snd_rawmidi_runtime_t *runtime = substream->runtime; | ||
857 | |||
858 | if (runtime->buffer == NULL) { | ||
859 | snd_printd("snd_rawmidi_receive: input is not active!!!\n"); | ||
860 | return -EINVAL; | ||
861 | } | ||
862 | spin_lock_irqsave(&runtime->lock, flags); | ||
863 | if (count == 1) { /* special case, faster code */ | ||
864 | substream->bytes++; | ||
865 | if (runtime->avail < runtime->buffer_size) { | ||
866 | runtime->buffer[runtime->hw_ptr++] = buffer[0]; | ||
867 | runtime->hw_ptr %= runtime->buffer_size; | ||
868 | runtime->avail++; | ||
869 | result++; | ||
870 | } else { | ||
871 | runtime->xruns++; | ||
872 | } | ||
873 | } else { | ||
874 | substream->bytes += count; | ||
875 | count1 = runtime->buffer_size - runtime->hw_ptr; | ||
876 | if (count1 > count) | ||
877 | count1 = count; | ||
878 | if (count1 > (int)(runtime->buffer_size - runtime->avail)) | ||
879 | count1 = runtime->buffer_size - runtime->avail; | ||
880 | memcpy(runtime->buffer + runtime->hw_ptr, buffer, count1); | ||
881 | runtime->hw_ptr += count1; | ||
882 | runtime->hw_ptr %= runtime->buffer_size; | ||
883 | runtime->avail += count1; | ||
884 | count -= count1; | ||
885 | result += count1; | ||
886 | if (count > 0) { | ||
887 | buffer += count1; | ||
888 | count1 = count; | ||
889 | if (count1 > (int)(runtime->buffer_size - runtime->avail)) { | ||
890 | count1 = runtime->buffer_size - runtime->avail; | ||
891 | runtime->xruns += count - count1; | ||
892 | } | ||
893 | if (count1 > 0) { | ||
894 | memcpy(runtime->buffer, buffer, count1); | ||
895 | runtime->hw_ptr = count1; | ||
896 | runtime->avail += count1; | ||
897 | result += count1; | ||
898 | } | ||
899 | } | ||
900 | } | ||
901 | if (result > 0) { | ||
902 | if (runtime->event) | ||
903 | tasklet_hi_schedule(&runtime->tasklet); | ||
904 | else if (snd_rawmidi_ready(substream)) | ||
905 | wake_up(&runtime->sleep); | ||
906 | } | ||
907 | spin_unlock_irqrestore(&runtime->lock, flags); | ||
908 | return result; | ||
909 | } | ||
910 | |||
911 | static long snd_rawmidi_kernel_read1(snd_rawmidi_substream_t *substream, | ||
912 | unsigned char *buf, long count, int kernel) | ||
913 | { | ||
914 | unsigned long flags; | ||
915 | long result = 0, count1; | ||
916 | snd_rawmidi_runtime_t *runtime = substream->runtime; | ||
917 | |||
918 | while (count > 0 && runtime->avail) { | ||
919 | count1 = runtime->buffer_size - runtime->appl_ptr; | ||
920 | if (count1 > count) | ||
921 | count1 = count; | ||
922 | spin_lock_irqsave(&runtime->lock, flags); | ||
923 | if (count1 > (int)runtime->avail) | ||
924 | count1 = runtime->avail; | ||
925 | if (kernel) { | ||
926 | memcpy(buf + result, runtime->buffer + runtime->appl_ptr, count1); | ||
927 | } else { | ||
928 | spin_unlock_irqrestore(&runtime->lock, flags); | ||
929 | if (copy_to_user((char __user *)buf + result, | ||
930 | runtime->buffer + runtime->appl_ptr, count1)) { | ||
931 | return result > 0 ? result : -EFAULT; | ||
932 | } | ||
933 | spin_lock_irqsave(&runtime->lock, flags); | ||
934 | } | ||
935 | runtime->appl_ptr += count1; | ||
936 | runtime->appl_ptr %= runtime->buffer_size; | ||
937 | runtime->avail -= count1; | ||
938 | spin_unlock_irqrestore(&runtime->lock, flags); | ||
939 | result += count1; | ||
940 | count -= count1; | ||
941 | } | ||
942 | return result; | ||
943 | } | ||
944 | |||
945 | long snd_rawmidi_kernel_read(snd_rawmidi_substream_t *substream, unsigned char *buf, long count) | ||
946 | { | ||
947 | snd_rawmidi_input_trigger(substream, 1); | ||
948 | return snd_rawmidi_kernel_read1(substream, buf, count, 1); | ||
949 | } | ||
950 | |||
951 | static ssize_t snd_rawmidi_read(struct file *file, char __user *buf, size_t count, loff_t *offset) | ||
952 | { | ||
953 | long result; | ||
954 | int count1; | ||
955 | snd_rawmidi_file_t *rfile; | ||
956 | snd_rawmidi_substream_t *substream; | ||
957 | snd_rawmidi_runtime_t *runtime; | ||
958 | |||
959 | rfile = file->private_data; | ||
960 | substream = rfile->input; | ||
961 | if (substream == NULL) | ||
962 | return -EIO; | ||
963 | runtime = substream->runtime; | ||
964 | snd_rawmidi_input_trigger(substream, 1); | ||
965 | result = 0; | ||
966 | while (count > 0) { | ||
967 | spin_lock_irq(&runtime->lock); | ||
968 | while (!snd_rawmidi_ready(substream)) { | ||
969 | wait_queue_t wait; | ||
970 | if ((file->f_flags & O_NONBLOCK) != 0 || result > 0) { | ||
971 | spin_unlock_irq(&runtime->lock); | ||
972 | return result > 0 ? result : -EAGAIN; | ||
973 | } | ||
974 | init_waitqueue_entry(&wait, current); | ||
975 | add_wait_queue(&runtime->sleep, &wait); | ||
976 | set_current_state(TASK_INTERRUPTIBLE); | ||
977 | spin_unlock_irq(&runtime->lock); | ||
978 | schedule(); | ||
979 | remove_wait_queue(&runtime->sleep, &wait); | ||
980 | if (signal_pending(current)) | ||
981 | return result > 0 ? result : -ERESTARTSYS; | ||
982 | if (!runtime->avail) | ||
983 | return result > 0 ? result : -EIO; | ||
984 | spin_lock_irq(&runtime->lock); | ||
985 | } | ||
986 | spin_unlock_irq(&runtime->lock); | ||
987 | count1 = snd_rawmidi_kernel_read1(substream, (unsigned char *)buf, count, 0); | ||
988 | if (count1 < 0) | ||
989 | return result > 0 ? result : count1; | ||
990 | result += count1; | ||
991 | buf += count1; | ||
992 | count -= count1; | ||
993 | } | ||
994 | return result; | ||
995 | } | ||
996 | |||
997 | /** | ||
998 | * snd_rawmidi_transmit_empty - check whether the output buffer is empty | ||
999 | * @substream: the rawmidi substream | ||
1000 | * | ||
1001 | * Returns 1 if the internal output buffer is empty, 0 if not. | ||
1002 | */ | ||
1003 | int snd_rawmidi_transmit_empty(snd_rawmidi_substream_t * substream) | ||
1004 | { | ||
1005 | snd_rawmidi_runtime_t *runtime = substream->runtime; | ||
1006 | int result; | ||
1007 | unsigned long flags; | ||
1008 | |||
1009 | if (runtime->buffer == NULL) { | ||
1010 | snd_printd("snd_rawmidi_transmit_empty: output is not active!!!\n"); | ||
1011 | return 1; | ||
1012 | } | ||
1013 | spin_lock_irqsave(&runtime->lock, flags); | ||
1014 | result = runtime->avail >= runtime->buffer_size; | ||
1015 | spin_unlock_irqrestore(&runtime->lock, flags); | ||
1016 | return result; | ||
1017 | } | ||
1018 | |||
1019 | /** | ||
1020 | * snd_rawmidi_transmit_peek - copy data from the internal buffer | ||
1021 | * @substream: the rawmidi substream | ||
1022 | * @buffer: the buffer pointer | ||
1023 | * @count: data size to transfer | ||
1024 | * | ||
1025 | * Copies data from the internal output buffer to the given buffer. | ||
1026 | * | ||
1027 | * Call this in the interrupt handler when the midi output is ready, | ||
1028 | * and call snd_rawmidi_transmit_ack() after the transmission is | ||
1029 | * finished. | ||
1030 | * | ||
1031 | * Returns the size of copied data, or a negative error code on failure. | ||
1032 | */ | ||
1033 | int snd_rawmidi_transmit_peek(snd_rawmidi_substream_t * substream, unsigned char *buffer, int count) | ||
1034 | { | ||
1035 | unsigned long flags; | ||
1036 | int result, count1; | ||
1037 | snd_rawmidi_runtime_t *runtime = substream->runtime; | ||
1038 | |||
1039 | if (runtime->buffer == NULL) { | ||
1040 | snd_printd("snd_rawmidi_transmit_peek: output is not active!!!\n"); | ||
1041 | return -EINVAL; | ||
1042 | } | ||
1043 | result = 0; | ||
1044 | spin_lock_irqsave(&runtime->lock, flags); | ||
1045 | if (runtime->avail >= runtime->buffer_size) { | ||
1046 | /* warning: lowlevel layer MUST trigger down the hardware */ | ||
1047 | goto __skip; | ||
1048 | } | ||
1049 | if (count == 1) { /* special case, faster code */ | ||
1050 | *buffer = runtime->buffer[runtime->hw_ptr]; | ||
1051 | result++; | ||
1052 | } else { | ||
1053 | count1 = runtime->buffer_size - runtime->hw_ptr; | ||
1054 | if (count1 > count) | ||
1055 | count1 = count; | ||
1056 | if (count1 > (int)(runtime->buffer_size - runtime->avail)) | ||
1057 | count1 = runtime->buffer_size - runtime->avail; | ||
1058 | memcpy(buffer, runtime->buffer + runtime->hw_ptr, count1); | ||
1059 | count -= count1; | ||
1060 | result += count1; | ||
1061 | if (count > 0) { | ||
1062 | if (count > (int)(runtime->buffer_size - runtime->avail - count1)) | ||
1063 | count = runtime->buffer_size - runtime->avail - count1; | ||
1064 | memcpy(buffer + count1, runtime->buffer, count); | ||
1065 | result += count; | ||
1066 | } | ||
1067 | } | ||
1068 | __skip: | ||
1069 | spin_unlock_irqrestore(&runtime->lock, flags); | ||
1070 | return result; | ||
1071 | } | ||
1072 | |||
1073 | /** | ||
1074 | * snd_rawmidi_transmit_ack - acknowledge the transmission | ||
1075 | * @substream: the rawmidi substream | ||
1076 | * @count: the tranferred count | ||
1077 | * | ||
1078 | * Advances the hardware pointer for the internal output buffer with | ||
1079 | * the given size and updates the condition. | ||
1080 | * Call after the transmission is finished. | ||
1081 | * | ||
1082 | * Returns the advanced size if successful, or a negative error code on failure. | ||
1083 | */ | ||
1084 | int snd_rawmidi_transmit_ack(snd_rawmidi_substream_t * substream, int count) | ||
1085 | { | ||
1086 | unsigned long flags; | ||
1087 | snd_rawmidi_runtime_t *runtime = substream->runtime; | ||
1088 | |||
1089 | if (runtime->buffer == NULL) { | ||
1090 | snd_printd("snd_rawmidi_transmit_ack: output is not active!!!\n"); | ||
1091 | return -EINVAL; | ||
1092 | } | ||
1093 | spin_lock_irqsave(&runtime->lock, flags); | ||
1094 | snd_assert(runtime->avail + count <= runtime->buffer_size, ); | ||
1095 | runtime->hw_ptr += count; | ||
1096 | runtime->hw_ptr %= runtime->buffer_size; | ||
1097 | runtime->avail += count; | ||
1098 | substream->bytes += count; | ||
1099 | if (count > 0) { | ||
1100 | if (runtime->drain || snd_rawmidi_ready(substream)) | ||
1101 | wake_up(&runtime->sleep); | ||
1102 | } | ||
1103 | spin_unlock_irqrestore(&runtime->lock, flags); | ||
1104 | return count; | ||
1105 | } | ||
1106 | |||
1107 | /** | ||
1108 | * snd_rawmidi_transmit - copy from the buffer to the device | ||
1109 | * @substream: the rawmidi substream | ||
1110 | * @buf: the buffer pointer | ||
1111 | * @count: the data size to transfer | ||
1112 | * | ||
1113 | * Copies data from the buffer to the device and advances the pointer. | ||
1114 | * | ||
1115 | * Returns the copied size if successful, or a negative error code on failure. | ||
1116 | */ | ||
1117 | int snd_rawmidi_transmit(snd_rawmidi_substream_t * substream, unsigned char *buffer, int count) | ||
1118 | { | ||
1119 | count = snd_rawmidi_transmit_peek(substream, buffer, count); | ||
1120 | if (count < 0) | ||
1121 | return count; | ||
1122 | return snd_rawmidi_transmit_ack(substream, count); | ||
1123 | } | ||
1124 | |||
1125 | static long snd_rawmidi_kernel_write1(snd_rawmidi_substream_t * substream, const unsigned char *buf, long count, int kernel) | ||
1126 | { | ||
1127 | unsigned long flags; | ||
1128 | long count1, result; | ||
1129 | snd_rawmidi_runtime_t *runtime = substream->runtime; | ||
1130 | |||
1131 | snd_assert(buf != NULL, return -EINVAL); | ||
1132 | snd_assert(runtime->buffer != NULL, return -EINVAL); | ||
1133 | |||
1134 | result = 0; | ||
1135 | spin_lock_irqsave(&runtime->lock, flags); | ||
1136 | if (substream->append) { | ||
1137 | if ((long)runtime->avail < count) { | ||
1138 | spin_unlock_irqrestore(&runtime->lock, flags); | ||
1139 | return -EAGAIN; | ||
1140 | } | ||
1141 | } | ||
1142 | while (count > 0 && runtime->avail > 0) { | ||
1143 | count1 = runtime->buffer_size - runtime->appl_ptr; | ||
1144 | if (count1 > count) | ||
1145 | count1 = count; | ||
1146 | if (count1 > (long)runtime->avail) | ||
1147 | count1 = runtime->avail; | ||
1148 | if (kernel) { | ||
1149 | memcpy(runtime->buffer + runtime->appl_ptr, buf, count1); | ||
1150 | } else { | ||
1151 | spin_unlock_irqrestore(&runtime->lock, flags); | ||
1152 | if (copy_from_user(runtime->buffer + runtime->appl_ptr, | ||
1153 | (char __user *)buf, count1)) { | ||
1154 | spin_lock_irqsave(&runtime->lock, flags); | ||
1155 | result = result > 0 ? result : -EFAULT; | ||
1156 | goto __end; | ||
1157 | } | ||
1158 | spin_lock_irqsave(&runtime->lock, flags); | ||
1159 | } | ||
1160 | runtime->appl_ptr += count1; | ||
1161 | runtime->appl_ptr %= runtime->buffer_size; | ||
1162 | runtime->avail -= count1; | ||
1163 | result += count1; | ||
1164 | buf += count1; | ||
1165 | count -= count1; | ||
1166 | } | ||
1167 | __end: | ||
1168 | count1 = runtime->avail < runtime->buffer_size; | ||
1169 | spin_unlock_irqrestore(&runtime->lock, flags); | ||
1170 | if (count1) | ||
1171 | snd_rawmidi_output_trigger(substream, 1); | ||
1172 | return result; | ||
1173 | } | ||
1174 | |||
1175 | long snd_rawmidi_kernel_write(snd_rawmidi_substream_t * substream, const unsigned char *buf, long count) | ||
1176 | { | ||
1177 | return snd_rawmidi_kernel_write1(substream, buf, count, 1); | ||
1178 | } | ||
1179 | |||
1180 | static ssize_t snd_rawmidi_write(struct file *file, const char __user *buf, size_t count, loff_t *offset) | ||
1181 | { | ||
1182 | long result, timeout; | ||
1183 | int count1; | ||
1184 | snd_rawmidi_file_t *rfile; | ||
1185 | snd_rawmidi_runtime_t *runtime; | ||
1186 | snd_rawmidi_substream_t *substream; | ||
1187 | |||
1188 | rfile = file->private_data; | ||
1189 | substream = rfile->output; | ||
1190 | runtime = substream->runtime; | ||
1191 | /* we cannot put an atomic message to our buffer */ | ||
1192 | if (substream->append && count > runtime->buffer_size) | ||
1193 | return -EIO; | ||
1194 | result = 0; | ||
1195 | while (count > 0) { | ||
1196 | spin_lock_irq(&runtime->lock); | ||
1197 | while (!snd_rawmidi_ready_append(substream, count)) { | ||
1198 | wait_queue_t wait; | ||
1199 | if (file->f_flags & O_NONBLOCK) { | ||
1200 | spin_unlock_irq(&runtime->lock); | ||
1201 | return result > 0 ? result : -EAGAIN; | ||
1202 | } | ||
1203 | init_waitqueue_entry(&wait, current); | ||
1204 | add_wait_queue(&runtime->sleep, &wait); | ||
1205 | set_current_state(TASK_INTERRUPTIBLE); | ||
1206 | spin_unlock_irq(&runtime->lock); | ||
1207 | timeout = schedule_timeout(30 * HZ); | ||
1208 | remove_wait_queue(&runtime->sleep, &wait); | ||
1209 | if (signal_pending(current)) | ||
1210 | return result > 0 ? result : -ERESTARTSYS; | ||
1211 | if (!runtime->avail && !timeout) | ||
1212 | return result > 0 ? result : -EIO; | ||
1213 | spin_lock_irq(&runtime->lock); | ||
1214 | } | ||
1215 | spin_unlock_irq(&runtime->lock); | ||
1216 | count1 = snd_rawmidi_kernel_write1(substream, (unsigned char *)buf, count, 0); | ||
1217 | if (count1 < 0) | ||
1218 | return result > 0 ? result : count1; | ||
1219 | result += count1; | ||
1220 | buf += count1; | ||
1221 | if ((size_t)count1 < count && (file->f_flags & O_NONBLOCK)) | ||
1222 | break; | ||
1223 | count -= count1; | ||
1224 | } | ||
1225 | if (file->f_flags & O_SYNC) { | ||
1226 | spin_lock_irq(&runtime->lock); | ||
1227 | while (runtime->avail != runtime->buffer_size) { | ||
1228 | wait_queue_t wait; | ||
1229 | unsigned int last_avail = runtime->avail; | ||
1230 | init_waitqueue_entry(&wait, current); | ||
1231 | add_wait_queue(&runtime->sleep, &wait); | ||
1232 | set_current_state(TASK_INTERRUPTIBLE); | ||
1233 | spin_unlock_irq(&runtime->lock); | ||
1234 | timeout = schedule_timeout(30 * HZ); | ||
1235 | remove_wait_queue(&runtime->sleep, &wait); | ||
1236 | if (signal_pending(current)) | ||
1237 | return result > 0 ? result : -ERESTARTSYS; | ||
1238 | if (runtime->avail == last_avail && !timeout) | ||
1239 | return result > 0 ? result : -EIO; | ||
1240 | spin_lock_irq(&runtime->lock); | ||
1241 | } | ||
1242 | spin_unlock_irq(&runtime->lock); | ||
1243 | } | ||
1244 | return result; | ||
1245 | } | ||
1246 | |||
1247 | static unsigned int snd_rawmidi_poll(struct file *file, poll_table * wait) | ||
1248 | { | ||
1249 | snd_rawmidi_file_t *rfile; | ||
1250 | snd_rawmidi_runtime_t *runtime; | ||
1251 | unsigned int mask; | ||
1252 | |||
1253 | rfile = file->private_data; | ||
1254 | if (rfile->input != NULL) { | ||
1255 | runtime = rfile->input->runtime; | ||
1256 | snd_rawmidi_input_trigger(rfile->input, 1); | ||
1257 | poll_wait(file, &runtime->sleep, wait); | ||
1258 | } | ||
1259 | if (rfile->output != NULL) { | ||
1260 | runtime = rfile->output->runtime; | ||
1261 | poll_wait(file, &runtime->sleep, wait); | ||
1262 | } | ||
1263 | mask = 0; | ||
1264 | if (rfile->input != NULL) { | ||
1265 | if (snd_rawmidi_ready(rfile->input)) | ||
1266 | mask |= POLLIN | POLLRDNORM; | ||
1267 | } | ||
1268 | if (rfile->output != NULL) { | ||
1269 | if (snd_rawmidi_ready(rfile->output)) | ||
1270 | mask |= POLLOUT | POLLWRNORM; | ||
1271 | } | ||
1272 | return mask; | ||
1273 | } | ||
1274 | |||
1275 | /* | ||
1276 | */ | ||
1277 | #ifdef CONFIG_COMPAT | ||
1278 | #include "rawmidi_compat.c" | ||
1279 | #else | ||
1280 | #define snd_rawmidi_ioctl_compat NULL | ||
1281 | #endif | ||
1282 | |||
1283 | /* | ||
1284 | |||
1285 | */ | ||
1286 | |||
1287 | static void snd_rawmidi_proc_info_read(snd_info_entry_t *entry, | ||
1288 | snd_info_buffer_t * buffer) | ||
1289 | { | ||
1290 | snd_rawmidi_t *rmidi; | ||
1291 | snd_rawmidi_substream_t *substream; | ||
1292 | snd_rawmidi_runtime_t *runtime; | ||
1293 | struct list_head *list; | ||
1294 | |||
1295 | rmidi = entry->private_data; | ||
1296 | snd_iprintf(buffer, "%s\n\n", rmidi->name); | ||
1297 | down(&rmidi->open_mutex); | ||
1298 | if (rmidi->info_flags & SNDRV_RAWMIDI_INFO_OUTPUT) { | ||
1299 | list_for_each(list, &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substreams) { | ||
1300 | substream = list_entry(list, snd_rawmidi_substream_t, list); | ||
1301 | snd_iprintf(buffer, | ||
1302 | "Output %d\n" | ||
1303 | " Tx bytes : %lu\n", | ||
1304 | substream->number, | ||
1305 | (unsigned long) substream->bytes); | ||
1306 | if (substream->opened) { | ||
1307 | runtime = substream->runtime; | ||
1308 | snd_iprintf(buffer, | ||
1309 | " Mode : %s\n" | ||
1310 | " Buffer size : %lu\n" | ||
1311 | " Avail : %lu\n", | ||
1312 | runtime->oss ? "OSS compatible" : "native", | ||
1313 | (unsigned long) runtime->buffer_size, | ||
1314 | (unsigned long) runtime->avail); | ||
1315 | } | ||
1316 | } | ||
1317 | } | ||
1318 | if (rmidi->info_flags & SNDRV_RAWMIDI_INFO_INPUT) { | ||
1319 | list_for_each(list, &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substreams) { | ||
1320 | substream = list_entry(list, snd_rawmidi_substream_t, list); | ||
1321 | snd_iprintf(buffer, | ||
1322 | "Input %d\n" | ||
1323 | " Rx bytes : %lu\n", | ||
1324 | substream->number, | ||
1325 | (unsigned long) substream->bytes); | ||
1326 | if (substream->opened) { | ||
1327 | runtime = substream->runtime; | ||
1328 | snd_iprintf(buffer, | ||
1329 | " Buffer size : %lu\n" | ||
1330 | " Avail : %lu\n" | ||
1331 | " Overruns : %lu\n", | ||
1332 | (unsigned long) runtime->buffer_size, | ||
1333 | (unsigned long) runtime->avail, | ||
1334 | (unsigned long) runtime->xruns); | ||
1335 | } | ||
1336 | } | ||
1337 | } | ||
1338 | up(&rmidi->open_mutex); | ||
1339 | } | ||
1340 | |||
1341 | /* | ||
1342 | * Register functions | ||
1343 | */ | ||
1344 | |||
1345 | static struct file_operations snd_rawmidi_f_ops = | ||
1346 | { | ||
1347 | .owner = THIS_MODULE, | ||
1348 | .read = snd_rawmidi_read, | ||
1349 | .write = snd_rawmidi_write, | ||
1350 | .open = snd_rawmidi_open, | ||
1351 | .release = snd_rawmidi_release, | ||
1352 | .poll = snd_rawmidi_poll, | ||
1353 | .unlocked_ioctl = snd_rawmidi_ioctl, | ||
1354 | .compat_ioctl = snd_rawmidi_ioctl_compat, | ||
1355 | }; | ||
1356 | |||
1357 | static snd_minor_t snd_rawmidi_reg = | ||
1358 | { | ||
1359 | .comment = "raw midi", | ||
1360 | .f_ops = &snd_rawmidi_f_ops, | ||
1361 | }; | ||
1362 | |||
1363 | static int snd_rawmidi_alloc_substreams(snd_rawmidi_t *rmidi, | ||
1364 | snd_rawmidi_str_t *stream, | ||
1365 | int direction, | ||
1366 | int count) | ||
1367 | { | ||
1368 | snd_rawmidi_substream_t *substream; | ||
1369 | int idx; | ||
1370 | |||
1371 | INIT_LIST_HEAD(&stream->substreams); | ||
1372 | for (idx = 0; idx < count; idx++) { | ||
1373 | substream = kcalloc(1, sizeof(*substream), GFP_KERNEL); | ||
1374 | if (substream == NULL) | ||
1375 | return -ENOMEM; | ||
1376 | substream->stream = direction; | ||
1377 | substream->number = idx; | ||
1378 | substream->rmidi = rmidi; | ||
1379 | substream->pstr = stream; | ||
1380 | list_add_tail(&substream->list, &stream->substreams); | ||
1381 | stream->substream_count++; | ||
1382 | } | ||
1383 | return 0; | ||
1384 | } | ||
1385 | |||
1386 | /** | ||
1387 | * snd_rawmidi_new - create a rawmidi instance | ||
1388 | * @card: the card instance | ||
1389 | * @id: the id string | ||
1390 | * @device: the device index | ||
1391 | * @output_count: the number of output streams | ||
1392 | * @input_count: the number of input streams | ||
1393 | * @rrawmidi: the pointer to store the new rawmidi instance | ||
1394 | * | ||
1395 | * Creates a new rawmidi instance. | ||
1396 | * Use snd_rawmidi_set_ops() to set the operators to the new instance. | ||
1397 | * | ||
1398 | * Returns zero if successful, or a negative error code on failure. | ||
1399 | */ | ||
1400 | int snd_rawmidi_new(snd_card_t * card, char *id, int device, | ||
1401 | int output_count, int input_count, | ||
1402 | snd_rawmidi_t ** rrawmidi) | ||
1403 | { | ||
1404 | snd_rawmidi_t *rmidi; | ||
1405 | int err; | ||
1406 | static snd_device_ops_t ops = { | ||
1407 | .dev_free = snd_rawmidi_dev_free, | ||
1408 | .dev_register = snd_rawmidi_dev_register, | ||
1409 | .dev_disconnect = snd_rawmidi_dev_disconnect, | ||
1410 | .dev_unregister = snd_rawmidi_dev_unregister | ||
1411 | }; | ||
1412 | |||
1413 | snd_assert(rrawmidi != NULL, return -EINVAL); | ||
1414 | *rrawmidi = NULL; | ||
1415 | snd_assert(card != NULL, return -ENXIO); | ||
1416 | rmidi = kcalloc(1, sizeof(*rmidi), GFP_KERNEL); | ||
1417 | if (rmidi == NULL) | ||
1418 | return -ENOMEM; | ||
1419 | rmidi->card = card; | ||
1420 | rmidi->device = device; | ||
1421 | init_MUTEX(&rmidi->open_mutex); | ||
1422 | init_waitqueue_head(&rmidi->open_wait); | ||
1423 | if (id != NULL) | ||
1424 | strlcpy(rmidi->id, id, sizeof(rmidi->id)); | ||
1425 | if ((err = snd_rawmidi_alloc_substreams(rmidi, &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT], SNDRV_RAWMIDI_STREAM_INPUT, input_count)) < 0) { | ||
1426 | snd_rawmidi_free(rmidi); | ||
1427 | return err; | ||
1428 | } | ||
1429 | if ((err = snd_rawmidi_alloc_substreams(rmidi, &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT], SNDRV_RAWMIDI_STREAM_OUTPUT, output_count)) < 0) { | ||
1430 | snd_rawmidi_free(rmidi); | ||
1431 | return err; | ||
1432 | } | ||
1433 | if ((err = snd_device_new(card, SNDRV_DEV_RAWMIDI, rmidi, &ops)) < 0) { | ||
1434 | snd_rawmidi_free(rmidi); | ||
1435 | return err; | ||
1436 | } | ||
1437 | *rrawmidi = rmidi; | ||
1438 | return 0; | ||
1439 | } | ||
1440 | |||
1441 | static void snd_rawmidi_free_substreams(snd_rawmidi_str_t *stream) | ||
1442 | { | ||
1443 | snd_rawmidi_substream_t *substream; | ||
1444 | |||
1445 | while (!list_empty(&stream->substreams)) { | ||
1446 | substream = list_entry(stream->substreams.next, snd_rawmidi_substream_t, list); | ||
1447 | list_del(&substream->list); | ||
1448 | kfree(substream); | ||
1449 | } | ||
1450 | } | ||
1451 | |||
1452 | static int snd_rawmidi_free(snd_rawmidi_t *rmidi) | ||
1453 | { | ||
1454 | snd_assert(rmidi != NULL, return -ENXIO); | ||
1455 | snd_rawmidi_free_substreams(&rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT]); | ||
1456 | snd_rawmidi_free_substreams(&rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT]); | ||
1457 | if (rmidi->private_free) | ||
1458 | rmidi->private_free(rmidi); | ||
1459 | kfree(rmidi); | ||
1460 | return 0; | ||
1461 | } | ||
1462 | |||
1463 | static int snd_rawmidi_dev_free(snd_device_t *device) | ||
1464 | { | ||
1465 | snd_rawmidi_t *rmidi = device->device_data; | ||
1466 | return snd_rawmidi_free(rmidi); | ||
1467 | } | ||
1468 | |||
1469 | #if defined(CONFIG_SND_SEQUENCER) || (defined(MODULE) && defined(CONFIG_SND_SEQUENCER_MODULE)) | ||
1470 | static void snd_rawmidi_dev_seq_free(snd_seq_device_t *device) | ||
1471 | { | ||
1472 | snd_rawmidi_t *rmidi = device->private_data; | ||
1473 | rmidi->seq_dev = NULL; | ||
1474 | } | ||
1475 | #endif | ||
1476 | |||
1477 | static int snd_rawmidi_dev_register(snd_device_t *device) | ||
1478 | { | ||
1479 | int idx, err; | ||
1480 | snd_info_entry_t *entry; | ||
1481 | char name[16]; | ||
1482 | snd_rawmidi_t *rmidi = device->device_data; | ||
1483 | |||
1484 | if (rmidi->device >= SNDRV_RAWMIDI_DEVICES) | ||
1485 | return -ENOMEM; | ||
1486 | down(®ister_mutex); | ||
1487 | idx = (rmidi->card->number * SNDRV_RAWMIDI_DEVICES) + rmidi->device; | ||
1488 | if (snd_rawmidi_devices[idx] != NULL) { | ||
1489 | up(®ister_mutex); | ||
1490 | return -EBUSY; | ||
1491 | } | ||
1492 | snd_rawmidi_devices[idx] = rmidi; | ||
1493 | sprintf(name, "midiC%iD%i", rmidi->card->number, rmidi->device); | ||
1494 | if ((err = snd_register_device(SNDRV_DEVICE_TYPE_RAWMIDI, | ||
1495 | rmidi->card, rmidi->device, | ||
1496 | &snd_rawmidi_reg, name)) < 0) { | ||
1497 | snd_printk(KERN_ERR "unable to register rawmidi device %i:%i\n", rmidi->card->number, rmidi->device); | ||
1498 | snd_rawmidi_devices[idx] = NULL; | ||
1499 | up(®ister_mutex); | ||
1500 | return err; | ||
1501 | } | ||
1502 | if (rmidi->ops && rmidi->ops->dev_register && | ||
1503 | (err = rmidi->ops->dev_register(rmidi)) < 0) { | ||
1504 | snd_unregister_device(SNDRV_DEVICE_TYPE_RAWMIDI, rmidi->card, rmidi->device); | ||
1505 | snd_rawmidi_devices[idx] = NULL; | ||
1506 | up(®ister_mutex); | ||
1507 | return err; | ||
1508 | } | ||
1509 | #ifdef CONFIG_SND_OSSEMUL | ||
1510 | rmidi->ossreg = 0; | ||
1511 | if ((int)rmidi->device == midi_map[rmidi->card->number]) { | ||
1512 | if (snd_register_oss_device(SNDRV_OSS_DEVICE_TYPE_MIDI, | ||
1513 | rmidi->card, 0, &snd_rawmidi_reg, name) < 0) { | ||
1514 | snd_printk(KERN_ERR "unable to register OSS rawmidi device %i:%i\n", rmidi->card->number, 0); | ||
1515 | } else { | ||
1516 | rmidi->ossreg++; | ||
1517 | #ifdef SNDRV_OSS_INFO_DEV_MIDI | ||
1518 | snd_oss_info_register(SNDRV_OSS_INFO_DEV_MIDI, rmidi->card->number, rmidi->name); | ||
1519 | #endif | ||
1520 | } | ||
1521 | } | ||
1522 | if ((int)rmidi->device == amidi_map[rmidi->card->number]) { | ||
1523 | if (snd_register_oss_device(SNDRV_OSS_DEVICE_TYPE_MIDI, | ||
1524 | rmidi->card, 1, &snd_rawmidi_reg, name) < 0) { | ||
1525 | snd_printk(KERN_ERR "unable to register OSS rawmidi device %i:%i\n", rmidi->card->number, 1); | ||
1526 | } else { | ||
1527 | rmidi->ossreg++; | ||
1528 | } | ||
1529 | } | ||
1530 | #endif /* CONFIG_SND_OSSEMUL */ | ||
1531 | up(®ister_mutex); | ||
1532 | sprintf(name, "midi%d", rmidi->device); | ||
1533 | entry = snd_info_create_card_entry(rmidi->card, name, rmidi->card->proc_root); | ||
1534 | if (entry) { | ||
1535 | entry->private_data = rmidi; | ||
1536 | entry->c.text.read_size = 1024; | ||
1537 | entry->c.text.read = snd_rawmidi_proc_info_read; | ||
1538 | if (snd_info_register(entry) < 0) { | ||
1539 | snd_info_free_entry(entry); | ||
1540 | entry = NULL; | ||
1541 | } | ||
1542 | } | ||
1543 | rmidi->proc_entry = entry; | ||
1544 | #if defined(CONFIG_SND_SEQUENCER) || (defined(MODULE) && defined(CONFIG_SND_SEQUENCER_MODULE)) | ||
1545 | if (!rmidi->ops || !rmidi->ops->dev_register) { /* own registration mechanism */ | ||
1546 | if (snd_seq_device_new(rmidi->card, rmidi->device, SNDRV_SEQ_DEV_ID_MIDISYNTH, 0, &rmidi->seq_dev) >= 0) { | ||
1547 | rmidi->seq_dev->private_data = rmidi; | ||
1548 | rmidi->seq_dev->private_free = snd_rawmidi_dev_seq_free; | ||
1549 | sprintf(rmidi->seq_dev->name, "MIDI %d-%d", rmidi->card->number, rmidi->device); | ||
1550 | snd_device_register(rmidi->card, rmidi->seq_dev); | ||
1551 | } | ||
1552 | } | ||
1553 | #endif | ||
1554 | return 0; | ||
1555 | } | ||
1556 | |||
1557 | static int snd_rawmidi_dev_disconnect(snd_device_t *device) | ||
1558 | { | ||
1559 | snd_rawmidi_t *rmidi = device->device_data; | ||
1560 | int idx; | ||
1561 | |||
1562 | down(®ister_mutex); | ||
1563 | idx = (rmidi->card->number * SNDRV_RAWMIDI_DEVICES) + rmidi->device; | ||
1564 | snd_rawmidi_devices[idx] = NULL; | ||
1565 | up(®ister_mutex); | ||
1566 | return 0; | ||
1567 | } | ||
1568 | |||
1569 | static int snd_rawmidi_dev_unregister(snd_device_t *device) | ||
1570 | { | ||
1571 | int idx; | ||
1572 | snd_rawmidi_t *rmidi = device->device_data; | ||
1573 | |||
1574 | snd_assert(rmidi != NULL, return -ENXIO); | ||
1575 | down(®ister_mutex); | ||
1576 | idx = (rmidi->card->number * SNDRV_RAWMIDI_DEVICES) + rmidi->device; | ||
1577 | snd_rawmidi_devices[idx] = NULL; | ||
1578 | if (rmidi->proc_entry) { | ||
1579 | snd_info_unregister(rmidi->proc_entry); | ||
1580 | rmidi->proc_entry = NULL; | ||
1581 | } | ||
1582 | #ifdef CONFIG_SND_OSSEMUL | ||
1583 | if (rmidi->ossreg) { | ||
1584 | if ((int)rmidi->device == midi_map[rmidi->card->number]) { | ||
1585 | snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_MIDI, rmidi->card, 0); | ||
1586 | #ifdef SNDRV_OSS_INFO_DEV_MIDI | ||
1587 | snd_oss_info_unregister(SNDRV_OSS_INFO_DEV_MIDI, rmidi->card->number); | ||
1588 | #endif | ||
1589 | } | ||
1590 | if ((int)rmidi->device == amidi_map[rmidi->card->number]) | ||
1591 | snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_MIDI, rmidi->card, 1); | ||
1592 | rmidi->ossreg = 0; | ||
1593 | } | ||
1594 | #endif /* CONFIG_SND_OSSEMUL */ | ||
1595 | if (rmidi->ops && rmidi->ops->dev_unregister) | ||
1596 | rmidi->ops->dev_unregister(rmidi); | ||
1597 | snd_unregister_device(SNDRV_DEVICE_TYPE_RAWMIDI, rmidi->card, rmidi->device); | ||
1598 | up(®ister_mutex); | ||
1599 | #if defined(CONFIG_SND_SEQUENCER) || (defined(MODULE) && defined(CONFIG_SND_SEQUENCER_MODULE)) | ||
1600 | if (rmidi->seq_dev) { | ||
1601 | snd_device_free(rmidi->card, rmidi->seq_dev); | ||
1602 | rmidi->seq_dev = NULL; | ||
1603 | } | ||
1604 | #endif | ||
1605 | return snd_rawmidi_free(rmidi); | ||
1606 | } | ||
1607 | |||
1608 | /** | ||
1609 | * snd_rawmidi_set_ops - set the rawmidi operators | ||
1610 | * @rmidi: the rawmidi instance | ||
1611 | * @stream: the stream direction, SNDRV_RAWMIDI_STREAM_XXX | ||
1612 | * @ops: the operator table | ||
1613 | * | ||
1614 | * Sets the rawmidi operators for the given stream direction. | ||
1615 | */ | ||
1616 | void snd_rawmidi_set_ops(snd_rawmidi_t *rmidi, int stream, snd_rawmidi_ops_t *ops) | ||
1617 | { | ||
1618 | struct list_head *list; | ||
1619 | snd_rawmidi_substream_t *substream; | ||
1620 | |||
1621 | list_for_each(list, &rmidi->streams[stream].substreams) { | ||
1622 | substream = list_entry(list, snd_rawmidi_substream_t, list); | ||
1623 | substream->ops = ops; | ||
1624 | } | ||
1625 | } | ||
1626 | |||
1627 | /* | ||
1628 | * ENTRY functions | ||
1629 | */ | ||
1630 | |||
1631 | static int __init alsa_rawmidi_init(void) | ||
1632 | { | ||
1633 | |||
1634 | snd_ctl_register_ioctl(snd_rawmidi_control_ioctl); | ||
1635 | snd_ctl_register_ioctl_compat(snd_rawmidi_control_ioctl); | ||
1636 | #ifdef CONFIG_SND_OSSEMUL | ||
1637 | { int i; | ||
1638 | /* check device map table */ | ||
1639 | for (i = 0; i < SNDRV_CARDS; i++) { | ||
1640 | if (midi_map[i] < 0 || midi_map[i] >= SNDRV_RAWMIDI_DEVICES) { | ||
1641 | snd_printk(KERN_ERR "invalid midi_map[%d] = %d\n", i, midi_map[i]); | ||
1642 | midi_map[i] = 0; | ||
1643 | } | ||
1644 | if (amidi_map[i] < 0 || amidi_map[i] >= SNDRV_RAWMIDI_DEVICES) { | ||
1645 | snd_printk(KERN_ERR "invalid amidi_map[%d] = %d\n", i, amidi_map[i]); | ||
1646 | amidi_map[i] = 1; | ||
1647 | } | ||
1648 | } | ||
1649 | } | ||
1650 | #endif /* CONFIG_SND_OSSEMUL */ | ||
1651 | return 0; | ||
1652 | } | ||
1653 | |||
1654 | static void __exit alsa_rawmidi_exit(void) | ||
1655 | { | ||
1656 | snd_ctl_unregister_ioctl(snd_rawmidi_control_ioctl); | ||
1657 | snd_ctl_unregister_ioctl_compat(snd_rawmidi_control_ioctl); | ||
1658 | } | ||
1659 | |||
1660 | module_init(alsa_rawmidi_init) | ||
1661 | module_exit(alsa_rawmidi_exit) | ||
1662 | |||
1663 | EXPORT_SYMBOL(snd_rawmidi_output_params); | ||
1664 | EXPORT_SYMBOL(snd_rawmidi_input_params); | ||
1665 | EXPORT_SYMBOL(snd_rawmidi_drop_output); | ||
1666 | EXPORT_SYMBOL(snd_rawmidi_drain_output); | ||
1667 | EXPORT_SYMBOL(snd_rawmidi_drain_input); | ||
1668 | EXPORT_SYMBOL(snd_rawmidi_receive); | ||
1669 | EXPORT_SYMBOL(snd_rawmidi_transmit_empty); | ||
1670 | EXPORT_SYMBOL(snd_rawmidi_transmit_peek); | ||
1671 | EXPORT_SYMBOL(snd_rawmidi_transmit_ack); | ||
1672 | EXPORT_SYMBOL(snd_rawmidi_transmit); | ||
1673 | EXPORT_SYMBOL(snd_rawmidi_new); | ||
1674 | EXPORT_SYMBOL(snd_rawmidi_set_ops); | ||
1675 | EXPORT_SYMBOL(snd_rawmidi_info); | ||
1676 | EXPORT_SYMBOL(snd_rawmidi_info_select); | ||
1677 | EXPORT_SYMBOL(snd_rawmidi_kernel_open); | ||
1678 | EXPORT_SYMBOL(snd_rawmidi_kernel_release); | ||
1679 | EXPORT_SYMBOL(snd_rawmidi_kernel_read); | ||
1680 | EXPORT_SYMBOL(snd_rawmidi_kernel_write); | ||
diff --git a/sound/core/rawmidi_compat.c b/sound/core/rawmidi_compat.c new file mode 100644 index 000000000000..d97631c3f3ad --- /dev/null +++ b/sound/core/rawmidi_compat.c | |||
@@ -0,0 +1,120 @@ | |||
1 | /* | ||
2 | * 32bit -> 64bit ioctl wrapper for raw MIDI 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 rawmidi.c */ | ||
22 | |||
23 | #include <linux/compat.h> | ||
24 | |||
25 | struct sndrv_rawmidi_params32 { | ||
26 | s32 stream; | ||
27 | u32 buffer_size; | ||
28 | u32 avail_min; | ||
29 | unsigned int no_active_sensing; /* avoid bit-field */ | ||
30 | unsigned char reserved[16]; | ||
31 | } __attribute__((packed)); | ||
32 | |||
33 | static int snd_rawmidi_ioctl_params_compat(snd_rawmidi_file_t *rfile, | ||
34 | struct sndrv_rawmidi_params32 __user *src) | ||
35 | { | ||
36 | snd_rawmidi_params_t params; | ||
37 | unsigned int val; | ||
38 | |||
39 | if (rfile->output == NULL) | ||
40 | return -EINVAL; | ||
41 | if (get_user(params.stream, &src->stream) || | ||
42 | get_user(params.buffer_size, &src->buffer_size) || | ||
43 | get_user(params.avail_min, &src->avail_min) || | ||
44 | get_user(val, &src->no_active_sensing)) | ||
45 | return -EFAULT; | ||
46 | params.no_active_sensing = val; | ||
47 | switch (params.stream) { | ||
48 | case SNDRV_RAWMIDI_STREAM_OUTPUT: | ||
49 | return snd_rawmidi_output_params(rfile->output, ¶ms); | ||
50 | case SNDRV_RAWMIDI_STREAM_INPUT: | ||
51 | return snd_rawmidi_input_params(rfile->input, ¶ms); | ||
52 | } | ||
53 | return -EINVAL; | ||
54 | } | ||
55 | |||
56 | struct sndrv_rawmidi_status32 { | ||
57 | s32 stream; | ||
58 | struct compat_timespec tstamp; | ||
59 | u32 avail; | ||
60 | u32 xruns; | ||
61 | unsigned char reserved[16]; | ||
62 | } __attribute__((packed)); | ||
63 | |||
64 | static int snd_rawmidi_ioctl_status_compat(snd_rawmidi_file_t *rfile, | ||
65 | struct sndrv_rawmidi_status32 __user *src) | ||
66 | { | ||
67 | int err; | ||
68 | snd_rawmidi_status_t status; | ||
69 | |||
70 | if (rfile->output == NULL) | ||
71 | return -EINVAL; | ||
72 | if (get_user(status.stream, &src->stream)) | ||
73 | return -EFAULT; | ||
74 | |||
75 | switch (status.stream) { | ||
76 | case SNDRV_RAWMIDI_STREAM_OUTPUT: | ||
77 | err = snd_rawmidi_output_status(rfile->output, &status); | ||
78 | break; | ||
79 | case SNDRV_RAWMIDI_STREAM_INPUT: | ||
80 | err = snd_rawmidi_input_status(rfile->input, &status); | ||
81 | break; | ||
82 | default: | ||
83 | return -EINVAL; | ||
84 | } | ||
85 | if (err < 0) | ||
86 | return err; | ||
87 | |||
88 | if (put_user(status.tstamp.tv_sec, &src->tstamp.tv_sec) || | ||
89 | put_user(status.tstamp.tv_nsec, &src->tstamp.tv_nsec) || | ||
90 | put_user(status.avail, &src->avail) || | ||
91 | put_user(status.xruns, &src->xruns)) | ||
92 | return -EFAULT; | ||
93 | |||
94 | return 0; | ||
95 | } | ||
96 | |||
97 | enum { | ||
98 | SNDRV_RAWMIDI_IOCTL_PARAMS32 = _IOWR('W', 0x10, struct sndrv_rawmidi_params32), | ||
99 | SNDRV_RAWMIDI_IOCTL_STATUS32 = _IOWR('W', 0x20, struct sndrv_rawmidi_status32), | ||
100 | }; | ||
101 | |||
102 | static long snd_rawmidi_ioctl_compat(struct file *file, unsigned int cmd, unsigned long arg) | ||
103 | { | ||
104 | snd_rawmidi_file_t *rfile; | ||
105 | void __user *argp = compat_ptr(arg); | ||
106 | |||
107 | rfile = file->private_data; | ||
108 | switch (cmd) { | ||
109 | case SNDRV_RAWMIDI_IOCTL_PVERSION: | ||
110 | case SNDRV_RAWMIDI_IOCTL_INFO: | ||
111 | case SNDRV_RAWMIDI_IOCTL_DROP: | ||
112 | case SNDRV_RAWMIDI_IOCTL_DRAIN: | ||
113 | return snd_rawmidi_ioctl(file, cmd, (unsigned long)argp); | ||
114 | case SNDRV_RAWMIDI_IOCTL_PARAMS32: | ||
115 | return snd_rawmidi_ioctl_params_compat(rfile, argp); | ||
116 | case SNDRV_RAWMIDI_IOCTL_STATUS32: | ||
117 | return snd_rawmidi_ioctl_status_compat(rfile, argp); | ||
118 | } | ||
119 | return -ENOIOCTLCMD; | ||
120 | } | ||
diff --git a/sound/core/rtctimer.c b/sound/core/rtctimer.c new file mode 100644 index 000000000000..bd5d584d284d --- /dev/null +++ b/sound/core/rtctimer.c | |||
@@ -0,0 +1,188 @@ | |||
1 | /* | ||
2 | * RTC based high-frequency timer | ||
3 | * | ||
4 | * Copyright (C) 2000 Takashi Iwai | ||
5 | * based on rtctimer.c by Steve Ratcliffe | ||
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/time.h> | ||
26 | #include <linux/threads.h> | ||
27 | #include <linux/interrupt.h> | ||
28 | #include <linux/moduleparam.h> | ||
29 | #include <sound/core.h> | ||
30 | #include <sound/timer.h> | ||
31 | #include <sound/info.h> | ||
32 | |||
33 | #if defined(CONFIG_RTC) || defined(CONFIG_RTC_MODULE) | ||
34 | |||
35 | #include <linux/mc146818rtc.h> | ||
36 | |||
37 | #define RTC_FREQ 1024 /* default frequency */ | ||
38 | #define NANO_SEC 1000000000L /* 10^9 in sec */ | ||
39 | |||
40 | /* | ||
41 | * prototypes | ||
42 | */ | ||
43 | static int rtctimer_open(snd_timer_t *t); | ||
44 | static int rtctimer_close(snd_timer_t *t); | ||
45 | static int rtctimer_start(snd_timer_t *t); | ||
46 | static int rtctimer_stop(snd_timer_t *t); | ||
47 | |||
48 | |||
49 | /* | ||
50 | * The hardware dependent description for this timer. | ||
51 | */ | ||
52 | static struct _snd_timer_hardware rtc_hw = { | ||
53 | .flags = SNDRV_TIMER_HW_FIRST|SNDRV_TIMER_HW_AUTO, | ||
54 | .ticks = 100000000L, /* FIXME: XXX */ | ||
55 | .open = rtctimer_open, | ||
56 | .close = rtctimer_close, | ||
57 | .start = rtctimer_start, | ||
58 | .stop = rtctimer_stop, | ||
59 | }; | ||
60 | |||
61 | static int rtctimer_freq = RTC_FREQ; /* frequency */ | ||
62 | static snd_timer_t *rtctimer; | ||
63 | static atomic_t rtc_inc = ATOMIC_INIT(0); | ||
64 | static rtc_task_t rtc_task; | ||
65 | |||
66 | |||
67 | static int | ||
68 | rtctimer_open(snd_timer_t *t) | ||
69 | { | ||
70 | int err; | ||
71 | |||
72 | err = rtc_register(&rtc_task); | ||
73 | if (err < 0) | ||
74 | return err; | ||
75 | t->private_data = &rtc_task; | ||
76 | return 0; | ||
77 | } | ||
78 | |||
79 | static int | ||
80 | rtctimer_close(snd_timer_t *t) | ||
81 | { | ||
82 | rtc_task_t *rtc = t->private_data; | ||
83 | if (rtc) { | ||
84 | rtc_unregister(rtc); | ||
85 | t->private_data = NULL; | ||
86 | } | ||
87 | return 0; | ||
88 | } | ||
89 | |||
90 | static int | ||
91 | rtctimer_start(snd_timer_t *timer) | ||
92 | { | ||
93 | rtc_task_t *rtc = timer->private_data; | ||
94 | snd_assert(rtc != NULL, return -EINVAL); | ||
95 | rtc_control(rtc, RTC_IRQP_SET, rtctimer_freq); | ||
96 | rtc_control(rtc, RTC_PIE_ON, 0); | ||
97 | atomic_set(&rtc_inc, 0); | ||
98 | return 0; | ||
99 | } | ||
100 | |||
101 | static int | ||
102 | rtctimer_stop(snd_timer_t *timer) | ||
103 | { | ||
104 | rtc_task_t *rtc = timer->private_data; | ||
105 | snd_assert(rtc != NULL, return -EINVAL); | ||
106 | rtc_control(rtc, RTC_PIE_OFF, 0); | ||
107 | return 0; | ||
108 | } | ||
109 | |||
110 | /* | ||
111 | * interrupt | ||
112 | */ | ||
113 | static void rtctimer_interrupt(void *private_data) | ||
114 | { | ||
115 | int ticks; | ||
116 | |||
117 | atomic_inc(&rtc_inc); | ||
118 | ticks = atomic_read(&rtc_inc); | ||
119 | snd_timer_interrupt((snd_timer_t*)private_data, ticks); | ||
120 | atomic_sub(ticks, &rtc_inc); | ||
121 | } | ||
122 | |||
123 | |||
124 | /* | ||
125 | * ENTRY functions | ||
126 | */ | ||
127 | static int __init rtctimer_init(void) | ||
128 | { | ||
129 | int order, err; | ||
130 | snd_timer_t *timer; | ||
131 | |||
132 | if (rtctimer_freq < 2 || rtctimer_freq > 8192) { | ||
133 | snd_printk(KERN_ERR "rtctimer: invalid frequency %d\n", rtctimer_freq); | ||
134 | return -EINVAL; | ||
135 | } | ||
136 | for (order = 1; rtctimer_freq > order; order <<= 1) | ||
137 | ; | ||
138 | if (rtctimer_freq != order) { | ||
139 | snd_printk(KERN_ERR "rtctimer: invalid frequency %d\n", rtctimer_freq); | ||
140 | return -EINVAL; | ||
141 | } | ||
142 | |||
143 | /* Create a new timer and set up the fields */ | ||
144 | err = snd_timer_global_new("rtc", SNDRV_TIMER_GLOBAL_RTC, &timer); | ||
145 | if (err < 0) | ||
146 | return err; | ||
147 | |||
148 | strcpy(timer->name, "RTC timer"); | ||
149 | timer->hw = rtc_hw; | ||
150 | timer->hw.resolution = NANO_SEC / rtctimer_freq; | ||
151 | |||
152 | /* set up RTC callback */ | ||
153 | rtc_task.func = rtctimer_interrupt; | ||
154 | rtc_task.private_data = timer; | ||
155 | |||
156 | err = snd_timer_global_register(timer); | ||
157 | if (err < 0) { | ||
158 | snd_timer_global_free(timer); | ||
159 | return err; | ||
160 | } | ||
161 | rtctimer = timer; /* remember this */ | ||
162 | |||
163 | return 0; | ||
164 | } | ||
165 | |||
166 | static void __exit rtctimer_exit(void) | ||
167 | { | ||
168 | if (rtctimer) { | ||
169 | snd_timer_global_unregister(rtctimer); | ||
170 | rtctimer = NULL; | ||
171 | } | ||
172 | } | ||
173 | |||
174 | |||
175 | /* | ||
176 | * exported stuff | ||
177 | */ | ||
178 | module_init(rtctimer_init) | ||
179 | module_exit(rtctimer_exit) | ||
180 | |||
181 | module_param(rtctimer_freq, int, 0444); | ||
182 | MODULE_PARM_DESC(rtctimer_freq, "timer frequency in Hz"); | ||
183 | |||
184 | MODULE_LICENSE("GPL"); | ||
185 | |||
186 | MODULE_ALIAS("snd-timer-" __stringify(SNDRV_TIMER_GLOBAL_RTC)); | ||
187 | |||
188 | #endif /* CONFIG_RTC || CONFIG_RTC_MODULE */ | ||
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); | ||
diff --git a/sound/core/sgbuf.c b/sound/core/sgbuf.c new file mode 100644 index 000000000000..74745da9deb6 --- /dev/null +++ b/sound/core/sgbuf.c | |||
@@ -0,0 +1,111 @@ | |||
1 | /* | ||
2 | * Scatter-Gather buffer | ||
3 | * | ||
4 | * Copyright (c) by 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 | |||
22 | #include <linux/config.h> | ||
23 | #include <linux/slab.h> | ||
24 | #include <linux/mm.h> | ||
25 | #include <linux/vmalloc.h> | ||
26 | #include <sound/memalloc.h> | ||
27 | |||
28 | |||
29 | /* table entries are align to 32 */ | ||
30 | #define SGBUF_TBL_ALIGN 32 | ||
31 | #define sgbuf_align_table(tbl) ((((tbl) + SGBUF_TBL_ALIGN - 1) / SGBUF_TBL_ALIGN) * SGBUF_TBL_ALIGN) | ||
32 | |||
33 | int snd_free_sgbuf_pages(struct snd_dma_buffer *dmab) | ||
34 | { | ||
35 | struct snd_sg_buf *sgbuf = dmab->private_data; | ||
36 | struct snd_dma_buffer tmpb; | ||
37 | int i; | ||
38 | |||
39 | if (! sgbuf) | ||
40 | return -EINVAL; | ||
41 | |||
42 | tmpb.dev.type = SNDRV_DMA_TYPE_DEV; | ||
43 | tmpb.dev.dev = sgbuf->dev; | ||
44 | for (i = 0; i < sgbuf->pages; i++) { | ||
45 | tmpb.area = sgbuf->table[i].buf; | ||
46 | tmpb.addr = sgbuf->table[i].addr; | ||
47 | tmpb.bytes = PAGE_SIZE; | ||
48 | snd_dma_free_pages(&tmpb); | ||
49 | } | ||
50 | if (dmab->area) | ||
51 | vunmap(dmab->area); | ||
52 | dmab->area = NULL; | ||
53 | |||
54 | kfree(sgbuf->table); | ||
55 | kfree(sgbuf->page_table); | ||
56 | kfree(sgbuf); | ||
57 | dmab->private_data = NULL; | ||
58 | |||
59 | return 0; | ||
60 | } | ||
61 | |||
62 | void *snd_malloc_sgbuf_pages(struct device *device, | ||
63 | size_t size, struct snd_dma_buffer *dmab, | ||
64 | size_t *res_size) | ||
65 | { | ||
66 | struct snd_sg_buf *sgbuf; | ||
67 | unsigned int i, pages; | ||
68 | struct snd_dma_buffer tmpb; | ||
69 | |||
70 | dmab->area = NULL; | ||
71 | dmab->addr = 0; | ||
72 | dmab->private_data = sgbuf = kmalloc(sizeof(*sgbuf), GFP_KERNEL); | ||
73 | if (! sgbuf) | ||
74 | return NULL; | ||
75 | memset(sgbuf, 0, sizeof(*sgbuf)); | ||
76 | sgbuf->dev = device; | ||
77 | pages = snd_sgbuf_aligned_pages(size); | ||
78 | sgbuf->tblsize = sgbuf_align_table(pages); | ||
79 | sgbuf->table = kmalloc(sizeof(*sgbuf->table) * sgbuf->tblsize, GFP_KERNEL); | ||
80 | if (! sgbuf->table) | ||
81 | goto _failed; | ||
82 | memset(sgbuf->table, 0, sizeof(*sgbuf->table) * sgbuf->tblsize); | ||
83 | sgbuf->page_table = kmalloc(sizeof(*sgbuf->page_table) * sgbuf->tblsize, GFP_KERNEL); | ||
84 | if (! sgbuf->page_table) | ||
85 | goto _failed; | ||
86 | memset(sgbuf->page_table, 0, sizeof(*sgbuf->page_table) * sgbuf->tblsize); | ||
87 | |||
88 | /* allocate each page */ | ||
89 | for (i = 0; i < pages; i++) { | ||
90 | if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, device, PAGE_SIZE, &tmpb) < 0) { | ||
91 | if (res_size == NULL) | ||
92 | goto _failed; | ||
93 | *res_size = size = sgbuf->pages * PAGE_SIZE; | ||
94 | break; | ||
95 | } | ||
96 | sgbuf->table[i].buf = tmpb.area; | ||
97 | sgbuf->table[i].addr = tmpb.addr; | ||
98 | sgbuf->page_table[i] = virt_to_page(tmpb.area); | ||
99 | sgbuf->pages++; | ||
100 | } | ||
101 | |||
102 | sgbuf->size = size; | ||
103 | dmab->area = vmap(sgbuf->page_table, sgbuf->pages, VM_MAP, PAGE_KERNEL); | ||
104 | if (! dmab->area) | ||
105 | goto _failed; | ||
106 | return dmab->area; | ||
107 | |||
108 | _failed: | ||
109 | snd_free_sgbuf_pages(dmab); /* free the table */ | ||
110 | return NULL; | ||
111 | } | ||
diff --git a/sound/core/sound.c b/sound/core/sound.c new file mode 100644 index 000000000000..88e052079f85 --- /dev/null +++ b/sound/core/sound.c | |||
@@ -0,0 +1,491 @@ | |||
1 | /* | ||
2 | * Advanced Linux Sound Architecture | ||
3 | * Copyright (c) by Jaroslav Kysela <perex@suse.cz> | ||
4 | * | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #include <sound/driver.h> | ||
23 | #include <linux/init.h> | ||
24 | #include <linux/slab.h> | ||
25 | #include <linux/time.h> | ||
26 | #include <linux/moduleparam.h> | ||
27 | #include <sound/core.h> | ||
28 | #include <sound/minors.h> | ||
29 | #include <sound/info.h> | ||
30 | #include <sound/version.h> | ||
31 | #include <sound/control.h> | ||
32 | #include <sound/initval.h> | ||
33 | #include <linux/kmod.h> | ||
34 | #include <linux/devfs_fs_kernel.h> | ||
35 | #include <linux/device.h> | ||
36 | |||
37 | #define SNDRV_OS_MINORS 256 | ||
38 | |||
39 | static int major = CONFIG_SND_MAJOR; | ||
40 | int snd_major; | ||
41 | static int cards_limit = 1; | ||
42 | static int device_mode = S_IFCHR | S_IRUGO | S_IWUGO; | ||
43 | |||
44 | MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>"); | ||
45 | MODULE_DESCRIPTION("Advanced Linux Sound Architecture driver for soundcards."); | ||
46 | MODULE_LICENSE("GPL"); | ||
47 | module_param(major, int, 0444); | ||
48 | MODULE_PARM_DESC(major, "Major # for sound driver."); | ||
49 | module_param(cards_limit, int, 0444); | ||
50 | MODULE_PARM_DESC(cards_limit, "Count of auto-loadable soundcards."); | ||
51 | #ifdef CONFIG_DEVFS_FS | ||
52 | module_param(device_mode, int, 0444); | ||
53 | MODULE_PARM_DESC(device_mode, "Device file permission mask for devfs."); | ||
54 | #endif | ||
55 | MODULE_ALIAS_CHARDEV_MAJOR(CONFIG_SND_MAJOR); | ||
56 | |||
57 | /* this one holds the actual max. card number currently available. | ||
58 | * as default, it's identical with cards_limit option. when more | ||
59 | * modules are loaded manually, this limit number increases, too. | ||
60 | */ | ||
61 | int snd_ecards_limit; | ||
62 | |||
63 | static struct list_head snd_minors_hash[SNDRV_CARDS]; | ||
64 | |||
65 | static DECLARE_MUTEX(sound_mutex); | ||
66 | |||
67 | extern struct class_simple *sound_class; | ||
68 | |||
69 | |||
70 | #ifdef CONFIG_KMOD | ||
71 | |||
72 | /** | ||
73 | * snd_request_card - try to load the card module | ||
74 | * @card: the card number | ||
75 | * | ||
76 | * Tries to load the module "snd-card-X" for the given card number | ||
77 | * via KMOD. Returns immediately if already loaded. | ||
78 | */ | ||
79 | void snd_request_card(int card) | ||
80 | { | ||
81 | int locked; | ||
82 | |||
83 | if (! current->fs->root) | ||
84 | return; | ||
85 | read_lock(&snd_card_rwlock); | ||
86 | locked = snd_cards_lock & (1 << card); | ||
87 | read_unlock(&snd_card_rwlock); | ||
88 | if (locked) | ||
89 | return; | ||
90 | if (card < 0 || card >= cards_limit) | ||
91 | return; | ||
92 | request_module("snd-card-%i", card); | ||
93 | } | ||
94 | |||
95 | static void snd_request_other(int minor) | ||
96 | { | ||
97 | char *str; | ||
98 | |||
99 | if (! current->fs->root) | ||
100 | return; | ||
101 | switch (minor) { | ||
102 | case SNDRV_MINOR_SEQUENCER: str = "snd-seq"; break; | ||
103 | case SNDRV_MINOR_TIMER: str = "snd-timer"; break; | ||
104 | default: return; | ||
105 | } | ||
106 | request_module(str); | ||
107 | } | ||
108 | |||
109 | #endif /* request_module support */ | ||
110 | |||
111 | static snd_minor_t *snd_minor_search(int minor) | ||
112 | { | ||
113 | struct list_head *list; | ||
114 | snd_minor_t *mptr; | ||
115 | |||
116 | list_for_each(list, &snd_minors_hash[SNDRV_MINOR_CARD(minor)]) { | ||
117 | mptr = list_entry(list, snd_minor_t, list); | ||
118 | if (mptr->number == minor) | ||
119 | return mptr; | ||
120 | } | ||
121 | return NULL; | ||
122 | } | ||
123 | |||
124 | static int snd_open(struct inode *inode, struct file *file) | ||
125 | { | ||
126 | int minor = iminor(inode); | ||
127 | int card = SNDRV_MINOR_CARD(minor); | ||
128 | int dev = SNDRV_MINOR_DEVICE(minor); | ||
129 | snd_minor_t *mptr = NULL; | ||
130 | struct file_operations *old_fops; | ||
131 | int err = 0; | ||
132 | |||
133 | if (dev != SNDRV_MINOR_SEQUENCER && dev != SNDRV_MINOR_TIMER) { | ||
134 | if (snd_cards[card] == NULL) { | ||
135 | #ifdef CONFIG_KMOD | ||
136 | snd_request_card(card); | ||
137 | if (snd_cards[card] == NULL) | ||
138 | #endif | ||
139 | return -ENODEV; | ||
140 | } | ||
141 | } else { | ||
142 | #ifdef CONFIG_KMOD | ||
143 | if ((mptr = snd_minor_search(minor)) == NULL) | ||
144 | snd_request_other(minor); | ||
145 | #endif | ||
146 | } | ||
147 | if (mptr == NULL && (mptr = snd_minor_search(minor)) == NULL) | ||
148 | return -ENODEV; | ||
149 | old_fops = file->f_op; | ||
150 | file->f_op = fops_get(mptr->f_ops); | ||
151 | if (file->f_op->open) | ||
152 | err = file->f_op->open(inode, file); | ||
153 | if (err) { | ||
154 | fops_put(file->f_op); | ||
155 | file->f_op = fops_get(old_fops); | ||
156 | } | ||
157 | fops_put(old_fops); | ||
158 | return err; | ||
159 | } | ||
160 | |||
161 | static struct file_operations snd_fops = | ||
162 | { | ||
163 | .owner = THIS_MODULE, | ||
164 | .open = snd_open | ||
165 | }; | ||
166 | |||
167 | static int snd_kernel_minor(int type, snd_card_t * card, int dev) | ||
168 | { | ||
169 | int minor; | ||
170 | |||
171 | switch (type) { | ||
172 | case SNDRV_DEVICE_TYPE_SEQUENCER: | ||
173 | case SNDRV_DEVICE_TYPE_TIMER: | ||
174 | minor = type; | ||
175 | break; | ||
176 | case SNDRV_DEVICE_TYPE_CONTROL: | ||
177 | snd_assert(card != NULL, return -EINVAL); | ||
178 | minor = SNDRV_MINOR(card->number, type); | ||
179 | break; | ||
180 | case SNDRV_DEVICE_TYPE_HWDEP: | ||
181 | case SNDRV_DEVICE_TYPE_RAWMIDI: | ||
182 | case SNDRV_DEVICE_TYPE_PCM_PLAYBACK: | ||
183 | case SNDRV_DEVICE_TYPE_PCM_CAPTURE: | ||
184 | snd_assert(card != NULL, return -EINVAL); | ||
185 | minor = SNDRV_MINOR(card->number, type + dev); | ||
186 | break; | ||
187 | default: | ||
188 | return -EINVAL; | ||
189 | } | ||
190 | snd_assert(minor >= 0 && minor < SNDRV_OS_MINORS, return -EINVAL); | ||
191 | return minor; | ||
192 | } | ||
193 | |||
194 | /** | ||
195 | * snd_register_device - Register the ALSA device file for the card | ||
196 | * @type: the device type, SNDRV_DEVICE_TYPE_XXX | ||
197 | * @card: the card instance | ||
198 | * @dev: the device index | ||
199 | * @reg: the snd_minor_t record | ||
200 | * @name: the device file name | ||
201 | * | ||
202 | * Registers an ALSA device file for the given card. | ||
203 | * The operators have to be set in reg parameter. | ||
204 | * | ||
205 | * Retrurns zero if successful, or a negative error code on failure. | ||
206 | */ | ||
207 | int snd_register_device(int type, snd_card_t * card, int dev, snd_minor_t * reg, const char *name) | ||
208 | { | ||
209 | int minor = snd_kernel_minor(type, card, dev); | ||
210 | snd_minor_t *preg; | ||
211 | struct device *device = NULL; | ||
212 | |||
213 | if (minor < 0) | ||
214 | return minor; | ||
215 | snd_assert(name, return -EINVAL); | ||
216 | preg = (snd_minor_t *)kmalloc(sizeof(snd_minor_t) + strlen(name) + 1, GFP_KERNEL); | ||
217 | if (preg == NULL) | ||
218 | return -ENOMEM; | ||
219 | *preg = *reg; | ||
220 | preg->number = minor; | ||
221 | preg->device = dev; | ||
222 | strcpy(preg->name, name); | ||
223 | down(&sound_mutex); | ||
224 | if (snd_minor_search(minor)) { | ||
225 | up(&sound_mutex); | ||
226 | kfree(preg); | ||
227 | return -EBUSY; | ||
228 | } | ||
229 | list_add_tail(&preg->list, &snd_minors_hash[SNDRV_MINOR_CARD(minor)]); | ||
230 | if (strncmp(name, "controlC", 8) || card->number >= cards_limit) | ||
231 | devfs_mk_cdev(MKDEV(major, minor), S_IFCHR | device_mode, "snd/%s", name); | ||
232 | if (card) | ||
233 | device = card->dev; | ||
234 | class_simple_device_add(sound_class, MKDEV(major, minor), device, name); | ||
235 | |||
236 | up(&sound_mutex); | ||
237 | return 0; | ||
238 | } | ||
239 | |||
240 | /** | ||
241 | * snd_unregister_device - unregister the device on the given card | ||
242 | * @type: the device type, SNDRV_DEVICE_TYPE_XXX | ||
243 | * @card: the card instance | ||
244 | * @dev: the device index | ||
245 | * | ||
246 | * Unregisters the device file already registered via | ||
247 | * snd_register_device(). | ||
248 | * | ||
249 | * Returns zero if sucecessful, or a negative error code on failure | ||
250 | */ | ||
251 | int snd_unregister_device(int type, snd_card_t * card, int dev) | ||
252 | { | ||
253 | int minor = snd_kernel_minor(type, card, dev); | ||
254 | snd_minor_t *mptr; | ||
255 | |||
256 | if (minor < 0) | ||
257 | return minor; | ||
258 | down(&sound_mutex); | ||
259 | if ((mptr = snd_minor_search(minor)) == NULL) { | ||
260 | up(&sound_mutex); | ||
261 | return -EINVAL; | ||
262 | } | ||
263 | |||
264 | if (strncmp(mptr->name, "controlC", 8) || card->number >= cards_limit) /* created in sound.c */ | ||
265 | devfs_remove("snd/%s", mptr->name); | ||
266 | class_simple_device_remove(MKDEV(major, minor)); | ||
267 | |||
268 | list_del(&mptr->list); | ||
269 | up(&sound_mutex); | ||
270 | kfree(mptr); | ||
271 | return 0; | ||
272 | } | ||
273 | |||
274 | /* | ||
275 | * INFO PART | ||
276 | */ | ||
277 | |||
278 | static snd_info_entry_t *snd_minor_info_entry = NULL; | ||
279 | |||
280 | static void snd_minor_info_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer) | ||
281 | { | ||
282 | int card, device; | ||
283 | struct list_head *list; | ||
284 | snd_minor_t *mptr; | ||
285 | |||
286 | down(&sound_mutex); | ||
287 | for (card = 0; card < SNDRV_CARDS; card++) { | ||
288 | list_for_each(list, &snd_minors_hash[card]) { | ||
289 | mptr = list_entry(list, snd_minor_t, list); | ||
290 | if (SNDRV_MINOR_DEVICE(mptr->number) != SNDRV_MINOR_SEQUENCER) { | ||
291 | if ((device = mptr->device) >= 0) | ||
292 | snd_iprintf(buffer, "%3i: [%i-%2i]: %s\n", mptr->number, card, device, mptr->comment); | ||
293 | else | ||
294 | snd_iprintf(buffer, "%3i: [%i] : %s\n", mptr->number, card, mptr->comment); | ||
295 | } else { | ||
296 | snd_iprintf(buffer, "%3i: : %s\n", mptr->number, mptr->comment); | ||
297 | } | ||
298 | } | ||
299 | } | ||
300 | up(&sound_mutex); | ||
301 | } | ||
302 | |||
303 | int __init snd_minor_info_init(void) | ||
304 | { | ||
305 | snd_info_entry_t *entry; | ||
306 | |||
307 | entry = snd_info_create_module_entry(THIS_MODULE, "devices", NULL); | ||
308 | if (entry) { | ||
309 | entry->c.text.read_size = PAGE_SIZE; | ||
310 | entry->c.text.read = snd_minor_info_read; | ||
311 | if (snd_info_register(entry) < 0) { | ||
312 | snd_info_free_entry(entry); | ||
313 | entry = NULL; | ||
314 | } | ||
315 | } | ||
316 | snd_minor_info_entry = entry; | ||
317 | return 0; | ||
318 | } | ||
319 | |||
320 | int __exit snd_minor_info_done(void) | ||
321 | { | ||
322 | if (snd_minor_info_entry) | ||
323 | snd_info_unregister(snd_minor_info_entry); | ||
324 | return 0; | ||
325 | } | ||
326 | |||
327 | /* | ||
328 | * INIT PART | ||
329 | */ | ||
330 | |||
331 | static int __init alsa_sound_init(void) | ||
332 | { | ||
333 | short controlnum; | ||
334 | int err; | ||
335 | int card; | ||
336 | |||
337 | snd_major = major; | ||
338 | snd_ecards_limit = cards_limit; | ||
339 | for (card = 0; card < SNDRV_CARDS; card++) | ||
340 | INIT_LIST_HEAD(&snd_minors_hash[card]); | ||
341 | if ((err = snd_oss_init_module()) < 0) | ||
342 | return err; | ||
343 | devfs_mk_dir("snd"); | ||
344 | if (register_chrdev(major, "alsa", &snd_fops)) { | ||
345 | snd_printk(KERN_ERR "unable to register native major device number %d\n", major); | ||
346 | devfs_remove("snd"); | ||
347 | return -EIO; | ||
348 | } | ||
349 | snd_memory_init(); | ||
350 | if (snd_info_init() < 0) { | ||
351 | snd_memory_done(); | ||
352 | unregister_chrdev(major, "alsa"); | ||
353 | devfs_remove("snd"); | ||
354 | return -ENOMEM; | ||
355 | } | ||
356 | snd_info_minor_register(); | ||
357 | for (controlnum = 0; controlnum < cards_limit; controlnum++) | ||
358 | devfs_mk_cdev(MKDEV(major, controlnum<<5), S_IFCHR | device_mode, "snd/controlC%d", controlnum); | ||
359 | #ifndef MODULE | ||
360 | printk(KERN_INFO "Advanced Linux Sound Architecture Driver Version " CONFIG_SND_VERSION CONFIG_SND_DATE ".\n"); | ||
361 | #endif | ||
362 | return 0; | ||
363 | } | ||
364 | |||
365 | static void __exit alsa_sound_exit(void) | ||
366 | { | ||
367 | short controlnum; | ||
368 | |||
369 | for (controlnum = 0; controlnum < cards_limit; controlnum++) | ||
370 | devfs_remove("snd/controlC%d", controlnum); | ||
371 | |||
372 | snd_info_minor_unregister(); | ||
373 | snd_info_done(); | ||
374 | snd_memory_done(); | ||
375 | if (unregister_chrdev(major, "alsa") != 0) | ||
376 | snd_printk(KERN_ERR "unable to unregister major device number %d\n", major); | ||
377 | devfs_remove("snd"); | ||
378 | } | ||
379 | |||
380 | module_init(alsa_sound_init) | ||
381 | module_exit(alsa_sound_exit) | ||
382 | |||
383 | /* sound.c */ | ||
384 | EXPORT_SYMBOL(snd_major); | ||
385 | EXPORT_SYMBOL(snd_ecards_limit); | ||
386 | #if defined(CONFIG_KMOD) | ||
387 | EXPORT_SYMBOL(snd_request_card); | ||
388 | #endif | ||
389 | EXPORT_SYMBOL(snd_register_device); | ||
390 | EXPORT_SYMBOL(snd_unregister_device); | ||
391 | #if defined(CONFIG_SND_OSSEMUL) | ||
392 | EXPORT_SYMBOL(snd_register_oss_device); | ||
393 | EXPORT_SYMBOL(snd_unregister_oss_device); | ||
394 | #endif | ||
395 | /* memory.c */ | ||
396 | #ifdef CONFIG_SND_DEBUG_MEMORY | ||
397 | EXPORT_SYMBOL(snd_hidden_kmalloc); | ||
398 | EXPORT_SYMBOL(snd_hidden_kcalloc); | ||
399 | EXPORT_SYMBOL(snd_hidden_kfree); | ||
400 | EXPORT_SYMBOL(snd_hidden_vmalloc); | ||
401 | EXPORT_SYMBOL(snd_hidden_vfree); | ||
402 | #endif | ||
403 | EXPORT_SYMBOL(snd_kmalloc_strdup); | ||
404 | EXPORT_SYMBOL(copy_to_user_fromio); | ||
405 | EXPORT_SYMBOL(copy_from_user_toio); | ||
406 | /* init.c */ | ||
407 | EXPORT_SYMBOL(snd_cards); | ||
408 | #if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE) | ||
409 | EXPORT_SYMBOL(snd_mixer_oss_notify_callback); | ||
410 | #endif | ||
411 | EXPORT_SYMBOL(snd_card_new); | ||
412 | EXPORT_SYMBOL(snd_card_disconnect); | ||
413 | EXPORT_SYMBOL(snd_card_free); | ||
414 | EXPORT_SYMBOL(snd_card_free_in_thread); | ||
415 | EXPORT_SYMBOL(snd_card_register); | ||
416 | EXPORT_SYMBOL(snd_component_add); | ||
417 | EXPORT_SYMBOL(snd_card_file_add); | ||
418 | EXPORT_SYMBOL(snd_card_file_remove); | ||
419 | #ifdef CONFIG_PM | ||
420 | EXPORT_SYMBOL(snd_power_wait); | ||
421 | EXPORT_SYMBOL(snd_card_set_pm_callback); | ||
422 | #if defined(CONFIG_PM) && defined(CONFIG_SND_GENERIC_PM) | ||
423 | EXPORT_SYMBOL(snd_card_set_generic_pm_callback); | ||
424 | #endif | ||
425 | #ifdef CONFIG_PCI | ||
426 | EXPORT_SYMBOL(snd_card_pci_suspend); | ||
427 | EXPORT_SYMBOL(snd_card_pci_resume); | ||
428 | #endif | ||
429 | #endif | ||
430 | /* device.c */ | ||
431 | EXPORT_SYMBOL(snd_device_new); | ||
432 | EXPORT_SYMBOL(snd_device_register); | ||
433 | EXPORT_SYMBOL(snd_device_free); | ||
434 | EXPORT_SYMBOL(snd_device_free_all); | ||
435 | /* isadma.c */ | ||
436 | #ifdef CONFIG_ISA | ||
437 | EXPORT_SYMBOL(snd_dma_program); | ||
438 | EXPORT_SYMBOL(snd_dma_disable); | ||
439 | EXPORT_SYMBOL(snd_dma_pointer); | ||
440 | #endif | ||
441 | /* info.c */ | ||
442 | #ifdef CONFIG_PROC_FS | ||
443 | EXPORT_SYMBOL(snd_seq_root); | ||
444 | EXPORT_SYMBOL(snd_iprintf); | ||
445 | EXPORT_SYMBOL(snd_info_get_line); | ||
446 | EXPORT_SYMBOL(snd_info_get_str); | ||
447 | EXPORT_SYMBOL(snd_info_create_module_entry); | ||
448 | EXPORT_SYMBOL(snd_info_create_card_entry); | ||
449 | EXPORT_SYMBOL(snd_info_free_entry); | ||
450 | EXPORT_SYMBOL(snd_info_register); | ||
451 | EXPORT_SYMBOL(snd_info_unregister); | ||
452 | EXPORT_SYMBOL(snd_card_proc_new); | ||
453 | #endif | ||
454 | /* info_oss.c */ | ||
455 | #if defined(CONFIG_SND_OSSEMUL) && defined(CONFIG_PROC_FS) | ||
456 | EXPORT_SYMBOL(snd_oss_info_register); | ||
457 | #endif | ||
458 | /* control.c */ | ||
459 | EXPORT_SYMBOL(snd_ctl_new); | ||
460 | EXPORT_SYMBOL(snd_ctl_new1); | ||
461 | EXPORT_SYMBOL(snd_ctl_free_one); | ||
462 | EXPORT_SYMBOL(snd_ctl_add); | ||
463 | EXPORT_SYMBOL(snd_ctl_remove); | ||
464 | EXPORT_SYMBOL(snd_ctl_remove_id); | ||
465 | EXPORT_SYMBOL(snd_ctl_rename_id); | ||
466 | EXPORT_SYMBOL(snd_ctl_find_numid); | ||
467 | EXPORT_SYMBOL(snd_ctl_find_id); | ||
468 | EXPORT_SYMBOL(snd_ctl_notify); | ||
469 | EXPORT_SYMBOL(snd_ctl_register_ioctl); | ||
470 | EXPORT_SYMBOL(snd_ctl_unregister_ioctl); | ||
471 | #ifdef CONFIG_COMPAT | ||
472 | EXPORT_SYMBOL(snd_ctl_register_ioctl_compat); | ||
473 | EXPORT_SYMBOL(snd_ctl_unregister_ioctl_compat); | ||
474 | #endif | ||
475 | EXPORT_SYMBOL(snd_ctl_elem_read); | ||
476 | EXPORT_SYMBOL(snd_ctl_elem_write); | ||
477 | /* misc.c */ | ||
478 | EXPORT_SYMBOL(snd_task_name); | ||
479 | #ifdef CONFIG_SND_VERBOSE_PRINTK | ||
480 | EXPORT_SYMBOL(snd_verbose_printk); | ||
481 | #endif | ||
482 | #if defined(CONFIG_SND_DEBUG) && defined(CONFIG_SND_VERBOSE_PRINTK) | ||
483 | EXPORT_SYMBOL(snd_verbose_printd); | ||
484 | #endif | ||
485 | /* wrappers */ | ||
486 | #ifdef CONFIG_SND_DEBUG_MEMORY | ||
487 | EXPORT_SYMBOL(snd_wrapper_kmalloc); | ||
488 | EXPORT_SYMBOL(snd_wrapper_kfree); | ||
489 | EXPORT_SYMBOL(snd_wrapper_vmalloc); | ||
490 | EXPORT_SYMBOL(snd_wrapper_vfree); | ||
491 | #endif | ||
diff --git a/sound/core/sound_oss.c b/sound/core/sound_oss.c new file mode 100644 index 000000000000..de39d212bc15 --- /dev/null +++ b/sound/core/sound_oss.c | |||
@@ -0,0 +1,250 @@ | |||
1 | /* | ||
2 | * Advanced Linux Sound Architecture | ||
3 | * Copyright (c) by Jaroslav Kysela <perex@suse.cz> | ||
4 | * | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #include <sound/driver.h> | ||
23 | |||
24 | #ifdef CONFIG_SND_OSSEMUL | ||
25 | |||
26 | #if !defined(CONFIG_SOUND) && !(defined(MODULE) && defined(CONFIG_SOUND_MODULE)) | ||
27 | #error "Enable the OSS soundcore multiplexer (CONFIG_SOUND) in the kernel." | ||
28 | #endif | ||
29 | |||
30 | #include <linux/init.h> | ||
31 | #include <linux/slab.h> | ||
32 | #include <linux/time.h> | ||
33 | #include <sound/core.h> | ||
34 | #include <sound/minors.h> | ||
35 | #include <sound/info.h> | ||
36 | #include <linux/sound.h> | ||
37 | |||
38 | #define SNDRV_OS_MINORS 256 | ||
39 | |||
40 | static struct list_head snd_oss_minors_hash[SNDRV_CARDS]; | ||
41 | |||
42 | static DECLARE_MUTEX(sound_oss_mutex); | ||
43 | |||
44 | static snd_minor_t *snd_oss_minor_search(int minor) | ||
45 | { | ||
46 | struct list_head *list; | ||
47 | snd_minor_t *mptr; | ||
48 | |||
49 | list_for_each(list, &snd_oss_minors_hash[SNDRV_MINOR_OSS_CARD(minor)]) { | ||
50 | mptr = list_entry(list, snd_minor_t, list); | ||
51 | if (mptr->number == minor) | ||
52 | return mptr; | ||
53 | } | ||
54 | return NULL; | ||
55 | } | ||
56 | |||
57 | static int snd_oss_kernel_minor(int type, snd_card_t * card, int dev) | ||
58 | { | ||
59 | int minor; | ||
60 | |||
61 | switch (type) { | ||
62 | case SNDRV_OSS_DEVICE_TYPE_MIXER: | ||
63 | snd_assert(card != NULL && dev <= 1, return -EINVAL); | ||
64 | minor = SNDRV_MINOR_OSS(card->number, (dev ? SNDRV_MINOR_OSS_MIXER1 : SNDRV_MINOR_OSS_MIXER)); | ||
65 | break; | ||
66 | case SNDRV_OSS_DEVICE_TYPE_SEQUENCER: | ||
67 | minor = SNDRV_MINOR_OSS_SEQUENCER; | ||
68 | break; | ||
69 | case SNDRV_OSS_DEVICE_TYPE_MUSIC: | ||
70 | minor = SNDRV_MINOR_OSS_MUSIC; | ||
71 | break; | ||
72 | case SNDRV_OSS_DEVICE_TYPE_PCM: | ||
73 | snd_assert(card != NULL && dev <= 1, return -EINVAL); | ||
74 | minor = SNDRV_MINOR_OSS(card->number, (dev ? SNDRV_MINOR_OSS_PCM1 : SNDRV_MINOR_OSS_PCM)); | ||
75 | break; | ||
76 | case SNDRV_OSS_DEVICE_TYPE_MIDI: | ||
77 | snd_assert(card != NULL && dev <= 1, return -EINVAL); | ||
78 | minor = SNDRV_MINOR_OSS(card->number, (dev ? SNDRV_MINOR_OSS_MIDI1 : SNDRV_MINOR_OSS_MIDI)); | ||
79 | break; | ||
80 | case SNDRV_OSS_DEVICE_TYPE_DMFM: | ||
81 | minor = SNDRV_MINOR_OSS(card->number, SNDRV_MINOR_OSS_DMFM); | ||
82 | break; | ||
83 | case SNDRV_OSS_DEVICE_TYPE_SNDSTAT: | ||
84 | minor = SNDRV_MINOR_OSS_SNDSTAT; | ||
85 | break; | ||
86 | default: | ||
87 | return -EINVAL; | ||
88 | } | ||
89 | snd_assert(minor >= 0 && minor < SNDRV_OS_MINORS, return -EINVAL); | ||
90 | return minor; | ||
91 | } | ||
92 | |||
93 | int snd_register_oss_device(int type, snd_card_t * card, int dev, snd_minor_t * reg, const char *name) | ||
94 | { | ||
95 | int minor = snd_oss_kernel_minor(type, card, dev); | ||
96 | int minor_unit; | ||
97 | snd_minor_t *preg; | ||
98 | int cidx = SNDRV_MINOR_OSS_CARD(minor); | ||
99 | int track2 = -1; | ||
100 | int register1 = -1, register2 = -1; | ||
101 | |||
102 | if (minor < 0) | ||
103 | return minor; | ||
104 | preg = (snd_minor_t *)kmalloc(sizeof(snd_minor_t), GFP_KERNEL); | ||
105 | if (preg == NULL) | ||
106 | return -ENOMEM; | ||
107 | *preg = *reg; | ||
108 | preg->number = minor; | ||
109 | preg->device = dev; | ||
110 | down(&sound_oss_mutex); | ||
111 | list_add_tail(&preg->list, &snd_oss_minors_hash[cidx]); | ||
112 | minor_unit = SNDRV_MINOR_OSS_DEVICE(minor); | ||
113 | switch (minor_unit) { | ||
114 | case SNDRV_MINOR_OSS_PCM: | ||
115 | track2 = SNDRV_MINOR_OSS(cidx, SNDRV_MINOR_OSS_AUDIO); | ||
116 | break; | ||
117 | case SNDRV_MINOR_OSS_MIDI: | ||
118 | track2 = SNDRV_MINOR_OSS(cidx, SNDRV_MINOR_OSS_DMMIDI); | ||
119 | break; | ||
120 | case SNDRV_MINOR_OSS_MIDI1: | ||
121 | track2 = SNDRV_MINOR_OSS(cidx, SNDRV_MINOR_OSS_DMMIDI1); | ||
122 | break; | ||
123 | } | ||
124 | register1 = register_sound_special(reg->f_ops, minor); | ||
125 | if (register1 != minor) | ||
126 | goto __end; | ||
127 | if (track2 >= 0) { | ||
128 | register2 = register_sound_special(reg->f_ops, track2); | ||
129 | if (register2 != track2) | ||
130 | goto __end; | ||
131 | } | ||
132 | up(&sound_oss_mutex); | ||
133 | return 0; | ||
134 | |||
135 | __end: | ||
136 | if (register2 >= 0) | ||
137 | unregister_sound_special(register2); | ||
138 | if (register1 >= 0) | ||
139 | unregister_sound_special(register1); | ||
140 | list_del(&preg->list); | ||
141 | up(&sound_oss_mutex); | ||
142 | kfree(preg); | ||
143 | return -EBUSY; | ||
144 | } | ||
145 | |||
146 | int snd_unregister_oss_device(int type, snd_card_t * card, int dev) | ||
147 | { | ||
148 | int minor = snd_oss_kernel_minor(type, card, dev); | ||
149 | int cidx = SNDRV_MINOR_OSS_CARD(minor); | ||
150 | int track2 = -1; | ||
151 | snd_minor_t *mptr; | ||
152 | |||
153 | if (minor < 0) | ||
154 | return minor; | ||
155 | down(&sound_oss_mutex); | ||
156 | mptr = snd_oss_minor_search(minor); | ||
157 | if (mptr == NULL) { | ||
158 | up(&sound_oss_mutex); | ||
159 | return -ENOENT; | ||
160 | } | ||
161 | unregister_sound_special(minor); | ||
162 | switch (SNDRV_MINOR_OSS_DEVICE(minor)) { | ||
163 | case SNDRV_MINOR_OSS_PCM: | ||
164 | track2 = SNDRV_MINOR_OSS(cidx, SNDRV_MINOR_OSS_AUDIO); | ||
165 | break; | ||
166 | case SNDRV_MINOR_OSS_MIDI: | ||
167 | track2 = SNDRV_MINOR_OSS(cidx, SNDRV_MINOR_OSS_DMMIDI); | ||
168 | break; | ||
169 | case SNDRV_MINOR_OSS_MIDI1: | ||
170 | track2 = SNDRV_MINOR_OSS(cidx, SNDRV_MINOR_OSS_DMMIDI1); | ||
171 | break; | ||
172 | } | ||
173 | if (track2 >= 0) | ||
174 | unregister_sound_special(track2); | ||
175 | list_del(&mptr->list); | ||
176 | up(&sound_oss_mutex); | ||
177 | kfree(mptr); | ||
178 | return 0; | ||
179 | } | ||
180 | |||
181 | /* | ||
182 | * INFO PART | ||
183 | */ | ||
184 | |||
185 | #ifdef CONFIG_PROC_FS | ||
186 | |||
187 | static snd_info_entry_t *snd_minor_info_oss_entry = NULL; | ||
188 | |||
189 | static void snd_minor_info_oss_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer) | ||
190 | { | ||
191 | int card, dev; | ||
192 | struct list_head *list; | ||
193 | snd_minor_t *mptr; | ||
194 | |||
195 | down(&sound_oss_mutex); | ||
196 | for (card = 0; card < SNDRV_CARDS; card++) { | ||
197 | list_for_each(list, &snd_oss_minors_hash[card]) { | ||
198 | mptr = list_entry(list, snd_minor_t, list); | ||
199 | dev = SNDRV_MINOR_OSS_DEVICE(mptr->number); | ||
200 | if (dev != SNDRV_MINOR_OSS_SNDSTAT && | ||
201 | dev != SNDRV_MINOR_OSS_SEQUENCER && | ||
202 | dev != SNDRV_MINOR_OSS_MUSIC) | ||
203 | snd_iprintf(buffer, "%3i: [%i-%2i]: %s\n", mptr->number, card, dev, mptr->comment); | ||
204 | else | ||
205 | snd_iprintf(buffer, "%3i: : %s\n", mptr->number, mptr->comment); | ||
206 | } | ||
207 | } | ||
208 | up(&sound_oss_mutex); | ||
209 | } | ||
210 | |||
211 | #endif /* CONFIG_PROC_FS */ | ||
212 | |||
213 | int __init snd_minor_info_oss_init(void) | ||
214 | { | ||
215 | #ifdef CONFIG_PROC_FS | ||
216 | snd_info_entry_t *entry; | ||
217 | |||
218 | entry = snd_info_create_module_entry(THIS_MODULE, "devices", snd_oss_root); | ||
219 | if (entry) { | ||
220 | entry->c.text.read_size = PAGE_SIZE; | ||
221 | entry->c.text.read = snd_minor_info_oss_read; | ||
222 | if (snd_info_register(entry) < 0) { | ||
223 | snd_info_free_entry(entry); | ||
224 | entry = NULL; | ||
225 | } | ||
226 | } | ||
227 | snd_minor_info_oss_entry = entry; | ||
228 | #endif | ||
229 | return 0; | ||
230 | } | ||
231 | |||
232 | int __exit snd_minor_info_oss_done(void) | ||
233 | { | ||
234 | #ifdef CONFIG_PROC_FS | ||
235 | if (snd_minor_info_oss_entry) | ||
236 | snd_info_unregister(snd_minor_info_oss_entry); | ||
237 | #endif | ||
238 | return 0; | ||
239 | } | ||
240 | |||
241 | int __init snd_oss_init_module(void) | ||
242 | { | ||
243 | int card; | ||
244 | |||
245 | for (card = 0; card < SNDRV_CARDS; card++) | ||
246 | INIT_LIST_HEAD(&snd_oss_minors_hash[card]); | ||
247 | return 0; | ||
248 | } | ||
249 | |||
250 | #endif /* CONFIG_SND_OSSEMUL */ | ||
diff --git a/sound/core/timer.c b/sound/core/timer.c new file mode 100644 index 000000000000..fa762ca439be --- /dev/null +++ b/sound/core/timer.c | |||
@@ -0,0 +1,1901 @@ | |||
1 | /* | ||
2 | * Timers abstract layer | ||
3 | * Copyright (c) by Jaroslav Kysela <perex@suse.cz> | ||
4 | * | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #include <sound/driver.h> | ||
23 | #include <linux/delay.h> | ||
24 | #include <linux/init.h> | ||
25 | #include <linux/smp_lock.h> | ||
26 | #include <linux/slab.h> | ||
27 | #include <linux/time.h> | ||
28 | #include <linux/moduleparam.h> | ||
29 | #include <sound/core.h> | ||
30 | #include <sound/timer.h> | ||
31 | #include <sound/control.h> | ||
32 | #include <sound/info.h> | ||
33 | #include <sound/minors.h> | ||
34 | #include <sound/initval.h> | ||
35 | #include <linux/kmod.h> | ||
36 | #ifdef CONFIG_KERNELD | ||
37 | #include <linux/kerneld.h> | ||
38 | #endif | ||
39 | |||
40 | #if defined(CONFIG_SND_HPET) || defined(CONFIG_SND_HPET_MODULE) | ||
41 | #define DEFAULT_TIMER_LIMIT 3 | ||
42 | #elif defined(CONFIG_SND_RTCTIMER) || defined(CONFIG_SND_RTCTIMER_MODULE) | ||
43 | #define DEFAULT_TIMER_LIMIT 2 | ||
44 | #else | ||
45 | #define DEFAULT_TIMER_LIMIT 1 | ||
46 | #endif | ||
47 | |||
48 | static int timer_limit = DEFAULT_TIMER_LIMIT; | ||
49 | MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>, Takashi Iwai <tiwai@suse.de>"); | ||
50 | MODULE_DESCRIPTION("ALSA timer interface"); | ||
51 | MODULE_LICENSE("GPL"); | ||
52 | module_param(timer_limit, int, 0444); | ||
53 | MODULE_PARM_DESC(timer_limit, "Maximum global timers in system."); | ||
54 | |||
55 | typedef struct { | ||
56 | snd_timer_instance_t *timeri; | ||
57 | int tread; /* enhanced read with timestamps and events */ | ||
58 | unsigned long ticks; | ||
59 | unsigned long overrun; | ||
60 | int qhead; | ||
61 | int qtail; | ||
62 | int qused; | ||
63 | int queue_size; | ||
64 | snd_timer_read_t *queue; | ||
65 | snd_timer_tread_t *tqueue; | ||
66 | spinlock_t qlock; | ||
67 | unsigned long last_resolution; | ||
68 | unsigned int filter; | ||
69 | struct timespec tstamp; /* trigger tstamp */ | ||
70 | wait_queue_head_t qchange_sleep; | ||
71 | struct fasync_struct *fasync; | ||
72 | } snd_timer_user_t; | ||
73 | |||
74 | /* list of timers */ | ||
75 | static LIST_HEAD(snd_timer_list); | ||
76 | |||
77 | /* list of slave instances */ | ||
78 | static LIST_HEAD(snd_timer_slave_list); | ||
79 | |||
80 | /* lock for slave active lists */ | ||
81 | static DEFINE_SPINLOCK(slave_active_lock); | ||
82 | |||
83 | static DECLARE_MUTEX(register_mutex); | ||
84 | |||
85 | static int snd_timer_free(snd_timer_t *timer); | ||
86 | static int snd_timer_dev_free(snd_device_t *device); | ||
87 | static int snd_timer_dev_register(snd_device_t *device); | ||
88 | static int snd_timer_dev_unregister(snd_device_t *device); | ||
89 | |||
90 | static void snd_timer_reschedule(snd_timer_t * timer, unsigned long ticks_left); | ||
91 | |||
92 | /* | ||
93 | * create a timer instance with the given owner string. | ||
94 | * when timer is not NULL, increments the module counter | ||
95 | */ | ||
96 | static snd_timer_instance_t *snd_timer_instance_new(char *owner, snd_timer_t *timer) | ||
97 | { | ||
98 | snd_timer_instance_t *timeri; | ||
99 | timeri = kcalloc(1, sizeof(*timeri), GFP_KERNEL); | ||
100 | if (timeri == NULL) | ||
101 | return NULL; | ||
102 | timeri->owner = snd_kmalloc_strdup(owner, GFP_KERNEL); | ||
103 | if (! timeri->owner) { | ||
104 | kfree(timeri); | ||
105 | return NULL; | ||
106 | } | ||
107 | INIT_LIST_HEAD(&timeri->open_list); | ||
108 | INIT_LIST_HEAD(&timeri->active_list); | ||
109 | INIT_LIST_HEAD(&timeri->ack_list); | ||
110 | INIT_LIST_HEAD(&timeri->slave_list_head); | ||
111 | INIT_LIST_HEAD(&timeri->slave_active_head); | ||
112 | |||
113 | timeri->timer = timer; | ||
114 | if (timer && timer->card && !try_module_get(timer->card->module)) { | ||
115 | kfree(timeri->owner); | ||
116 | kfree(timeri); | ||
117 | return NULL; | ||
118 | } | ||
119 | |||
120 | return timeri; | ||
121 | } | ||
122 | |||
123 | /* | ||
124 | * find a timer instance from the given timer id | ||
125 | */ | ||
126 | static snd_timer_t *snd_timer_find(snd_timer_id_t *tid) | ||
127 | { | ||
128 | snd_timer_t *timer = NULL; | ||
129 | struct list_head *p; | ||
130 | |||
131 | list_for_each(p, &snd_timer_list) { | ||
132 | timer = (snd_timer_t *)list_entry(p, snd_timer_t, device_list); | ||
133 | |||
134 | if (timer->tmr_class != tid->dev_class) | ||
135 | continue; | ||
136 | if ((timer->tmr_class == SNDRV_TIMER_CLASS_CARD || | ||
137 | timer->tmr_class == SNDRV_TIMER_CLASS_PCM) && | ||
138 | (timer->card == NULL || | ||
139 | timer->card->number != tid->card)) | ||
140 | continue; | ||
141 | if (timer->tmr_device != tid->device) | ||
142 | continue; | ||
143 | if (timer->tmr_subdevice != tid->subdevice) | ||
144 | continue; | ||
145 | return timer; | ||
146 | } | ||
147 | return NULL; | ||
148 | } | ||
149 | |||
150 | #ifdef CONFIG_KMOD | ||
151 | |||
152 | static void snd_timer_request(snd_timer_id_t *tid) | ||
153 | { | ||
154 | if (! current->fs->root) | ||
155 | return; | ||
156 | switch (tid->dev_class) { | ||
157 | case SNDRV_TIMER_CLASS_GLOBAL: | ||
158 | if (tid->device < timer_limit) | ||
159 | request_module("snd-timer-%i", tid->device); | ||
160 | break; | ||
161 | case SNDRV_TIMER_CLASS_CARD: | ||
162 | case SNDRV_TIMER_CLASS_PCM: | ||
163 | if (tid->card < snd_ecards_limit) | ||
164 | request_module("snd-card-%i", tid->card); | ||
165 | break; | ||
166 | default: | ||
167 | break; | ||
168 | } | ||
169 | } | ||
170 | |||
171 | #endif | ||
172 | |||
173 | /* | ||
174 | * look for a master instance matching with the slave id of the given slave. | ||
175 | * when found, relink the open_link of the slave. | ||
176 | * | ||
177 | * call this with register_mutex down. | ||
178 | */ | ||
179 | static void snd_timer_check_slave(snd_timer_instance_t *slave) | ||
180 | { | ||
181 | snd_timer_t *timer; | ||
182 | snd_timer_instance_t *master; | ||
183 | struct list_head *p, *q; | ||
184 | |||
185 | /* FIXME: it's really dumb to look up all entries.. */ | ||
186 | list_for_each(p, &snd_timer_list) { | ||
187 | timer = (snd_timer_t *)list_entry(p, snd_timer_t, device_list); | ||
188 | list_for_each(q, &timer->open_list_head) { | ||
189 | master = (snd_timer_instance_t *)list_entry(q, snd_timer_instance_t, open_list); | ||
190 | if (slave->slave_class == master->slave_class && | ||
191 | slave->slave_id == master->slave_id) { | ||
192 | list_del(&slave->open_list); | ||
193 | list_add_tail(&slave->open_list, &master->slave_list_head); | ||
194 | spin_lock_irq(&slave_active_lock); | ||
195 | slave->master = master; | ||
196 | slave->timer = master->timer; | ||
197 | spin_unlock_irq(&slave_active_lock); | ||
198 | return; | ||
199 | } | ||
200 | } | ||
201 | } | ||
202 | } | ||
203 | |||
204 | /* | ||
205 | * look for slave instances matching with the slave id of the given master. | ||
206 | * when found, relink the open_link of slaves. | ||
207 | * | ||
208 | * call this with register_mutex down. | ||
209 | */ | ||
210 | static void snd_timer_check_master(snd_timer_instance_t *master) | ||
211 | { | ||
212 | snd_timer_instance_t *slave; | ||
213 | struct list_head *p, *n; | ||
214 | |||
215 | /* check all pending slaves */ | ||
216 | list_for_each_safe(p, n, &snd_timer_slave_list) { | ||
217 | slave = (snd_timer_instance_t *)list_entry(p, snd_timer_instance_t, open_list); | ||
218 | if (slave->slave_class == master->slave_class && | ||
219 | slave->slave_id == master->slave_id) { | ||
220 | list_del(p); | ||
221 | list_add_tail(p, &master->slave_list_head); | ||
222 | spin_lock_irq(&slave_active_lock); | ||
223 | slave->master = master; | ||
224 | slave->timer = master->timer; | ||
225 | if (slave->flags & SNDRV_TIMER_IFLG_RUNNING) | ||
226 | list_add_tail(&slave->active_list, &master->slave_active_head); | ||
227 | spin_unlock_irq(&slave_active_lock); | ||
228 | } | ||
229 | } | ||
230 | } | ||
231 | |||
232 | /* | ||
233 | * open a timer instance | ||
234 | * when opening a master, the slave id must be here given. | ||
235 | */ | ||
236 | int snd_timer_open(snd_timer_instance_t **ti, | ||
237 | char *owner, snd_timer_id_t *tid, | ||
238 | unsigned int slave_id) | ||
239 | { | ||
240 | snd_timer_t *timer; | ||
241 | snd_timer_instance_t *timeri = NULL; | ||
242 | |||
243 | if (tid->dev_class == SNDRV_TIMER_CLASS_SLAVE) { | ||
244 | /* open a slave instance */ | ||
245 | if (tid->dev_sclass <= SNDRV_TIMER_SCLASS_NONE || | ||
246 | tid->dev_sclass > SNDRV_TIMER_SCLASS_OSS_SEQUENCER) { | ||
247 | snd_printd("invalid slave class %i\n", tid->dev_sclass); | ||
248 | return -EINVAL; | ||
249 | } | ||
250 | down(®ister_mutex); | ||
251 | timeri = snd_timer_instance_new(owner, NULL); | ||
252 | timeri->slave_class = tid->dev_sclass; | ||
253 | timeri->slave_id = tid->device; | ||
254 | timeri->flags |= SNDRV_TIMER_IFLG_SLAVE; | ||
255 | list_add_tail(&timeri->open_list, &snd_timer_slave_list); | ||
256 | snd_timer_check_slave(timeri); | ||
257 | up(®ister_mutex); | ||
258 | *ti = timeri; | ||
259 | return 0; | ||
260 | } | ||
261 | |||
262 | /* open a master instance */ | ||
263 | down(®ister_mutex); | ||
264 | timer = snd_timer_find(tid); | ||
265 | #ifdef CONFIG_KMOD | ||
266 | if (timer == NULL) { | ||
267 | up(®ister_mutex); | ||
268 | snd_timer_request(tid); | ||
269 | down(®ister_mutex); | ||
270 | timer = snd_timer_find(tid); | ||
271 | } | ||
272 | #endif | ||
273 | if (timer) { | ||
274 | if (!list_empty(&timer->open_list_head)) { | ||
275 | timeri = (snd_timer_instance_t *)list_entry(timer->open_list_head.next, snd_timer_instance_t, open_list); | ||
276 | if (timeri->flags & SNDRV_TIMER_IFLG_EXCLUSIVE) { | ||
277 | up(®ister_mutex); | ||
278 | return -EBUSY; | ||
279 | } | ||
280 | } | ||
281 | timeri = snd_timer_instance_new(owner, timer); | ||
282 | if (timeri) { | ||
283 | timeri->slave_class = tid->dev_sclass; | ||
284 | timeri->slave_id = slave_id; | ||
285 | if (list_empty(&timer->open_list_head) && timer->hw.open) | ||
286 | timer->hw.open(timer); | ||
287 | list_add_tail(&timeri->open_list, &timer->open_list_head); | ||
288 | snd_timer_check_master(timeri); | ||
289 | } | ||
290 | } else { | ||
291 | up(®ister_mutex); | ||
292 | return -ENODEV; | ||
293 | } | ||
294 | up(®ister_mutex); | ||
295 | *ti = timeri; | ||
296 | return 0; | ||
297 | } | ||
298 | |||
299 | static int _snd_timer_stop(snd_timer_instance_t * timeri, int keep_flag, enum sndrv_timer_event event); | ||
300 | |||
301 | /* | ||
302 | * close a timer instance | ||
303 | */ | ||
304 | int snd_timer_close(snd_timer_instance_t * timeri) | ||
305 | { | ||
306 | snd_timer_t *timer = NULL; | ||
307 | struct list_head *p, *n; | ||
308 | snd_timer_instance_t *slave; | ||
309 | |||
310 | snd_assert(timeri != NULL, return -ENXIO); | ||
311 | |||
312 | /* force to stop the timer */ | ||
313 | snd_timer_stop(timeri); | ||
314 | |||
315 | if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) { | ||
316 | /* wait, until the active callback is finished */ | ||
317 | spin_lock_irq(&slave_active_lock); | ||
318 | while (timeri->flags & SNDRV_TIMER_IFLG_CALLBACK) { | ||
319 | spin_unlock_irq(&slave_active_lock); | ||
320 | udelay(10); | ||
321 | spin_lock_irq(&slave_active_lock); | ||
322 | } | ||
323 | spin_unlock_irq(&slave_active_lock); | ||
324 | down(®ister_mutex); | ||
325 | list_del(&timeri->open_list); | ||
326 | up(®ister_mutex); | ||
327 | } else { | ||
328 | timer = timeri->timer; | ||
329 | /* wait, until the active callback is finished */ | ||
330 | spin_lock_irq(&timer->lock); | ||
331 | while (timeri->flags & SNDRV_TIMER_IFLG_CALLBACK) { | ||
332 | spin_unlock_irq(&timer->lock); | ||
333 | udelay(10); | ||
334 | spin_lock_irq(&timer->lock); | ||
335 | } | ||
336 | spin_unlock_irq(&timer->lock); | ||
337 | down(®ister_mutex); | ||
338 | list_del(&timeri->open_list); | ||
339 | if (timer && list_empty(&timer->open_list_head) && timer->hw.close) | ||
340 | timer->hw.close(timer); | ||
341 | /* remove slave links */ | ||
342 | list_for_each_safe(p, n, &timeri->slave_list_head) { | ||
343 | slave = (snd_timer_instance_t *)list_entry(p, snd_timer_instance_t, open_list); | ||
344 | spin_lock_irq(&slave_active_lock); | ||
345 | _snd_timer_stop(slave, 1, SNDRV_TIMER_EVENT_RESOLUTION); | ||
346 | list_del(p); | ||
347 | list_add_tail(p, &snd_timer_slave_list); | ||
348 | slave->master = NULL; | ||
349 | slave->timer = NULL; | ||
350 | spin_unlock_irq(&slave_active_lock); | ||
351 | } | ||
352 | up(®ister_mutex); | ||
353 | } | ||
354 | if (timeri->private_free) | ||
355 | timeri->private_free(timeri); | ||
356 | kfree(timeri->owner); | ||
357 | kfree(timeri); | ||
358 | if (timer && timer->card) | ||
359 | module_put(timer->card->module); | ||
360 | return 0; | ||
361 | } | ||
362 | |||
363 | unsigned long snd_timer_resolution(snd_timer_instance_t * timeri) | ||
364 | { | ||
365 | snd_timer_t * timer; | ||
366 | |||
367 | if (timeri == NULL) | ||
368 | return 0; | ||
369 | if ((timer = timeri->timer) != NULL) { | ||
370 | if (timer->hw.c_resolution) | ||
371 | return timer->hw.c_resolution(timer); | ||
372 | return timer->hw.resolution; | ||
373 | } | ||
374 | return 0; | ||
375 | } | ||
376 | |||
377 | static void snd_timer_notify1(snd_timer_instance_t *ti, enum sndrv_timer_event event) | ||
378 | { | ||
379 | snd_timer_t *timer; | ||
380 | unsigned long flags; | ||
381 | unsigned long resolution = 0; | ||
382 | snd_timer_instance_t *ts; | ||
383 | struct list_head *n; | ||
384 | struct timespec tstamp; | ||
385 | |||
386 | snd_timestamp_now(&tstamp, 1); | ||
387 | snd_assert(event >= SNDRV_TIMER_EVENT_START && event <= SNDRV_TIMER_EVENT_PAUSE, return); | ||
388 | if (event == SNDRV_TIMER_EVENT_START || event == SNDRV_TIMER_EVENT_CONTINUE) | ||
389 | resolution = snd_timer_resolution(ti); | ||
390 | if (ti->ccallback) | ||
391 | ti->ccallback(ti, SNDRV_TIMER_EVENT_START, &tstamp, resolution); | ||
392 | if (ti->flags & SNDRV_TIMER_IFLG_SLAVE) | ||
393 | return; | ||
394 | timer = ti->timer; | ||
395 | if (timer == NULL) | ||
396 | return; | ||
397 | if (timer->hw.flags & SNDRV_TIMER_HW_SLAVE) | ||
398 | return; | ||
399 | spin_lock_irqsave(&timer->lock, flags); | ||
400 | list_for_each(n, &ti->slave_active_head) { | ||
401 | ts = (snd_timer_instance_t *)list_entry(n, snd_timer_instance_t, active_list); | ||
402 | if (ts->ccallback) | ||
403 | ts->ccallback(ti, event + 100, &tstamp, resolution); | ||
404 | } | ||
405 | spin_unlock_irqrestore(&timer->lock, flags); | ||
406 | } | ||
407 | |||
408 | static int snd_timer_start1(snd_timer_t *timer, snd_timer_instance_t *timeri, unsigned long sticks) | ||
409 | { | ||
410 | list_del(&timeri->active_list); | ||
411 | list_add_tail(&timeri->active_list, &timer->active_list_head); | ||
412 | if (timer->running) { | ||
413 | if (timer->hw.flags & SNDRV_TIMER_HW_SLAVE) | ||
414 | goto __start_now; | ||
415 | timer->flags |= SNDRV_TIMER_FLG_RESCHED; | ||
416 | timeri->flags |= SNDRV_TIMER_IFLG_START; | ||
417 | return 1; /* delayed start */ | ||
418 | } else { | ||
419 | timer->sticks = sticks; | ||
420 | timer->hw.start(timer); | ||
421 | __start_now: | ||
422 | timer->running++; | ||
423 | timeri->flags |= SNDRV_TIMER_IFLG_RUNNING; | ||
424 | return 0; | ||
425 | } | ||
426 | } | ||
427 | |||
428 | static int snd_timer_start_slave(snd_timer_instance_t *timeri) | ||
429 | { | ||
430 | unsigned long flags; | ||
431 | |||
432 | spin_lock_irqsave(&slave_active_lock, flags); | ||
433 | timeri->flags |= SNDRV_TIMER_IFLG_RUNNING; | ||
434 | if (timeri->master) | ||
435 | list_add_tail(&timeri->active_list, &timeri->master->slave_active_head); | ||
436 | spin_unlock_irqrestore(&slave_active_lock, flags); | ||
437 | return 1; /* delayed start */ | ||
438 | } | ||
439 | |||
440 | /* | ||
441 | * start the timer instance | ||
442 | */ | ||
443 | int snd_timer_start(snd_timer_instance_t * timeri, unsigned int ticks) | ||
444 | { | ||
445 | snd_timer_t *timer; | ||
446 | int result = -EINVAL; | ||
447 | unsigned long flags; | ||
448 | |||
449 | if (timeri == NULL || ticks < 1) | ||
450 | return -EINVAL; | ||
451 | if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) { | ||
452 | result = snd_timer_start_slave(timeri); | ||
453 | snd_timer_notify1(timeri, SNDRV_TIMER_EVENT_START); | ||
454 | return result; | ||
455 | } | ||
456 | timer = timeri->timer; | ||
457 | if (timer == NULL) | ||
458 | return -EINVAL; | ||
459 | spin_lock_irqsave(&timer->lock, flags); | ||
460 | timeri->ticks = timeri->cticks = ticks; | ||
461 | timeri->pticks = 0; | ||
462 | result = snd_timer_start1(timer, timeri, ticks); | ||
463 | spin_unlock_irqrestore(&timer->lock, flags); | ||
464 | snd_timer_notify1(timeri, SNDRV_TIMER_EVENT_START); | ||
465 | return result; | ||
466 | } | ||
467 | |||
468 | static int _snd_timer_stop(snd_timer_instance_t * timeri, int keep_flag, enum sndrv_timer_event event) | ||
469 | { | ||
470 | snd_timer_t *timer; | ||
471 | unsigned long flags; | ||
472 | |||
473 | snd_assert(timeri != NULL, return -ENXIO); | ||
474 | |||
475 | if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) { | ||
476 | if (!keep_flag) { | ||
477 | spin_lock_irqsave(&slave_active_lock, flags); | ||
478 | timeri->flags &= ~SNDRV_TIMER_IFLG_RUNNING; | ||
479 | spin_unlock_irqrestore(&slave_active_lock, flags); | ||
480 | } | ||
481 | goto __end; | ||
482 | } | ||
483 | timer = timeri->timer; | ||
484 | if (!timer) | ||
485 | return -EINVAL; | ||
486 | spin_lock_irqsave(&timer->lock, flags); | ||
487 | list_del_init(&timeri->ack_list); | ||
488 | list_del_init(&timeri->active_list); | ||
489 | if ((timeri->flags & SNDRV_TIMER_IFLG_RUNNING) && | ||
490 | !(--timer->running)) { | ||
491 | timer->hw.stop(timer); | ||
492 | if (timer->flags & SNDRV_TIMER_FLG_RESCHED) { | ||
493 | timer->flags &= ~SNDRV_TIMER_FLG_RESCHED; | ||
494 | snd_timer_reschedule(timer, 0); | ||
495 | if (timer->flags & SNDRV_TIMER_FLG_CHANGE) { | ||
496 | timer->flags &= ~SNDRV_TIMER_FLG_CHANGE; | ||
497 | timer->hw.start(timer); | ||
498 | } | ||
499 | } | ||
500 | } | ||
501 | if (!keep_flag) | ||
502 | timeri->flags &= ~(SNDRV_TIMER_IFLG_RUNNING|SNDRV_TIMER_IFLG_START); | ||
503 | spin_unlock_irqrestore(&timer->lock, flags); | ||
504 | __end: | ||
505 | if (event != SNDRV_TIMER_EVENT_RESOLUTION) | ||
506 | snd_timer_notify1(timeri, event); | ||
507 | return 0; | ||
508 | } | ||
509 | |||
510 | /* | ||
511 | * stop the timer instance. | ||
512 | * | ||
513 | * do not call this from the timer callback! | ||
514 | */ | ||
515 | int snd_timer_stop(snd_timer_instance_t * timeri) | ||
516 | { | ||
517 | snd_timer_t *timer; | ||
518 | unsigned long flags; | ||
519 | int err; | ||
520 | |||
521 | err = _snd_timer_stop(timeri, 0, SNDRV_TIMER_EVENT_STOP); | ||
522 | if (err < 0) | ||
523 | return err; | ||
524 | timer = timeri->timer; | ||
525 | spin_lock_irqsave(&timer->lock, flags); | ||
526 | timeri->cticks = timeri->ticks; | ||
527 | timeri->pticks = 0; | ||
528 | spin_unlock_irqrestore(&timer->lock, flags); | ||
529 | return 0; | ||
530 | } | ||
531 | |||
532 | /* | ||
533 | * start again.. the tick is kept. | ||
534 | */ | ||
535 | int snd_timer_continue(snd_timer_instance_t * timeri) | ||
536 | { | ||
537 | snd_timer_t *timer; | ||
538 | int result = -EINVAL; | ||
539 | unsigned long flags; | ||
540 | |||
541 | if (timeri == NULL) | ||
542 | return result; | ||
543 | if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) | ||
544 | return snd_timer_start_slave(timeri); | ||
545 | timer = timeri->timer; | ||
546 | if (! timer) | ||
547 | return -EINVAL; | ||
548 | spin_lock_irqsave(&timer->lock, flags); | ||
549 | if (!timeri->cticks) | ||
550 | timeri->cticks = 1; | ||
551 | timeri->pticks = 0; | ||
552 | result = snd_timer_start1(timer, timeri, timer->sticks); | ||
553 | spin_unlock_irqrestore(&timer->lock, flags); | ||
554 | snd_timer_notify1(timeri, SNDRV_TIMER_EVENT_CONTINUE); | ||
555 | return result; | ||
556 | } | ||
557 | |||
558 | /* | ||
559 | * pause.. remember the ticks left | ||
560 | */ | ||
561 | int snd_timer_pause(snd_timer_instance_t * timeri) | ||
562 | { | ||
563 | return _snd_timer_stop(timeri, 0, SNDRV_TIMER_EVENT_PAUSE); | ||
564 | } | ||
565 | |||
566 | /* | ||
567 | * reschedule the timer | ||
568 | * | ||
569 | * start pending instances and check the scheduling ticks. | ||
570 | * when the scheduling ticks is changed set CHANGE flag to reprogram the timer. | ||
571 | */ | ||
572 | static void snd_timer_reschedule(snd_timer_t * timer, unsigned long ticks_left) | ||
573 | { | ||
574 | snd_timer_instance_t *ti; | ||
575 | unsigned long ticks = ~0UL; | ||
576 | struct list_head *p; | ||
577 | |||
578 | list_for_each(p, &timer->active_list_head) { | ||
579 | ti = (snd_timer_instance_t *)list_entry(p, snd_timer_instance_t, active_list); | ||
580 | if (ti->flags & SNDRV_TIMER_IFLG_START) { | ||
581 | ti->flags &= ~SNDRV_TIMER_IFLG_START; | ||
582 | ti->flags |= SNDRV_TIMER_IFLG_RUNNING; | ||
583 | timer->running++; | ||
584 | } | ||
585 | if (ti->flags & SNDRV_TIMER_IFLG_RUNNING) { | ||
586 | if (ticks > ti->cticks) | ||
587 | ticks = ti->cticks; | ||
588 | } | ||
589 | } | ||
590 | if (ticks == ~0UL) { | ||
591 | timer->flags &= ~SNDRV_TIMER_FLG_RESCHED; | ||
592 | return; | ||
593 | } | ||
594 | if (ticks > timer->hw.ticks) | ||
595 | ticks = timer->hw.ticks; | ||
596 | if (ticks_left != ticks) | ||
597 | timer->flags |= SNDRV_TIMER_FLG_CHANGE; | ||
598 | timer->sticks = ticks; | ||
599 | } | ||
600 | |||
601 | /* | ||
602 | * timer tasklet | ||
603 | * | ||
604 | */ | ||
605 | static void snd_timer_tasklet(unsigned long arg) | ||
606 | { | ||
607 | snd_timer_t *timer = (snd_timer_t *) arg; | ||
608 | snd_timer_instance_t *ti; | ||
609 | struct list_head *p; | ||
610 | unsigned long resolution, ticks; | ||
611 | |||
612 | spin_lock(&timer->lock); | ||
613 | /* now process all callbacks */ | ||
614 | while (!list_empty(&timer->sack_list_head)) { | ||
615 | p = timer->sack_list_head.next; /* get first item */ | ||
616 | ti = (snd_timer_instance_t *)list_entry(p, snd_timer_instance_t, ack_list); | ||
617 | |||
618 | /* remove from ack_list and make empty */ | ||
619 | list_del_init(p); | ||
620 | |||
621 | ticks = ti->pticks; | ||
622 | ti->pticks = 0; | ||
623 | resolution = ti->resolution; | ||
624 | |||
625 | ti->flags |= SNDRV_TIMER_IFLG_CALLBACK; | ||
626 | spin_unlock(&timer->lock); | ||
627 | if (ti->callback) | ||
628 | ti->callback(ti, resolution, ticks); | ||
629 | spin_lock(&timer->lock); | ||
630 | ti->flags &= ~SNDRV_TIMER_IFLG_CALLBACK; | ||
631 | } | ||
632 | spin_unlock(&timer->lock); | ||
633 | } | ||
634 | |||
635 | /* | ||
636 | * timer interrupt | ||
637 | * | ||
638 | * ticks_left is usually equal to timer->sticks. | ||
639 | * | ||
640 | */ | ||
641 | void snd_timer_interrupt(snd_timer_t * timer, unsigned long ticks_left) | ||
642 | { | ||
643 | snd_timer_instance_t *ti, *ts; | ||
644 | unsigned long resolution, ticks; | ||
645 | struct list_head *p, *q, *n; | ||
646 | int use_tasklet = 0; | ||
647 | |||
648 | if (timer == NULL) | ||
649 | return; | ||
650 | |||
651 | spin_lock(&timer->lock); | ||
652 | |||
653 | /* remember the current resolution */ | ||
654 | if (timer->hw.c_resolution) | ||
655 | resolution = timer->hw.c_resolution(timer); | ||
656 | else | ||
657 | resolution = timer->hw.resolution; | ||
658 | |||
659 | /* loop for all active instances | ||
660 | * here we cannot use list_for_each because the active_list of a processed | ||
661 | * instance is relinked to done_list_head before callback is called. | ||
662 | */ | ||
663 | list_for_each_safe(p, n, &timer->active_list_head) { | ||
664 | ti = (snd_timer_instance_t *)list_entry(p, snd_timer_instance_t, active_list); | ||
665 | if (!(ti->flags & SNDRV_TIMER_IFLG_RUNNING)) | ||
666 | continue; | ||
667 | ti->pticks += ticks_left; | ||
668 | ti->resolution = resolution; | ||
669 | if (ti->cticks < ticks_left) | ||
670 | ti->cticks = 0; | ||
671 | else | ||
672 | ti->cticks -= ticks_left; | ||
673 | if (ti->cticks) /* not expired */ | ||
674 | continue; | ||
675 | if (ti->flags & SNDRV_TIMER_IFLG_AUTO) { | ||
676 | ti->cticks = ti->ticks; | ||
677 | } else { | ||
678 | ti->flags &= ~SNDRV_TIMER_IFLG_RUNNING; | ||
679 | if (--timer->running) | ||
680 | list_del(p); | ||
681 | } | ||
682 | if (list_empty(&ti->ack_list)) { | ||
683 | if ((timer->hw.flags & SNDRV_TIMER_HW_TASKLET) || | ||
684 | (ti->flags & SNDRV_TIMER_IFLG_FAST)) { | ||
685 | list_add_tail(&ti->ack_list, &timer->ack_list_head); | ||
686 | } else { | ||
687 | list_add_tail(&ti->ack_list, &timer->sack_list_head); | ||
688 | } | ||
689 | } | ||
690 | list_for_each(q, &ti->slave_active_head) { | ||
691 | ts = (snd_timer_instance_t *)list_entry(q, snd_timer_instance_t, active_list); | ||
692 | ts->pticks = ti->pticks; | ||
693 | ts->resolution = resolution; | ||
694 | if (list_empty(&ts->ack_list)) { | ||
695 | if ((timer->hw.flags & SNDRV_TIMER_HW_TASKLET) || | ||
696 | (ti->flags & SNDRV_TIMER_IFLG_FAST)) { | ||
697 | list_add_tail(&ts->ack_list, &timer->ack_list_head); | ||
698 | } else { | ||
699 | list_add_tail(&ts->ack_list, &timer->sack_list_head); | ||
700 | } | ||
701 | } | ||
702 | } | ||
703 | } | ||
704 | if (timer->flags & SNDRV_TIMER_FLG_RESCHED) | ||
705 | snd_timer_reschedule(timer, ticks_left); | ||
706 | if (timer->running) { | ||
707 | if (timer->hw.flags & SNDRV_TIMER_HW_STOP) { | ||
708 | timer->hw.stop(timer); | ||
709 | timer->flags |= SNDRV_TIMER_FLG_CHANGE; | ||
710 | } | ||
711 | if (!(timer->hw.flags & SNDRV_TIMER_HW_AUTO) || | ||
712 | (timer->flags & SNDRV_TIMER_FLG_CHANGE)) { | ||
713 | /* restart timer */ | ||
714 | timer->flags &= ~SNDRV_TIMER_FLG_CHANGE; | ||
715 | timer->hw.start(timer); | ||
716 | } | ||
717 | } else { | ||
718 | timer->hw.stop(timer); | ||
719 | } | ||
720 | |||
721 | /* now process all fast callbacks */ | ||
722 | while (!list_empty(&timer->ack_list_head)) { | ||
723 | p = timer->ack_list_head.next; /* get first item */ | ||
724 | ti = (snd_timer_instance_t *)list_entry(p, snd_timer_instance_t, ack_list); | ||
725 | |||
726 | /* remove from ack_list and make empty */ | ||
727 | list_del_init(p); | ||
728 | |||
729 | ticks = ti->pticks; | ||
730 | ti->pticks = 0; | ||
731 | |||
732 | ti->flags |= SNDRV_TIMER_IFLG_CALLBACK; | ||
733 | spin_unlock(&timer->lock); | ||
734 | if (ti->callback) | ||
735 | ti->callback(ti, resolution, ticks); | ||
736 | spin_lock(&timer->lock); | ||
737 | ti->flags &= ~SNDRV_TIMER_IFLG_CALLBACK; | ||
738 | } | ||
739 | |||
740 | /* do we have any slow callbacks? */ | ||
741 | use_tasklet = !list_empty(&timer->sack_list_head); | ||
742 | spin_unlock(&timer->lock); | ||
743 | |||
744 | if (use_tasklet) | ||
745 | tasklet_hi_schedule(&timer->task_queue); | ||
746 | } | ||
747 | |||
748 | /* | ||
749 | |||
750 | */ | ||
751 | |||
752 | int snd_timer_new(snd_card_t *card, char *id, snd_timer_id_t *tid, snd_timer_t ** rtimer) | ||
753 | { | ||
754 | snd_timer_t *timer; | ||
755 | int err; | ||
756 | static snd_device_ops_t ops = { | ||
757 | .dev_free = snd_timer_dev_free, | ||
758 | .dev_register = snd_timer_dev_register, | ||
759 | .dev_unregister = snd_timer_dev_unregister | ||
760 | }; | ||
761 | |||
762 | snd_assert(tid != NULL, return -EINVAL); | ||
763 | snd_assert(rtimer != NULL, return -EINVAL); | ||
764 | *rtimer = NULL; | ||
765 | timer = kcalloc(1, sizeof(*timer), GFP_KERNEL); | ||
766 | if (timer == NULL) | ||
767 | return -ENOMEM; | ||
768 | timer->tmr_class = tid->dev_class; | ||
769 | timer->card = card; | ||
770 | timer->tmr_device = tid->device; | ||
771 | timer->tmr_subdevice = tid->subdevice; | ||
772 | if (id) | ||
773 | strlcpy(timer->id, id, sizeof(timer->id)); | ||
774 | INIT_LIST_HEAD(&timer->device_list); | ||
775 | INIT_LIST_HEAD(&timer->open_list_head); | ||
776 | INIT_LIST_HEAD(&timer->active_list_head); | ||
777 | INIT_LIST_HEAD(&timer->ack_list_head); | ||
778 | INIT_LIST_HEAD(&timer->sack_list_head); | ||
779 | spin_lock_init(&timer->lock); | ||
780 | tasklet_init(&timer->task_queue, snd_timer_tasklet, (unsigned long)timer); | ||
781 | if (card != NULL) { | ||
782 | if ((err = snd_device_new(card, SNDRV_DEV_TIMER, timer, &ops)) < 0) { | ||
783 | snd_timer_free(timer); | ||
784 | return err; | ||
785 | } | ||
786 | } | ||
787 | *rtimer = timer; | ||
788 | return 0; | ||
789 | } | ||
790 | |||
791 | static int snd_timer_free(snd_timer_t *timer) | ||
792 | { | ||
793 | snd_assert(timer != NULL, return -ENXIO); | ||
794 | if (timer->private_free) | ||
795 | timer->private_free(timer); | ||
796 | kfree(timer); | ||
797 | return 0; | ||
798 | } | ||
799 | |||
800 | int snd_timer_dev_free(snd_device_t *device) | ||
801 | { | ||
802 | snd_timer_t *timer = device->device_data; | ||
803 | return snd_timer_free(timer); | ||
804 | } | ||
805 | |||
806 | int snd_timer_dev_register(snd_device_t *dev) | ||
807 | { | ||
808 | snd_timer_t *timer = dev->device_data; | ||
809 | snd_timer_t *timer1; | ||
810 | struct list_head *p; | ||
811 | |||
812 | snd_assert(timer != NULL && timer->hw.start != NULL && timer->hw.stop != NULL, return -ENXIO); | ||
813 | if (!(timer->hw.flags & SNDRV_TIMER_HW_SLAVE) && | ||
814 | !timer->hw.resolution && timer->hw.c_resolution == NULL) | ||
815 | return -EINVAL; | ||
816 | |||
817 | down(®ister_mutex); | ||
818 | list_for_each(p, &snd_timer_list) { | ||
819 | timer1 = (snd_timer_t *)list_entry(p, snd_timer_t, device_list); | ||
820 | if (timer1->tmr_class > timer->tmr_class) | ||
821 | break; | ||
822 | if (timer1->tmr_class < timer->tmr_class) | ||
823 | continue; | ||
824 | if (timer1->card && timer->card) { | ||
825 | if (timer1->card->number > timer->card->number) | ||
826 | break; | ||
827 | if (timer1->card->number < timer->card->number) | ||
828 | continue; | ||
829 | } | ||
830 | if (timer1->tmr_device > timer->tmr_device) | ||
831 | break; | ||
832 | if (timer1->tmr_device < timer->tmr_device) | ||
833 | continue; | ||
834 | if (timer1->tmr_subdevice > timer->tmr_subdevice) | ||
835 | break; | ||
836 | if (timer1->tmr_subdevice < timer->tmr_subdevice) | ||
837 | continue; | ||
838 | /* conflicts.. */ | ||
839 | up(®ister_mutex); | ||
840 | return -EBUSY; | ||
841 | } | ||
842 | list_add_tail(&timer->device_list, p); | ||
843 | up(®ister_mutex); | ||
844 | return 0; | ||
845 | } | ||
846 | |||
847 | int snd_timer_unregister(snd_timer_t *timer) | ||
848 | { | ||
849 | struct list_head *p, *n; | ||
850 | snd_timer_instance_t *ti; | ||
851 | |||
852 | snd_assert(timer != NULL, return -ENXIO); | ||
853 | down(®ister_mutex); | ||
854 | if (! list_empty(&timer->open_list_head)) { | ||
855 | snd_printk(KERN_WARNING "timer 0x%lx is busy?\n", (long)timer); | ||
856 | list_for_each_safe(p, n, &timer->open_list_head) { | ||
857 | list_del_init(p); | ||
858 | ti = (snd_timer_instance_t *)list_entry(p, snd_timer_instance_t, open_list); | ||
859 | ti->timer = NULL; | ||
860 | } | ||
861 | } | ||
862 | list_del(&timer->device_list); | ||
863 | up(®ister_mutex); | ||
864 | return snd_timer_free(timer); | ||
865 | } | ||
866 | |||
867 | static int snd_timer_dev_unregister(snd_device_t *device) | ||
868 | { | ||
869 | snd_timer_t *timer = device->device_data; | ||
870 | return snd_timer_unregister(timer); | ||
871 | } | ||
872 | |||
873 | void snd_timer_notify(snd_timer_t *timer, enum sndrv_timer_event event, struct timespec *tstamp) | ||
874 | { | ||
875 | unsigned long flags; | ||
876 | unsigned long resolution = 0; | ||
877 | snd_timer_instance_t *ti, *ts; | ||
878 | struct list_head *p, *n; | ||
879 | |||
880 | snd_runtime_check(timer->hw.flags & SNDRV_TIMER_HW_SLAVE, return); | ||
881 | snd_assert(event >= SNDRV_TIMER_EVENT_MSTART && event <= SNDRV_TIMER_EVENT_MPAUSE, return); | ||
882 | spin_lock_irqsave(&timer->lock, flags); | ||
883 | if (event == SNDRV_TIMER_EVENT_MSTART || event == SNDRV_TIMER_EVENT_MCONTINUE) { | ||
884 | if (timer->hw.c_resolution) | ||
885 | resolution = timer->hw.c_resolution(timer); | ||
886 | else | ||
887 | resolution = timer->hw.resolution; | ||
888 | } | ||
889 | list_for_each(p, &timer->active_list_head) { | ||
890 | ti = (snd_timer_instance_t *)list_entry(p, snd_timer_instance_t, active_list); | ||
891 | if (ti->ccallback) | ||
892 | ti->ccallback(ti, event, tstamp, resolution); | ||
893 | list_for_each(n, &ti->slave_active_head) { | ||
894 | ts = (snd_timer_instance_t *)list_entry(n, snd_timer_instance_t, active_list); | ||
895 | if (ts->ccallback) | ||
896 | ts->ccallback(ts, event, tstamp, resolution); | ||
897 | } | ||
898 | } | ||
899 | spin_unlock_irqrestore(&timer->lock, flags); | ||
900 | } | ||
901 | |||
902 | /* | ||
903 | * exported functions for global timers | ||
904 | */ | ||
905 | int snd_timer_global_new(char *id, int device, snd_timer_t **rtimer) | ||
906 | { | ||
907 | snd_timer_id_t tid; | ||
908 | |||
909 | tid.dev_class = SNDRV_TIMER_CLASS_GLOBAL; | ||
910 | tid.dev_sclass = SNDRV_TIMER_SCLASS_NONE; | ||
911 | tid.card = -1; | ||
912 | tid.device = device; | ||
913 | tid.subdevice = 0; | ||
914 | return snd_timer_new(NULL, id, &tid, rtimer); | ||
915 | } | ||
916 | |||
917 | int snd_timer_global_free(snd_timer_t *timer) | ||
918 | { | ||
919 | return snd_timer_free(timer); | ||
920 | } | ||
921 | |||
922 | int snd_timer_global_register(snd_timer_t *timer) | ||
923 | { | ||
924 | snd_device_t dev; | ||
925 | |||
926 | memset(&dev, 0, sizeof(dev)); | ||
927 | dev.device_data = timer; | ||
928 | return snd_timer_dev_register(&dev); | ||
929 | } | ||
930 | |||
931 | int snd_timer_global_unregister(snd_timer_t *timer) | ||
932 | { | ||
933 | return snd_timer_unregister(timer); | ||
934 | } | ||
935 | |||
936 | /* | ||
937 | * System timer | ||
938 | */ | ||
939 | |||
940 | struct snd_timer_system_private { | ||
941 | struct timer_list tlist; | ||
942 | struct timer * timer; | ||
943 | unsigned long last_expires; | ||
944 | unsigned long last_jiffies; | ||
945 | unsigned long correction; | ||
946 | }; | ||
947 | |||
948 | unsigned int snd_timer_system_resolution(void) | ||
949 | { | ||
950 | return 1000000000L / HZ; | ||
951 | } | ||
952 | |||
953 | static void snd_timer_s_function(unsigned long data) | ||
954 | { | ||
955 | snd_timer_t *timer = (snd_timer_t *)data; | ||
956 | struct snd_timer_system_private *priv = timer->private_data; | ||
957 | unsigned long jiff = jiffies; | ||
958 | if (time_after(jiff, priv->last_expires)) | ||
959 | priv->correction = (long)jiff - (long)priv->last_expires; | ||
960 | snd_timer_interrupt(timer, (long)jiff - (long)priv->last_jiffies); | ||
961 | } | ||
962 | |||
963 | static int snd_timer_s_start(snd_timer_t * timer) | ||
964 | { | ||
965 | struct snd_timer_system_private *priv; | ||
966 | unsigned long njiff; | ||
967 | |||
968 | priv = (struct snd_timer_system_private *) timer->private_data; | ||
969 | njiff = (priv->last_jiffies = jiffies); | ||
970 | if (priv->correction > timer->sticks - 1) { | ||
971 | priv->correction -= timer->sticks - 1; | ||
972 | njiff++; | ||
973 | } else { | ||
974 | njiff += timer->sticks - priv->correction; | ||
975 | priv->correction -= timer->sticks; | ||
976 | } | ||
977 | priv->last_expires = priv->tlist.expires = njiff; | ||
978 | add_timer(&priv->tlist); | ||
979 | return 0; | ||
980 | } | ||
981 | |||
982 | static int snd_timer_s_stop(snd_timer_t * timer) | ||
983 | { | ||
984 | struct snd_timer_system_private *priv; | ||
985 | unsigned long jiff; | ||
986 | |||
987 | priv = (struct snd_timer_system_private *) timer->private_data; | ||
988 | del_timer(&priv->tlist); | ||
989 | jiff = jiffies; | ||
990 | if (time_before(jiff, priv->last_expires)) | ||
991 | timer->sticks = priv->last_expires - jiff; | ||
992 | else | ||
993 | timer->sticks = 1; | ||
994 | return 0; | ||
995 | } | ||
996 | |||
997 | static struct _snd_timer_hardware snd_timer_system = | ||
998 | { | ||
999 | .flags = SNDRV_TIMER_HW_FIRST | SNDRV_TIMER_HW_TASKLET, | ||
1000 | .resolution = 1000000000L / HZ, | ||
1001 | .ticks = 10000000L, | ||
1002 | .start = snd_timer_s_start, | ||
1003 | .stop = snd_timer_s_stop | ||
1004 | }; | ||
1005 | |||
1006 | static void snd_timer_free_system(snd_timer_t *timer) | ||
1007 | { | ||
1008 | kfree(timer->private_data); | ||
1009 | } | ||
1010 | |||
1011 | static int snd_timer_register_system(void) | ||
1012 | { | ||
1013 | snd_timer_t *timer; | ||
1014 | struct snd_timer_system_private *priv; | ||
1015 | int err; | ||
1016 | |||
1017 | if ((err = snd_timer_global_new("system", SNDRV_TIMER_GLOBAL_SYSTEM, &timer)) < 0) | ||
1018 | return err; | ||
1019 | strcpy(timer->name, "system timer"); | ||
1020 | timer->hw = snd_timer_system; | ||
1021 | priv = kcalloc(1, sizeof(*priv), GFP_KERNEL); | ||
1022 | if (priv == NULL) { | ||
1023 | snd_timer_free(timer); | ||
1024 | return -ENOMEM; | ||
1025 | } | ||
1026 | init_timer(&priv->tlist); | ||
1027 | priv->tlist.function = snd_timer_s_function; | ||
1028 | priv->tlist.data = (unsigned long) timer; | ||
1029 | timer->private_data = priv; | ||
1030 | timer->private_free = snd_timer_free_system; | ||
1031 | return snd_timer_global_register(timer); | ||
1032 | } | ||
1033 | |||
1034 | /* | ||
1035 | * Info interface | ||
1036 | */ | ||
1037 | |||
1038 | static void snd_timer_proc_read(snd_info_entry_t *entry, | ||
1039 | snd_info_buffer_t * buffer) | ||
1040 | { | ||
1041 | unsigned long flags; | ||
1042 | snd_timer_t *timer; | ||
1043 | snd_timer_instance_t *ti; | ||
1044 | struct list_head *p, *q; | ||
1045 | |||
1046 | down(®ister_mutex); | ||
1047 | list_for_each(p, &snd_timer_list) { | ||
1048 | timer = (snd_timer_t *)list_entry(p, snd_timer_t, device_list); | ||
1049 | switch (timer->tmr_class) { | ||
1050 | case SNDRV_TIMER_CLASS_GLOBAL: | ||
1051 | snd_iprintf(buffer, "G%i: ", timer->tmr_device); | ||
1052 | break; | ||
1053 | case SNDRV_TIMER_CLASS_CARD: | ||
1054 | snd_iprintf(buffer, "C%i-%i: ", timer->card->number, timer->tmr_device); | ||
1055 | break; | ||
1056 | case SNDRV_TIMER_CLASS_PCM: | ||
1057 | snd_iprintf(buffer, "P%i-%i-%i: ", timer->card->number, timer->tmr_device, timer->tmr_subdevice); | ||
1058 | break; | ||
1059 | default: | ||
1060 | snd_iprintf(buffer, "?%i-%i-%i-%i: ", timer->tmr_class, timer->card ? timer->card->number : -1, timer->tmr_device, timer->tmr_subdevice); | ||
1061 | } | ||
1062 | snd_iprintf(buffer, "%s :", timer->name); | ||
1063 | if (timer->hw.resolution) | ||
1064 | snd_iprintf(buffer, " %lu.%03luus (%lu ticks)", timer->hw.resolution / 1000, timer->hw.resolution % 1000, timer->hw.ticks); | ||
1065 | if (timer->hw.flags & SNDRV_TIMER_HW_SLAVE) | ||
1066 | snd_iprintf(buffer, " SLAVE"); | ||
1067 | snd_iprintf(buffer, "\n"); | ||
1068 | spin_lock_irqsave(&timer->lock, flags); | ||
1069 | list_for_each(q, &timer->open_list_head) { | ||
1070 | ti = (snd_timer_instance_t *)list_entry(q, snd_timer_instance_t, open_list); | ||
1071 | snd_iprintf(buffer, " Client %s : %s : lost interrupts %li\n", | ||
1072 | ti->owner ? ti->owner : "unknown", | ||
1073 | ti->flags & (SNDRV_TIMER_IFLG_START|SNDRV_TIMER_IFLG_RUNNING) ? "running" : "stopped", | ||
1074 | ti->lost); | ||
1075 | } | ||
1076 | spin_unlock_irqrestore(&timer->lock, flags); | ||
1077 | } | ||
1078 | up(®ister_mutex); | ||
1079 | } | ||
1080 | |||
1081 | /* | ||
1082 | * USER SPACE interface | ||
1083 | */ | ||
1084 | |||
1085 | static void snd_timer_user_interrupt(snd_timer_instance_t *timeri, | ||
1086 | unsigned long resolution, | ||
1087 | unsigned long ticks) | ||
1088 | { | ||
1089 | snd_timer_user_t *tu = timeri->callback_data; | ||
1090 | snd_timer_read_t *r; | ||
1091 | int prev; | ||
1092 | |||
1093 | spin_lock(&tu->qlock); | ||
1094 | if (tu->qused > 0) { | ||
1095 | prev = tu->qtail == 0 ? tu->queue_size - 1 : tu->qtail - 1; | ||
1096 | r = &tu->queue[prev]; | ||
1097 | if (r->resolution == resolution) { | ||
1098 | r->ticks += ticks; | ||
1099 | goto __wake; | ||
1100 | } | ||
1101 | } | ||
1102 | if (tu->qused >= tu->queue_size) { | ||
1103 | tu->overrun++; | ||
1104 | } else { | ||
1105 | r = &tu->queue[tu->qtail++]; | ||
1106 | tu->qtail %= tu->queue_size; | ||
1107 | r->resolution = resolution; | ||
1108 | r->ticks = ticks; | ||
1109 | tu->qused++; | ||
1110 | } | ||
1111 | __wake: | ||
1112 | spin_unlock(&tu->qlock); | ||
1113 | kill_fasync(&tu->fasync, SIGIO, POLL_IN); | ||
1114 | wake_up(&tu->qchange_sleep); | ||
1115 | } | ||
1116 | |||
1117 | static void snd_timer_user_append_to_tqueue(snd_timer_user_t *tu, snd_timer_tread_t *tread) | ||
1118 | { | ||
1119 | if (tu->qused >= tu->queue_size) { | ||
1120 | tu->overrun++; | ||
1121 | } else { | ||
1122 | memcpy(&tu->tqueue[tu->qtail++], tread, sizeof(*tread)); | ||
1123 | tu->qtail %= tu->queue_size; | ||
1124 | tu->qused++; | ||
1125 | } | ||
1126 | } | ||
1127 | |||
1128 | static void snd_timer_user_ccallback(snd_timer_instance_t *timeri, | ||
1129 | enum sndrv_timer_event event, | ||
1130 | struct timespec *tstamp, | ||
1131 | unsigned long resolution) | ||
1132 | { | ||
1133 | snd_timer_user_t *tu = timeri->callback_data; | ||
1134 | snd_timer_tread_t r1; | ||
1135 | |||
1136 | if (event >= SNDRV_TIMER_EVENT_START && event <= SNDRV_TIMER_EVENT_PAUSE) | ||
1137 | tu->tstamp = *tstamp; | ||
1138 | if ((tu->filter & (1 << event)) == 0 || !tu->tread) | ||
1139 | return; | ||
1140 | r1.event = event; | ||
1141 | r1.tstamp = *tstamp; | ||
1142 | r1.val = resolution; | ||
1143 | spin_lock(&tu->qlock); | ||
1144 | snd_timer_user_append_to_tqueue(tu, &r1); | ||
1145 | spin_unlock(&tu->qlock); | ||
1146 | kill_fasync(&tu->fasync, SIGIO, POLL_IN); | ||
1147 | wake_up(&tu->qchange_sleep); | ||
1148 | } | ||
1149 | |||
1150 | static void snd_timer_user_tinterrupt(snd_timer_instance_t *timeri, | ||
1151 | unsigned long resolution, | ||
1152 | unsigned long ticks) | ||
1153 | { | ||
1154 | snd_timer_user_t *tu = timeri->callback_data; | ||
1155 | snd_timer_tread_t *r, r1; | ||
1156 | struct timespec tstamp; | ||
1157 | int prev, append = 0; | ||
1158 | |||
1159 | snd_timestamp_zero(&tstamp); | ||
1160 | spin_lock(&tu->qlock); | ||
1161 | if ((tu->filter & ((1 << SNDRV_TIMER_EVENT_RESOLUTION)|(1 << SNDRV_TIMER_EVENT_TICK))) == 0) { | ||
1162 | spin_unlock(&tu->qlock); | ||
1163 | return; | ||
1164 | } | ||
1165 | if (tu->last_resolution != resolution || ticks > 0) | ||
1166 | snd_timestamp_now(&tstamp, 1); | ||
1167 | if ((tu->filter & (1 << SNDRV_TIMER_EVENT_RESOLUTION)) && tu->last_resolution != resolution) { | ||
1168 | r1.event = SNDRV_TIMER_EVENT_RESOLUTION; | ||
1169 | r1.tstamp = tstamp; | ||
1170 | r1.val = resolution; | ||
1171 | snd_timer_user_append_to_tqueue(tu, &r1); | ||
1172 | tu->last_resolution = resolution; | ||
1173 | append++; | ||
1174 | } | ||
1175 | if ((tu->filter & (1 << SNDRV_TIMER_EVENT_TICK)) == 0) | ||
1176 | goto __wake; | ||
1177 | if (ticks == 0) | ||
1178 | goto __wake; | ||
1179 | if (tu->qused > 0) { | ||
1180 | prev = tu->qtail == 0 ? tu->queue_size - 1 : tu->qtail - 1; | ||
1181 | r = &tu->tqueue[prev]; | ||
1182 | if (r->event == SNDRV_TIMER_EVENT_TICK) { | ||
1183 | r->tstamp = tstamp; | ||
1184 | r->val += ticks; | ||
1185 | append++; | ||
1186 | goto __wake; | ||
1187 | } | ||
1188 | } | ||
1189 | r1.event = SNDRV_TIMER_EVENT_TICK; | ||
1190 | r1.tstamp = tstamp; | ||
1191 | r1.val = ticks; | ||
1192 | snd_timer_user_append_to_tqueue(tu, &r1); | ||
1193 | append++; | ||
1194 | __wake: | ||
1195 | spin_unlock(&tu->qlock); | ||
1196 | if (append == 0) | ||
1197 | return; | ||
1198 | kill_fasync(&tu->fasync, SIGIO, POLL_IN); | ||
1199 | wake_up(&tu->qchange_sleep); | ||
1200 | } | ||
1201 | |||
1202 | static int snd_timer_user_open(struct inode *inode, struct file *file) | ||
1203 | { | ||
1204 | snd_timer_user_t *tu; | ||
1205 | |||
1206 | tu = kcalloc(1, sizeof(*tu), GFP_KERNEL); | ||
1207 | if (tu == NULL) | ||
1208 | return -ENOMEM; | ||
1209 | spin_lock_init(&tu->qlock); | ||
1210 | init_waitqueue_head(&tu->qchange_sleep); | ||
1211 | tu->ticks = 1; | ||
1212 | tu->queue_size = 128; | ||
1213 | tu->queue = (snd_timer_read_t *)kmalloc(tu->queue_size * sizeof(snd_timer_read_t), GFP_KERNEL); | ||
1214 | if (tu->queue == NULL) { | ||
1215 | kfree(tu); | ||
1216 | return -ENOMEM; | ||
1217 | } | ||
1218 | file->private_data = tu; | ||
1219 | return 0; | ||
1220 | } | ||
1221 | |||
1222 | static int snd_timer_user_release(struct inode *inode, struct file *file) | ||
1223 | { | ||
1224 | snd_timer_user_t *tu; | ||
1225 | |||
1226 | if (file->private_data) { | ||
1227 | tu = file->private_data; | ||
1228 | file->private_data = NULL; | ||
1229 | fasync_helper(-1, file, 0, &tu->fasync); | ||
1230 | if (tu->timeri) | ||
1231 | snd_timer_close(tu->timeri); | ||
1232 | kfree(tu->queue); | ||
1233 | kfree(tu->tqueue); | ||
1234 | kfree(tu); | ||
1235 | } | ||
1236 | return 0; | ||
1237 | } | ||
1238 | |||
1239 | static void snd_timer_user_zero_id(snd_timer_id_t *id) | ||
1240 | { | ||
1241 | id->dev_class = SNDRV_TIMER_CLASS_NONE; | ||
1242 | id->dev_sclass = SNDRV_TIMER_SCLASS_NONE; | ||
1243 | id->card = -1; | ||
1244 | id->device = -1; | ||
1245 | id->subdevice = -1; | ||
1246 | } | ||
1247 | |||
1248 | static void snd_timer_user_copy_id(snd_timer_id_t *id, snd_timer_t *timer) | ||
1249 | { | ||
1250 | id->dev_class = timer->tmr_class; | ||
1251 | id->dev_sclass = SNDRV_TIMER_SCLASS_NONE; | ||
1252 | id->card = timer->card ? timer->card->number : -1; | ||
1253 | id->device = timer->tmr_device; | ||
1254 | id->subdevice = timer->tmr_subdevice; | ||
1255 | } | ||
1256 | |||
1257 | static int snd_timer_user_next_device(snd_timer_id_t __user *_tid) | ||
1258 | { | ||
1259 | snd_timer_id_t id; | ||
1260 | snd_timer_t *timer; | ||
1261 | struct list_head *p; | ||
1262 | |||
1263 | if (copy_from_user(&id, _tid, sizeof(id))) | ||
1264 | return -EFAULT; | ||
1265 | down(®ister_mutex); | ||
1266 | if (id.dev_class < 0) { /* first item */ | ||
1267 | if (list_empty(&snd_timer_list)) | ||
1268 | snd_timer_user_zero_id(&id); | ||
1269 | else { | ||
1270 | timer = (snd_timer_t *)list_entry(snd_timer_list.next, snd_timer_t, device_list); | ||
1271 | snd_timer_user_copy_id(&id, timer); | ||
1272 | } | ||
1273 | } else { | ||
1274 | switch (id.dev_class) { | ||
1275 | case SNDRV_TIMER_CLASS_GLOBAL: | ||
1276 | id.device = id.device < 0 ? 0 : id.device + 1; | ||
1277 | list_for_each(p, &snd_timer_list) { | ||
1278 | timer = (snd_timer_t *)list_entry(p, snd_timer_t, device_list); | ||
1279 | if (timer->tmr_class > SNDRV_TIMER_CLASS_GLOBAL) { | ||
1280 | snd_timer_user_copy_id(&id, timer); | ||
1281 | break; | ||
1282 | } | ||
1283 | if (timer->tmr_device >= id.device) { | ||
1284 | snd_timer_user_copy_id(&id, timer); | ||
1285 | break; | ||
1286 | } | ||
1287 | } | ||
1288 | if (p == &snd_timer_list) | ||
1289 | snd_timer_user_zero_id(&id); | ||
1290 | break; | ||
1291 | case SNDRV_TIMER_CLASS_CARD: | ||
1292 | case SNDRV_TIMER_CLASS_PCM: | ||
1293 | if (id.card < 0) { | ||
1294 | id.card = 0; | ||
1295 | } else { | ||
1296 | if (id.card < 0) { | ||
1297 | id.card = 0; | ||
1298 | } else { | ||
1299 | if (id.device < 0) { | ||
1300 | id.device = 0; | ||
1301 | } else { | ||
1302 | id.subdevice = id.subdevice < 0 ? 0 : id.subdevice + 1; | ||
1303 | } | ||
1304 | } | ||
1305 | } | ||
1306 | list_for_each(p, &snd_timer_list) { | ||
1307 | timer = (snd_timer_t *)list_entry(p, snd_timer_t, device_list); | ||
1308 | if (timer->tmr_class > id.dev_class) { | ||
1309 | snd_timer_user_copy_id(&id, timer); | ||
1310 | break; | ||
1311 | } | ||
1312 | if (timer->tmr_class < id.dev_class) | ||
1313 | continue; | ||
1314 | if (timer->card->number > id.card) { | ||
1315 | snd_timer_user_copy_id(&id, timer); | ||
1316 | break; | ||
1317 | } | ||
1318 | if (timer->card->number < id.card) | ||
1319 | continue; | ||
1320 | if (timer->tmr_device > id.device) { | ||
1321 | snd_timer_user_copy_id(&id, timer); | ||
1322 | break; | ||
1323 | } | ||
1324 | if (timer->tmr_device < id.device) | ||
1325 | continue; | ||
1326 | if (timer->tmr_subdevice > id.subdevice) { | ||
1327 | snd_timer_user_copy_id(&id, timer); | ||
1328 | break; | ||
1329 | } | ||
1330 | if (timer->tmr_subdevice < id.subdevice) | ||
1331 | continue; | ||
1332 | snd_timer_user_copy_id(&id, timer); | ||
1333 | break; | ||
1334 | } | ||
1335 | if (p == &snd_timer_list) | ||
1336 | snd_timer_user_zero_id(&id); | ||
1337 | break; | ||
1338 | default: | ||
1339 | snd_timer_user_zero_id(&id); | ||
1340 | } | ||
1341 | } | ||
1342 | up(®ister_mutex); | ||
1343 | if (copy_to_user(_tid, &id, sizeof(*_tid))) | ||
1344 | return -EFAULT; | ||
1345 | return 0; | ||
1346 | } | ||
1347 | |||
1348 | static int snd_timer_user_ginfo(struct file *file, snd_timer_ginfo_t __user *_ginfo) | ||
1349 | { | ||
1350 | snd_timer_ginfo_t *ginfo; | ||
1351 | snd_timer_id_t tid; | ||
1352 | snd_timer_t *t; | ||
1353 | struct list_head *p; | ||
1354 | int err = 0; | ||
1355 | |||
1356 | ginfo = kmalloc(sizeof(*ginfo), GFP_KERNEL); | ||
1357 | if (! ginfo) | ||
1358 | return -ENOMEM; | ||
1359 | if (copy_from_user(ginfo, _ginfo, sizeof(*ginfo))) { | ||
1360 | kfree(ginfo); | ||
1361 | return -EFAULT; | ||
1362 | } | ||
1363 | tid = ginfo->tid; | ||
1364 | memset(ginfo, 0, sizeof(*ginfo)); | ||
1365 | ginfo->tid = tid; | ||
1366 | down(®ister_mutex); | ||
1367 | t = snd_timer_find(&tid); | ||
1368 | if (t != NULL) { | ||
1369 | ginfo->card = t->card ? t->card->number : -1; | ||
1370 | if (t->hw.flags & SNDRV_TIMER_HW_SLAVE) | ||
1371 | ginfo->flags |= SNDRV_TIMER_FLG_SLAVE; | ||
1372 | strlcpy(ginfo->id, t->id, sizeof(ginfo->id)); | ||
1373 | strlcpy(ginfo->name, t->name, sizeof(ginfo->name)); | ||
1374 | ginfo->resolution = t->hw.resolution; | ||
1375 | if (t->hw.resolution_min > 0) { | ||
1376 | ginfo->resolution_min = t->hw.resolution_min; | ||
1377 | ginfo->resolution_max = t->hw.resolution_max; | ||
1378 | } | ||
1379 | list_for_each(p, &t->open_list_head) { | ||
1380 | ginfo->clients++; | ||
1381 | } | ||
1382 | } else { | ||
1383 | err = -ENODEV; | ||
1384 | } | ||
1385 | up(®ister_mutex); | ||
1386 | if (err >= 0 && copy_to_user(_ginfo, ginfo, sizeof(*ginfo))) | ||
1387 | err = -EFAULT; | ||
1388 | kfree(ginfo); | ||
1389 | return err; | ||
1390 | } | ||
1391 | |||
1392 | static int snd_timer_user_gparams(struct file *file, snd_timer_gparams_t __user *_gparams) | ||
1393 | { | ||
1394 | snd_timer_gparams_t gparams; | ||
1395 | snd_timer_t *t; | ||
1396 | int err; | ||
1397 | |||
1398 | if (copy_from_user(&gparams, _gparams, sizeof(gparams))) | ||
1399 | return -EFAULT; | ||
1400 | down(®ister_mutex); | ||
1401 | t = snd_timer_find(&gparams.tid); | ||
1402 | if (t != NULL) { | ||
1403 | if (list_empty(&t->open_list_head)) { | ||
1404 | if (t->hw.set_period) | ||
1405 | err = t->hw.set_period(t, gparams.period_num, gparams.period_den); | ||
1406 | else | ||
1407 | err = -ENOSYS; | ||
1408 | } else { | ||
1409 | err = -EBUSY; | ||
1410 | } | ||
1411 | } else { | ||
1412 | err = -ENODEV; | ||
1413 | } | ||
1414 | up(®ister_mutex); | ||
1415 | return err; | ||
1416 | } | ||
1417 | |||
1418 | static int snd_timer_user_gstatus(struct file *file, snd_timer_gstatus_t __user *_gstatus) | ||
1419 | { | ||
1420 | snd_timer_gstatus_t gstatus; | ||
1421 | snd_timer_id_t tid; | ||
1422 | snd_timer_t *t; | ||
1423 | int err = 0; | ||
1424 | |||
1425 | if (copy_from_user(&gstatus, _gstatus, sizeof(gstatus))) | ||
1426 | return -EFAULT; | ||
1427 | tid = gstatus.tid; | ||
1428 | memset(&gstatus, 0, sizeof(gstatus)); | ||
1429 | gstatus.tid = tid; | ||
1430 | down(®ister_mutex); | ||
1431 | t = snd_timer_find(&tid); | ||
1432 | if (t != NULL) { | ||
1433 | if (t->hw.c_resolution) | ||
1434 | gstatus.resolution = t->hw.c_resolution(t); | ||
1435 | else | ||
1436 | gstatus.resolution = t->hw.resolution; | ||
1437 | if (t->hw.precise_resolution) { | ||
1438 | t->hw.precise_resolution(t, &gstatus.resolution_num, &gstatus.resolution_den); | ||
1439 | } else { | ||
1440 | gstatus.resolution_num = gstatus.resolution; | ||
1441 | gstatus.resolution_den = 1000000000uL; | ||
1442 | } | ||
1443 | } else { | ||
1444 | err = -ENODEV; | ||
1445 | } | ||
1446 | up(®ister_mutex); | ||
1447 | if (err >= 0 && copy_to_user(_gstatus, &gstatus, sizeof(gstatus))) | ||
1448 | err = -EFAULT; | ||
1449 | return err; | ||
1450 | } | ||
1451 | |||
1452 | static int snd_timer_user_tselect(struct file *file, snd_timer_select_t __user *_tselect) | ||
1453 | { | ||
1454 | snd_timer_user_t *tu; | ||
1455 | snd_timer_select_t tselect; | ||
1456 | char str[32]; | ||
1457 | int err; | ||
1458 | |||
1459 | tu = file->private_data; | ||
1460 | if (tu->timeri) | ||
1461 | snd_timer_close(tu->timeri); | ||
1462 | if (copy_from_user(&tselect, _tselect, sizeof(tselect))) | ||
1463 | return -EFAULT; | ||
1464 | sprintf(str, "application %i", current->pid); | ||
1465 | if (tselect.id.dev_class != SNDRV_TIMER_CLASS_SLAVE) | ||
1466 | tselect.id.dev_sclass = SNDRV_TIMER_SCLASS_APPLICATION; | ||
1467 | if ((err = snd_timer_open(&tu->timeri, str, &tselect.id, current->pid)) < 0) | ||
1468 | return err; | ||
1469 | |||
1470 | if (tu->queue) { | ||
1471 | kfree(tu->queue); | ||
1472 | tu->queue = NULL; | ||
1473 | } | ||
1474 | if (tu->tqueue) { | ||
1475 | kfree(tu->tqueue); | ||
1476 | tu->tqueue = NULL; | ||
1477 | } | ||
1478 | if (tu->tread) { | ||
1479 | tu->tqueue = (snd_timer_tread_t *)kmalloc(tu->queue_size * sizeof(snd_timer_tread_t), GFP_KERNEL); | ||
1480 | if (tu->tqueue == NULL) { | ||
1481 | snd_timer_close(tu->timeri); | ||
1482 | return -ENOMEM; | ||
1483 | } | ||
1484 | } else { | ||
1485 | tu->queue = (snd_timer_read_t *)kmalloc(tu->queue_size * sizeof(snd_timer_read_t), GFP_KERNEL); | ||
1486 | if (tu->queue == NULL) { | ||
1487 | snd_timer_close(tu->timeri); | ||
1488 | return -ENOMEM; | ||
1489 | } | ||
1490 | } | ||
1491 | |||
1492 | tu->timeri->flags |= SNDRV_TIMER_IFLG_FAST; | ||
1493 | tu->timeri->callback = tu->tread ? snd_timer_user_tinterrupt : snd_timer_user_interrupt; | ||
1494 | tu->timeri->ccallback = snd_timer_user_ccallback; | ||
1495 | tu->timeri->callback_data = (void *)tu; | ||
1496 | return 0; | ||
1497 | } | ||
1498 | |||
1499 | static int snd_timer_user_info(struct file *file, snd_timer_info_t __user *_info) | ||
1500 | { | ||
1501 | snd_timer_user_t *tu; | ||
1502 | snd_timer_info_t *info; | ||
1503 | snd_timer_t *t; | ||
1504 | int err = 0; | ||
1505 | |||
1506 | tu = file->private_data; | ||
1507 | snd_assert(tu->timeri != NULL, return -ENXIO); | ||
1508 | t = tu->timeri->timer; | ||
1509 | snd_assert(t != NULL, return -ENXIO); | ||
1510 | |||
1511 | info = kcalloc(1, sizeof(*info), GFP_KERNEL); | ||
1512 | if (! info) | ||
1513 | return -ENOMEM; | ||
1514 | info->card = t->card ? t->card->number : -1; | ||
1515 | if (t->hw.flags & SNDRV_TIMER_HW_SLAVE) | ||
1516 | info->flags |= SNDRV_TIMER_FLG_SLAVE; | ||
1517 | strlcpy(info->id, t->id, sizeof(info->id)); | ||
1518 | strlcpy(info->name, t->name, sizeof(info->name)); | ||
1519 | info->resolution = t->hw.resolution; | ||
1520 | if (copy_to_user(_info, info, sizeof(*_info))) | ||
1521 | err = -EFAULT; | ||
1522 | kfree(info); | ||
1523 | return err; | ||
1524 | } | ||
1525 | |||
1526 | static int snd_timer_user_params(struct file *file, snd_timer_params_t __user *_params) | ||
1527 | { | ||
1528 | snd_timer_user_t *tu; | ||
1529 | snd_timer_params_t params; | ||
1530 | snd_timer_t *t; | ||
1531 | snd_timer_read_t *tr; | ||
1532 | snd_timer_tread_t *ttr; | ||
1533 | int err; | ||
1534 | |||
1535 | tu = file->private_data; | ||
1536 | snd_assert(tu->timeri != NULL, return -ENXIO); | ||
1537 | t = tu->timeri->timer; | ||
1538 | snd_assert(t != NULL, return -ENXIO); | ||
1539 | if (copy_from_user(¶ms, _params, sizeof(params))) | ||
1540 | return -EFAULT; | ||
1541 | if (!(t->hw.flags & SNDRV_TIMER_HW_SLAVE) && params.ticks < 1) { | ||
1542 | err = -EINVAL; | ||
1543 | goto _end; | ||
1544 | } | ||
1545 | if (params.queue_size > 0 && (params.queue_size < 32 || params.queue_size > 1024)) { | ||
1546 | err = -EINVAL; | ||
1547 | goto _end; | ||
1548 | } | ||
1549 | if (params.filter & ~((1<<SNDRV_TIMER_EVENT_RESOLUTION)| | ||
1550 | (1<<SNDRV_TIMER_EVENT_TICK)| | ||
1551 | (1<<SNDRV_TIMER_EVENT_START)| | ||
1552 | (1<<SNDRV_TIMER_EVENT_STOP)| | ||
1553 | (1<<SNDRV_TIMER_EVENT_CONTINUE)| | ||
1554 | (1<<SNDRV_TIMER_EVENT_PAUSE)| | ||
1555 | (1<<SNDRV_TIMER_EVENT_MSTART)| | ||
1556 | (1<<SNDRV_TIMER_EVENT_MSTOP)| | ||
1557 | (1<<SNDRV_TIMER_EVENT_MCONTINUE)| | ||
1558 | (1<<SNDRV_TIMER_EVENT_MPAUSE))) { | ||
1559 | err = -EINVAL; | ||
1560 | goto _end; | ||
1561 | } | ||
1562 | snd_timer_stop(tu->timeri); | ||
1563 | spin_lock_irq(&t->lock); | ||
1564 | tu->timeri->flags &= ~(SNDRV_TIMER_IFLG_AUTO| | ||
1565 | SNDRV_TIMER_IFLG_EXCLUSIVE| | ||
1566 | SNDRV_TIMER_IFLG_EARLY_EVENT); | ||
1567 | if (params.flags & SNDRV_TIMER_PSFLG_AUTO) | ||
1568 | tu->timeri->flags |= SNDRV_TIMER_IFLG_AUTO; | ||
1569 | if (params.flags & SNDRV_TIMER_PSFLG_EXCLUSIVE) | ||
1570 | tu->timeri->flags |= SNDRV_TIMER_IFLG_EXCLUSIVE; | ||
1571 | if (params.flags & SNDRV_TIMER_PSFLG_EARLY_EVENT) | ||
1572 | tu->timeri->flags |= SNDRV_TIMER_IFLG_EARLY_EVENT; | ||
1573 | spin_unlock_irq(&t->lock); | ||
1574 | if (params.queue_size > 0 && (unsigned int)tu->queue_size != params.queue_size) { | ||
1575 | if (tu->tread) { | ||
1576 | ttr = (snd_timer_tread_t *)kmalloc(params.queue_size * sizeof(snd_timer_tread_t), GFP_KERNEL); | ||
1577 | if (ttr) { | ||
1578 | kfree(tu->tqueue); | ||
1579 | tu->queue_size = params.queue_size; | ||
1580 | tu->tqueue = ttr; | ||
1581 | } | ||
1582 | } else { | ||
1583 | tr = (snd_timer_read_t *)kmalloc(params.queue_size * sizeof(snd_timer_read_t), GFP_KERNEL); | ||
1584 | if (tr) { | ||
1585 | kfree(tu->queue); | ||
1586 | tu->queue_size = params.queue_size; | ||
1587 | tu->queue = tr; | ||
1588 | } | ||
1589 | } | ||
1590 | } | ||
1591 | tu->qhead = tu->qtail = tu->qused = 0; | ||
1592 | if (tu->timeri->flags & SNDRV_TIMER_IFLG_EARLY_EVENT) { | ||
1593 | if (tu->tread) { | ||
1594 | snd_timer_tread_t tread; | ||
1595 | tread.event = SNDRV_TIMER_EVENT_EARLY; | ||
1596 | tread.tstamp.tv_sec = 0; | ||
1597 | tread.tstamp.tv_nsec = 0; | ||
1598 | tread.val = 0; | ||
1599 | snd_timer_user_append_to_tqueue(tu, &tread); | ||
1600 | } else { | ||
1601 | snd_timer_read_t *r = &tu->queue[0]; | ||
1602 | r->resolution = 0; | ||
1603 | r->ticks = 0; | ||
1604 | tu->qused++; | ||
1605 | tu->qtail++; | ||
1606 | } | ||
1607 | |||
1608 | } | ||
1609 | tu->filter = params.filter; | ||
1610 | tu->ticks = params.ticks; | ||
1611 | err = 0; | ||
1612 | _end: | ||
1613 | if (copy_to_user(_params, ¶ms, sizeof(params))) | ||
1614 | return -EFAULT; | ||
1615 | return err; | ||
1616 | } | ||
1617 | |||
1618 | static int snd_timer_user_status(struct file *file, snd_timer_status_t __user *_status) | ||
1619 | { | ||
1620 | snd_timer_user_t *tu; | ||
1621 | snd_timer_status_t status; | ||
1622 | |||
1623 | tu = file->private_data; | ||
1624 | snd_assert(tu->timeri != NULL, return -ENXIO); | ||
1625 | memset(&status, 0, sizeof(status)); | ||
1626 | status.tstamp = tu->tstamp; | ||
1627 | status.resolution = snd_timer_resolution(tu->timeri); | ||
1628 | status.lost = tu->timeri->lost; | ||
1629 | status.overrun = tu->overrun; | ||
1630 | spin_lock_irq(&tu->qlock); | ||
1631 | status.queue = tu->qused; | ||
1632 | spin_unlock_irq(&tu->qlock); | ||
1633 | if (copy_to_user(_status, &status, sizeof(status))) | ||
1634 | return -EFAULT; | ||
1635 | return 0; | ||
1636 | } | ||
1637 | |||
1638 | static int snd_timer_user_start(struct file *file) | ||
1639 | { | ||
1640 | int err; | ||
1641 | snd_timer_user_t *tu; | ||
1642 | |||
1643 | tu = file->private_data; | ||
1644 | snd_assert(tu->timeri != NULL, return -ENXIO); | ||
1645 | snd_timer_stop(tu->timeri); | ||
1646 | tu->timeri->lost = 0; | ||
1647 | tu->last_resolution = 0; | ||
1648 | return (err = snd_timer_start(tu->timeri, tu->ticks)) < 0 ? err : 0; | ||
1649 | } | ||
1650 | |||
1651 | static int snd_timer_user_stop(struct file *file) | ||
1652 | { | ||
1653 | int err; | ||
1654 | snd_timer_user_t *tu; | ||
1655 | |||
1656 | tu = file->private_data; | ||
1657 | snd_assert(tu->timeri != NULL, return -ENXIO); | ||
1658 | return (err = snd_timer_stop(tu->timeri)) < 0 ? err : 0; | ||
1659 | } | ||
1660 | |||
1661 | static int snd_timer_user_continue(struct file *file) | ||
1662 | { | ||
1663 | int err; | ||
1664 | snd_timer_user_t *tu; | ||
1665 | |||
1666 | tu = file->private_data; | ||
1667 | snd_assert(tu->timeri != NULL, return -ENXIO); | ||
1668 | tu->timeri->lost = 0; | ||
1669 | return (err = snd_timer_continue(tu->timeri)) < 0 ? err : 0; | ||
1670 | } | ||
1671 | |||
1672 | static long snd_timer_user_ioctl(struct file *file, unsigned int cmd, unsigned long arg) | ||
1673 | { | ||
1674 | snd_timer_user_t *tu; | ||
1675 | void __user *argp = (void __user *)arg; | ||
1676 | int __user *p = argp; | ||
1677 | |||
1678 | tu = file->private_data; | ||
1679 | switch (cmd) { | ||
1680 | case SNDRV_TIMER_IOCTL_PVERSION: | ||
1681 | return put_user(SNDRV_TIMER_VERSION, p) ? -EFAULT : 0; | ||
1682 | case SNDRV_TIMER_IOCTL_NEXT_DEVICE: | ||
1683 | return snd_timer_user_next_device(argp); | ||
1684 | case SNDRV_TIMER_IOCTL_TREAD: | ||
1685 | { | ||
1686 | int xarg; | ||
1687 | |||
1688 | if (tu->timeri) /* too late */ | ||
1689 | return -EBUSY; | ||
1690 | if (get_user(xarg, p)) | ||
1691 | return -EFAULT; | ||
1692 | tu->tread = xarg ? 1 : 0; | ||
1693 | return 0; | ||
1694 | } | ||
1695 | case SNDRV_TIMER_IOCTL_GINFO: | ||
1696 | return snd_timer_user_ginfo(file, argp); | ||
1697 | case SNDRV_TIMER_IOCTL_GPARAMS: | ||
1698 | return snd_timer_user_gparams(file, argp); | ||
1699 | case SNDRV_TIMER_IOCTL_GSTATUS: | ||
1700 | return snd_timer_user_gstatus(file, argp); | ||
1701 | case SNDRV_TIMER_IOCTL_SELECT: | ||
1702 | return snd_timer_user_tselect(file, argp); | ||
1703 | case SNDRV_TIMER_IOCTL_INFO: | ||
1704 | return snd_timer_user_info(file, argp); | ||
1705 | case SNDRV_TIMER_IOCTL_PARAMS: | ||
1706 | return snd_timer_user_params(file, argp); | ||
1707 | case SNDRV_TIMER_IOCTL_STATUS: | ||
1708 | return snd_timer_user_status(file, argp); | ||
1709 | case SNDRV_TIMER_IOCTL_START: | ||
1710 | return snd_timer_user_start(file); | ||
1711 | case SNDRV_TIMER_IOCTL_STOP: | ||
1712 | return snd_timer_user_stop(file); | ||
1713 | case SNDRV_TIMER_IOCTL_CONTINUE: | ||
1714 | return snd_timer_user_continue(file); | ||
1715 | } | ||
1716 | return -ENOTTY; | ||
1717 | } | ||
1718 | |||
1719 | static int snd_timer_user_fasync(int fd, struct file * file, int on) | ||
1720 | { | ||
1721 | snd_timer_user_t *tu; | ||
1722 | int err; | ||
1723 | |||
1724 | tu = file->private_data; | ||
1725 | err = fasync_helper(fd, file, on, &tu->fasync); | ||
1726 | if (err < 0) | ||
1727 | return err; | ||
1728 | return 0; | ||
1729 | } | ||
1730 | |||
1731 | static ssize_t snd_timer_user_read(struct file *file, char __user *buffer, size_t count, loff_t *offset) | ||
1732 | { | ||
1733 | snd_timer_user_t *tu; | ||
1734 | long result = 0, unit; | ||
1735 | int err = 0; | ||
1736 | |||
1737 | tu = file->private_data; | ||
1738 | unit = tu->tread ? sizeof(snd_timer_tread_t) : sizeof(snd_timer_read_t); | ||
1739 | spin_lock_irq(&tu->qlock); | ||
1740 | while ((long)count - result >= unit) { | ||
1741 | while (!tu->qused) { | ||
1742 | wait_queue_t wait; | ||
1743 | |||
1744 | if ((file->f_flags & O_NONBLOCK) != 0 || result > 0) { | ||
1745 | err = -EAGAIN; | ||
1746 | break; | ||
1747 | } | ||
1748 | |||
1749 | set_current_state(TASK_INTERRUPTIBLE); | ||
1750 | init_waitqueue_entry(&wait, current); | ||
1751 | add_wait_queue(&tu->qchange_sleep, &wait); | ||
1752 | |||
1753 | spin_unlock_irq(&tu->qlock); | ||
1754 | schedule(); | ||
1755 | spin_lock_irq(&tu->qlock); | ||
1756 | |||
1757 | remove_wait_queue(&tu->qchange_sleep, &wait); | ||
1758 | |||
1759 | if (signal_pending(current)) { | ||
1760 | err = -ERESTARTSYS; | ||
1761 | break; | ||
1762 | } | ||
1763 | } | ||
1764 | |||
1765 | spin_unlock_irq(&tu->qlock); | ||
1766 | if (err < 0) | ||
1767 | goto _error; | ||
1768 | |||
1769 | if (tu->tread) { | ||
1770 | if (copy_to_user(buffer, &tu->tqueue[tu->qhead++], sizeof(snd_timer_tread_t))) { | ||
1771 | err = -EFAULT; | ||
1772 | goto _error; | ||
1773 | } | ||
1774 | } else { | ||
1775 | if (copy_to_user(buffer, &tu->queue[tu->qhead++], sizeof(snd_timer_read_t))) { | ||
1776 | err = -EFAULT; | ||
1777 | goto _error; | ||
1778 | } | ||
1779 | } | ||
1780 | |||
1781 | tu->qhead %= tu->queue_size; | ||
1782 | |||
1783 | result += unit; | ||
1784 | buffer += unit; | ||
1785 | |||
1786 | spin_lock_irq(&tu->qlock); | ||
1787 | tu->qused--; | ||
1788 | } | ||
1789 | spin_unlock_irq(&tu->qlock); | ||
1790 | _error: | ||
1791 | return result > 0 ? result : err; | ||
1792 | } | ||
1793 | |||
1794 | static unsigned int snd_timer_user_poll(struct file *file, poll_table * wait) | ||
1795 | { | ||
1796 | unsigned int mask; | ||
1797 | snd_timer_user_t *tu; | ||
1798 | |||
1799 | tu = file->private_data; | ||
1800 | |||
1801 | poll_wait(file, &tu->qchange_sleep, wait); | ||
1802 | |||
1803 | mask = 0; | ||
1804 | if (tu->qused) | ||
1805 | mask |= POLLIN | POLLRDNORM; | ||
1806 | |||
1807 | return mask; | ||
1808 | } | ||
1809 | |||
1810 | #ifdef CONFIG_COMPAT | ||
1811 | #include "timer_compat.c" | ||
1812 | #else | ||
1813 | #define snd_timer_user_ioctl_compat NULL | ||
1814 | #endif | ||
1815 | |||
1816 | static struct file_operations snd_timer_f_ops = | ||
1817 | { | ||
1818 | .owner = THIS_MODULE, | ||
1819 | .read = snd_timer_user_read, | ||
1820 | .open = snd_timer_user_open, | ||
1821 | .release = snd_timer_user_release, | ||
1822 | .poll = snd_timer_user_poll, | ||
1823 | .unlocked_ioctl = snd_timer_user_ioctl, | ||
1824 | .compat_ioctl = snd_timer_user_ioctl_compat, | ||
1825 | .fasync = snd_timer_user_fasync, | ||
1826 | }; | ||
1827 | |||
1828 | static snd_minor_t snd_timer_reg = | ||
1829 | { | ||
1830 | .comment = "timer", | ||
1831 | .f_ops = &snd_timer_f_ops, | ||
1832 | }; | ||
1833 | |||
1834 | /* | ||
1835 | * ENTRY functions | ||
1836 | */ | ||
1837 | |||
1838 | static snd_info_entry_t *snd_timer_proc_entry = NULL; | ||
1839 | |||
1840 | static int __init alsa_timer_init(void) | ||
1841 | { | ||
1842 | int err; | ||
1843 | snd_info_entry_t *entry; | ||
1844 | |||
1845 | #ifdef SNDRV_OSS_INFO_DEV_TIMERS | ||
1846 | snd_oss_info_register(SNDRV_OSS_INFO_DEV_TIMERS, SNDRV_CARDS - 1, "system timer"); | ||
1847 | #endif | ||
1848 | if ((entry = snd_info_create_module_entry(THIS_MODULE, "timers", NULL)) != NULL) { | ||
1849 | entry->c.text.read_size = SNDRV_TIMER_DEVICES * 128; | ||
1850 | entry->c.text.read = snd_timer_proc_read; | ||
1851 | if (snd_info_register(entry) < 0) { | ||
1852 | snd_info_free_entry(entry); | ||
1853 | entry = NULL; | ||
1854 | } | ||
1855 | } | ||
1856 | snd_timer_proc_entry = entry; | ||
1857 | if ((err = snd_timer_register_system()) < 0) | ||
1858 | snd_printk(KERN_ERR "unable to register system timer (%i)\n", err); | ||
1859 | if ((err = snd_register_device(SNDRV_DEVICE_TYPE_TIMER, | ||
1860 | NULL, 0, &snd_timer_reg, "timer"))<0) | ||
1861 | snd_printk(KERN_ERR "unable to register timer device (%i)\n", err); | ||
1862 | return 0; | ||
1863 | } | ||
1864 | |||
1865 | static void __exit alsa_timer_exit(void) | ||
1866 | { | ||
1867 | struct list_head *p, *n; | ||
1868 | |||
1869 | snd_unregister_device(SNDRV_DEVICE_TYPE_TIMER, NULL, 0); | ||
1870 | /* unregister the system timer */ | ||
1871 | list_for_each_safe(p, n, &snd_timer_list) { | ||
1872 | snd_timer_t *timer = (snd_timer_t *)list_entry(p, snd_timer_t, device_list); | ||
1873 | snd_timer_unregister(timer); | ||
1874 | } | ||
1875 | if (snd_timer_proc_entry) { | ||
1876 | snd_info_unregister(snd_timer_proc_entry); | ||
1877 | snd_timer_proc_entry = NULL; | ||
1878 | } | ||
1879 | #ifdef SNDRV_OSS_INFO_DEV_TIMERS | ||
1880 | snd_oss_info_unregister(SNDRV_OSS_INFO_DEV_TIMERS, SNDRV_CARDS - 1); | ||
1881 | #endif | ||
1882 | } | ||
1883 | |||
1884 | module_init(alsa_timer_init) | ||
1885 | module_exit(alsa_timer_exit) | ||
1886 | |||
1887 | EXPORT_SYMBOL(snd_timer_open); | ||
1888 | EXPORT_SYMBOL(snd_timer_close); | ||
1889 | EXPORT_SYMBOL(snd_timer_resolution); | ||
1890 | EXPORT_SYMBOL(snd_timer_start); | ||
1891 | EXPORT_SYMBOL(snd_timer_stop); | ||
1892 | EXPORT_SYMBOL(snd_timer_continue); | ||
1893 | EXPORT_SYMBOL(snd_timer_pause); | ||
1894 | EXPORT_SYMBOL(snd_timer_new); | ||
1895 | EXPORT_SYMBOL(snd_timer_notify); | ||
1896 | EXPORT_SYMBOL(snd_timer_global_new); | ||
1897 | EXPORT_SYMBOL(snd_timer_global_free); | ||
1898 | EXPORT_SYMBOL(snd_timer_global_register); | ||
1899 | EXPORT_SYMBOL(snd_timer_global_unregister); | ||
1900 | EXPORT_SYMBOL(snd_timer_interrupt); | ||
1901 | EXPORT_SYMBOL(snd_timer_system_resolution); | ||
diff --git a/sound/core/timer_compat.c b/sound/core/timer_compat.c new file mode 100644 index 000000000000..9fbc3957a22d --- /dev/null +++ b/sound/core/timer_compat.c | |||
@@ -0,0 +1,119 @@ | |||
1 | /* | ||
2 | * 32bit -> 64bit ioctl wrapper for timer 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 timer.c */ | ||
22 | |||
23 | #include <linux/compat.h> | ||
24 | |||
25 | struct sndrv_timer_info32 { | ||
26 | u32 flags; | ||
27 | s32 card; | ||
28 | unsigned char id[64]; | ||
29 | unsigned char name[80]; | ||
30 | u32 reserved0; | ||
31 | u32 resolution; | ||
32 | unsigned char reserved[64]; | ||
33 | }; | ||
34 | |||
35 | static int snd_timer_user_info_compat(struct file *file, | ||
36 | struct sndrv_timer_info32 __user *_info) | ||
37 | { | ||
38 | snd_timer_user_t *tu; | ||
39 | struct sndrv_timer_info32 info; | ||
40 | snd_timer_t *t; | ||
41 | |||
42 | tu = file->private_data; | ||
43 | snd_assert(tu->timeri != NULL, return -ENXIO); | ||
44 | t = tu->timeri->timer; | ||
45 | snd_assert(t != NULL, return -ENXIO); | ||
46 | memset(&info, 0, sizeof(info)); | ||
47 | info.card = t->card ? t->card->number : -1; | ||
48 | if (t->hw.flags & SNDRV_TIMER_HW_SLAVE) | ||
49 | info.flags |= SNDRV_TIMER_FLG_SLAVE; | ||
50 | strlcpy(info.id, t->id, sizeof(info.id)); | ||
51 | strlcpy(info.name, t->name, sizeof(info.name)); | ||
52 | info.resolution = t->hw.resolution; | ||
53 | if (copy_to_user(_info, &info, sizeof(*_info))) | ||
54 | return -EFAULT; | ||
55 | return 0; | ||
56 | } | ||
57 | |||
58 | struct sndrv_timer_status32 { | ||
59 | struct compat_timespec tstamp; | ||
60 | u32 resolution; | ||
61 | u32 lost; | ||
62 | u32 overrun; | ||
63 | u32 queue; | ||
64 | unsigned char reserved[64]; | ||
65 | }; | ||
66 | |||
67 | static int snd_timer_user_status_compat(struct file *file, | ||
68 | struct sndrv_timer_status32 __user *_status) | ||
69 | { | ||
70 | snd_timer_user_t *tu; | ||
71 | snd_timer_status_t status; | ||
72 | |||
73 | tu = file->private_data; | ||
74 | snd_assert(tu->timeri != NULL, return -ENXIO); | ||
75 | memset(&status, 0, sizeof(status)); | ||
76 | status.tstamp = tu->tstamp; | ||
77 | status.resolution = snd_timer_resolution(tu->timeri); | ||
78 | status.lost = tu->timeri->lost; | ||
79 | status.overrun = tu->overrun; | ||
80 | spin_lock_irq(&tu->qlock); | ||
81 | status.queue = tu->qused; | ||
82 | spin_unlock_irq(&tu->qlock); | ||
83 | if (copy_to_user(_status, &status, sizeof(status))) | ||
84 | return -EFAULT; | ||
85 | return 0; | ||
86 | } | ||
87 | |||
88 | /* | ||
89 | */ | ||
90 | |||
91 | enum { | ||
92 | SNDRV_TIMER_IOCTL_INFO32 = _IOR('T', 0x11, struct sndrv_timer_info32), | ||
93 | SNDRV_TIMER_IOCTL_STATUS32 = _IOW('T', 0x14, struct sndrv_timer_status32), | ||
94 | }; | ||
95 | |||
96 | static long snd_timer_user_ioctl_compat(struct file *file, unsigned int cmd, unsigned long arg) | ||
97 | { | ||
98 | void __user *argp = compat_ptr(arg); | ||
99 | |||
100 | switch (cmd) { | ||
101 | case SNDRV_TIMER_IOCTL_PVERSION: | ||
102 | case SNDRV_TIMER_IOCTL_TREAD: | ||
103 | case SNDRV_TIMER_IOCTL_GINFO: | ||
104 | case SNDRV_TIMER_IOCTL_GPARAMS: | ||
105 | case SNDRV_TIMER_IOCTL_GSTATUS: | ||
106 | case SNDRV_TIMER_IOCTL_SELECT: | ||
107 | case SNDRV_TIMER_IOCTL_PARAMS: | ||
108 | case SNDRV_TIMER_IOCTL_START: | ||
109 | case SNDRV_TIMER_IOCTL_STOP: | ||
110 | case SNDRV_TIMER_IOCTL_CONTINUE: | ||
111 | case SNDRV_TIMER_IOCTL_NEXT_DEVICE: | ||
112 | return snd_timer_user_ioctl(file, cmd, (unsigned long)argp); | ||
113 | case SNDRV_TIMER_IOCTL_INFO32: | ||
114 | return snd_timer_user_info_compat(file, argp); | ||
115 | case SNDRV_TIMER_IOCTL_STATUS32: | ||
116 | return snd_timer_user_status_compat(file, argp); | ||
117 | } | ||
118 | return -ENOIOCTLCMD; | ||
119 | } | ||
diff --git a/sound/core/wrappers.c b/sound/core/wrappers.c new file mode 100644 index 000000000000..9f393023c327 --- /dev/null +++ b/sound/core/wrappers.c | |||
@@ -0,0 +1,50 @@ | |||
1 | /* | ||
2 | * Various wrappers | ||
3 | * Copyright (c) by Jaroslav Kysela <perex@suse.cz> | ||
4 | * | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #include <linux/config.h> | ||
23 | #include <linux/module.h> | ||
24 | #include <linux/slab.h> | ||
25 | #include <linux/ioport.h> | ||
26 | #include <linux/vmalloc.h> | ||
27 | #include <linux/fs.h> | ||
28 | |||
29 | #ifdef CONFIG_SND_DEBUG_MEMORY | ||
30 | void *snd_wrapper_kmalloc(size_t size, int flags) | ||
31 | { | ||
32 | return kmalloc(size, flags); | ||
33 | } | ||
34 | |||
35 | void snd_wrapper_kfree(const void *obj) | ||
36 | { | ||
37 | kfree(obj); | ||
38 | } | ||
39 | |||
40 | void *snd_wrapper_vmalloc(unsigned long size) | ||
41 | { | ||
42 | return vmalloc(size); | ||
43 | } | ||
44 | |||
45 | void snd_wrapper_vfree(void *obj) | ||
46 | { | ||
47 | vfree(obj); | ||
48 | } | ||
49 | #endif | ||
50 | |||