brintos

brintos / linux-shallow public Read only

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