2 * Generic Instrument routines for ALSA sequencer
3 * Copyright (c) 1999 by Jaroslav Kysela <perex@suse.cz>
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.
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.
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
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>
29 MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
30 MODULE_DESCRIPTION("Advanced Linux Sound Architecture sequencer instrument library.");
31 MODULE_LICENSE("GPL");
34 static void snd_instr_lock_ops(snd_seq_kinstr_list_t
*list
)
36 if (!(list
->flags
& SNDRV_SEQ_INSTR_FLG_DIRECT
)) {
37 spin_lock_irqsave(&list
->ops_lock
, list
->ops_flags
);
39 down(&list
->ops_mutex
);
43 static void snd_instr_unlock_ops(snd_seq_kinstr_list_t
*list
)
45 if (!(list
->flags
& SNDRV_SEQ_INSTR_FLG_DIRECT
)) {
46 spin_unlock_irqrestore(&list
->ops_lock
, list
->ops_flags
);
52 static snd_seq_kinstr_t
*snd_seq_instr_new(int add_len
, int atomic
)
54 snd_seq_kinstr_t
*instr
;
56 instr
= kzalloc(sizeof(snd_seq_kinstr_t
) + add_len
, atomic
? GFP_ATOMIC
: GFP_KERNEL
);
59 instr
->add_len
= add_len
;
63 static int snd_seq_instr_free(snd_seq_kinstr_t
*instr
, int atomic
)
69 if (instr
->ops
&& instr
->ops
->remove
)
70 result
= instr
->ops
->remove(instr
->ops
->private_data
, instr
, 1);
76 snd_seq_kinstr_list_t
*snd_seq_instr_list_new(void)
78 snd_seq_kinstr_list_t
*list
;
80 list
= kzalloc(sizeof(snd_seq_kinstr_list_t
), GFP_KERNEL
);
83 spin_lock_init(&list
->lock
);
84 spin_lock_init(&list
->ops_lock
);
85 init_MUTEX(&list
->ops_mutex
);
90 void snd_seq_instr_list_free(snd_seq_kinstr_list_t
**list_ptr
)
92 snd_seq_kinstr_list_t
*list
;
93 snd_seq_kinstr_t
*instr
;
94 snd_seq_kcluster_t
*cluster
;
105 for (idx
= 0; idx
< SNDRV_SEQ_INSTR_HASH_SIZE
; idx
++) {
106 while ((instr
= list
->hash
[idx
]) != NULL
) {
107 list
->hash
[idx
] = instr
->next
;
109 spin_lock_irqsave(&list
->lock
, flags
);
111 spin_unlock_irqrestore(&list
->lock
, flags
);
112 schedule_timeout_interruptible(1);
113 spin_lock_irqsave(&list
->lock
, flags
);
115 spin_unlock_irqrestore(&list
->lock
, flags
);
116 if (snd_seq_instr_free(instr
, 0)<0)
117 snd_printk(KERN_WARNING
"instrument free problem\n");
119 while ((cluster
= list
->chash
[idx
]) != NULL
) {
120 list
->chash
[idx
] = cluster
->next
;
128 static int instr_free_compare(snd_seq_kinstr_t
*instr
,
129 snd_seq_instr_header_t
*ifree
,
132 switch (ifree
->cmd
) {
133 case SNDRV_SEQ_INSTR_FREE_CMD_ALL
:
134 /* all, except private for other clients */
135 if ((instr
->instr
.std
& 0xff000000) == 0)
137 if (((instr
->instr
.std
>> 24) & 0xff) == client
)
140 case SNDRV_SEQ_INSTR_FREE_CMD_PRIVATE
:
141 /* all my private instruments */
142 if ((instr
->instr
.std
& 0xff000000) == 0)
144 if (((instr
->instr
.std
>> 24) & 0xff) == client
)
147 case SNDRV_SEQ_INSTR_FREE_CMD_CLUSTER
:
148 /* all my private instruments */
149 if ((instr
->instr
.std
& 0xff000000) == 0) {
150 if (instr
->instr
.cluster
== ifree
->id
.cluster
)
154 if (((instr
->instr
.std
>> 24) & 0xff) == client
) {
155 if (instr
->instr
.cluster
== ifree
->id
.cluster
)
163 int snd_seq_instr_list_free_cond(snd_seq_kinstr_list_t
*list
,
164 snd_seq_instr_header_t
*ifree
,
168 snd_seq_kinstr_t
*instr
, *prev
, *next
, *flist
;
172 snd_instr_lock_ops(list
);
173 for (idx
= 0; idx
< SNDRV_SEQ_INSTR_HASH_SIZE
; idx
++) {
174 spin_lock_irqsave(&list
->lock
, flags
);
175 instr
= list
->hash
[idx
];
178 while (instr
&& instr_free_compare(instr
, ifree
, (unsigned int)client
)) {
184 if (instr
->ops
&& instr
->ops
->notify
)
185 instr
->ops
->notify(instr
->ops
->private_data
, instr
, SNDRV_SEQ_INSTR_NOTIFY_REMOVE
);
188 list
->hash
[idx
] = next
;
197 spin_unlock_irqrestore(&list
->lock
, flags
);
202 schedule_timeout_interruptible(1);
203 if (snd_seq_instr_free(instr
, atomic
)<0)
204 snd_printk(KERN_WARNING
"instrument free problem\n");
208 snd_instr_unlock_ops(list
);
212 static int compute_hash_instr_key(snd_seq_instr_t
*instr
)
216 result
= instr
->bank
| (instr
->prg
<< 16);
217 result
+= result
>> 24;
218 result
+= result
>> 16;
219 result
+= result
>> 8;
220 return result
& (SNDRV_SEQ_INSTR_HASH_SIZE
-1);
224 static int compute_hash_cluster_key(snd_seq_instr_cluster_t cluster
)
229 result
+= result
>> 24;
230 result
+= result
>> 16;
231 result
+= result
>> 8;
232 return result
& (SNDRV_SEQ_INSTR_HASH_SIZE
-1);
236 static int compare_instr(snd_seq_instr_t
*i1
, snd_seq_instr_t
*i2
, int exact
)
239 if (i1
->cluster
!= i2
->cluster
||
240 i1
->bank
!= i2
->bank
||
243 if ((i1
->std
& 0xff000000) != (i2
->std
& 0xff000000))
245 if (!(i1
->std
& i2
->std
))
249 unsigned int client_check
;
251 if (i2
->cluster
&& i1
->cluster
!= i2
->cluster
)
253 client_check
= i2
->std
& 0xff000000;
255 if ((i1
->std
& 0xff000000) != client_check
)
258 if ((i1
->std
& i2
->std
) != i2
->std
)
261 return i1
->bank
!= i2
->bank
|| i1
->prg
!= i2
->prg
;
265 snd_seq_kinstr_t
*snd_seq_instr_find(snd_seq_kinstr_list_t
*list
,
266 snd_seq_instr_t
*instr
,
272 snd_seq_kinstr_t
*result
;
274 if (list
== NULL
|| instr
== NULL
)
276 spin_lock_irqsave(&list
->lock
, flags
);
278 result
= list
->hash
[compute_hash_instr_key(instr
)];
280 if (!compare_instr(&result
->instr
, instr
, exact
)) {
281 if (follow_alias
&& (result
->type
== SNDRV_SEQ_INSTR_ATYPE_ALIAS
)) {
282 instr
= (snd_seq_instr_t
*)KINSTR_DATA(result
);
288 spin_unlock_irqrestore(&list
->lock
, flags
);
291 result
= result
->next
;
294 spin_unlock_irqrestore(&list
->lock
, flags
);
298 void snd_seq_instr_free_use(snd_seq_kinstr_list_t
*list
,
299 snd_seq_kinstr_t
*instr
)
303 if (list
== NULL
|| instr
== NULL
)
305 spin_lock_irqsave(&list
->lock
, flags
);
306 if (instr
->use
<= 0) {
307 snd_printk(KERN_ERR
"free_use: fatal!!! use = %i, name = '%s'\n", instr
->use
, instr
->name
);
311 spin_unlock_irqrestore(&list
->lock
, flags
);
314 static snd_seq_kinstr_ops_t
*instr_ops(snd_seq_kinstr_ops_t
*ops
, char *instr_type
)
317 if (!strcmp(ops
->instr_type
, instr_type
))
324 static int instr_result(snd_seq_event_t
*ev
,
325 int type
, int result
,
330 memset(&sev
, 0, sizeof(sev
));
331 sev
.type
= SNDRV_SEQ_EVENT_RESULT
;
332 sev
.flags
= SNDRV_SEQ_TIME_STAMP_REAL
| SNDRV_SEQ_EVENT_LENGTH_FIXED
|
333 SNDRV_SEQ_PRIORITY_NORMAL
;
334 sev
.source
= ev
->dest
;
335 sev
.dest
= ev
->source
;
336 sev
.data
.result
.event
= type
;
337 sev
.data
.result
.result
= result
;
339 printk("instr result - type = %i, result = %i, queue = %i, source.client:port = %i:%i, dest.client:port = %i:%i\n",
342 sev
.source
.client
, sev
.source
.port
,
343 sev
.dest
.client
, sev
.dest
.port
);
345 return snd_seq_kernel_client_dispatch(sev
.source
.client
, &sev
, atomic
, 0);
348 static int instr_begin(snd_seq_kinstr_ops_t
*ops
,
349 snd_seq_kinstr_list_t
*list
,
355 spin_lock_irqsave(&list
->lock
, flags
);
356 if (list
->owner
>= 0 && list
->owner
!= ev
->source
.client
) {
357 spin_unlock_irqrestore(&list
->lock
, flags
);
358 return instr_result(ev
, SNDRV_SEQ_EVENT_INSTR_BEGIN
, -EBUSY
, atomic
);
360 list
->owner
= ev
->source
.client
;
361 spin_unlock_irqrestore(&list
->lock
, flags
);
362 return instr_result(ev
, SNDRV_SEQ_EVENT_INSTR_BEGIN
, 0, atomic
);
365 static int instr_end(snd_seq_kinstr_ops_t
*ops
,
366 snd_seq_kinstr_list_t
*list
,
372 /* TODO: timeout handling */
373 spin_lock_irqsave(&list
->lock
, flags
);
374 if (list
->owner
== ev
->source
.client
) {
376 spin_unlock_irqrestore(&list
->lock
, flags
);
377 return instr_result(ev
, SNDRV_SEQ_EVENT_INSTR_END
, 0, atomic
);
379 spin_unlock_irqrestore(&list
->lock
, flags
);
380 return instr_result(ev
, SNDRV_SEQ_EVENT_INSTR_END
, -EINVAL
, atomic
);
383 static int instr_info(snd_seq_kinstr_ops_t
*ops
,
384 snd_seq_kinstr_list_t
*list
,
391 static int instr_format_info(snd_seq_kinstr_ops_t
*ops
,
392 snd_seq_kinstr_list_t
*list
,
399 static int instr_reset(snd_seq_kinstr_ops_t
*ops
,
400 snd_seq_kinstr_list_t
*list
,
407 static int instr_status(snd_seq_kinstr_ops_t
*ops
,
408 snd_seq_kinstr_list_t
*list
,
415 static int instr_put(snd_seq_kinstr_ops_t
*ops
,
416 snd_seq_kinstr_list_t
*list
,
421 snd_seq_instr_header_t put
;
422 snd_seq_kinstr_t
*instr
;
423 int result
= -EINVAL
, len
, key
;
425 if ((ev
->flags
& SNDRV_SEQ_EVENT_LENGTH_MASK
) != SNDRV_SEQ_EVENT_LENGTH_VARUSR
)
428 if (ev
->data
.ext
.len
< sizeof(snd_seq_instr_header_t
))
430 if (copy_from_user(&put
, (void __user
*)ev
->data
.ext
.ptr
, sizeof(snd_seq_instr_header_t
))) {
434 snd_instr_lock_ops(list
);
435 if (put
.id
.instr
.std
& 0xff000000) { /* private instrument */
436 put
.id
.instr
.std
&= 0x00ffffff;
437 put
.id
.instr
.std
|= (unsigned int)ev
->source
.client
<< 24;
439 if ((instr
= snd_seq_instr_find(list
, &put
.id
.instr
, 1, 0))) {
440 snd_seq_instr_free_use(list
, instr
);
441 snd_instr_unlock_ops(list
);
445 ops
= instr_ops(ops
, put
.data
.data
.format
);
447 snd_instr_unlock_ops(list
);
451 if (put
.data
.type
== SNDRV_SEQ_INSTR_ATYPE_ALIAS
)
452 len
= sizeof(snd_seq_instr_t
);
453 instr
= snd_seq_instr_new(len
, atomic
);
455 snd_instr_unlock_ops(list
);
460 instr
->instr
= put
.id
.instr
;
461 strlcpy(instr
->name
, put
.data
.name
, sizeof(instr
->name
));
462 instr
->type
= put
.data
.type
;
463 if (instr
->type
== SNDRV_SEQ_INSTR_ATYPE_DATA
) {
464 result
= ops
->put(ops
->private_data
,
466 (void __user
*)ev
->data
.ext
.ptr
+ sizeof(snd_seq_instr_header_t
),
467 ev
->data
.ext
.len
- sizeof(snd_seq_instr_header_t
),
471 snd_seq_instr_free(instr
, atomic
);
472 snd_instr_unlock_ops(list
);
476 key
= compute_hash_instr_key(&instr
->instr
);
477 spin_lock_irqsave(&list
->lock
, flags
);
478 instr
->next
= list
->hash
[key
];
479 list
->hash
[key
] = instr
;
481 spin_unlock_irqrestore(&list
->lock
, flags
);
482 snd_instr_unlock_ops(list
);
485 instr_result(ev
, SNDRV_SEQ_EVENT_INSTR_PUT
, result
, atomic
);
489 static int instr_get(snd_seq_kinstr_ops_t
*ops
,
490 snd_seq_kinstr_list_t
*list
,
497 static int instr_free(snd_seq_kinstr_ops_t
*ops
,
498 snd_seq_kinstr_list_t
*list
,
502 snd_seq_instr_header_t ifree
;
503 snd_seq_kinstr_t
*instr
, *prev
;
504 int result
= -EINVAL
;
508 if ((ev
->flags
& SNDRV_SEQ_EVENT_LENGTH_MASK
) != SNDRV_SEQ_EVENT_LENGTH_VARUSR
)
511 if (ev
->data
.ext
.len
< sizeof(snd_seq_instr_header_t
))
513 if (copy_from_user(&ifree
, (void __user
*)ev
->data
.ext
.ptr
, sizeof(snd_seq_instr_header_t
))) {
517 if (ifree
.cmd
== SNDRV_SEQ_INSTR_FREE_CMD_ALL
||
518 ifree
.cmd
== SNDRV_SEQ_INSTR_FREE_CMD_PRIVATE
||
519 ifree
.cmd
== SNDRV_SEQ_INSTR_FREE_CMD_CLUSTER
) {
520 result
= snd_seq_instr_list_free_cond(list
, &ifree
, ev
->dest
.client
, atomic
);
523 if (ifree
.cmd
== SNDRV_SEQ_INSTR_FREE_CMD_SINGLE
) {
524 if (ifree
.id
.instr
.std
& 0xff000000) {
525 ifree
.id
.instr
.std
&= 0x00ffffff;
526 ifree
.id
.instr
.std
|= (unsigned int)ev
->source
.client
<< 24;
528 hash
= compute_hash_instr_key(&ifree
.id
.instr
);
529 snd_instr_lock_ops(list
);
530 spin_lock_irqsave(&list
->lock
, flags
);
531 instr
= list
->hash
[hash
];
534 if (!compare_instr(&instr
->instr
, &ifree
.id
.instr
, 1))
540 spin_unlock_irqrestore(&list
->lock
, flags
);
541 snd_instr_unlock_ops(list
);
546 prev
->next
= instr
->next
;
548 list
->hash
[hash
] = instr
->next
;
550 if (instr
->ops
&& instr
->ops
->notify
)
551 instr
->ops
->notify(instr
->ops
->private_data
, instr
, SNDRV_SEQ_INSTR_NOTIFY_REMOVE
);
553 spin_unlock_irqrestore(&list
->lock
, flags
);
554 schedule_timeout_interruptible(1);
555 spin_lock_irqsave(&list
->lock
, flags
);
557 spin_unlock_irqrestore(&list
->lock
, flags
);
558 result
= snd_seq_instr_free(instr
, atomic
);
559 snd_instr_unlock_ops(list
);
564 instr_result(ev
, SNDRV_SEQ_EVENT_INSTR_FREE
, result
, atomic
);
568 static int instr_list(snd_seq_kinstr_ops_t
*ops
,
569 snd_seq_kinstr_list_t
*list
,
576 static int instr_cluster(snd_seq_kinstr_ops_t
*ops
,
577 snd_seq_kinstr_list_t
*list
,
584 int snd_seq_instr_event(snd_seq_kinstr_ops_t
*ops
,
585 snd_seq_kinstr_list_t
*list
,
593 snd_assert(ops
!= NULL
&& list
!= NULL
&& ev
!= NULL
, return -EINVAL
);
594 if (snd_seq_ev_is_direct(ev
)) {
597 case SNDRV_SEQ_EVENT_INSTR_BEGIN
:
598 return instr_begin(ops
, list
, ev
, atomic
, hop
);
599 case SNDRV_SEQ_EVENT_INSTR_END
:
600 return instr_end(ops
, list
, ev
, atomic
, hop
);
603 if ((list
->flags
& SNDRV_SEQ_INSTR_FLG_DIRECT
) && !direct
)
606 case SNDRV_SEQ_EVENT_INSTR_INFO
:
607 return instr_info(ops
, list
, ev
, atomic
, hop
);
608 case SNDRV_SEQ_EVENT_INSTR_FINFO
:
609 return instr_format_info(ops
, list
, ev
, atomic
, hop
);
610 case SNDRV_SEQ_EVENT_INSTR_RESET
:
611 return instr_reset(ops
, list
, ev
, atomic
, hop
);
612 case SNDRV_SEQ_EVENT_INSTR_STATUS
:
613 return instr_status(ops
, list
, ev
, atomic
, hop
);
614 case SNDRV_SEQ_EVENT_INSTR_PUT
:
615 return instr_put(ops
, list
, ev
, atomic
, hop
);
616 case SNDRV_SEQ_EVENT_INSTR_GET
:
617 return instr_get(ops
, list
, ev
, atomic
, hop
);
618 case SNDRV_SEQ_EVENT_INSTR_FREE
:
619 return instr_free(ops
, list
, ev
, atomic
, hop
);
620 case SNDRV_SEQ_EVENT_INSTR_LIST
:
621 return instr_list(ops
, list
, ev
, atomic
, hop
);
622 case SNDRV_SEQ_EVENT_INSTR_CLUSTER
:
623 return instr_cluster(ops
, list
, ev
, atomic
, hop
);
632 static int __init
alsa_seq_instr_init(void)
637 static void __exit
alsa_seq_instr_exit(void)
641 module_init(alsa_seq_instr_init
)
642 module_exit(alsa_seq_instr_exit
)
644 EXPORT_SYMBOL(snd_seq_instr_list_new
);
645 EXPORT_SYMBOL(snd_seq_instr_list_free
);
646 EXPORT_SYMBOL(snd_seq_instr_list_free_cond
);
647 EXPORT_SYMBOL(snd_seq_instr_find
);
648 EXPORT_SYMBOL(snd_seq_instr_free_use
);
649 EXPORT_SYMBOL(snd_seq_instr_event
);