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#include <linux/export.h>
32#include <linux/moduleparam.h>
33#include <linux/slab.h>
34#include <linux/workqueue.h>
35
36/*
37 * common variables
38 */
39static int maxqlen = SNDRV_SEQ_OSS_MAX_QLEN;
40module_param(maxqlen, int, 0444);
41MODULE_PARM_DESC(maxqlen, "maximum queue length");
42
43static int system_client = -1; /* ALSA sequencer client number */
44static int system_port = -1;
45
46static int num_clients;
47static struct seq_oss_devinfo *client_table[SNDRV_SEQ_OSS_MAX_CLIENTS];
48
49
50/*
51 * prototypes
52 */
53static int receive_announce(struct snd_seq_event *ev, int direct, void *private, int atomic, int hop);
54static int translate_mode(struct file *file);
55static int create_port(struct seq_oss_devinfo *dp);
56static int delete_port(struct seq_oss_devinfo *dp);
57static int alloc_seq_queue(struct seq_oss_devinfo *dp);
58static int delete_seq_queue(int queue);
59static void free_devinfo(void *private);
60
61#define call_ctl(type,rec) snd_seq_kernel_client_ctl(system_client, type, rec)
62
63
64/* call snd_seq_oss_midi_lookup_ports() asynchronously */
65static void async_call_lookup_ports(struct work_struct *work)
66{
67	snd_seq_oss_midi_lookup_ports(system_client);
68}
69
70static DECLARE_WORK(async_lookup_work, async_call_lookup_ports);
71
72/*
73 * create sequencer client for OSS sequencer
74 */
75int __init
76snd_seq_oss_create_client(void)
77{
78	int rc;
79	struct snd_seq_port_info *port;
80	struct snd_seq_port_callback port_callback;
81
82	port = kmalloc(sizeof(*port), GFP_KERNEL);
83	if (!port) {
84		rc = -ENOMEM;
85		goto __error;
86	}
87
88	/* create ALSA client */
89	rc = snd_seq_create_kernel_client(NULL, SNDRV_SEQ_CLIENT_OSS,
90					  "OSS sequencer");
91	if (rc < 0)
92		goto __error;
93
94	system_client = rc;
95
96	/* create annoucement receiver port */
97	memset(port, 0, sizeof(*port));
98	strcpy(port->name, "Receiver");
99	port->addr.client = system_client;
100	port->capability = SNDRV_SEQ_PORT_CAP_WRITE; /* receive only */
101	port->type = 0;
102
103	memset(&port_callback, 0, sizeof(port_callback));
104	/* don't set port_callback.owner here. otherwise the module counter
105	 * is incremented and we can no longer release the module..
106	 */
107	port_callback.event_input = receive_announce;
108	port->kernel = &port_callback;
109
110	call_ctl(SNDRV_SEQ_IOCTL_CREATE_PORT, port);
111	if ((system_port = port->addr.port) >= 0) {
112		struct snd_seq_port_subscribe subs;
113
114		memset(&subs, 0, sizeof(subs));
115		subs.sender.client = SNDRV_SEQ_CLIENT_SYSTEM;
116		subs.sender.port = SNDRV_SEQ_PORT_SYSTEM_ANNOUNCE;
117		subs.dest.client = system_client;
118		subs.dest.port = system_port;
119		call_ctl(SNDRV_SEQ_IOCTL_SUBSCRIBE_PORT, &subs);
120	}
121	rc = 0;
122
123	/* look up midi devices */
124	schedule_work(&async_lookup_work);
125
126 __error:
127	kfree(port);
128	return rc;
129}
130
131
132/*
133 * receive annoucement from system port, and check the midi device
134 */
135static int
136receive_announce(struct snd_seq_event *ev, int direct, void *private, int atomic, int hop)
137{
138	struct snd_seq_port_info pinfo;
139
140	if (atomic)
141		return 0; /* it must not happen */
142
143	switch (ev->type) {
144	case SNDRV_SEQ_EVENT_PORT_START:
145	case SNDRV_SEQ_EVENT_PORT_CHANGE:
146		if (ev->data.addr.client == system_client)
147			break; /* ignore myself */
148		memset(&pinfo, 0, sizeof(pinfo));
149		pinfo.addr = ev->data.addr;
150		if (call_ctl(SNDRV_SEQ_IOCTL_GET_PORT_INFO, &pinfo) >= 0)
151			snd_seq_oss_midi_check_new_port(&pinfo);
152		break;
153
154	case SNDRV_SEQ_EVENT_PORT_EXIT:
155		if (ev->data.addr.client == system_client)
156			break; /* ignore myself */
157		snd_seq_oss_midi_check_exit_port(ev->data.addr.client,
158						ev->data.addr.port);
159		break;
160	}
161	return 0;
162}
163
164
165/*
166 * delete OSS sequencer client
167 */
168int
169snd_seq_oss_delete_client(void)
170{
171	cancel_work_sync(&async_lookup_work);
172	if (system_client >= 0)
173		snd_seq_delete_kernel_client(system_client);
174
175	snd_seq_oss_midi_clear_all();
176
177	return 0;
178}
179
180
181/*
182 * open sequencer device
183 */
184int
185snd_seq_oss_open(struct file *file, int level)
186{
187	int i, rc;
188	struct seq_oss_devinfo *dp;
189
190	dp = kzalloc(sizeof(*dp), GFP_KERNEL);
191	if (!dp)
192		return -ENOMEM;
193
194	dp->cseq = system_client;
195	dp->port = -1;
196	dp->queue = -1;
197
198	for (i = 0; i < SNDRV_SEQ_OSS_MAX_CLIENTS; i++) {
199		if (client_table[i] == NULL)
200			break;
201	}
202
203	dp->index = i;
204	if (i >= SNDRV_SEQ_OSS_MAX_CLIENTS) {
205		pr_debug("ALSA: seq_oss: too many applications\n");
206		rc = -ENOMEM;
207		goto _error;
208	}
209
210	/* look up synth and midi devices */
211	snd_seq_oss_synth_setup(dp);
212	snd_seq_oss_midi_setup(dp);
213
214	if (dp->synth_opened == 0 && dp->max_mididev == 0) {
215		/* pr_err("ALSA: seq_oss: no device found\n"); */
216		rc = -ENODEV;
217		goto _error;
218	}
219
220	/* create port */
221	rc = create_port(dp);
222	if (rc < 0) {
223		pr_err("ALSA: seq_oss: can't create port\n");
224		goto _error;
225	}
226
227	/* allocate queue */
228	rc = alloc_seq_queue(dp);
229	if (rc < 0)
230		goto _error;
231
232	/* set address */
233	dp->addr.client = dp->cseq;
234	dp->addr.port = dp->port;
235	/*dp->addr.queue = dp->queue;*/
236	/*dp->addr.channel = 0;*/
237
238	dp->seq_mode = level;
239
240	/* set up file mode */
241	dp->file_mode = translate_mode(file);
242
243	/* initialize read queue */
244	if (is_read_mode(dp->file_mode)) {
245		dp->readq = snd_seq_oss_readq_new(dp, maxqlen);
246		if (!dp->readq) {
247			rc = -ENOMEM;
248			goto _error;
249		}
250	}
251
252	/* initialize write queue */
253	if (is_write_mode(dp->file_mode)) {
254		dp->writeq = snd_seq_oss_writeq_new(dp, maxqlen);
255		if (!dp->writeq) {
256			rc = -ENOMEM;
257			goto _error;
258		}
259	}
260
261	/* initialize timer */
262	dp->timer = snd_seq_oss_timer_new(dp);
263	if (!dp->timer) {
264		pr_err("ALSA: seq_oss: can't alloc timer\n");
265		rc = -ENOMEM;
266		goto _error;
267	}
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	return 0;
282
283 _error:
284	snd_seq_oss_synth_cleanup(dp);
285	snd_seq_oss_midi_cleanup(dp);
286	delete_seq_queue(dp->queue);
287	delete_port(dp);
288
289	return rc;
290}
291
292/*
293 * translate file flags to private mode
294 */
295static int
296translate_mode(struct file *file)
297{
298	int file_mode = 0;
299	if ((file->f_flags & O_ACCMODE) != O_RDONLY)
300		file_mode |= SNDRV_SEQ_OSS_FILE_WRITE;
301	if ((file->f_flags & O_ACCMODE) != O_WRONLY)
302		file_mode |= SNDRV_SEQ_OSS_FILE_READ;
303	if (file->f_flags & O_NONBLOCK)
304		file_mode |= SNDRV_SEQ_OSS_FILE_NONBLOCK;
305	return file_mode;
306}
307
308
309/*
310 * create sequencer port
311 */
312static int
313create_port(struct seq_oss_devinfo *dp)
314{
315	int rc;
316	struct snd_seq_port_info port;
317	struct snd_seq_port_callback callback;
318
319	memset(&port, 0, sizeof(port));
320	port.addr.client = dp->cseq;
321	sprintf(port.name, "Sequencer-%d", dp->index);
322	port.capability = SNDRV_SEQ_PORT_CAP_READ|SNDRV_SEQ_PORT_CAP_WRITE; /* no subscription */
323	port.type = SNDRV_SEQ_PORT_TYPE_SPECIFIC;
324	port.midi_channels = 128;
325	port.synth_voices = 128;
326
327	memset(&callback, 0, sizeof(callback));
328	callback.owner = THIS_MODULE;
329	callback.private_data = dp;
330	callback.event_input = snd_seq_oss_event_input;
331	callback.private_free = free_devinfo;
332	port.kernel = &callback;
333
334	rc = call_ctl(SNDRV_SEQ_IOCTL_CREATE_PORT, &port);
335	if (rc < 0)
336		return rc;
337
338	dp->port = port.addr.port;
339
340	return 0;
341}
342
343/*
344 * delete ALSA port
345 */
346static int
347delete_port(struct seq_oss_devinfo *dp)
348{
349	if (dp->port < 0) {
350		kfree(dp);
351		return 0;
352	}
353
354	return snd_seq_event_port_detach(dp->cseq, dp->port);
355}
356
357/*
358 * allocate a queue
359 */
360static int
361alloc_seq_queue(struct seq_oss_devinfo *dp)
362{
363	struct snd_seq_queue_info qinfo;
364	int rc;
365
366	memset(&qinfo, 0, sizeof(qinfo));
367	qinfo.owner = system_client;
368	qinfo.locked = 1;
369	strcpy(qinfo.name, "OSS Sequencer Emulation");
370	if ((rc = call_ctl(SNDRV_SEQ_IOCTL_CREATE_QUEUE, &qinfo)) < 0)
371		return rc;
372	dp->queue = qinfo.queue;
373	return 0;
374}
375
376/*
377 * release queue
378 */
379static int
380delete_seq_queue(int queue)
381{
382	struct snd_seq_queue_info qinfo;
383	int rc;
384
385	if (queue < 0)
386		return 0;
387	memset(&qinfo, 0, sizeof(qinfo));
388	qinfo.queue = queue;
389	rc = call_ctl(SNDRV_SEQ_IOCTL_DELETE_QUEUE, &qinfo);
390	if (rc < 0)
391		pr_err("ALSA: seq_oss: unable to delete queue %d (%d)\n", queue, rc);
392	return rc;
393}
394
395
396/*
397 * free device informations - private_free callback of port
398 */
399static void
400free_devinfo(void *private)
401{
402	struct seq_oss_devinfo *dp = (struct seq_oss_devinfo *)private;
403
404	snd_seq_oss_timer_delete(dp->timer);
405
406	snd_seq_oss_writeq_delete(dp->writeq);
407
408	snd_seq_oss_readq_delete(dp->readq);
409
410	kfree(dp);
411}
412
413
414/*
415 * close sequencer device
416 */
417void
418snd_seq_oss_release(struct seq_oss_devinfo *dp)
419{
420	int queue;
421
422	client_table[dp->index] = NULL;
423	num_clients--;
424
425	snd_seq_oss_reset(dp);
426
427	snd_seq_oss_synth_cleanup(dp);
428	snd_seq_oss_midi_cleanup(dp);
429
430	/* clear slot */
431	queue = dp->queue;
432	if (dp->port >= 0)
433		delete_port(dp);
434	delete_seq_queue(queue);
435}
436
437
438/*
439 * reset sequencer devices
440 */
441void
442snd_seq_oss_reset(struct seq_oss_devinfo *dp)
443{
444	int i;
445
446	/* reset all synth devices */
447	for (i = 0; i < dp->max_synthdev; i++)
448		snd_seq_oss_synth_reset(dp, i);
449
450	/* reset all midi devices */
451	if (dp->seq_mode != SNDRV_SEQ_OSS_MODE_MUSIC) {
452		for (i = 0; i < dp->max_mididev; i++)
453			snd_seq_oss_midi_reset(dp, i);
454	}
455
456	/* remove queues */
457	if (dp->readq)
458		snd_seq_oss_readq_clear(dp->readq);
459	if (dp->writeq)
460		snd_seq_oss_writeq_clear(dp->writeq);
461
462	/* reset timer */
463	snd_seq_oss_timer_stop(dp->timer);
464}
465
466
467#ifdef CONFIG_PROC_FS
468/*
469 * misc. functions for proc interface
470 */
471char *
472enabled_str(int bool)
473{
474	return bool ? "enabled" : "disabled";
475}
476
477static char *
478filemode_str(int val)
479{
480	static char *str[] = {
481		"none", "read", "write", "read/write",
482	};
483	return str[val & SNDRV_SEQ_OSS_FILE_ACMODE];
484}
485
486
487/*
488 * proc interface
489 */
490void
491snd_seq_oss_system_info_read(struct snd_info_buffer *buf)
492{
493	int i;
494	struct seq_oss_devinfo *dp;
495
496	snd_iprintf(buf, "ALSA client number %d\n", system_client);
497	snd_iprintf(buf, "ALSA receiver port %d\n", system_port);
498
499	snd_iprintf(buf, "\nNumber of applications: %d\n", num_clients);
500	for (i = 0; i < num_clients; i++) {
501		snd_iprintf(buf, "\nApplication %d: ", i);
502		if ((dp = client_table[i]) == NULL) {
503			snd_iprintf(buf, "*empty*\n");
504			continue;
505		}
506		snd_iprintf(buf, "port %d : queue %d\n", dp->port, dp->queue);
507		snd_iprintf(buf, "  sequencer mode = %s : file open mode = %s\n",
508			    (dp->seq_mode ? "music" : "synth"),
509			    filemode_str(dp->file_mode));
510		if (dp->seq_mode)
511			snd_iprintf(buf, "  timer tempo = %d, timebase = %d\n",
512				    dp->timer->oss_tempo, dp->timer->oss_timebase);
513		snd_iprintf(buf, "  max queue length %d\n", maxqlen);
514		if (is_read_mode(dp->file_mode) && dp->readq)
515			snd_seq_oss_readq_info_read(dp->readq, buf);
516	}
517}
518#endif /* CONFIG_PROC_FS */
519