1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /* ALSA sequencer binding for UMP device */
3
4 #include <linux/init.h>
5 #include <linux/slab.h>
6 #include <linux/errno.h>
7 #include <linux/mutex.h>
8 #include <linux/string.h>
9 #include <linux/module.h>
10 #include <asm/byteorder.h>
11 #include <sound/core.h>
12 #include <sound/ump.h>
13 #include <sound/seq_kernel.h>
14 #include <sound/seq_device.h>
15 #include "seq_clientmgr.h"
16 #include "seq_system.h"
17
18 struct seq_ump_client;
19 struct seq_ump_group;
20
21 enum {
22 STR_IN = SNDRV_RAWMIDI_STREAM_INPUT,
23 STR_OUT = SNDRV_RAWMIDI_STREAM_OUTPUT
24 };
25
26 /* context for UMP input parsing, per EP */
27 struct seq_ump_input_buffer {
28 unsigned char len; /* total length in words */
29 unsigned char pending; /* pending words */
30 unsigned char type; /* parsed UMP packet type */
31 unsigned char group; /* parsed UMP packet group */
32 u32 buf[4]; /* incoming UMP packet */
33 };
34
35 /* sequencer client, per UMP EP (rawmidi) */
36 struct seq_ump_client {
37 struct snd_ump_endpoint *ump; /* assigned endpoint */
38 int seq_client; /* sequencer client id */
39 int opened[2]; /* current opens for each direction */
40 struct snd_rawmidi_file out_rfile; /* rawmidi for output */
41 struct seq_ump_input_buffer input; /* input parser context */
42 void *ump_info[SNDRV_UMP_MAX_BLOCKS + 1]; /* shadow of seq client ump_info */
43 struct work_struct group_notify_work; /* FB change notification */
44 };
45
46 /* number of 32bit words for each UMP message type */
47 static unsigned char ump_packet_words[0x10] = {
48 1, 1, 1, 2, 2, 4, 1, 1, 2, 2, 2, 3, 3, 4, 4, 4
49 };
50
51 /* conversion between UMP group and seq port;
52 * assume the port number is equal with UMP group number (1-based)
53 */
ump_group_to_seq_port(unsigned char group)54 static unsigned char ump_group_to_seq_port(unsigned char group)
55 {
56 return group + 1;
57 }
58
59 /* process the incoming rawmidi stream */
seq_ump_input_receive(struct snd_ump_endpoint * ump,const u32 * val,int words)60 static void seq_ump_input_receive(struct snd_ump_endpoint *ump,
61 const u32 *val, int words)
62 {
63 struct seq_ump_client *client = ump->seq_client;
64 struct snd_seq_ump_event ev = {};
65
66 if (!client->opened[STR_IN])
67 return;
68
69 if (ump_is_groupless_msg(ump_message_type(*val)))
70 ev.source.port = 0; /* UMP EP port */
71 else
72 ev.source.port = ump_group_to_seq_port(ump_message_group(*val));
73 ev.dest.client = SNDRV_SEQ_ADDRESS_SUBSCRIBERS;
74 ev.flags = SNDRV_SEQ_EVENT_UMP;
75 memcpy(ev.ump, val, words << 2);
76 snd_seq_kernel_client_dispatch(client->seq_client,
77 (struct snd_seq_event *)&ev,
78 true, 0);
79 }
80
81 /* process an input sequencer event; only deal with UMP types */
seq_ump_process_event(struct snd_seq_event * ev,int direct,void * private_data,int atomic,int hop)82 static int seq_ump_process_event(struct snd_seq_event *ev, int direct,
83 void *private_data, int atomic, int hop)
84 {
85 struct seq_ump_client *client = private_data;
86 struct snd_rawmidi_substream *substream;
87 struct snd_seq_ump_event *ump_ev;
88 unsigned char type;
89 int len;
90
91 substream = client->out_rfile.output;
92 if (!substream)
93 return -ENODEV;
94 if (!snd_seq_ev_is_ump(ev))
95 return 0; /* invalid event, skip */
96 ump_ev = (struct snd_seq_ump_event *)ev;
97 type = ump_message_type(ump_ev->ump[0]);
98 len = ump_packet_words[type];
99 if (len > 4)
100 return 0; // invalid - skip
101 snd_rawmidi_kernel_write(substream, ev->data.raw8.d, len << 2);
102 return 0;
103 }
104
105 /* open the rawmidi */
seq_ump_client_open(struct seq_ump_client * client,int dir)106 static int seq_ump_client_open(struct seq_ump_client *client, int dir)
107 {
108 struct snd_ump_endpoint *ump = client->ump;
109 int err = 0;
110
111 mutex_lock(&ump->open_mutex);
112 if (dir == STR_OUT && !client->opened[dir]) {
113 err = snd_rawmidi_kernel_open(&ump->core, 0,
114 SNDRV_RAWMIDI_LFLG_OUTPUT |
115 SNDRV_RAWMIDI_LFLG_APPEND,
116 &client->out_rfile);
117 if (err < 0)
118 goto unlock;
119 }
120 client->opened[dir]++;
121 unlock:
122 mutex_unlock(&ump->open_mutex);
123 return err;
124 }
125
126 /* close the rawmidi */
seq_ump_client_close(struct seq_ump_client * client,int dir)127 static int seq_ump_client_close(struct seq_ump_client *client, int dir)
128 {
129 struct snd_ump_endpoint *ump = client->ump;
130
131 mutex_lock(&ump->open_mutex);
132 if (!--client->opened[dir])
133 if (dir == STR_OUT)
134 snd_rawmidi_kernel_release(&client->out_rfile);
135 mutex_unlock(&ump->open_mutex);
136 return 0;
137 }
138
139 /* sequencer subscription ops for each client */
seq_ump_subscribe(void * pdata,struct snd_seq_port_subscribe * info)140 static int seq_ump_subscribe(void *pdata, struct snd_seq_port_subscribe *info)
141 {
142 struct seq_ump_client *client = pdata;
143
144 return seq_ump_client_open(client, STR_IN);
145 }
146
seq_ump_unsubscribe(void * pdata,struct snd_seq_port_subscribe * info)147 static int seq_ump_unsubscribe(void *pdata, struct snd_seq_port_subscribe *info)
148 {
149 struct seq_ump_client *client = pdata;
150
151 return seq_ump_client_close(client, STR_IN);
152 }
153
seq_ump_use(void * pdata,struct snd_seq_port_subscribe * info)154 static int seq_ump_use(void *pdata, struct snd_seq_port_subscribe *info)
155 {
156 struct seq_ump_client *client = pdata;
157
158 return seq_ump_client_open(client, STR_OUT);
159 }
160
seq_ump_unuse(void * pdata,struct snd_seq_port_subscribe * info)161 static int seq_ump_unuse(void *pdata, struct snd_seq_port_subscribe *info)
162 {
163 struct seq_ump_client *client = pdata;
164
165 return seq_ump_client_close(client, STR_OUT);
166 }
167
168 /* fill port_info from the given UMP EP and group info */
fill_port_info(struct snd_seq_port_info * port,struct seq_ump_client * client,struct snd_ump_group * group)169 static void fill_port_info(struct snd_seq_port_info *port,
170 struct seq_ump_client *client,
171 struct snd_ump_group *group)
172 {
173 unsigned int rawmidi_info = client->ump->core.info_flags;
174
175 port->addr.client = client->seq_client;
176 port->addr.port = ump_group_to_seq_port(group->group);
177 port->capability = 0;
178 if (rawmidi_info & SNDRV_RAWMIDI_INFO_OUTPUT)
179 port->capability |= SNDRV_SEQ_PORT_CAP_WRITE |
180 SNDRV_SEQ_PORT_CAP_SYNC_WRITE |
181 SNDRV_SEQ_PORT_CAP_SUBS_WRITE;
182 if (rawmidi_info & SNDRV_RAWMIDI_INFO_INPUT)
183 port->capability |= SNDRV_SEQ_PORT_CAP_READ |
184 SNDRV_SEQ_PORT_CAP_SYNC_READ |
185 SNDRV_SEQ_PORT_CAP_SUBS_READ;
186 if (rawmidi_info & SNDRV_RAWMIDI_INFO_DUPLEX)
187 port->capability |= SNDRV_SEQ_PORT_CAP_DUPLEX;
188 if (group->dir_bits & (1 << STR_IN))
189 port->direction |= SNDRV_SEQ_PORT_DIR_INPUT;
190 if (group->dir_bits & (1 << STR_OUT))
191 port->direction |= SNDRV_SEQ_PORT_DIR_OUTPUT;
192 port->ump_group = group->group + 1;
193 if (!group->active)
194 port->capability |= SNDRV_SEQ_PORT_CAP_INACTIVE;
195 port->type = SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC |
196 SNDRV_SEQ_PORT_TYPE_MIDI_UMP |
197 SNDRV_SEQ_PORT_TYPE_HARDWARE |
198 SNDRV_SEQ_PORT_TYPE_PORT;
199 port->midi_channels = 16;
200 if (*group->name)
201 snprintf(port->name, sizeof(port->name), "Group %d (%.53s)",
202 group->group + 1, group->name);
203 else
204 sprintf(port->name, "Group %d", group->group + 1);
205 }
206
207 /* skip non-existing group for static blocks */
skip_group(struct seq_ump_client * client,struct snd_ump_group * group)208 static bool skip_group(struct seq_ump_client *client, struct snd_ump_group *group)
209 {
210 return !group->valid &&
211 (client->ump->info.flags & SNDRV_UMP_EP_INFO_STATIC_BLOCKS);
212 }
213
214 /* create a new sequencer port per UMP group */
seq_ump_group_init(struct seq_ump_client * client,int group_index)215 static int seq_ump_group_init(struct seq_ump_client *client, int group_index)
216 {
217 struct snd_ump_group *group = &client->ump->groups[group_index];
218 struct snd_seq_port_info *port __free(kfree) = NULL;
219 struct snd_seq_port_callback pcallbacks;
220
221 if (skip_group(client, group))
222 return 0;
223
224 port = kzalloc(sizeof(*port), GFP_KERNEL);
225 if (!port)
226 return -ENOMEM;
227
228 fill_port_info(port, client, group);
229 port->flags = SNDRV_SEQ_PORT_FLG_GIVEN_PORT;
230 memset(&pcallbacks, 0, sizeof(pcallbacks));
231 pcallbacks.owner = THIS_MODULE;
232 pcallbacks.private_data = client;
233 pcallbacks.subscribe = seq_ump_subscribe;
234 pcallbacks.unsubscribe = seq_ump_unsubscribe;
235 pcallbacks.use = seq_ump_use;
236 pcallbacks.unuse = seq_ump_unuse;
237 pcallbacks.event_input = seq_ump_process_event;
238 port->kernel = &pcallbacks;
239 return snd_seq_kernel_client_ctl(client->seq_client,
240 SNDRV_SEQ_IOCTL_CREATE_PORT,
241 port);
242 }
243
244 /* update the sequencer ports; called from notify_fb_change callback */
update_port_infos(struct seq_ump_client * client)245 static void update_port_infos(struct seq_ump_client *client)
246 {
247 struct snd_seq_port_info *old __free(kfree) = NULL;
248 struct snd_seq_port_info *new __free(kfree) = NULL;
249 int i, err;
250
251 old = kzalloc(sizeof(*old), GFP_KERNEL);
252 new = kzalloc(sizeof(*new), GFP_KERNEL);
253 if (!old || !new)
254 return;
255
256 for (i = 0; i < SNDRV_UMP_MAX_GROUPS; i++) {
257 if (skip_group(client, &client->ump->groups[i]))
258 continue;
259
260 old->addr.client = client->seq_client;
261 old->addr.port = ump_group_to_seq_port(i);
262 err = snd_seq_kernel_client_ctl(client->seq_client,
263 SNDRV_SEQ_IOCTL_GET_PORT_INFO,
264 old);
265 if (err < 0)
266 continue;
267 fill_port_info(new, client, &client->ump->groups[i]);
268 if (old->capability == new->capability &&
269 !strcmp(old->name, new->name))
270 continue;
271 err = snd_seq_kernel_client_ctl(client->seq_client,
272 SNDRV_SEQ_IOCTL_SET_PORT_INFO,
273 new);
274 if (err < 0)
275 continue;
276 /* notify to system port */
277 snd_seq_system_client_ev_port_change(client->seq_client, i);
278 }
279 }
280
281 /* create a UMP Endpoint port */
create_ump_endpoint_port(struct seq_ump_client * client)282 static int create_ump_endpoint_port(struct seq_ump_client *client)
283 {
284 struct snd_seq_port_info *port __free(kfree) = NULL;
285 struct snd_seq_port_callback pcallbacks;
286 unsigned int rawmidi_info = client->ump->core.info_flags;
287 int err;
288
289 port = kzalloc(sizeof(*port), GFP_KERNEL);
290 if (!port)
291 return -ENOMEM;
292
293 port->addr.client = client->seq_client;
294 port->addr.port = 0; /* fixed */
295 port->flags = SNDRV_SEQ_PORT_FLG_GIVEN_PORT;
296 port->capability = SNDRV_SEQ_PORT_CAP_UMP_ENDPOINT;
297 if (rawmidi_info & SNDRV_RAWMIDI_INFO_INPUT) {
298 port->capability |= SNDRV_SEQ_PORT_CAP_READ |
299 SNDRV_SEQ_PORT_CAP_SYNC_READ |
300 SNDRV_SEQ_PORT_CAP_SUBS_READ;
301 port->direction |= SNDRV_SEQ_PORT_DIR_INPUT;
302 }
303 if (rawmidi_info & SNDRV_RAWMIDI_INFO_OUTPUT) {
304 port->capability |= SNDRV_SEQ_PORT_CAP_WRITE |
305 SNDRV_SEQ_PORT_CAP_SYNC_WRITE |
306 SNDRV_SEQ_PORT_CAP_SUBS_WRITE;
307 port->direction |= SNDRV_SEQ_PORT_DIR_OUTPUT;
308 }
309 if (rawmidi_info & SNDRV_RAWMIDI_INFO_DUPLEX)
310 port->capability |= SNDRV_SEQ_PORT_CAP_DUPLEX;
311 port->ump_group = 0; /* no associated group, no conversion */
312 port->type = SNDRV_SEQ_PORT_TYPE_MIDI_UMP |
313 SNDRV_SEQ_PORT_TYPE_HARDWARE |
314 SNDRV_SEQ_PORT_TYPE_PORT;
315 port->midi_channels = 16;
316 strcpy(port->name, "MIDI 2.0");
317 memset(&pcallbacks, 0, sizeof(pcallbacks));
318 pcallbacks.owner = THIS_MODULE;
319 pcallbacks.private_data = client;
320 if (rawmidi_info & SNDRV_RAWMIDI_INFO_INPUT) {
321 pcallbacks.subscribe = seq_ump_subscribe;
322 pcallbacks.unsubscribe = seq_ump_unsubscribe;
323 }
324 if (rawmidi_info & SNDRV_RAWMIDI_INFO_OUTPUT) {
325 pcallbacks.use = seq_ump_use;
326 pcallbacks.unuse = seq_ump_unuse;
327 pcallbacks.event_input = seq_ump_process_event;
328 }
329 port->kernel = &pcallbacks;
330 err = snd_seq_kernel_client_ctl(client->seq_client,
331 SNDRV_SEQ_IOCTL_CREATE_PORT,
332 port);
333 return err;
334 }
335
336 /* release the client resources */
seq_ump_client_free(struct seq_ump_client * client)337 static void seq_ump_client_free(struct seq_ump_client *client)
338 {
339 cancel_work_sync(&client->group_notify_work);
340
341 if (client->seq_client >= 0)
342 snd_seq_delete_kernel_client(client->seq_client);
343
344 client->ump->seq_ops = NULL;
345 client->ump->seq_client = NULL;
346
347 kfree(client);
348 }
349
350 /* update the MIDI version for the given client */
setup_client_midi_version(struct seq_ump_client * client)351 static void setup_client_midi_version(struct seq_ump_client *client)
352 {
353 struct snd_seq_client *cptr;
354
355 cptr = snd_seq_kernel_client_get(client->seq_client);
356 if (!cptr)
357 return;
358 if (client->ump->info.protocol & SNDRV_UMP_EP_INFO_PROTO_MIDI2)
359 cptr->midi_version = SNDRV_SEQ_CLIENT_UMP_MIDI_2_0;
360 else
361 cptr->midi_version = SNDRV_SEQ_CLIENT_UMP_MIDI_1_0;
362 snd_seq_kernel_client_put(cptr);
363 }
364
365 /* set up client's group_filter bitmap */
setup_client_group_filter(struct seq_ump_client * client)366 static void setup_client_group_filter(struct seq_ump_client *client)
367 {
368 struct snd_seq_client *cptr;
369 unsigned int filter;
370 int p;
371
372 cptr = snd_seq_kernel_client_get(client->seq_client);
373 if (!cptr)
374 return;
375 filter = ~(1U << 0); /* always allow groupless messages */
376 for (p = 0; p < SNDRV_UMP_MAX_GROUPS; p++) {
377 if (client->ump->groups[p].active)
378 filter &= ~(1U << (p + 1));
379 }
380 cptr->group_filter = filter;
381 snd_seq_kernel_client_put(cptr);
382 }
383
384 /* UMP group change notification */
handle_group_notify(struct work_struct * work)385 static void handle_group_notify(struct work_struct *work)
386 {
387 struct seq_ump_client *client =
388 container_of(work, struct seq_ump_client, group_notify_work);
389
390 update_port_infos(client);
391 setup_client_group_filter(client);
392 }
393
394 /* UMP FB change notification */
seq_ump_notify_fb_change(struct snd_ump_endpoint * ump,struct snd_ump_block * fb)395 static int seq_ump_notify_fb_change(struct snd_ump_endpoint *ump,
396 struct snd_ump_block *fb)
397 {
398 struct seq_ump_client *client = ump->seq_client;
399
400 if (!client)
401 return -ENODEV;
402 schedule_work(&client->group_notify_work);
403 return 0;
404 }
405
406 /* UMP protocol change notification; just update the midi_version field */
seq_ump_switch_protocol(struct snd_ump_endpoint * ump)407 static int seq_ump_switch_protocol(struct snd_ump_endpoint *ump)
408 {
409 if (!ump->seq_client)
410 return -ENODEV;
411 setup_client_midi_version(ump->seq_client);
412 return 0;
413 }
414
415 static const struct snd_seq_ump_ops seq_ump_ops = {
416 .input_receive = seq_ump_input_receive,
417 .notify_fb_change = seq_ump_notify_fb_change,
418 .switch_protocol = seq_ump_switch_protocol,
419 };
420
421 /* create a sequencer client and ports for the given UMP endpoint */
snd_seq_ump_probe(struct device * _dev)422 static int snd_seq_ump_probe(struct device *_dev)
423 {
424 struct snd_seq_device *dev = to_seq_dev(_dev);
425 struct snd_ump_endpoint *ump = dev->private_data;
426 struct snd_card *card = dev->card;
427 struct seq_ump_client *client;
428 struct snd_ump_block *fb;
429 struct snd_seq_client *cptr;
430 int p, err;
431
432 client = kzalloc(sizeof(*client), GFP_KERNEL);
433 if (!client)
434 return -ENOMEM;
435
436 INIT_WORK(&client->group_notify_work, handle_group_notify);
437 client->ump = ump;
438
439 client->seq_client =
440 snd_seq_create_kernel_client(card, ump->core.device,
441 ump->core.name);
442 if (client->seq_client < 0) {
443 err = client->seq_client;
444 goto error;
445 }
446
447 client->ump_info[0] = &ump->info;
448 list_for_each_entry(fb, &ump->block_list, list)
449 client->ump_info[fb->info.block_id + 1] = &fb->info;
450
451 setup_client_midi_version(client);
452
453 for (p = 0; p < SNDRV_UMP_MAX_GROUPS; p++) {
454 err = seq_ump_group_init(client, p);
455 if (err < 0)
456 goto error;
457 }
458
459 setup_client_group_filter(client);
460
461 err = create_ump_endpoint_port(client);
462 if (err < 0)
463 goto error;
464
465 cptr = snd_seq_kernel_client_get(client->seq_client);
466 if (!cptr) {
467 err = -EINVAL;
468 goto error;
469 }
470 cptr->ump_info = client->ump_info;
471 snd_seq_kernel_client_put(cptr);
472
473 ump->seq_client = client;
474 ump->seq_ops = &seq_ump_ops;
475 return 0;
476
477 error:
478 seq_ump_client_free(client);
479 return err;
480 }
481
482 /* remove a sequencer client */
snd_seq_ump_remove(struct device * _dev)483 static int snd_seq_ump_remove(struct device *_dev)
484 {
485 struct snd_seq_device *dev = to_seq_dev(_dev);
486 struct snd_ump_endpoint *ump = dev->private_data;
487
488 if (ump->seq_client)
489 seq_ump_client_free(ump->seq_client);
490 return 0;
491 }
492
493 static struct snd_seq_driver seq_ump_driver = {
494 .driver = {
495 .name = KBUILD_MODNAME,
496 .probe = snd_seq_ump_probe,
497 .remove = snd_seq_ump_remove,
498 },
499 .id = SNDRV_SEQ_DEV_ID_UMP,
500 .argsize = 0,
501 };
502
503 module_snd_seq_driver(seq_ump_driver);
504
505 MODULE_DESCRIPTION("ALSA sequencer client for UMP rawmidi");
506 MODULE_LICENSE("GPL");
507