vserver 1.9.3
[linux-2.6.git] / sound / core / seq / seq_instr.c
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 snd_seq_kcluster_t *snd_seq_cluster_new(int atomic)
53 {
54         return kcalloc(1, sizeof(snd_seq_kcluster_t), atomic ? GFP_ATOMIC : GFP_KERNEL);
55 }
56
57 void snd_seq_cluster_free(snd_seq_kcluster_t *cluster, int atomic)
58 {
59         if (cluster == NULL)
60                 return;
61         kfree(cluster);
62 }
63
64 snd_seq_kinstr_t *snd_seq_instr_new(int add_len, int atomic)
65 {
66         snd_seq_kinstr_t *instr;
67         
68         instr = kcalloc(1, sizeof(snd_seq_kinstr_t) + add_len, atomic ? GFP_ATOMIC : GFP_KERNEL);
69         if (instr == NULL)
70                 return NULL;
71         instr->add_len = add_len;
72         return instr;
73 }
74
75 int snd_seq_instr_free(snd_seq_kinstr_t *instr, int atomic)
76 {
77         int result = 0;
78
79         if (instr == NULL)
80                 return -EINVAL;
81         if (instr->ops && instr->ops->remove)
82                 result = instr->ops->remove(instr->ops->private_data, instr, 1);
83         if (!result)
84                 kfree(instr);
85         return result;
86 }
87
88 snd_seq_kinstr_list_t *snd_seq_instr_list_new(void)
89 {
90         snd_seq_kinstr_list_t *list;
91
92         list = kcalloc(1, sizeof(snd_seq_kinstr_list_t), GFP_KERNEL);
93         if (list == NULL)
94                 return NULL;
95         spin_lock_init(&list->lock);
96         spin_lock_init(&list->ops_lock);
97         init_MUTEX(&list->ops_mutex);
98         list->owner = -1;
99         return list;
100 }
101
102 void snd_seq_instr_list_free(snd_seq_kinstr_list_t **list_ptr)
103 {
104         snd_seq_kinstr_list_t *list;
105         snd_seq_kinstr_t *instr;
106         snd_seq_kcluster_t *cluster;
107         int idx;
108         unsigned long flags;
109
110         if (list_ptr == NULL)
111                 return;
112         list = *list_ptr;
113         *list_ptr = NULL;
114         if (list == NULL)
115                 return;
116         
117         for (idx = 0; idx < SNDRV_SEQ_INSTR_HASH_SIZE; idx++) {         
118                 while ((instr = list->hash[idx]) != NULL) {
119                         list->hash[idx] = instr->next;
120                         list->count--;
121                         spin_lock_irqsave(&list->lock, flags);
122                         while (instr->use) {
123                                 spin_unlock_irqrestore(&list->lock, flags);
124                                 set_current_state(TASK_INTERRUPTIBLE);
125                                 schedule_timeout(1);
126                                 spin_lock_irqsave(&list->lock, flags);
127                         }                               
128                         spin_unlock_irqrestore(&list->lock, flags);
129                         if (snd_seq_instr_free(instr, 0)<0)
130                                 snd_printk(KERN_WARNING "instrument free problem\n");
131                 }
132                 while ((cluster = list->chash[idx]) != NULL) {
133                         list->chash[idx] = cluster->next;
134                         list->ccount--;
135                         snd_seq_cluster_free(cluster, 0);
136                 }
137         }
138         kfree(list);
139 }
140
141 static int instr_free_compare(snd_seq_kinstr_t *instr,
142                               snd_seq_instr_header_t *ifree,
143                               unsigned int client)
144 {
145         switch (ifree->cmd) {
146         case SNDRV_SEQ_INSTR_FREE_CMD_ALL:
147                 /* all, except private for other clients */
148                 if ((instr->instr.std & 0xff000000) == 0)
149                         return 0;
150                 if (((instr->instr.std >> 24) & 0xff) == client)
151                         return 0;
152                 return 1;
153         case SNDRV_SEQ_INSTR_FREE_CMD_PRIVATE:
154                 /* all my private instruments */
155                 if ((instr->instr.std & 0xff000000) == 0)
156                         return 1;
157                 if (((instr->instr.std >> 24) & 0xff) == client)
158                         return 0;
159                 return 1;
160         case SNDRV_SEQ_INSTR_FREE_CMD_CLUSTER:
161                 /* all my private instruments */
162                 if ((instr->instr.std & 0xff000000) == 0) {
163                         if (instr->instr.cluster == ifree->id.cluster)
164                                 return 0;
165                         return 1;
166                 }
167                 if (((instr->instr.std >> 24) & 0xff) == client) {
168                         if (instr->instr.cluster == ifree->id.cluster)
169                                 return 0;
170                 }
171                 return 1;
172         }
173         return 1;
174 }
175
176 int snd_seq_instr_list_free_cond(snd_seq_kinstr_list_t *list,
177                                  snd_seq_instr_header_t *ifree,
178                                  int client,
179                                  int atomic)
180 {
181         snd_seq_kinstr_t *instr, *prev, *next, *flist;
182         int idx;
183         unsigned long flags;
184
185         snd_instr_lock_ops(list);
186         for (idx = 0; idx < SNDRV_SEQ_INSTR_HASH_SIZE; idx++) {
187                 spin_lock_irqsave(&list->lock, flags);
188                 instr = list->hash[idx];
189                 prev = flist = NULL;
190                 while (instr) {
191                         while (instr && instr_free_compare(instr, ifree, (unsigned int)client)) {
192                                 prev = instr;
193                                 instr = instr->next;
194                         }
195                         if (instr == NULL)
196                                 continue;
197                         if (instr->ops && instr->ops->notify)
198                                 instr->ops->notify(instr->ops->private_data, instr, SNDRV_SEQ_INSTR_NOTIFY_REMOVE);
199                         next = instr->next;
200                         if (prev == NULL) {
201                                 list->hash[idx] = next;
202                         } else {
203                                 prev->next = next;
204                         }
205                         list->count--;
206                         instr->next = flist;
207                         flist = instr;
208                         instr = next;
209                 }
210                 spin_unlock_irqrestore(&list->lock, flags);
211                 while (flist) {
212                         instr = flist;
213                         flist = instr->next;
214                         while (instr->use) {
215                                 set_current_state(TASK_INTERRUPTIBLE);
216                                 schedule_timeout(1);
217                         }                               
218                         if (snd_seq_instr_free(instr, atomic)<0)
219                                 snd_printk(KERN_WARNING "instrument free problem\n");
220                         instr = next;
221                 }
222         }
223         snd_instr_unlock_ops(list);
224         return 0;       
225 }
226
227 static int compute_hash_instr_key(snd_seq_instr_t *instr)
228 {
229         int result;
230         
231         result = instr->bank | (instr->prg << 16);
232         result += result >> 24;
233         result += result >> 16;
234         result += result >> 8;
235         return result & (SNDRV_SEQ_INSTR_HASH_SIZE-1);
236 }
237
238 #if 0
239 static int compute_hash_cluster_key(snd_seq_instr_cluster_t cluster)
240 {
241         int result;
242         
243         result = cluster;
244         result += result >> 24;
245         result += result >> 16;
246         result += result >> 8;
247         return result & (SNDRV_SEQ_INSTR_HASH_SIZE-1);
248 }
249 #endif
250
251 static int compare_instr(snd_seq_instr_t *i1, snd_seq_instr_t *i2, int exact)
252 {
253         if (exact) {
254                 if (i1->cluster != i2->cluster ||
255                     i1->bank != i2->bank ||
256                     i1->prg != i2->prg)
257                         return 1;
258                 if ((i1->std & 0xff000000) != (i2->std & 0xff000000))
259                         return 1;
260                 if (!(i1->std & i2->std))
261                         return 1;
262                 return 0;
263         } else {
264                 unsigned int client_check;
265                 
266                 if (i2->cluster && i1->cluster != i2->cluster)
267                         return 1;
268                 client_check = i2->std & 0xff000000;
269                 if (client_check) {
270                         if ((i1->std & 0xff000000) != client_check)
271                                 return 1;
272                 } else {
273                         if ((i1->std & i2->std) != i2->std)
274                                 return 1;
275                 }
276                 return i1->bank != i2->bank || i1->prg != i2->prg;
277         }
278 }
279
280 snd_seq_kinstr_t *snd_seq_instr_find(snd_seq_kinstr_list_t *list,
281                                      snd_seq_instr_t *instr,
282                                      int exact,
283                                      int follow_alias)
284 {
285         unsigned long flags;
286         int depth = 0;
287         snd_seq_kinstr_t *result;
288
289         if (list == NULL || instr == NULL)
290                 return NULL;
291         spin_lock_irqsave(&list->lock, flags);
292       __again:
293         result = list->hash[compute_hash_instr_key(instr)];
294         while (result) {
295                 if (!compare_instr(&result->instr, instr, exact)) {
296                         if (follow_alias && (result->type == SNDRV_SEQ_INSTR_ATYPE_ALIAS)) {
297                                 instr = (snd_seq_instr_t *)KINSTR_DATA(result);
298                                 if (++depth > 10)
299                                         goto __not_found;
300                                 goto __again;
301                         }
302                         result->use++;
303                         spin_unlock_irqrestore(&list->lock, flags);
304                         return result;
305                 }
306                 result = result->next;
307         }
308       __not_found:
309         spin_unlock_irqrestore(&list->lock, flags);
310         return NULL;
311 }
312
313 void snd_seq_instr_free_use(snd_seq_kinstr_list_t *list,
314                             snd_seq_kinstr_t *instr)
315 {
316         unsigned long flags;
317
318         if (list == NULL || instr == NULL)
319                 return;
320         spin_lock_irqsave(&list->lock, flags);
321         if (instr->use <= 0) {
322                 snd_printk(KERN_ERR "free_use: fatal!!! use = %i, name = '%s'\n", instr->use, instr->name);
323         } else {
324                 instr->use--;
325         }
326         spin_unlock_irqrestore(&list->lock, flags);
327 }
328
329 static snd_seq_kinstr_ops_t *instr_ops(snd_seq_kinstr_ops_t *ops, char *instr_type)
330 {
331         while (ops) {
332                 if (!strcmp(ops->instr_type, instr_type))
333                         return ops;
334                 ops = ops->next;
335         }
336         return NULL;
337 }
338
339 static int instr_result(snd_seq_event_t *ev,
340                         int type, int result,
341                         int atomic)
342 {
343         snd_seq_event_t sev;
344         
345         memset(&sev, 0, sizeof(sev));
346         sev.type = SNDRV_SEQ_EVENT_RESULT;
347         sev.flags = SNDRV_SEQ_TIME_STAMP_REAL | SNDRV_SEQ_EVENT_LENGTH_FIXED |
348                     SNDRV_SEQ_PRIORITY_NORMAL;
349         sev.source = ev->dest;
350         sev.dest = ev->source;
351         sev.data.result.event = type;
352         sev.data.result.result = result;
353 #if 0
354         printk("instr result - type = %i, result = %i, queue = %i, source.client:port = %i:%i, dest.client:port = %i:%i\n",
355                                 type, result,
356                                 sev.queue,
357                                 sev.source.client, sev.source.port,
358                                 sev.dest.client, sev.dest.port);
359 #endif
360         return snd_seq_kernel_client_dispatch(sev.source.client, &sev, atomic, 0);
361 }
362
363 static int instr_begin(snd_seq_kinstr_ops_t *ops,
364                        snd_seq_kinstr_list_t *list,
365                        snd_seq_event_t *ev,
366                        int atomic, int hop)
367 {
368         unsigned long flags;
369
370         spin_lock_irqsave(&list->lock, flags);
371         if (list->owner >= 0 && list->owner != ev->source.client) {
372                 spin_unlock_irqrestore(&list->lock, flags);
373                 return instr_result(ev, SNDRV_SEQ_EVENT_INSTR_BEGIN, -EBUSY, atomic);
374         }
375         list->owner = ev->source.client;
376         spin_unlock_irqrestore(&list->lock, flags);
377         return instr_result(ev, SNDRV_SEQ_EVENT_INSTR_BEGIN, 0, atomic);
378 }
379
380 static int instr_end(snd_seq_kinstr_ops_t *ops,
381                      snd_seq_kinstr_list_t *list,
382                      snd_seq_event_t *ev,
383                      int atomic, int hop)
384 {
385         unsigned long flags;
386
387         /* TODO: timeout handling */
388         spin_lock_irqsave(&list->lock, flags);
389         if (list->owner == ev->source.client) {
390                 list->owner = -1;
391                 spin_unlock_irqrestore(&list->lock, flags);
392                 return instr_result(ev, SNDRV_SEQ_EVENT_INSTR_END, 0, atomic);
393         }
394         spin_unlock_irqrestore(&list->lock, flags);
395         return instr_result(ev, SNDRV_SEQ_EVENT_INSTR_END, -EINVAL, atomic);
396 }
397
398 static int instr_info(snd_seq_kinstr_ops_t *ops,
399                       snd_seq_kinstr_list_t *list,
400                       snd_seq_event_t *ev,
401                       int atomic, int hop)
402 {
403         return -ENXIO;
404 }
405
406 static int instr_format_info(snd_seq_kinstr_ops_t *ops,
407                              snd_seq_kinstr_list_t *list,
408                              snd_seq_event_t *ev,
409                              int atomic, int hop)
410 {
411         return -ENXIO;
412 }
413
414 static int instr_reset(snd_seq_kinstr_ops_t *ops,
415                        snd_seq_kinstr_list_t *list,
416                        snd_seq_event_t *ev,
417                        int atomic, int hop)
418 {
419         return -ENXIO;
420 }
421
422 static int instr_status(snd_seq_kinstr_ops_t *ops,
423                         snd_seq_kinstr_list_t *list,
424                         snd_seq_event_t *ev,
425                         int atomic, int hop)
426 {
427         return -ENXIO;
428 }
429
430 static int instr_put(snd_seq_kinstr_ops_t *ops,
431                      snd_seq_kinstr_list_t *list,
432                      snd_seq_event_t *ev,
433                      int atomic, int hop)
434 {
435         unsigned long flags;
436         snd_seq_instr_header_t put;
437         snd_seq_kinstr_t *instr;
438         int result = -EINVAL, len, key;
439
440         if ((ev->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) != SNDRV_SEQ_EVENT_LENGTH_VARUSR)
441                 goto __return;
442
443         if (ev->data.ext.len < sizeof(snd_seq_instr_header_t))
444                 goto __return;
445         if (copy_from_user(&put, ev->data.ext.ptr, sizeof(snd_seq_instr_header_t))) {
446                 result = -EFAULT;
447                 goto __return;
448         }
449         snd_instr_lock_ops(list);
450         if (put.id.instr.std & 0xff000000) {    /* private instrument */
451                 put.id.instr.std &= 0x00ffffff;
452                 put.id.instr.std |= (unsigned int)ev->source.client << 24;
453         }
454         if ((instr = snd_seq_instr_find(list, &put.id.instr, 1, 0))) {
455                 snd_seq_instr_free_use(list, instr);
456                 snd_instr_unlock_ops(list);
457                 result = -EBUSY;
458                 goto __return;
459         }
460         ops = instr_ops(ops, put.data.data.format);
461         if (ops == NULL) {
462                 snd_instr_unlock_ops(list);
463                 goto __return;
464         }
465         len = ops->add_len;
466         if (put.data.type == SNDRV_SEQ_INSTR_ATYPE_ALIAS)
467                 len = sizeof(snd_seq_instr_t);
468         instr = snd_seq_instr_new(len, atomic);
469         if (instr == NULL) {
470                 snd_instr_unlock_ops(list);
471                 result = -ENOMEM;
472                 goto __return;
473         }
474         instr->ops = ops;
475         instr->instr = put.id.instr;
476         strlcpy(instr->name, put.data.name, sizeof(instr->name));
477         instr->type = put.data.type;
478         if (instr->type == SNDRV_SEQ_INSTR_ATYPE_DATA) {
479                 result = ops->put(ops->private_data,
480                                   instr,
481                                   ev->data.ext.ptr + sizeof(snd_seq_instr_header_t),
482                                   ev->data.ext.len - sizeof(snd_seq_instr_header_t),
483                                   atomic,
484                                   put.cmd);
485                 if (result < 0) {
486                         snd_seq_instr_free(instr, atomic);
487                         snd_instr_unlock_ops(list);
488                         goto __return;
489                 }
490         }
491         key = compute_hash_instr_key(&instr->instr);
492         spin_lock_irqsave(&list->lock, flags);
493         instr->next = list->hash[key];
494         list->hash[key] = instr;
495         list->count++;
496         spin_unlock_irqrestore(&list->lock, flags);
497         snd_instr_unlock_ops(list);
498         result = 0;
499       __return:
500         instr_result(ev, SNDRV_SEQ_EVENT_INSTR_PUT, result, atomic);
501         return result;
502 }
503
504 static int instr_get(snd_seq_kinstr_ops_t *ops,
505                      snd_seq_kinstr_list_t *list,
506                      snd_seq_event_t *ev,
507                      int atomic, int hop)
508 {
509         return -ENXIO;
510 }
511
512 static int instr_free(snd_seq_kinstr_ops_t *ops,
513                       snd_seq_kinstr_list_t *list,
514                       snd_seq_event_t *ev,
515                       int atomic, int hop)
516 {
517         snd_seq_instr_header_t ifree;
518         snd_seq_kinstr_t *instr, *prev;
519         int result = -EINVAL;
520         unsigned long flags;
521         unsigned int hash;
522
523         if ((ev->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) != SNDRV_SEQ_EVENT_LENGTH_VARUSR)
524                 goto __return;
525
526         if (ev->data.ext.len < sizeof(snd_seq_instr_header_t))
527                 goto __return;
528         if (copy_from_user(&ifree, ev->data.ext.ptr, sizeof(snd_seq_instr_header_t))) {
529                 result = -EFAULT;
530                 goto __return;
531         }
532         if (ifree.cmd == SNDRV_SEQ_INSTR_FREE_CMD_ALL ||
533             ifree.cmd == SNDRV_SEQ_INSTR_FREE_CMD_PRIVATE ||
534             ifree.cmd == SNDRV_SEQ_INSTR_FREE_CMD_CLUSTER) {
535                 result = snd_seq_instr_list_free_cond(list, &ifree, ev->dest.client, atomic);
536                 goto __return;
537         }
538         if (ifree.cmd == SNDRV_SEQ_INSTR_FREE_CMD_SINGLE) {
539                 if (ifree.id.instr.std & 0xff000000) {
540                         ifree.id.instr.std &= 0x00ffffff;
541                         ifree.id.instr.std |= (unsigned int)ev->source.client << 24;
542                 }
543                 hash = compute_hash_instr_key(&ifree.id.instr);
544                 snd_instr_lock_ops(list);
545                 spin_lock_irqsave(&list->lock, flags);
546                 instr = list->hash[hash];
547                 prev = NULL;
548                 while (instr) {
549                         if (!compare_instr(&instr->instr, &ifree.id.instr, 1))
550                                 goto __free_single;
551                         prev = instr;
552                         instr = instr->next;
553                 }
554                 result = -ENOENT;
555                 spin_unlock_irqrestore(&list->lock, flags);
556                 snd_instr_unlock_ops(list);
557                 goto __return;
558                 
559               __free_single:
560                 if (prev) {
561                         prev->next = instr->next;
562                 } else {
563                         list->hash[hash] = instr->next;
564                 }
565                 if (instr->ops && instr->ops->notify)
566                         instr->ops->notify(instr->ops->private_data, instr, SNDRV_SEQ_INSTR_NOTIFY_REMOVE);
567                 while (instr->use) {
568                         spin_unlock_irqrestore(&list->lock, flags);
569                         set_current_state(TASK_INTERRUPTIBLE);
570                         schedule_timeout(1);
571                         spin_lock_irqsave(&list->lock, flags);
572                 }                               
573                 spin_unlock_irqrestore(&list->lock, flags);
574                 result = snd_seq_instr_free(instr, atomic);
575                 snd_instr_unlock_ops(list);
576                 goto __return;
577         }
578
579       __return:
580         instr_result(ev, SNDRV_SEQ_EVENT_INSTR_FREE, result, atomic);
581         return result;
582 }
583
584 static int instr_list(snd_seq_kinstr_ops_t *ops,
585                       snd_seq_kinstr_list_t *list,
586                       snd_seq_event_t *ev,
587                       int atomic, int hop)
588 {
589         return -ENXIO;
590 }
591
592 static int instr_cluster(snd_seq_kinstr_ops_t *ops,
593                          snd_seq_kinstr_list_t *list,
594                          snd_seq_event_t *ev,
595                          int atomic, int hop)
596 {
597         return -ENXIO;
598 }
599
600 int snd_seq_instr_event(snd_seq_kinstr_ops_t *ops,
601                         snd_seq_kinstr_list_t *list,
602                         snd_seq_event_t *ev,
603                         int client,
604                         int atomic,
605                         int hop)
606 {
607         int direct = 0;
608
609         snd_assert(ops != NULL && list != NULL && ev != NULL, return -EINVAL);
610         if (snd_seq_ev_is_direct(ev)) {
611                 direct = 1;
612                 switch (ev->type) {
613                 case SNDRV_SEQ_EVENT_INSTR_BEGIN:
614                         return instr_begin(ops, list, ev, atomic, hop);
615                 case SNDRV_SEQ_EVENT_INSTR_END:
616                         return instr_end(ops, list, ev, atomic, hop);
617                 }
618         }
619         if ((list->flags & SNDRV_SEQ_INSTR_FLG_DIRECT) && !direct)
620                 return -EINVAL;
621         switch (ev->type) {
622         case SNDRV_SEQ_EVENT_INSTR_INFO:
623                 return instr_info(ops, list, ev, atomic, hop);
624         case SNDRV_SEQ_EVENT_INSTR_FINFO:
625                 return instr_format_info(ops, list, ev, atomic, hop);
626         case SNDRV_SEQ_EVENT_INSTR_RESET:
627                 return instr_reset(ops, list, ev, atomic, hop);
628         case SNDRV_SEQ_EVENT_INSTR_STATUS:
629                 return instr_status(ops, list, ev, atomic, hop);
630         case SNDRV_SEQ_EVENT_INSTR_PUT:
631                 return instr_put(ops, list, ev, atomic, hop);
632         case SNDRV_SEQ_EVENT_INSTR_GET:
633                 return instr_get(ops, list, ev, atomic, hop);
634         case SNDRV_SEQ_EVENT_INSTR_FREE:
635                 return instr_free(ops, list, ev, atomic, hop);
636         case SNDRV_SEQ_EVENT_INSTR_LIST:
637                 return instr_list(ops, list, ev, atomic, hop);
638         case SNDRV_SEQ_EVENT_INSTR_CLUSTER:
639                 return instr_cluster(ops, list, ev, atomic, hop);
640         }
641         return -EINVAL;
642 }
643                         
644 /*
645  *  Init part
646  */
647
648 static int __init alsa_seq_instr_init(void)
649 {
650         return 0;
651 }
652
653 static void __exit alsa_seq_instr_exit(void)
654 {
655 }
656
657 module_init(alsa_seq_instr_init)
658 module_exit(alsa_seq_instr_exit)
659
660 EXPORT_SYMBOL(snd_seq_instr_list_new);
661 EXPORT_SYMBOL(snd_seq_instr_list_free);
662 EXPORT_SYMBOL(snd_seq_instr_list_free_cond);
663 EXPORT_SYMBOL(snd_seq_instr_find);
664 EXPORT_SYMBOL(snd_seq_instr_free_use);
665 EXPORT_SYMBOL(snd_seq_instr_event);