1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21#include <linux/init.h>
22#include <linux/slab.h>
23#include <linux/module.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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");
63MODULE_DESCRIPTION("ALSA sequencer MIDI-through client");
64MODULE_LICENSE("GPL");
65MODULE_ALIAS("snd-seq-client-" __stringify(SNDRV_SEQ_CLIENT_DUMMY));
66
67static int ports = 1;
68static bool duplex;
69
70module_param(ports, int, 0444);
71MODULE_PARM_DESC(ports, "number of ports to be created");
72module_param(duplex, bool, 0444);
73MODULE_PARM_DESC(duplex, "create DUPLEX ports");
74
75struct snd_seq_dummy_port {
76 int client;
77 int port;
78 int duplex;
79 int connect;
80};
81
82static int my_client = -1;
83
84
85
86
87static int
88dummy_input(struct snd_seq_event *ev, int direct, void *private_data,
89 int atomic, int hop)
90{
91 struct snd_seq_dummy_port *p;
92 struct snd_seq_event tmpev;
93
94 p = private_data;
95 if (ev->source.client == SNDRV_SEQ_CLIENT_SYSTEM ||
96 ev->type == SNDRV_SEQ_EVENT_KERNEL_ERROR)
97 return 0;
98 tmpev = *ev;
99 if (p->duplex)
100 tmpev.source.port = p->connect;
101 else
102 tmpev.source.port = p->port;
103 tmpev.dest.client = SNDRV_SEQ_ADDRESS_SUBSCRIBERS;
104 return snd_seq_kernel_client_dispatch(p->client, &tmpev, atomic, hop);
105}
106
107
108
109
110static void
111dummy_free(void *private_data)
112{
113 kfree(private_data);
114}
115
116
117
118
119static struct snd_seq_dummy_port __init *
120create_port(int idx, int type)
121{
122 struct snd_seq_port_info pinfo;
123 struct snd_seq_port_callback pcb;
124 struct snd_seq_dummy_port *rec;
125
126 if ((rec = kzalloc(sizeof(*rec), GFP_KERNEL)) == NULL)
127 return NULL;
128
129 rec->client = my_client;
130 rec->duplex = duplex;
131 rec->connect = 0;
132 memset(&pinfo, 0, sizeof(pinfo));
133 pinfo.addr.client = my_client;
134 if (duplex)
135 sprintf(pinfo.name, "Midi Through Port-%d:%c", idx,
136 (type ? 'B' : 'A'));
137 else
138 sprintf(pinfo.name, "Midi Through Port-%d", idx);
139 pinfo.capability = SNDRV_SEQ_PORT_CAP_READ | SNDRV_SEQ_PORT_CAP_SUBS_READ;
140 pinfo.capability |= SNDRV_SEQ_PORT_CAP_WRITE | SNDRV_SEQ_PORT_CAP_SUBS_WRITE;
141 if (duplex)
142 pinfo.capability |= SNDRV_SEQ_PORT_CAP_DUPLEX;
143 pinfo.type = SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC
144 | SNDRV_SEQ_PORT_TYPE_SOFTWARE
145 | SNDRV_SEQ_PORT_TYPE_PORT;
146 memset(&pcb, 0, sizeof(pcb));
147 pcb.owner = THIS_MODULE;
148 pcb.event_input = dummy_input;
149 pcb.private_free = dummy_free;
150 pcb.private_data = rec;
151 pinfo.kernel = &pcb;
152 if (snd_seq_kernel_client_ctl(my_client, SNDRV_SEQ_IOCTL_CREATE_PORT, &pinfo) < 0) {
153 kfree(rec);
154 return NULL;
155 }
156 rec->port = pinfo.addr.port;
157 return rec;
158}
159
160
161
162
163static int __init
164register_client(void)
165{
166 struct snd_seq_dummy_port *rec1, *rec2;
167 int i;
168
169 if (ports < 1) {
170 pr_err("ALSA: seq_dummy: invalid number of ports %d\n", ports);
171 return -EINVAL;
172 }
173
174
175 my_client = snd_seq_create_kernel_client(NULL, SNDRV_SEQ_CLIENT_DUMMY,
176 "Midi Through");
177 if (my_client < 0)
178 return my_client;
179
180
181 for (i = 0; i < ports; i++) {
182 rec1 = create_port(i, 0);
183 if (rec1 == NULL) {
184 snd_seq_delete_kernel_client(my_client);
185 return -ENOMEM;
186 }
187 if (duplex) {
188 rec2 = create_port(i, 1);
189 if (rec2 == NULL) {
190 snd_seq_delete_kernel_client(my_client);
191 return -ENOMEM;
192 }
193 rec1->connect = rec2->port;
194 rec2->connect = rec1->port;
195 }
196 }
197
198 return 0;
199}
200
201
202
203
204static void __exit
205delete_client(void)
206{
207 if (my_client >= 0)
208 snd_seq_delete_kernel_client(my_client);
209}
210
211
212
213
214
215static int __init alsa_seq_dummy_init(void)
216{
217 return register_client();
218}
219
220static void __exit alsa_seq_dummy_exit(void)
221{
222 delete_client();
223}
224
225module_init(alsa_seq_dummy_init)
226module_exit(alsa_seq_dummy_exit)
227