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