diff options
Diffstat (limited to 'sound/core/seq/seq_instr.c')
-rw-r--r-- | sound/core/seq/seq_instr.c | 653 |
1 files changed, 653 insertions, 0 deletions
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); | ||