ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.6.tar.bz2
[linux-2.6.git] / sound / core / seq / oss / seq_oss_init.c
1 /*
2  * OSS compatible sequencer driver
3  *
4  * open/close and reset interface
5  *
6  * Copyright (C) 1998-1999 Takashi Iwai <tiwai@suse.de>
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
21  */
22
23 #include "seq_oss_device.h"
24 #include "seq_oss_synth.h"
25 #include "seq_oss_midi.h"
26 #include "seq_oss_writeq.h"
27 #include "seq_oss_readq.h"
28 #include "seq_oss_timer.h"
29 #include "seq_oss_event.h"
30 #include <linux/init.h>
31
32 /*
33  * common variables
34  */
35 MODULE_PARM(maxqlen, "i");
36 MODULE_PARM_DESC(maxqlen, "maximum queue length");
37
38 static int system_client = -1; /* ALSA sequencer client number */
39 static int system_port = -1;
40
41 int maxqlen = SNDRV_SEQ_OSS_MAX_QLEN;
42 static int num_clients;
43 static seq_oss_devinfo_t *client_table[SNDRV_SEQ_OSS_MAX_CLIENTS];
44
45
46 /*
47  * prototypes
48  */
49 static int receive_announce(snd_seq_event_t *ev, int direct, void *private, int atomic, int hop);
50 static int translate_mode(struct file *file);
51 static int create_port(seq_oss_devinfo_t *dp);
52 static int delete_port(seq_oss_devinfo_t *dp);
53 static int alloc_seq_queue(seq_oss_devinfo_t *dp);
54 static int delete_seq_queue(int queue);
55 static void free_devinfo(void *private);
56
57 #define call_ctl(type,rec) snd_seq_kernel_client_ctl(system_client, type, rec)
58
59
60 /*
61  * create sequencer client for OSS sequencer
62  */
63 int __init
64 snd_seq_oss_create_client(void)
65 {
66         int rc;
67         snd_seq_client_callback_t callback;
68         snd_seq_client_info_t info;
69         snd_seq_port_info_t port;
70         snd_seq_port_callback_t port_callback;
71
72         /* create ALSA client */
73         memset(&callback, 0, sizeof(callback));
74
75         callback.private_data = NULL;
76         callback.allow_input = 1;
77         callback.allow_output = 1;
78
79         rc = snd_seq_create_kernel_client(NULL, SNDRV_SEQ_CLIENT_OSS, &callback);
80         if (rc < 0)
81                 return rc;
82
83         system_client = rc;
84         debug_printk(("new client = %d\n", rc));
85
86         /* set client information */
87         memset(&info, 0, sizeof(info));
88         info.client = system_client;
89         info.type = KERNEL_CLIENT;
90         strcpy(info.name, "OSS sequencer");
91
92         rc = call_ctl(SNDRV_SEQ_IOCTL_SET_CLIENT_INFO, &info);
93
94         /* look up midi devices */
95         snd_seq_oss_midi_lookup_ports(system_client);
96
97         /* create annoucement receiver port */
98         memset(&port, 0, sizeof(port));
99         strcpy(port.name, "Receiver");
100         port.addr.client = system_client;
101         port.capability = SNDRV_SEQ_PORT_CAP_WRITE; /* receive only */
102         port.type = 0;
103
104         memset(&port_callback, 0, sizeof(port_callback));
105         /* don't set port_callback.owner here. otherwise the module counter
106          * is incremented and we can no longer release the module..
107          */
108         port_callback.event_input = receive_announce;
109         port.kernel = &port_callback;
110         
111         call_ctl(SNDRV_SEQ_IOCTL_CREATE_PORT, &port);
112         if ((system_port = port.addr.port) >= 0) {
113                 snd_seq_port_subscribe_t subs;
114
115                 memset(&subs, 0, sizeof(subs));
116                 subs.sender.client = SNDRV_SEQ_CLIENT_SYSTEM;
117                 subs.sender.port = SNDRV_SEQ_PORT_SYSTEM_ANNOUNCE;
118                 subs.dest.client = system_client;
119                 subs.dest.port = system_port;
120                 call_ctl(SNDRV_SEQ_IOCTL_SUBSCRIBE_PORT, &subs);
121         }
122
123
124         return 0;
125 }
126
127
128 /*
129  * receive annoucement from system port, and check the midi device
130  */
131 static int
132 receive_announce(snd_seq_event_t *ev, int direct, void *private, int atomic, int hop)
133 {
134         snd_seq_port_info_t pinfo;
135
136         if (atomic)
137                 return 0; /* it must not happen */
138
139         switch (ev->type) {
140         case SNDRV_SEQ_EVENT_PORT_START:
141         case SNDRV_SEQ_EVENT_PORT_CHANGE:
142                 if (ev->data.addr.client == system_client)
143                         break; /* ignore myself */
144                 memset(&pinfo, 0, sizeof(pinfo));
145                 pinfo.addr = ev->data.addr;
146                 if (call_ctl(SNDRV_SEQ_IOCTL_GET_PORT_INFO, &pinfo) >= 0)
147                         snd_seq_oss_midi_check_new_port(&pinfo);
148                 break;
149
150         case SNDRV_SEQ_EVENT_PORT_EXIT:
151                 if (ev->data.addr.client == system_client)
152                         break; /* ignore myself */
153                 snd_seq_oss_midi_check_exit_port(ev->data.addr.client,
154                                                 ev->data.addr.port);
155                 break;
156         }
157         return 0;
158 }
159
160
161 /*
162  * delete OSS sequencer client
163  */
164 int
165 snd_seq_oss_delete_client(void)
166 {
167         if (system_client >= 0)
168                 snd_seq_delete_kernel_client(system_client);
169
170         snd_seq_oss_midi_clear_all();
171
172         return 0;
173 }
174
175
176 /*
177  * open sequencer device
178  */
179 int
180 snd_seq_oss_open(struct file *file, int level)
181 {
182         int i, rc;
183         seq_oss_devinfo_t *dp;
184
185         if ((dp = snd_kcalloc(sizeof(*dp), GFP_KERNEL)) == NULL) {
186                 snd_printk(KERN_ERR "can't malloc device info\n");
187                 return -ENOMEM;
188         }
189         debug_printk(("oss_open: dp = %p\n", dp));
190
191         for (i = 0; i < SNDRV_SEQ_OSS_MAX_CLIENTS; i++) {
192                 if (client_table[i] == NULL)
193                         break;
194         }
195         if (i >= SNDRV_SEQ_OSS_MAX_CLIENTS) {
196                 snd_printk(KERN_ERR "too many applications\n");
197                 kfree(dp);
198                 return -ENOMEM;
199         }
200
201         dp->index = i;
202         dp->cseq = system_client;
203         dp->port = -1;
204         dp->queue = -1;
205         dp->readq = NULL;
206         dp->writeq = NULL;
207
208         /* look up synth and midi devices */
209         snd_seq_oss_synth_setup(dp);
210         snd_seq_oss_midi_setup(dp);
211
212         if (dp->synth_opened == 0 && dp->max_mididev == 0) {
213                 snd_printk(KERN_ERR "no device found\n");
214                 rc = -ENODEV;
215                 goto _error;
216         }
217
218         /* create port */
219         debug_printk(("create new port\n"));
220         if ((rc = create_port(dp)) < 0) {
221                 snd_printk(KERN_ERR "can't create port\n");
222                 goto _error;
223         }
224
225         /* allocate queue */
226         debug_printk(("allocate queue\n"));
227         if ((rc = alloc_seq_queue(dp)) < 0)
228                 goto _error;
229
230         /* set address */
231         dp->addr.client = dp->cseq;
232         dp->addr.port = dp->port;
233         /*dp->addr.queue = dp->queue;*/
234         /*dp->addr.channel = 0;*/
235
236         dp->seq_mode = level;
237
238         /* set up file mode */
239         dp->file_mode = translate_mode(file);
240
241         /* initialize read queue */
242         debug_printk(("initialize read queue\n"));
243         if (is_read_mode(dp->file_mode)) {
244                 if ((dp->readq = snd_seq_oss_readq_new(dp, maxqlen)) == NULL) {
245                         rc = -ENOMEM;
246                         goto _error;
247                 }
248         }
249
250         /* initialize write queue */
251         debug_printk(("initialize write queue\n"));
252         if (is_write_mode(dp->file_mode)) {
253                 dp->writeq = snd_seq_oss_writeq_new(dp, maxqlen);
254                 if (dp->writeq == NULL) {
255                         rc = -ENOMEM;
256                         goto _error;
257                 }
258         }
259
260         /* initialize timer */
261         debug_printk(("initialize timer\n"));
262         if ((dp->timer = snd_seq_oss_timer_new(dp)) == NULL) {
263                 snd_printk(KERN_ERR "can't alloc timer\n");
264                 rc = -ENOMEM;
265                 goto _error;
266         }
267         debug_printk(("timer initialized\n"));
268
269         /* set private data pointer */
270         file->private_data = dp;
271
272         /* set up for mode2 */
273         if (level == SNDRV_SEQ_OSS_MODE_MUSIC)
274                 snd_seq_oss_synth_setup_midi(dp);
275         else if (is_read_mode(dp->file_mode))
276                 snd_seq_oss_midi_open_all(dp, SNDRV_SEQ_OSS_FILE_READ);
277
278         client_table[dp->index] = dp;
279         num_clients++;
280
281         debug_printk(("open done\n"));
282         return 0;
283
284  _error:
285         snd_seq_oss_synth_cleanup(dp);
286         snd_seq_oss_midi_cleanup(dp);
287         i = dp->queue;
288         delete_port(dp);
289         delete_seq_queue(i);
290
291         return rc;
292 }
293
294 /*
295  * translate file flags to private mode
296  */
297 static int
298 translate_mode(struct file *file)
299 {
300         int file_mode = 0;
301         if ((file->f_flags & O_ACCMODE) != O_RDONLY)
302                 file_mode |= SNDRV_SEQ_OSS_FILE_WRITE;
303         if ((file->f_flags & O_ACCMODE) != O_WRONLY)
304                 file_mode |= SNDRV_SEQ_OSS_FILE_READ;
305         if (file->f_flags & O_NONBLOCK)
306                 file_mode |= SNDRV_SEQ_OSS_FILE_NONBLOCK;
307         return file_mode;
308 }
309
310
311 /*
312  * create sequencer port
313  */
314 static int
315 create_port(seq_oss_devinfo_t *dp)
316 {
317         int rc;
318         snd_seq_port_info_t port;
319         snd_seq_port_callback_t callback;
320
321         memset(&port, 0, sizeof(port));
322         port.addr.client = dp->cseq;
323         sprintf(port.name, "Sequencer-%d", dp->index);
324         port.capability = SNDRV_SEQ_PORT_CAP_READ|SNDRV_SEQ_PORT_CAP_WRITE; /* no subscription */
325         port.type = SNDRV_SEQ_PORT_TYPE_SPECIFIC;
326         port.midi_channels = 128;
327         port.synth_voices = 128;
328
329         memset(&callback, 0, sizeof(callback));
330         callback.owner = THIS_MODULE;
331         callback.private_data = dp;
332         callback.event_input = snd_seq_oss_event_input;
333         callback.private_free = free_devinfo;
334         port.kernel = &callback;
335
336         rc = call_ctl(SNDRV_SEQ_IOCTL_CREATE_PORT, &port);
337         if (rc < 0)
338                 return rc;
339
340         dp->port = port.addr.port;
341         debug_printk(("new port = %d\n", port.addr.port));
342
343         return 0;
344 }
345
346 /*
347  * delete ALSA port
348  */
349 static int
350 delete_port(seq_oss_devinfo_t *dp)
351 {
352         if (dp->port < 0)
353                 return 0;
354
355         debug_printk(("delete_port %i\n", dp->port));
356         return snd_seq_event_port_detach(dp->cseq, dp->port);
357 }
358
359 /*
360  * allocate a queue
361  */
362 static int
363 alloc_seq_queue(seq_oss_devinfo_t *dp)
364 {
365         snd_seq_queue_info_t qinfo;
366         int rc;
367
368         memset(&qinfo, 0, sizeof(qinfo));
369         qinfo.owner = system_client;
370         qinfo.locked = 1;
371         strcpy(qinfo.name, "OSS Sequencer Emulation");
372         if ((rc = call_ctl(SNDRV_SEQ_IOCTL_CREATE_QUEUE, &qinfo)) < 0)
373                 return rc;
374         dp->queue = qinfo.queue;
375         return 0;
376 }
377
378 /*
379  * release queue
380  */
381 static int
382 delete_seq_queue(int queue)
383 {
384         snd_seq_queue_info_t qinfo;
385         int rc;
386
387         if (queue < 0)
388                 return 0;
389         memset(&qinfo, 0, sizeof(qinfo));
390         qinfo.queue = queue;
391         rc = call_ctl(SNDRV_SEQ_IOCTL_DELETE_QUEUE, &qinfo);
392         if (rc < 0)
393                 printk(KERN_ERR "seq-oss: unable to delete queue %d (%d)\n", queue, rc);
394         return rc;
395 }
396
397
398 /*
399  * free device informations - private_free callback of port
400  */
401 static void
402 free_devinfo(void *private)
403 {
404         seq_oss_devinfo_t *dp = (seq_oss_devinfo_t *)private;
405
406         if (dp->timer)
407                 snd_seq_oss_timer_delete(dp->timer);
408                 
409         if (dp->writeq)
410                 snd_seq_oss_writeq_delete(dp->writeq);
411
412         if (dp->readq)
413                 snd_seq_oss_readq_delete(dp->readq);
414         
415         kfree(dp);
416 }
417
418
419 /*
420  * close sequencer device
421  */
422 void
423 snd_seq_oss_release(seq_oss_devinfo_t *dp)
424 {
425         int queue;
426
427         client_table[dp->index] = NULL;
428         num_clients--;
429
430         debug_printk(("resetting..\n"));
431         snd_seq_oss_reset(dp);
432
433         debug_printk(("cleaning up..\n"));
434         snd_seq_oss_synth_cleanup(dp);
435         snd_seq_oss_midi_cleanup(dp);
436
437         /* clear slot */
438         debug_printk(("releasing resource..\n"));
439         queue = dp->queue;
440         if (dp->port >= 0)
441                 delete_port(dp);
442         delete_seq_queue(queue);
443
444         debug_printk(("release done\n"));
445 }
446
447
448 /*
449  * Wait until the queue is empty (if we don't have nonblock)
450  */
451 void
452 snd_seq_oss_drain_write(seq_oss_devinfo_t *dp)
453 {
454         if (! dp->timer->running)
455                 return;
456         if (is_write_mode(dp->file_mode) && !is_nonblock_mode(dp->file_mode) &&
457             dp->writeq) {
458                 debug_printk(("syncing..\n"));
459                 while (snd_seq_oss_writeq_sync(dp->writeq))
460                         ;
461         }
462 }
463
464
465 /*
466  * reset sequencer devices
467  */
468 void
469 snd_seq_oss_reset(seq_oss_devinfo_t *dp)
470 {
471         int i;
472
473         /* reset all synth devices */
474         for (i = 0; i < dp->max_synthdev; i++)
475                 snd_seq_oss_synth_reset(dp, i);
476
477         /* reset all midi devices */
478         if (dp->seq_mode != SNDRV_SEQ_OSS_MODE_MUSIC) {
479                 for (i = 0; i < dp->max_mididev; i++)
480                         snd_seq_oss_midi_reset(dp, i);
481         }
482
483         /* remove queues */
484         if (dp->readq)
485                 snd_seq_oss_readq_clear(dp->readq);
486         if (dp->writeq)
487                 snd_seq_oss_writeq_clear(dp->writeq);
488
489         /* reset timer */
490         snd_seq_oss_timer_stop(dp->timer);
491 }
492
493 /*
494  * proc interface
495  */
496 void
497 snd_seq_oss_system_info_read(snd_info_buffer_t *buf)
498 {
499         int i;
500         seq_oss_devinfo_t *dp;
501
502         snd_iprintf(buf, "ALSA client number %d\n", system_client);
503         snd_iprintf(buf, "ALSA receiver port %d\n", system_port);
504
505         snd_iprintf(buf, "\nNumber of applications: %d\n", num_clients);
506         for (i = 0; i < num_clients; i++) {
507                 snd_iprintf(buf, "\nApplication %d: ", i);
508                 if ((dp = client_table[i]) == NULL) {
509                         snd_iprintf(buf, "*empty*\n");
510                         continue;
511                 }
512                 snd_iprintf(buf, "port %d : queue %d\n", dp->port, dp->queue);
513                 snd_iprintf(buf, "  sequencer mode = %s : file open mode = %s\n",
514                             (dp->seq_mode ? "music" : "synth"),
515                             filemode_str(dp->file_mode));
516                 if (dp->seq_mode)
517                         snd_iprintf(buf, "  timer tempo = %d, timebase = %d\n",
518                                     dp->timer->oss_tempo, dp->timer->oss_timebase);
519                 snd_iprintf(buf, "  max queue length %d\n", maxqlen);
520                 if (is_read_mode(dp->file_mode) && dp->readq)
521                         snd_seq_oss_readq_info_read(dp->readq, buf);
522         }
523 }
524
525 /*
526  * misc. functions for proc interface
527  */
528 char *
529 enabled_str(int bool)
530 {
531         return bool ? "enabled" : "disabled";
532 }
533
534 char *
535 filemode_str(int val)
536 {
537         static char *str[] = {
538                 "none", "read", "write", "read/write",
539         };
540         return str[val & SNDRV_SEQ_OSS_FILE_ACMODE];
541 }
542
543