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