Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/sound/core/seq/seq_dummy.c
26439 views
1
// SPDX-License-Identifier: GPL-2.0-or-later
2
/*
3
* ALSA sequencer MIDI-through client
4
* Copyright (c) 1999-2000 by Takashi Iwai <[email protected]>
5
*/
6
7
#include <linux/init.h>
8
#include <linux/slab.h>
9
#include <linux/module.h>
10
#include <sound/core.h>
11
#include "seq_clientmgr.h"
12
#include <sound/initval.h>
13
#include <sound/asoundef.h>
14
15
/*
16
17
Sequencer MIDI-through client
18
19
This gives a simple midi-through client. All the normal input events
20
are redirected to output port immediately.
21
The routing can be done via aconnect program in alsa-utils.
22
23
Each client has a static client number 14 (= SNDRV_SEQ_CLIENT_DUMMY).
24
If you want to auto-load this module, you may add the following alias
25
in your /etc/conf.modules file.
26
27
alias snd-seq-client-14 snd-seq-dummy
28
29
The module is loaded on demand for client 14, or /proc/asound/seq/
30
is accessed. If you don't need this module to be loaded, alias
31
snd-seq-client-14 as "off". This will help modprobe.
32
33
The number of ports to be created can be specified via the module
34
parameter "ports". For example, to create four ports, add the
35
following option in a configuration file under /etc/modprobe.d/:
36
37
option snd-seq-dummy ports=4
38
39
The model option "duplex=1" enables duplex operation to the port.
40
In duplex mode, a pair of ports are created instead of single port,
41
and events are tunneled between pair-ports. For example, input to
42
port A is sent to output port of another port B and vice versa.
43
In duplex mode, each port has DUPLEX capability.
44
45
*/
46
47
48
MODULE_AUTHOR("Takashi Iwai <[email protected]>");
49
MODULE_DESCRIPTION("ALSA sequencer MIDI-through client");
50
MODULE_LICENSE("GPL");
51
MODULE_ALIAS("snd-seq-client-" __stringify(SNDRV_SEQ_CLIENT_DUMMY));
52
53
static int ports = 1;
54
static bool duplex;
55
56
module_param(ports, int, 0444);
57
MODULE_PARM_DESC(ports, "number of ports to be created");
58
module_param(duplex, bool, 0444);
59
MODULE_PARM_DESC(duplex, "create DUPLEX ports");
60
61
#if IS_ENABLED(CONFIG_SND_SEQ_UMP)
62
static int ump;
63
module_param(ump, int, 0444);
64
MODULE_PARM_DESC(ump, "UMP conversion (0: no convert, 1: MIDI 1.0, 2: MIDI 2.0)");
65
#endif
66
67
struct snd_seq_dummy_port {
68
int client;
69
int port;
70
int duplex;
71
int connect;
72
};
73
74
static int my_client = -1;
75
76
/*
77
* event input callback - just redirect events to subscribers
78
*/
79
static int
80
dummy_input(struct snd_seq_event *ev, int direct, void *private_data,
81
int atomic, int hop)
82
{
83
struct snd_seq_dummy_port *p;
84
struct snd_seq_event tmpev;
85
86
p = private_data;
87
if (ev->source.client == SNDRV_SEQ_CLIENT_SYSTEM ||
88
ev->type == SNDRV_SEQ_EVENT_KERNEL_ERROR)
89
return 0; /* ignore system messages */
90
tmpev = *ev;
91
if (p->duplex)
92
tmpev.source.port = p->connect;
93
else
94
tmpev.source.port = p->port;
95
tmpev.dest.client = SNDRV_SEQ_ADDRESS_SUBSCRIBERS;
96
return snd_seq_kernel_client_dispatch(p->client, &tmpev, atomic, hop);
97
}
98
99
/*
100
* free_private callback
101
*/
102
static void
103
dummy_free(void *private_data)
104
{
105
kfree(private_data);
106
}
107
108
/*
109
* create a port
110
*/
111
static struct snd_seq_dummy_port __init *
112
create_port(int idx, int type)
113
{
114
struct snd_seq_port_info pinfo;
115
struct snd_seq_port_callback pcb;
116
struct snd_seq_dummy_port *rec;
117
118
rec = kzalloc(sizeof(*rec), GFP_KERNEL);
119
if (!rec)
120
return NULL;
121
122
rec->client = my_client;
123
rec->duplex = duplex;
124
rec->connect = 0;
125
memset(&pinfo, 0, sizeof(pinfo));
126
pinfo.addr.client = my_client;
127
if (duplex)
128
sprintf(pinfo.name, "Midi Through Port-%d:%c", idx,
129
(type ? 'B' : 'A'));
130
else
131
sprintf(pinfo.name, "Midi Through Port-%d", idx);
132
pinfo.capability = SNDRV_SEQ_PORT_CAP_READ | SNDRV_SEQ_PORT_CAP_SUBS_READ;
133
pinfo.capability |= SNDRV_SEQ_PORT_CAP_WRITE | SNDRV_SEQ_PORT_CAP_SUBS_WRITE;
134
if (duplex)
135
pinfo.capability |= SNDRV_SEQ_PORT_CAP_DUPLEX;
136
pinfo.direction = SNDRV_SEQ_PORT_DIR_BIDIRECTION;
137
pinfo.type = SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC
138
| SNDRV_SEQ_PORT_TYPE_SOFTWARE
139
| SNDRV_SEQ_PORT_TYPE_PORT;
140
memset(&pcb, 0, sizeof(pcb));
141
pcb.owner = THIS_MODULE;
142
pcb.event_input = dummy_input;
143
pcb.private_free = dummy_free;
144
pcb.private_data = rec;
145
pinfo.kernel = &pcb;
146
if (snd_seq_kernel_client_ctl(my_client, SNDRV_SEQ_IOCTL_CREATE_PORT, &pinfo) < 0) {
147
kfree(rec);
148
return NULL;
149
}
150
rec->port = pinfo.addr.port;
151
return rec;
152
}
153
154
/*
155
* register client and create ports
156
*/
157
static int __init
158
register_client(void)
159
{
160
struct snd_seq_dummy_port *rec1, *rec2;
161
#if IS_ENABLED(CONFIG_SND_SEQ_UMP)
162
struct snd_seq_client *client;
163
#endif
164
int i;
165
166
if (ports < 1) {
167
pr_err("ALSA: seq_dummy: invalid number of ports %d\n", ports);
168
return -EINVAL;
169
}
170
171
/* create client */
172
my_client = snd_seq_create_kernel_client(NULL, SNDRV_SEQ_CLIENT_DUMMY,
173
"Midi Through");
174
if (my_client < 0)
175
return my_client;
176
177
#if IS_ENABLED(CONFIG_SND_SEQ_UMP)
178
client = snd_seq_kernel_client_get(my_client);
179
if (!client)
180
return -EINVAL;
181
switch (ump) {
182
case 1:
183
client->midi_version = SNDRV_SEQ_CLIENT_UMP_MIDI_1_0;
184
break;
185
case 2:
186
client->midi_version = SNDRV_SEQ_CLIENT_UMP_MIDI_2_0;
187
break;
188
default:
189
/* don't convert events but just pass-through */
190
client->filter = SNDRV_SEQ_FILTER_NO_CONVERT;
191
break;
192
}
193
snd_seq_kernel_client_put(client);
194
#endif
195
196
/* create ports */
197
for (i = 0; i < ports; i++) {
198
rec1 = create_port(i, 0);
199
if (rec1 == NULL) {
200
snd_seq_delete_kernel_client(my_client);
201
return -ENOMEM;
202
}
203
if (duplex) {
204
rec2 = create_port(i, 1);
205
if (rec2 == NULL) {
206
snd_seq_delete_kernel_client(my_client);
207
return -ENOMEM;
208
}
209
rec1->connect = rec2->port;
210
rec2->connect = rec1->port;
211
}
212
}
213
214
return 0;
215
}
216
217
/*
218
* delete client if exists
219
*/
220
static void __exit
221
delete_client(void)
222
{
223
if (my_client >= 0)
224
snd_seq_delete_kernel_client(my_client);
225
}
226
227
/*
228
* Init part
229
*/
230
231
static int __init alsa_seq_dummy_init(void)
232
{
233
return register_client();
234
}
235
236
static void __exit alsa_seq_dummy_exit(void)
237
{
238
delete_client();
239
}
240
241
module_init(alsa_seq_dummy_init)
242
module_exit(alsa_seq_dummy_exit)
243
244