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