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