Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/sound/core/seq/seq_virmidi.c
10817 views
1
/*
2
* Virtual Raw MIDI client on Sequencer
3
*
4
* Copyright (c) 2000 by Takashi Iwai <[email protected]>,
5
* Jaroslav Kysela <[email protected]>
6
*
7
* This program is free software; you can redistribute it and/or modify
8
* it under the terms of the GNU General Public License as published by
9
* the Free Software Foundation; either version 2 of the License, or
10
* (at your option) any later version.
11
*
12
* This program is distributed in the hope that it will be useful,
13
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
* GNU General Public License for more details.
16
*
17
* You should have received a copy of the GNU General Public License
18
* along with this program; if not, write to the Free Software
19
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20
*
21
*/
22
23
/*
24
* Virtual Raw MIDI client
25
*
26
* The virtual rawmidi client is a sequencer client which associate
27
* a rawmidi device file. The created rawmidi device file can be
28
* accessed as a normal raw midi, but its MIDI source and destination
29
* are arbitrary. For example, a user-client software synth connected
30
* to this port can be used as a normal midi device as well.
31
*
32
* The virtual rawmidi device accepts also multiple opens. Each file
33
* has its own input buffer, so that no conflict would occur. The drain
34
* of input/output buffer acts only to the local buffer.
35
*
36
*/
37
38
#include <linux/init.h>
39
#include <linux/wait.h>
40
#include <linux/slab.h>
41
#include <sound/core.h>
42
#include <sound/rawmidi.h>
43
#include <sound/info.h>
44
#include <sound/control.h>
45
#include <sound/minors.h>
46
#include <sound/seq_kernel.h>
47
#include <sound/seq_midi_event.h>
48
#include <sound/seq_virmidi.h>
49
50
MODULE_AUTHOR("Takashi Iwai <[email protected]>");
51
MODULE_DESCRIPTION("Virtual Raw MIDI client on Sequencer");
52
MODULE_LICENSE("GPL");
53
54
/*
55
* initialize an event record
56
*/
57
static void snd_virmidi_init_event(struct snd_virmidi *vmidi,
58
struct snd_seq_event *ev)
59
{
60
memset(ev, 0, sizeof(*ev));
61
ev->source.port = vmidi->port;
62
switch (vmidi->seq_mode) {
63
case SNDRV_VIRMIDI_SEQ_DISPATCH:
64
ev->dest.client = SNDRV_SEQ_ADDRESS_SUBSCRIBERS;
65
break;
66
case SNDRV_VIRMIDI_SEQ_ATTACH:
67
/* FIXME: source and destination are same - not good.. */
68
ev->dest.client = vmidi->client;
69
ev->dest.port = vmidi->port;
70
break;
71
}
72
ev->type = SNDRV_SEQ_EVENT_NONE;
73
}
74
75
/*
76
* decode input event and put to read buffer of each opened file
77
*/
78
static int snd_virmidi_dev_receive_event(struct snd_virmidi_dev *rdev,
79
struct snd_seq_event *ev)
80
{
81
struct snd_virmidi *vmidi;
82
unsigned char msg[4];
83
int len;
84
85
read_lock(&rdev->filelist_lock);
86
list_for_each_entry(vmidi, &rdev->filelist, list) {
87
if (!vmidi->trigger)
88
continue;
89
if (ev->type == SNDRV_SEQ_EVENT_SYSEX) {
90
if ((ev->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) != SNDRV_SEQ_EVENT_LENGTH_VARIABLE)
91
continue;
92
snd_seq_dump_var_event(ev, (snd_seq_dump_func_t)snd_rawmidi_receive, vmidi->substream);
93
} else {
94
len = snd_midi_event_decode(vmidi->parser, msg, sizeof(msg), ev);
95
if (len > 0)
96
snd_rawmidi_receive(vmidi->substream, msg, len);
97
}
98
}
99
read_unlock(&rdev->filelist_lock);
100
101
return 0;
102
}
103
104
/*
105
* receive an event from the remote virmidi port
106
*
107
* for rawmidi inputs, you can call this function from the event
108
* handler of a remote port which is attached to the virmidi via
109
* SNDRV_VIRMIDI_SEQ_ATTACH.
110
*/
111
#if 0
112
int snd_virmidi_receive(struct snd_rawmidi *rmidi, struct snd_seq_event *ev)
113
{
114
struct snd_virmidi_dev *rdev;
115
116
rdev = rmidi->private_data;
117
return snd_virmidi_dev_receive_event(rdev, ev);
118
}
119
#endif /* 0 */
120
121
/*
122
* event handler of virmidi port
123
*/
124
static int snd_virmidi_event_input(struct snd_seq_event *ev, int direct,
125
void *private_data, int atomic, int hop)
126
{
127
struct snd_virmidi_dev *rdev;
128
129
rdev = private_data;
130
if (!(rdev->flags & SNDRV_VIRMIDI_USE))
131
return 0; /* ignored */
132
return snd_virmidi_dev_receive_event(rdev, ev);
133
}
134
135
/*
136
* trigger rawmidi stream for input
137
*/
138
static void snd_virmidi_input_trigger(struct snd_rawmidi_substream *substream, int up)
139
{
140
struct snd_virmidi *vmidi = substream->runtime->private_data;
141
142
if (up) {
143
vmidi->trigger = 1;
144
} else {
145
vmidi->trigger = 0;
146
}
147
}
148
149
/*
150
* trigger rawmidi stream for output
151
*/
152
static void snd_virmidi_output_trigger(struct snd_rawmidi_substream *substream, int up)
153
{
154
struct snd_virmidi *vmidi = substream->runtime->private_data;
155
int count, res;
156
unsigned char buf[32], *pbuf;
157
158
if (up) {
159
vmidi->trigger = 1;
160
if (vmidi->seq_mode == SNDRV_VIRMIDI_SEQ_DISPATCH &&
161
!(vmidi->rdev->flags & SNDRV_VIRMIDI_SUBSCRIBE)) {
162
snd_rawmidi_transmit_ack(substream, substream->runtime->buffer_size - substream->runtime->avail);
163
return; /* ignored */
164
}
165
if (vmidi->event.type != SNDRV_SEQ_EVENT_NONE) {
166
if (snd_seq_kernel_client_dispatch(vmidi->client, &vmidi->event, in_atomic(), 0) < 0)
167
return;
168
vmidi->event.type = SNDRV_SEQ_EVENT_NONE;
169
}
170
while (1) {
171
count = snd_rawmidi_transmit_peek(substream, buf, sizeof(buf));
172
if (count <= 0)
173
break;
174
pbuf = buf;
175
while (count > 0) {
176
res = snd_midi_event_encode(vmidi->parser, pbuf, count, &vmidi->event);
177
if (res < 0) {
178
snd_midi_event_reset_encode(vmidi->parser);
179
continue;
180
}
181
snd_rawmidi_transmit_ack(substream, res);
182
pbuf += res;
183
count -= res;
184
if (vmidi->event.type != SNDRV_SEQ_EVENT_NONE) {
185
if (snd_seq_kernel_client_dispatch(vmidi->client, &vmidi->event, in_atomic(), 0) < 0)
186
return;
187
vmidi->event.type = SNDRV_SEQ_EVENT_NONE;
188
}
189
}
190
}
191
} else {
192
vmidi->trigger = 0;
193
}
194
}
195
196
/*
197
* open rawmidi handle for input
198
*/
199
static int snd_virmidi_input_open(struct snd_rawmidi_substream *substream)
200
{
201
struct snd_virmidi_dev *rdev = substream->rmidi->private_data;
202
struct snd_rawmidi_runtime *runtime = substream->runtime;
203
struct snd_virmidi *vmidi;
204
unsigned long flags;
205
206
vmidi = kzalloc(sizeof(*vmidi), GFP_KERNEL);
207
if (vmidi == NULL)
208
return -ENOMEM;
209
vmidi->substream = substream;
210
if (snd_midi_event_new(0, &vmidi->parser) < 0) {
211
kfree(vmidi);
212
return -ENOMEM;
213
}
214
vmidi->seq_mode = rdev->seq_mode;
215
vmidi->client = rdev->client;
216
vmidi->port = rdev->port;
217
runtime->private_data = vmidi;
218
write_lock_irqsave(&rdev->filelist_lock, flags);
219
list_add_tail(&vmidi->list, &rdev->filelist);
220
write_unlock_irqrestore(&rdev->filelist_lock, flags);
221
vmidi->rdev = rdev;
222
return 0;
223
}
224
225
/*
226
* open rawmidi handle for output
227
*/
228
static int snd_virmidi_output_open(struct snd_rawmidi_substream *substream)
229
{
230
struct snd_virmidi_dev *rdev = substream->rmidi->private_data;
231
struct snd_rawmidi_runtime *runtime = substream->runtime;
232
struct snd_virmidi *vmidi;
233
234
vmidi = kzalloc(sizeof(*vmidi), GFP_KERNEL);
235
if (vmidi == NULL)
236
return -ENOMEM;
237
vmidi->substream = substream;
238
if (snd_midi_event_new(MAX_MIDI_EVENT_BUF, &vmidi->parser) < 0) {
239
kfree(vmidi);
240
return -ENOMEM;
241
}
242
vmidi->seq_mode = rdev->seq_mode;
243
vmidi->client = rdev->client;
244
vmidi->port = rdev->port;
245
snd_virmidi_init_event(vmidi, &vmidi->event);
246
vmidi->rdev = rdev;
247
runtime->private_data = vmidi;
248
return 0;
249
}
250
251
/*
252
* close rawmidi handle for input
253
*/
254
static int snd_virmidi_input_close(struct snd_rawmidi_substream *substream)
255
{
256
struct snd_virmidi *vmidi = substream->runtime->private_data;
257
snd_midi_event_free(vmidi->parser);
258
list_del(&vmidi->list);
259
substream->runtime->private_data = NULL;
260
kfree(vmidi);
261
return 0;
262
}
263
264
/*
265
* close rawmidi handle for output
266
*/
267
static int snd_virmidi_output_close(struct snd_rawmidi_substream *substream)
268
{
269
struct snd_virmidi *vmidi = substream->runtime->private_data;
270
snd_midi_event_free(vmidi->parser);
271
substream->runtime->private_data = NULL;
272
kfree(vmidi);
273
return 0;
274
}
275
276
/*
277
* subscribe callback - allow output to rawmidi device
278
*/
279
static int snd_virmidi_subscribe(void *private_data,
280
struct snd_seq_port_subscribe *info)
281
{
282
struct snd_virmidi_dev *rdev;
283
284
rdev = private_data;
285
if (!try_module_get(rdev->card->module))
286
return -EFAULT;
287
rdev->flags |= SNDRV_VIRMIDI_SUBSCRIBE;
288
return 0;
289
}
290
291
/*
292
* unsubscribe callback - disallow output to rawmidi device
293
*/
294
static int snd_virmidi_unsubscribe(void *private_data,
295
struct snd_seq_port_subscribe *info)
296
{
297
struct snd_virmidi_dev *rdev;
298
299
rdev = private_data;
300
rdev->flags &= ~SNDRV_VIRMIDI_SUBSCRIBE;
301
module_put(rdev->card->module);
302
return 0;
303
}
304
305
306
/*
307
* use callback - allow input to rawmidi device
308
*/
309
static int snd_virmidi_use(void *private_data,
310
struct snd_seq_port_subscribe *info)
311
{
312
struct snd_virmidi_dev *rdev;
313
314
rdev = private_data;
315
if (!try_module_get(rdev->card->module))
316
return -EFAULT;
317
rdev->flags |= SNDRV_VIRMIDI_USE;
318
return 0;
319
}
320
321
/*
322
* unuse callback - disallow input to rawmidi device
323
*/
324
static int snd_virmidi_unuse(void *private_data,
325
struct snd_seq_port_subscribe *info)
326
{
327
struct snd_virmidi_dev *rdev;
328
329
rdev = private_data;
330
rdev->flags &= ~SNDRV_VIRMIDI_USE;
331
module_put(rdev->card->module);
332
return 0;
333
}
334
335
336
/*
337
* Register functions
338
*/
339
340
static struct snd_rawmidi_ops snd_virmidi_input_ops = {
341
.open = snd_virmidi_input_open,
342
.close = snd_virmidi_input_close,
343
.trigger = snd_virmidi_input_trigger,
344
};
345
346
static struct snd_rawmidi_ops snd_virmidi_output_ops = {
347
.open = snd_virmidi_output_open,
348
.close = snd_virmidi_output_close,
349
.trigger = snd_virmidi_output_trigger,
350
};
351
352
/*
353
* create a sequencer client and a port
354
*/
355
static 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
*/
420
static 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
*/
431
static 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
snd_printk(KERN_ERR "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
*/
458
static 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
*/
470
static 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
*/
478
static 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 */
489
int 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
if ((err = snd_rawmidi_new(card, "VirMidi", device,
497
16, /* may be configurable */
498
16, /* may be configurable */
499
&rmidi)) < 0)
500
return err;
501
strcpy(rmidi->name, rmidi->id);
502
rdev = kzalloc(sizeof(*rdev), GFP_KERNEL);
503
if (rdev == NULL) {
504
snd_device_free(card, rmidi);
505
return -ENOMEM;
506
}
507
rdev->card = card;
508
rdev->rmidi = rmidi;
509
rdev->device = device;
510
rdev->client = -1;
511
rwlock_init(&rdev->filelist_lock);
512
INIT_LIST_HEAD(&rdev->filelist);
513
rdev->seq_mode = SNDRV_VIRMIDI_SEQ_DISPATCH;
514
rmidi->private_data = rdev;
515
rmidi->private_free = snd_virmidi_free;
516
rmidi->ops = &snd_virmidi_global_ops;
517
snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_virmidi_input_ops);
518
snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_virmidi_output_ops);
519
rmidi->info_flags = SNDRV_RAWMIDI_INFO_INPUT |
520
SNDRV_RAWMIDI_INFO_OUTPUT |
521
SNDRV_RAWMIDI_INFO_DUPLEX;
522
*rrmidi = rmidi;
523
return 0;
524
}
525
526
/*
527
* ENTRY functions
528
*/
529
530
static int __init alsa_virmidi_init(void)
531
{
532
return 0;
533
}
534
535
static void __exit alsa_virmidi_exit(void)
536
{
537
}
538
539
module_init(alsa_virmidi_init)
540
module_exit(alsa_virmidi_exit)
541
542
EXPORT_SYMBOL(snd_virmidi_new);
543
544