Commit | Line | Data |
---|---|---|
1da177e4 LT |
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 | ||
ecca82b4 | 56 | instr = kzalloc(sizeof(snd_seq_kinstr_t) + add_len, atomic ? GFP_ATOMIC : GFP_KERNEL); |
1da177e4 LT |
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 | ||
ecca82b4 | 80 | list = kzalloc(sizeof(snd_seq_kinstr_list_t), GFP_KERNEL); |
1da177e4 LT |
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); |