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