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