cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

seq_oss_init.c (10858B)


      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 */
     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 * prototypes
     39 */
     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 sequencer
     61 */
     62int __init
     63snd_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 */
    121static int
    122receive_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 */
    154int
    155snd_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 */
    170int
    171snd_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 */
    281static int
    282translate_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 */
    298static int
    299create_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 */
    332static int
    333delete_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 */
    346static int
    347alloc_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 */
    366static int
    367delete_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 */
    386static void
    387free_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 */
    404void
    405snd_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 */
    428void
    429snd_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 */
    457char *
    458enabled_str(int bool)
    459{
    460	return bool ? "enabled" : "disabled";
    461}
    462
    463static const char *
    464filemode_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 */
    476void
    477snd_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 */