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