xref: /linux/sound/core/seq/seq_ump_client.c (revision daa2be74b1b2302004945b2a5e32424e177cc7da)
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  */
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 */
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 */
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 */
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;
120 
121 	guard(mutex)(&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 			return err;
129 	}
130 	client->opened[dir]++;
131 	return 0;
132 }
133 
134 /* close the rawmidi */
135 static int seq_ump_client_close(struct seq_ump_client *client, int dir)
136 {
137 	struct snd_ump_endpoint *ump = client->ump;
138 
139 	guard(mutex)(&ump->open_mutex);
140 	if (!--client->opened[dir])
141 		if (dir == STR_OUT)
142 			snd_rawmidi_kernel_release(&client->out_rfile);
143 	return 0;
144 }
145 
146 /* sequencer subscription ops for each client */
147 static int seq_ump_subscribe(void *pdata, struct snd_seq_port_subscribe *info)
148 {
149 	struct seq_ump_client *client = pdata;
150 
151 	return seq_ump_client_open(client, STR_IN);
152 }
153 
154 static int seq_ump_unsubscribe(void *pdata, struct snd_seq_port_subscribe *info)
155 {
156 	struct seq_ump_client *client = pdata;
157 
158 	return seq_ump_client_close(client, STR_IN);
159 }
160 
161 static int seq_ump_use(void *pdata, struct snd_seq_port_subscribe *info)
162 {
163 	struct seq_ump_client *client = pdata;
164 
165 	return seq_ump_client_open(client, STR_OUT);
166 }
167 
168 static int seq_ump_unuse(void *pdata, struct snd_seq_port_subscribe *info)
169 {
170 	struct seq_ump_client *client = pdata;
171 
172 	return seq_ump_client_close(client, STR_OUT);
173 }
174 
175 /* fill port_info from the given UMP EP and group info */
176 static void fill_port_info(struct snd_seq_port_info *port,
177 			   struct seq_ump_client *client,
178 			   struct seq_ump_group *group)
179 {
180 	unsigned int rawmidi_info = client->ump->core.info_flags;
181 
182 	port->addr.client = client->seq_client;
183 	port->addr.port = ump_group_to_seq_port(group->group);
184 	port->capability = 0;
185 	if (rawmidi_info & SNDRV_RAWMIDI_INFO_OUTPUT)
186 		port->capability |= SNDRV_SEQ_PORT_CAP_WRITE |
187 			SNDRV_SEQ_PORT_CAP_SYNC_WRITE |
188 			SNDRV_SEQ_PORT_CAP_SUBS_WRITE;
189 	if (rawmidi_info & SNDRV_RAWMIDI_INFO_INPUT)
190 		port->capability |= SNDRV_SEQ_PORT_CAP_READ |
191 			SNDRV_SEQ_PORT_CAP_SYNC_READ |
192 			SNDRV_SEQ_PORT_CAP_SUBS_READ;
193 	if (rawmidi_info & SNDRV_RAWMIDI_INFO_DUPLEX)
194 		port->capability |= SNDRV_SEQ_PORT_CAP_DUPLEX;
195 	if (group->dir_bits & (1 << STR_IN))
196 		port->direction |= SNDRV_SEQ_PORT_DIR_INPUT;
197 	if (group->dir_bits & (1 << STR_OUT))
198 		port->direction |= SNDRV_SEQ_PORT_DIR_OUTPUT;
199 	port->ump_group = group->group + 1;
200 	if (!group->active)
201 		port->capability |= SNDRV_SEQ_PORT_CAP_INACTIVE;
202 	port->type = SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC |
203 		SNDRV_SEQ_PORT_TYPE_MIDI_UMP |
204 		SNDRV_SEQ_PORT_TYPE_HARDWARE |
205 		SNDRV_SEQ_PORT_TYPE_PORT;
206 	port->midi_channels = 16;
207 	if (*group->name)
208 		snprintf(port->name, sizeof(port->name), "Group %d (%.53s)",
209 			 group->group + 1, group->name);
210 	else
211 		sprintf(port->name, "Group %d", group->group + 1);
212 }
213 
214 /* skip non-existing group for static blocks */
215 static bool skip_group(struct seq_ump_client *client, struct seq_ump_group *group)
216 {
217 	return !group->valid &&
218 		(client->ump->info.flags & SNDRV_UMP_EP_INFO_STATIC_BLOCKS);
219 }
220 
221 /* create a new sequencer port per UMP group */
222 static int seq_ump_group_init(struct seq_ump_client *client, int group_index)
223 {
224 	struct seq_ump_group *group = &client->groups[group_index];
225 	struct snd_seq_port_info *port __free(kfree) = NULL;
226 	struct snd_seq_port_callback pcallbacks;
227 
228 	if (skip_group(client, group))
229 		return 0;
230 
231 	port = kzalloc(sizeof(*port), GFP_KERNEL);
232 	if (!port)
233 		return -ENOMEM;
234 
235 	fill_port_info(port, client, group);
236 	port->flags = SNDRV_SEQ_PORT_FLG_GIVEN_PORT;
237 	memset(&pcallbacks, 0, sizeof(pcallbacks));
238 	pcallbacks.owner = THIS_MODULE;
239 	pcallbacks.private_data = client;
240 	pcallbacks.subscribe = seq_ump_subscribe;
241 	pcallbacks.unsubscribe = seq_ump_unsubscribe;
242 	pcallbacks.use = seq_ump_use;
243 	pcallbacks.unuse = seq_ump_unuse;
244 	pcallbacks.event_input = seq_ump_process_event;
245 	port->kernel = &pcallbacks;
246 	return snd_seq_kernel_client_ctl(client->seq_client,
247 					 SNDRV_SEQ_IOCTL_CREATE_PORT,
248 					 port);
249 }
250 
251 /* update the sequencer ports; called from notify_fb_change callback */
252 static void update_port_infos(struct seq_ump_client *client)
253 {
254 	struct snd_seq_port_info *old __free(kfree) = NULL;
255 	struct snd_seq_port_info *new __free(kfree) = NULL;
256 	int i, err;
257 
258 	old = kzalloc(sizeof(*old), GFP_KERNEL);
259 	new = kzalloc(sizeof(*new), GFP_KERNEL);
260 	if (!old || !new)
261 		return;
262 
263 	for (i = 0; i < SNDRV_UMP_MAX_GROUPS; i++) {
264 		if (skip_group(client, &client->groups[i]))
265 			continue;
266 
267 		old->addr.client = client->seq_client;
268 		old->addr.port = i;
269 		err = snd_seq_kernel_client_ctl(client->seq_client,
270 						SNDRV_SEQ_IOCTL_GET_PORT_INFO,
271 						old);
272 		if (err < 0)
273 			return;
274 		fill_port_info(new, client, &client->groups[i]);
275 		if (old->capability == new->capability &&
276 		    !strcmp(old->name, new->name))
277 			continue;
278 		err = snd_seq_kernel_client_ctl(client->seq_client,
279 						SNDRV_SEQ_IOCTL_SET_PORT_INFO,
280 						new);
281 		if (err < 0)
282 			return;
283 		/* notify to system port */
284 		snd_seq_system_client_ev_port_change(client->seq_client, i);
285 	}
286 }
287 
288 /* update dir_bits and active flag for all groups in the client */
289 static void update_group_attrs(struct seq_ump_client *client)
290 {
291 	struct snd_ump_block *fb;
292 	struct seq_ump_group *group;
293 	int i;
294 
295 	for (i = 0; i < SNDRV_UMP_MAX_GROUPS; i++) {
296 		group = &client->groups[i];
297 		*group->name = 0;
298 		group->dir_bits = 0;
299 		group->active = 0;
300 		group->group = i;
301 		group->valid = false;
302 	}
303 
304 	list_for_each_entry(fb, &client->ump->block_list, list) {
305 		if (fb->info.first_group + fb->info.num_groups > SNDRV_UMP_MAX_GROUPS)
306 			break;
307 		group = &client->groups[fb->info.first_group];
308 		for (i = 0; i < fb->info.num_groups; i++, group++) {
309 			group->valid = true;
310 			if (fb->info.active)
311 				group->active = 1;
312 			switch (fb->info.direction) {
313 			case SNDRV_UMP_DIR_INPUT:
314 				group->dir_bits |= (1 << STR_IN);
315 				break;
316 			case SNDRV_UMP_DIR_OUTPUT:
317 				group->dir_bits |= (1 << STR_OUT);
318 				break;
319 			case SNDRV_UMP_DIR_BIDIRECTION:
320 				group->dir_bits |= (1 << STR_OUT) | (1 << STR_IN);
321 				break;
322 			}
323 			if (!*fb->info.name)
324 				continue;
325 			if (!*group->name) {
326 				/* store the first matching name */
327 				strscpy(group->name, fb->info.name,
328 					sizeof(group->name));
329 			} else {
330 				/* when overlapping, concat names */
331 				strlcat(group->name, ", ", sizeof(group->name));
332 				strlcat(group->name, fb->info.name,
333 					sizeof(group->name));
334 			}
335 		}
336 	}
337 }
338 
339 /* create a UMP Endpoint port */
340 static int create_ump_endpoint_port(struct seq_ump_client *client)
341 {
342 	struct snd_seq_port_info *port __free(kfree) = NULL;
343 	struct snd_seq_port_callback pcallbacks;
344 	unsigned int rawmidi_info = client->ump->core.info_flags;
345 	int err;
346 
347 	port = kzalloc(sizeof(*port), GFP_KERNEL);
348 	if (!port)
349 		return -ENOMEM;
350 
351 	port->addr.client = client->seq_client;
352 	port->addr.port = 0; /* fixed */
353 	port->flags = SNDRV_SEQ_PORT_FLG_GIVEN_PORT;
354 	port->capability = SNDRV_SEQ_PORT_CAP_UMP_ENDPOINT;
355 	if (rawmidi_info & SNDRV_RAWMIDI_INFO_INPUT) {
356 		port->capability |= SNDRV_SEQ_PORT_CAP_READ |
357 			SNDRV_SEQ_PORT_CAP_SYNC_READ |
358 			SNDRV_SEQ_PORT_CAP_SUBS_READ;
359 		port->direction |= SNDRV_SEQ_PORT_DIR_INPUT;
360 	}
361 	if (rawmidi_info & SNDRV_RAWMIDI_INFO_OUTPUT) {
362 		port->capability |= SNDRV_SEQ_PORT_CAP_WRITE |
363 			SNDRV_SEQ_PORT_CAP_SYNC_WRITE |
364 			SNDRV_SEQ_PORT_CAP_SUBS_WRITE;
365 		port->direction |= SNDRV_SEQ_PORT_DIR_OUTPUT;
366 	}
367 	if (rawmidi_info & SNDRV_RAWMIDI_INFO_DUPLEX)
368 		port->capability |= SNDRV_SEQ_PORT_CAP_DUPLEX;
369 	port->ump_group = 0; /* no associated group, no conversion */
370 	port->type = SNDRV_SEQ_PORT_TYPE_MIDI_UMP |
371 		SNDRV_SEQ_PORT_TYPE_HARDWARE |
372 		SNDRV_SEQ_PORT_TYPE_PORT;
373 	port->midi_channels = 16;
374 	strcpy(port->name, "MIDI 2.0");
375 	memset(&pcallbacks, 0, sizeof(pcallbacks));
376 	pcallbacks.owner = THIS_MODULE;
377 	pcallbacks.private_data = client;
378 	if (rawmidi_info & SNDRV_RAWMIDI_INFO_INPUT) {
379 		pcallbacks.subscribe = seq_ump_subscribe;
380 		pcallbacks.unsubscribe = seq_ump_unsubscribe;
381 	}
382 	if (rawmidi_info & SNDRV_RAWMIDI_INFO_OUTPUT) {
383 		pcallbacks.use = seq_ump_use;
384 		pcallbacks.unuse = seq_ump_unuse;
385 		pcallbacks.event_input = seq_ump_process_event;
386 	}
387 	port->kernel = &pcallbacks;
388 	err = snd_seq_kernel_client_ctl(client->seq_client,
389 					SNDRV_SEQ_IOCTL_CREATE_PORT,
390 					port);
391 	return err;
392 }
393 
394 /* release the client resources */
395 static void seq_ump_client_free(struct seq_ump_client *client)
396 {
397 	cancel_work_sync(&client->group_notify_work);
398 
399 	if (client->seq_client >= 0)
400 		snd_seq_delete_kernel_client(client->seq_client);
401 
402 	client->ump->seq_ops = NULL;
403 	client->ump->seq_client = NULL;
404 
405 	kfree(client);
406 }
407 
408 /* update the MIDI version for the given client */
409 static void setup_client_midi_version(struct seq_ump_client *client)
410 {
411 	struct snd_seq_client *cptr;
412 
413 	cptr = snd_seq_kernel_client_get(client->seq_client);
414 	if (!cptr)
415 		return;
416 	if (client->ump->info.protocol & SNDRV_UMP_EP_INFO_PROTO_MIDI2)
417 		cptr->midi_version = SNDRV_SEQ_CLIENT_UMP_MIDI_2_0;
418 	else
419 		cptr->midi_version = SNDRV_SEQ_CLIENT_UMP_MIDI_1_0;
420 	snd_seq_kernel_client_put(cptr);
421 }
422 
423 /* set up client's group_filter bitmap */
424 static void setup_client_group_filter(struct seq_ump_client *client)
425 {
426 	struct snd_seq_client *cptr;
427 	unsigned int filter;
428 	int p;
429 
430 	cptr = snd_seq_kernel_client_get(client->seq_client);
431 	if (!cptr)
432 		return;
433 	filter = ~(1U << 0); /* always allow groupless messages */
434 	for (p = 0; p < SNDRV_UMP_MAX_GROUPS; p++) {
435 		if (client->groups[p].active)
436 			filter &= ~(1U << (p + 1));
437 	}
438 	cptr->group_filter = filter;
439 	snd_seq_kernel_client_put(cptr);
440 }
441 
442 /* UMP group change notification */
443 static void handle_group_notify(struct work_struct *work)
444 {
445 	struct seq_ump_client *client =
446 		container_of(work, struct seq_ump_client, group_notify_work);
447 
448 	update_group_attrs(client);
449 	update_port_infos(client);
450 	setup_client_group_filter(client);
451 }
452 
453 /* UMP FB change notification */
454 static int seq_ump_notify_fb_change(struct snd_ump_endpoint *ump,
455 				    struct snd_ump_block *fb)
456 {
457 	struct seq_ump_client *client = ump->seq_client;
458 
459 	if (!client)
460 		return -ENODEV;
461 	schedule_work(&client->group_notify_work);
462 	return 0;
463 }
464 
465 /* UMP protocol change notification; just update the midi_version field */
466 static int seq_ump_switch_protocol(struct snd_ump_endpoint *ump)
467 {
468 	if (!ump->seq_client)
469 		return -ENODEV;
470 	setup_client_midi_version(ump->seq_client);
471 	return 0;
472 }
473 
474 static const struct snd_seq_ump_ops seq_ump_ops = {
475 	.input_receive = seq_ump_input_receive,
476 	.notify_fb_change = seq_ump_notify_fb_change,
477 	.switch_protocol = seq_ump_switch_protocol,
478 };
479 
480 /* create a sequencer client and ports for the given UMP endpoint */
481 static int snd_seq_ump_probe(struct device *_dev)
482 {
483 	struct snd_seq_device *dev = to_seq_dev(_dev);
484 	struct snd_ump_endpoint *ump = dev->private_data;
485 	struct snd_card *card = dev->card;
486 	struct seq_ump_client *client;
487 	struct snd_ump_block *fb;
488 	struct snd_seq_client *cptr;
489 	int p, err;
490 
491 	client = kzalloc(sizeof(*client), GFP_KERNEL);
492 	if (!client)
493 		return -ENOMEM;
494 
495 	INIT_WORK(&client->group_notify_work, handle_group_notify);
496 	client->ump = ump;
497 
498 	client->seq_client =
499 		snd_seq_create_kernel_client(card, ump->core.device,
500 					     ump->core.name);
501 	if (client->seq_client < 0) {
502 		err = client->seq_client;
503 		goto error;
504 	}
505 
506 	client->ump_info[0] = &ump->info;
507 	list_for_each_entry(fb, &ump->block_list, list)
508 		client->ump_info[fb->info.block_id + 1] = &fb->info;
509 
510 	setup_client_midi_version(client);
511 	update_group_attrs(client);
512 
513 	for (p = 0; p < SNDRV_UMP_MAX_GROUPS; p++) {
514 		err = seq_ump_group_init(client, p);
515 		if (err < 0)
516 			goto error;
517 	}
518 
519 	setup_client_group_filter(client);
520 
521 	err = create_ump_endpoint_port(client);
522 	if (err < 0)
523 		goto error;
524 
525 	cptr = snd_seq_kernel_client_get(client->seq_client);
526 	if (!cptr) {
527 		err = -EINVAL;
528 		goto error;
529 	}
530 	cptr->ump_info = client->ump_info;
531 	snd_seq_kernel_client_put(cptr);
532 
533 	ump->seq_client = client;
534 	ump->seq_ops = &seq_ump_ops;
535 	return 0;
536 
537  error:
538 	seq_ump_client_free(client);
539 	return err;
540 }
541 
542 /* remove a sequencer client */
543 static int snd_seq_ump_remove(struct device *_dev)
544 {
545 	struct snd_seq_device *dev = to_seq_dev(_dev);
546 	struct snd_ump_endpoint *ump = dev->private_data;
547 
548 	if (ump->seq_client)
549 		seq_ump_client_free(ump->seq_client);
550 	return 0;
551 }
552 
553 static struct snd_seq_driver seq_ump_driver = {
554 	.driver = {
555 		.name = KBUILD_MODNAME,
556 		.probe = snd_seq_ump_probe,
557 		.remove = snd_seq_ump_remove,
558 	},
559 	.id = SNDRV_SEQ_DEV_ID_UMP,
560 	.argsize = 0,
561 };
562 
563 module_snd_seq_driver(seq_ump_driver);
564 
565 MODULE_DESCRIPTION("ALSA sequencer client for UMP rawmidi");
566 MODULE_LICENSE("GPL");
567