Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/sound/core/seq/seq_dummy.c
10817 views
1
/*
2
* ALSA sequencer MIDI-through client
3
* Copyright (c) 1999-2000 by Takashi Iwai <[email protected]>
4
*
5
* This program is free software; you can redistribute it and/or modify
6
* it under the terms of the GNU General Public License as published by
7
* the Free Software Foundation; either version 2 of the License, or
8
* (at your option) any later version.
9
*
10
* This program is distributed in the hope that it will be useful,
11
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
* GNU General Public License for more details.
14
*
15
* You should have received a copy of the GNU General Public License
16
* along with this program; if not, write to the Free Software
17
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
*
19
*/
20
21
#include <linux/init.h>
22
#include <linux/slab.h>
23
#include <linux/moduleparam.h>
24
#include <sound/core.h>
25
#include "seq_clientmgr.h"
26
#include <sound/initval.h>
27
#include <sound/asoundef.h>
28
29
/*
30
31
Sequencer MIDI-through client
32
33
This gives a simple midi-through client. All the normal input events
34
are redirected to output port immediately.
35
The routing can be done via aconnect program in alsa-utils.
36
37
Each client has a static client number 62 (= SNDRV_SEQ_CLIENT_DUMMY).
38
If you want to auto-load this module, you may add the following alias
39
in your /etc/conf.modules file.
40
41
alias snd-seq-client-62 snd-seq-dummy
42
43
The module is loaded on demand for client 62, or /proc/asound/seq/
44
is accessed. If you don't need this module to be loaded, alias
45
snd-seq-client-62 as "off". This will help modprobe.
46
47
The number of ports to be created can be specified via the module
48
parameter "ports". For example, to create four ports, add the
49
following option in /etc/modprobe.conf:
50
51
option snd-seq-dummy ports=4
52
53
The model option "duplex=1" enables duplex operation to the port.
54
In duplex mode, a pair of ports are created instead of single port,
55
and events are tunneled between pair-ports. For example, input to
56
port A is sent to output port of another port B and vice versa.
57
In duplex mode, each port has DUPLEX capability.
58
59
*/
60
61
62
MODULE_AUTHOR("Takashi Iwai <[email protected]>");
63
MODULE_DESCRIPTION("ALSA sequencer MIDI-through client");
64
MODULE_LICENSE("GPL");
65
MODULE_ALIAS("snd-seq-client-" __stringify(SNDRV_SEQ_CLIENT_DUMMY));
66
67
static int ports = 1;
68
static int duplex;
69
70
module_param(ports, int, 0444);
71
MODULE_PARM_DESC(ports, "number of ports to be created");
72
module_param(duplex, bool, 0444);
73
MODULE_PARM_DESC(duplex, "create DUPLEX ports");
74
75
struct snd_seq_dummy_port {
76
int client;
77
int port;
78
int duplex;
79
int connect;
80
};
81
82
static int my_client = -1;
83
84
/*
85
* unuse callback - send ALL_SOUNDS_OFF and RESET_CONTROLLERS events
86
* to subscribers.
87
* Note: this callback is called only after all subscribers are removed.
88
*/
89
static int
90
dummy_unuse(void *private_data, struct snd_seq_port_subscribe *info)
91
{
92
struct snd_seq_dummy_port *p;
93
int i;
94
struct snd_seq_event ev;
95
96
p = private_data;
97
memset(&ev, 0, sizeof(ev));
98
if (p->duplex)
99
ev.source.port = p->connect;
100
else
101
ev.source.port = p->port;
102
ev.dest.client = SNDRV_SEQ_ADDRESS_SUBSCRIBERS;
103
ev.type = SNDRV_SEQ_EVENT_CONTROLLER;
104
for (i = 0; i < 16; i++) {
105
ev.data.control.channel = i;
106
ev.data.control.param = MIDI_CTL_ALL_SOUNDS_OFF;
107
snd_seq_kernel_client_dispatch(p->client, &ev, 0, 0);
108
ev.data.control.param = MIDI_CTL_RESET_CONTROLLERS;
109
snd_seq_kernel_client_dispatch(p->client, &ev, 0, 0);
110
}
111
return 0;
112
}
113
114
/*
115
* event input callback - just redirect events to subscribers
116
*/
117
static int
118
dummy_input(struct snd_seq_event *ev, int direct, void *private_data,
119
int atomic, int hop)
120
{
121
struct snd_seq_dummy_port *p;
122
struct snd_seq_event tmpev;
123
124
p = private_data;
125
if (ev->source.client == SNDRV_SEQ_CLIENT_SYSTEM ||
126
ev->type == SNDRV_SEQ_EVENT_KERNEL_ERROR)
127
return 0; /* ignore system messages */
128
tmpev = *ev;
129
if (p->duplex)
130
tmpev.source.port = p->connect;
131
else
132
tmpev.source.port = p->port;
133
tmpev.dest.client = SNDRV_SEQ_ADDRESS_SUBSCRIBERS;
134
return snd_seq_kernel_client_dispatch(p->client, &tmpev, atomic, hop);
135
}
136
137
/*
138
* free_private callback
139
*/
140
static void
141
dummy_free(void *private_data)
142
{
143
kfree(private_data);
144
}
145
146
/*
147
* create a port
148
*/
149
static struct snd_seq_dummy_port __init *
150
create_port(int idx, int type)
151
{
152
struct snd_seq_port_info pinfo;
153
struct snd_seq_port_callback pcb;
154
struct snd_seq_dummy_port *rec;
155
156
if ((rec = kzalloc(sizeof(*rec), GFP_KERNEL)) == NULL)
157
return NULL;
158
159
rec->client = my_client;
160
rec->duplex = duplex;
161
rec->connect = 0;
162
memset(&pinfo, 0, sizeof(pinfo));
163
pinfo.addr.client = my_client;
164
if (duplex)
165
sprintf(pinfo.name, "Midi Through Port-%d:%c", idx,
166
(type ? 'B' : 'A'));
167
else
168
sprintf(pinfo.name, "Midi Through Port-%d", idx);
169
pinfo.capability = SNDRV_SEQ_PORT_CAP_READ | SNDRV_SEQ_PORT_CAP_SUBS_READ;
170
pinfo.capability |= SNDRV_SEQ_PORT_CAP_WRITE | SNDRV_SEQ_PORT_CAP_SUBS_WRITE;
171
if (duplex)
172
pinfo.capability |= SNDRV_SEQ_PORT_CAP_DUPLEX;
173
pinfo.type = SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC
174
| SNDRV_SEQ_PORT_TYPE_SOFTWARE
175
| SNDRV_SEQ_PORT_TYPE_PORT;
176
memset(&pcb, 0, sizeof(pcb));
177
pcb.owner = THIS_MODULE;
178
pcb.unuse = dummy_unuse;
179
pcb.event_input = dummy_input;
180
pcb.private_free = dummy_free;
181
pcb.private_data = rec;
182
pinfo.kernel = &pcb;
183
if (snd_seq_kernel_client_ctl(my_client, SNDRV_SEQ_IOCTL_CREATE_PORT, &pinfo) < 0) {
184
kfree(rec);
185
return NULL;
186
}
187
rec->port = pinfo.addr.port;
188
return rec;
189
}
190
191
/*
192
* register client and create ports
193
*/
194
static int __init
195
register_client(void)
196
{
197
struct snd_seq_dummy_port *rec1, *rec2;
198
int i;
199
200
if (ports < 1) {
201
snd_printk(KERN_ERR "invalid number of ports %d\n", ports);
202
return -EINVAL;
203
}
204
205
/* create client */
206
my_client = snd_seq_create_kernel_client(NULL, SNDRV_SEQ_CLIENT_DUMMY,
207
"Midi Through");
208
if (my_client < 0)
209
return my_client;
210
211
/* create ports */
212
for (i = 0; i < ports; i++) {
213
rec1 = create_port(i, 0);
214
if (rec1 == NULL) {
215
snd_seq_delete_kernel_client(my_client);
216
return -ENOMEM;
217
}
218
if (duplex) {
219
rec2 = create_port(i, 1);
220
if (rec2 == NULL) {
221
snd_seq_delete_kernel_client(my_client);
222
return -ENOMEM;
223
}
224
rec1->connect = rec2->port;
225
rec2->connect = rec1->port;
226
}
227
}
228
229
return 0;
230
}
231
232
/*
233
* delete client if exists
234
*/
235
static void __exit
236
delete_client(void)
237
{
238
if (my_client >= 0)
239
snd_seq_delete_kernel_client(my_client);
240
}
241
242
/*
243
* Init part
244
*/
245
246
static int __init alsa_seq_dummy_init(void)
247
{
248
int err;
249
snd_seq_autoload_lock();
250
err = register_client();
251
snd_seq_autoload_unlock();
252
return err;
253
}
254
255
static void __exit alsa_seq_dummy_exit(void)
256
{
257
delete_client();
258
}
259
260
module_init(alsa_seq_dummy_init)
261
module_exit(alsa_seq_dummy_exit)
262
263