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_virmidi.c (13484B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 *  Virtual Raw MIDI client on Sequencer
      4 *
      5 *  Copyright (c) 2000 by Takashi Iwai <tiwai@suse.de>,
      6 *                        Jaroslav Kysela <perex@perex.cz>
      7 */
      8
      9/*
     10 * Virtual Raw MIDI client
     11 *
     12 * The virtual rawmidi client is a sequencer client which associate
     13 * a rawmidi device file.  The created rawmidi device file can be
     14 * accessed as a normal raw midi, but its MIDI source and destination
     15 * are arbitrary.  For example, a user-client software synth connected
     16 * to this port can be used as a normal midi device as well.
     17 *
     18 * The virtual rawmidi device accepts also multiple opens.  Each file
     19 * has its own input buffer, so that no conflict would occur.  The drain
     20 * of input/output buffer acts only to the local buffer.
     21 *
     22 */
     23
     24#include <linux/init.h>
     25#include <linux/wait.h>
     26#include <linux/module.h>
     27#include <linux/slab.h>
     28#include <sound/core.h>
     29#include <sound/rawmidi.h>
     30#include <sound/info.h>
     31#include <sound/control.h>
     32#include <sound/minors.h>
     33#include <sound/seq_kernel.h>
     34#include <sound/seq_midi_event.h>
     35#include <sound/seq_virmidi.h>
     36
     37MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");
     38MODULE_DESCRIPTION("Virtual Raw MIDI client on Sequencer");
     39MODULE_LICENSE("GPL");
     40
     41/*
     42 * initialize an event record
     43 */
     44static void snd_virmidi_init_event(struct snd_virmidi *vmidi,
     45				   struct snd_seq_event *ev)
     46{
     47	memset(ev, 0, sizeof(*ev));
     48	ev->source.port = vmidi->port;
     49	switch (vmidi->seq_mode) {
     50	case SNDRV_VIRMIDI_SEQ_DISPATCH:
     51		ev->dest.client = SNDRV_SEQ_ADDRESS_SUBSCRIBERS;
     52		break;
     53	case SNDRV_VIRMIDI_SEQ_ATTACH:
     54		/* FIXME: source and destination are same - not good.. */
     55		ev->dest.client = vmidi->client;
     56		ev->dest.port = vmidi->port;
     57		break;
     58	}
     59	ev->type = SNDRV_SEQ_EVENT_NONE;
     60}
     61
     62/*
     63 * decode input event and put to read buffer of each opened file
     64 */
     65static int snd_virmidi_dev_receive_event(struct snd_virmidi_dev *rdev,
     66					 struct snd_seq_event *ev,
     67					 bool atomic)
     68{
     69	struct snd_virmidi *vmidi;
     70	unsigned char msg[4];
     71	int len;
     72
     73	if (atomic)
     74		read_lock(&rdev->filelist_lock);
     75	else
     76		down_read(&rdev->filelist_sem);
     77	list_for_each_entry(vmidi, &rdev->filelist, list) {
     78		if (!READ_ONCE(vmidi->trigger))
     79			continue;
     80		if (ev->type == SNDRV_SEQ_EVENT_SYSEX) {
     81			if ((ev->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) != SNDRV_SEQ_EVENT_LENGTH_VARIABLE)
     82				continue;
     83			snd_seq_dump_var_event(ev, (snd_seq_dump_func_t)snd_rawmidi_receive, vmidi->substream);
     84			snd_midi_event_reset_decode(vmidi->parser);
     85		} else {
     86			len = snd_midi_event_decode(vmidi->parser, msg, sizeof(msg), ev);
     87			if (len > 0)
     88				snd_rawmidi_receive(vmidi->substream, msg, len);
     89		}
     90	}
     91	if (atomic)
     92		read_unlock(&rdev->filelist_lock);
     93	else
     94		up_read(&rdev->filelist_sem);
     95
     96	return 0;
     97}
     98
     99/*
    100 * event handler of virmidi port
    101 */
    102static int snd_virmidi_event_input(struct snd_seq_event *ev, int direct,
    103				   void *private_data, int atomic, int hop)
    104{
    105	struct snd_virmidi_dev *rdev;
    106
    107	rdev = private_data;
    108	if (!(rdev->flags & SNDRV_VIRMIDI_USE))
    109		return 0; /* ignored */
    110	return snd_virmidi_dev_receive_event(rdev, ev, atomic);
    111}
    112
    113/*
    114 * trigger rawmidi stream for input
    115 */
    116static void snd_virmidi_input_trigger(struct snd_rawmidi_substream *substream, int up)
    117{
    118	struct snd_virmidi *vmidi = substream->runtime->private_data;
    119
    120	WRITE_ONCE(vmidi->trigger, !!up);
    121}
    122
    123/* process rawmidi bytes and send events;
    124 * we need no lock here for vmidi->event since it's handled only in this work
    125 */
    126static void snd_vmidi_output_work(struct work_struct *work)
    127{
    128	struct snd_virmidi *vmidi;
    129	struct snd_rawmidi_substream *substream;
    130	unsigned char input;
    131	int ret;
    132
    133	vmidi = container_of(work, struct snd_virmidi, output_work);
    134	substream = vmidi->substream;
    135
    136	/* discard the outputs in dispatch mode unless subscribed */
    137	if (vmidi->seq_mode == SNDRV_VIRMIDI_SEQ_DISPATCH &&
    138	    !(vmidi->rdev->flags & SNDRV_VIRMIDI_SUBSCRIBE)) {
    139		snd_rawmidi_proceed(substream);
    140		return;
    141	}
    142
    143	while (READ_ONCE(vmidi->trigger)) {
    144		if (snd_rawmidi_transmit(substream, &input, 1) != 1)
    145			break;
    146		if (!snd_midi_event_encode_byte(vmidi->parser, input,
    147						&vmidi->event))
    148			continue;
    149		if (vmidi->event.type != SNDRV_SEQ_EVENT_NONE) {
    150			ret = snd_seq_kernel_client_dispatch(vmidi->client,
    151							     &vmidi->event,
    152							     false, 0);
    153			vmidi->event.type = SNDRV_SEQ_EVENT_NONE;
    154			if (ret < 0)
    155				break;
    156		}
    157		/* rawmidi input might be huge, allow to have a break */
    158		cond_resched();
    159	}
    160}
    161
    162/*
    163 * trigger rawmidi stream for output
    164 */
    165static void snd_virmidi_output_trigger(struct snd_rawmidi_substream *substream, int up)
    166{
    167	struct snd_virmidi *vmidi = substream->runtime->private_data;
    168
    169	WRITE_ONCE(vmidi->trigger, !!up);
    170	if (up)
    171		queue_work(system_highpri_wq, &vmidi->output_work);
    172}
    173
    174/*
    175 * open rawmidi handle for input
    176 */
    177static int snd_virmidi_input_open(struct snd_rawmidi_substream *substream)
    178{
    179	struct snd_virmidi_dev *rdev = substream->rmidi->private_data;
    180	struct snd_rawmidi_runtime *runtime = substream->runtime;
    181	struct snd_virmidi *vmidi;
    182
    183	vmidi = kzalloc(sizeof(*vmidi), GFP_KERNEL);
    184	if (vmidi == NULL)
    185		return -ENOMEM;
    186	vmidi->substream = substream;
    187	if (snd_midi_event_new(0, &vmidi->parser) < 0) {
    188		kfree(vmidi);
    189		return -ENOMEM;
    190	}
    191	vmidi->seq_mode = rdev->seq_mode;
    192	vmidi->client = rdev->client;
    193	vmidi->port = rdev->port;	
    194	runtime->private_data = vmidi;
    195	down_write(&rdev->filelist_sem);
    196	write_lock_irq(&rdev->filelist_lock);
    197	list_add_tail(&vmidi->list, &rdev->filelist);
    198	write_unlock_irq(&rdev->filelist_lock);
    199	up_write(&rdev->filelist_sem);
    200	vmidi->rdev = rdev;
    201	return 0;
    202}
    203
    204/*
    205 * open rawmidi handle for output
    206 */
    207static int snd_virmidi_output_open(struct snd_rawmidi_substream *substream)
    208{
    209	struct snd_virmidi_dev *rdev = substream->rmidi->private_data;
    210	struct snd_rawmidi_runtime *runtime = substream->runtime;
    211	struct snd_virmidi *vmidi;
    212
    213	vmidi = kzalloc(sizeof(*vmidi), GFP_KERNEL);
    214	if (vmidi == NULL)
    215		return -ENOMEM;
    216	vmidi->substream = substream;
    217	if (snd_midi_event_new(MAX_MIDI_EVENT_BUF, &vmidi->parser) < 0) {
    218		kfree(vmidi);
    219		return -ENOMEM;
    220	}
    221	vmidi->seq_mode = rdev->seq_mode;
    222	vmidi->client = rdev->client;
    223	vmidi->port = rdev->port;
    224	snd_virmidi_init_event(vmidi, &vmidi->event);
    225	vmidi->rdev = rdev;
    226	INIT_WORK(&vmidi->output_work, snd_vmidi_output_work);
    227	runtime->private_data = vmidi;
    228	return 0;
    229}
    230
    231/*
    232 * close rawmidi handle for input
    233 */
    234static int snd_virmidi_input_close(struct snd_rawmidi_substream *substream)
    235{
    236	struct snd_virmidi_dev *rdev = substream->rmidi->private_data;
    237	struct snd_virmidi *vmidi = substream->runtime->private_data;
    238
    239	down_write(&rdev->filelist_sem);
    240	write_lock_irq(&rdev->filelist_lock);
    241	list_del(&vmidi->list);
    242	write_unlock_irq(&rdev->filelist_lock);
    243	up_write(&rdev->filelist_sem);
    244	snd_midi_event_free(vmidi->parser);
    245	substream->runtime->private_data = NULL;
    246	kfree(vmidi);
    247	return 0;
    248}
    249
    250/*
    251 * close rawmidi handle for output
    252 */
    253static int snd_virmidi_output_close(struct snd_rawmidi_substream *substream)
    254{
    255	struct snd_virmidi *vmidi = substream->runtime->private_data;
    256
    257	WRITE_ONCE(vmidi->trigger, false); /* to be sure */
    258	cancel_work_sync(&vmidi->output_work);
    259	snd_midi_event_free(vmidi->parser);
    260	substream->runtime->private_data = NULL;
    261	kfree(vmidi);
    262	return 0;
    263}
    264
    265/*
    266 * drain output work queue
    267 */
    268static void snd_virmidi_output_drain(struct snd_rawmidi_substream *substream)
    269{
    270	struct snd_virmidi *vmidi = substream->runtime->private_data;
    271
    272	flush_work(&vmidi->output_work);
    273}
    274
    275/*
    276 * subscribe callback - allow output to rawmidi device
    277 */
    278static int snd_virmidi_subscribe(void *private_data,
    279				 struct snd_seq_port_subscribe *info)
    280{
    281	struct snd_virmidi_dev *rdev;
    282
    283	rdev = private_data;
    284	if (!try_module_get(rdev->card->module))
    285		return -EFAULT;
    286	rdev->flags |= SNDRV_VIRMIDI_SUBSCRIBE;
    287	return 0;
    288}
    289
    290/*
    291 * unsubscribe callback - disallow output to rawmidi device
    292 */
    293static int snd_virmidi_unsubscribe(void *private_data,
    294				   struct snd_seq_port_subscribe *info)
    295{
    296	struct snd_virmidi_dev *rdev;
    297
    298	rdev = private_data;
    299	rdev->flags &= ~SNDRV_VIRMIDI_SUBSCRIBE;
    300	module_put(rdev->card->module);
    301	return 0;
    302}
    303
    304
    305/*
    306 * use callback - allow input to rawmidi device
    307 */
    308static int snd_virmidi_use(void *private_data,
    309			   struct snd_seq_port_subscribe *info)
    310{
    311	struct snd_virmidi_dev *rdev;
    312
    313	rdev = private_data;
    314	if (!try_module_get(rdev->card->module))
    315		return -EFAULT;
    316	rdev->flags |= SNDRV_VIRMIDI_USE;
    317	return 0;
    318}
    319
    320/*
    321 * unuse callback - disallow input to rawmidi device
    322 */
    323static int snd_virmidi_unuse(void *private_data,
    324			     struct snd_seq_port_subscribe *info)
    325{
    326	struct snd_virmidi_dev *rdev;
    327
    328	rdev = private_data;
    329	rdev->flags &= ~SNDRV_VIRMIDI_USE;
    330	module_put(rdev->card->module);
    331	return 0;
    332}
    333
    334
    335/*
    336 *  Register functions
    337 */
    338
    339static const struct snd_rawmidi_ops snd_virmidi_input_ops = {
    340	.open = snd_virmidi_input_open,
    341	.close = snd_virmidi_input_close,
    342	.trigger = snd_virmidi_input_trigger,
    343};
    344
    345static const struct snd_rawmidi_ops snd_virmidi_output_ops = {
    346	.open = snd_virmidi_output_open,
    347	.close = snd_virmidi_output_close,
    348	.trigger = snd_virmidi_output_trigger,
    349	.drain = snd_virmidi_output_drain,
    350};
    351
    352/*
    353 * create a sequencer client and a port
    354 */
    355static int snd_virmidi_dev_attach_seq(struct snd_virmidi_dev *rdev)
    356{
    357	int client;
    358	struct snd_seq_port_callback pcallbacks;
    359	struct snd_seq_port_info *pinfo;
    360	int err;
    361
    362	if (rdev->client >= 0)
    363		return 0;
    364
    365	pinfo = kzalloc(sizeof(*pinfo), GFP_KERNEL);
    366	if (!pinfo) {
    367		err = -ENOMEM;
    368		goto __error;
    369	}
    370
    371	client = snd_seq_create_kernel_client(rdev->card, rdev->device,
    372					      "%s %d-%d", rdev->rmidi->name,
    373					      rdev->card->number,
    374					      rdev->device);
    375	if (client < 0) {
    376		err = client;
    377		goto __error;
    378	}
    379	rdev->client = client;
    380
    381	/* create a port */
    382	pinfo->addr.client = client;
    383	sprintf(pinfo->name, "VirMIDI %d-%d", rdev->card->number, rdev->device);
    384	/* set all capabilities */
    385	pinfo->capability |= SNDRV_SEQ_PORT_CAP_WRITE | SNDRV_SEQ_PORT_CAP_SYNC_WRITE | SNDRV_SEQ_PORT_CAP_SUBS_WRITE;
    386	pinfo->capability |= SNDRV_SEQ_PORT_CAP_READ | SNDRV_SEQ_PORT_CAP_SYNC_READ | SNDRV_SEQ_PORT_CAP_SUBS_READ;
    387	pinfo->capability |= SNDRV_SEQ_PORT_CAP_DUPLEX;
    388	pinfo->type = SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC
    389		| SNDRV_SEQ_PORT_TYPE_SOFTWARE
    390		| SNDRV_SEQ_PORT_TYPE_PORT;
    391	pinfo->midi_channels = 16;
    392	memset(&pcallbacks, 0, sizeof(pcallbacks));
    393	pcallbacks.owner = THIS_MODULE;
    394	pcallbacks.private_data = rdev;
    395	pcallbacks.subscribe = snd_virmidi_subscribe;
    396	pcallbacks.unsubscribe = snd_virmidi_unsubscribe;
    397	pcallbacks.use = snd_virmidi_use;
    398	pcallbacks.unuse = snd_virmidi_unuse;
    399	pcallbacks.event_input = snd_virmidi_event_input;
    400	pinfo->kernel = &pcallbacks;
    401	err = snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_CREATE_PORT, pinfo);
    402	if (err < 0) {
    403		snd_seq_delete_kernel_client(client);
    404		rdev->client = -1;
    405		goto __error;
    406	}
    407
    408	rdev->port = pinfo->addr.port;
    409	err = 0; /* success */
    410
    411 __error:
    412	kfree(pinfo);
    413	return err;
    414}
    415
    416
    417/*
    418 * release the sequencer client
    419 */
    420static void snd_virmidi_dev_detach_seq(struct snd_virmidi_dev *rdev)
    421{
    422	if (rdev->client >= 0) {
    423		snd_seq_delete_kernel_client(rdev->client);
    424		rdev->client = -1;
    425	}
    426}
    427
    428/*
    429 * register the device
    430 */
    431static int snd_virmidi_dev_register(struct snd_rawmidi *rmidi)
    432{
    433	struct snd_virmidi_dev *rdev = rmidi->private_data;
    434	int err;
    435
    436	switch (rdev->seq_mode) {
    437	case SNDRV_VIRMIDI_SEQ_DISPATCH:
    438		err = snd_virmidi_dev_attach_seq(rdev);
    439		if (err < 0)
    440			return err;
    441		break;
    442	case SNDRV_VIRMIDI_SEQ_ATTACH:
    443		if (rdev->client == 0)
    444			return -EINVAL;
    445		/* should check presence of port more strictly.. */
    446		break;
    447	default:
    448		pr_err("ALSA: seq_virmidi: seq_mode is not set: %d\n", rdev->seq_mode);
    449		return -EINVAL;
    450	}
    451	return 0;
    452}
    453
    454
    455/*
    456 * unregister the device
    457 */
    458static int snd_virmidi_dev_unregister(struct snd_rawmidi *rmidi)
    459{
    460	struct snd_virmidi_dev *rdev = rmidi->private_data;
    461
    462	if (rdev->seq_mode == SNDRV_VIRMIDI_SEQ_DISPATCH)
    463		snd_virmidi_dev_detach_seq(rdev);
    464	return 0;
    465}
    466
    467/*
    468 *
    469 */
    470static const struct snd_rawmidi_global_ops snd_virmidi_global_ops = {
    471	.dev_register = snd_virmidi_dev_register,
    472	.dev_unregister = snd_virmidi_dev_unregister,
    473};
    474
    475/*
    476 * free device
    477 */
    478static void snd_virmidi_free(struct snd_rawmidi *rmidi)
    479{
    480	struct snd_virmidi_dev *rdev = rmidi->private_data;
    481	kfree(rdev);
    482}
    483
    484/*
    485 * create a new device
    486 *
    487 */
    488/* exported */
    489int snd_virmidi_new(struct snd_card *card, int device, struct snd_rawmidi **rrmidi)
    490{
    491	struct snd_rawmidi *rmidi;
    492	struct snd_virmidi_dev *rdev;
    493	int err;
    494	
    495	*rrmidi = NULL;
    496	err = snd_rawmidi_new(card, "VirMidi", device,
    497			      16,	/* may be configurable */
    498			      16,	/* may be configurable */
    499			      &rmidi);
    500	if (err < 0)
    501		return err;
    502	strcpy(rmidi->name, rmidi->id);
    503	rdev = kzalloc(sizeof(*rdev), GFP_KERNEL);
    504	if (rdev == NULL) {
    505		snd_device_free(card, rmidi);
    506		return -ENOMEM;
    507	}
    508	rdev->card = card;
    509	rdev->rmidi = rmidi;
    510	rdev->device = device;
    511	rdev->client = -1;
    512	init_rwsem(&rdev->filelist_sem);
    513	rwlock_init(&rdev->filelist_lock);
    514	INIT_LIST_HEAD(&rdev->filelist);
    515	rdev->seq_mode = SNDRV_VIRMIDI_SEQ_DISPATCH;
    516	rmidi->private_data = rdev;
    517	rmidi->private_free = snd_virmidi_free;
    518	rmidi->ops = &snd_virmidi_global_ops;
    519	snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_virmidi_input_ops);
    520	snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_virmidi_output_ops);
    521	rmidi->info_flags = SNDRV_RAWMIDI_INFO_INPUT |
    522			    SNDRV_RAWMIDI_INFO_OUTPUT |
    523			    SNDRV_RAWMIDI_INFO_DUPLEX;
    524	*rrmidi = rmidi;
    525	return 0;
    526}
    527EXPORT_SYMBOL(snd_virmidi_new);