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