File Index
Files and directories
Sign in
FILE

seq_ports.c

File preview with syntax highlighting.

linux-6.19.14/sound/core/seq/seq_ports.c C
  1// SPDX-License-Identifier: GPL-2.0-or-later
  2/*
  3 *   ALSA sequencer Ports
  4 *   Copyright (c) 1998 by Frank van de Pol <fvdpol@coil.demon.nl>
  5 *                         Jaroslav Kysela <perex@perex.cz>
  6 */
  7
  8#include <sound/core.h>
  9#include <linux/slab.h>
 10#include <linux/module.h>
 11#include "seq_system.h"
 12#include "seq_ports.h"
 13#include "seq_clientmgr.h"
 14
 15/*
 16
 17   registration of client ports
 18
 19 */
 20
 21
 22/* 
 23
 24NOTE: the current implementation of the port structure as a linked list is
 25not optimal for clients that have many ports. For sending messages to all
 26subscribers of a port we first need to find the address of the port
 27structure, which means we have to traverse the list. A direct access table
 28(array) would be better, but big preallocated arrays waste memory.
 29
 30Possible actions:
 31
 321) leave it this way, a client does normaly does not have more than a few
 33ports
 34
 352) replace the linked list of ports by a array of pointers which is
 36dynamicly kmalloced. When a port is added or deleted we can simply allocate
 37a new array, copy the corresponding pointers, and delete the old one. We
 38then only need a pointer to this array, and an integer that tells us how
 39much elements are in array.
 40
 41*/
 42
 43/* return pointer to port structure - port is locked if found */
 44struct snd_seq_client_port *snd_seq_port_use_ptr(struct snd_seq_client *client,
 45						 int num)
 46{
 47	struct snd_seq_client_port *port;
 48
 49	if (client == NULL)
 50		return NULL;
 51	guard(read_lock)(&client->ports_lock);
 52	list_for_each_entry(port, &client->ports_list_head, list) {
 53		if (port->addr.port == num) {
 54			if (port->closing)
 55				break; /* deleting now */
 56			snd_use_lock_use(&port->use_lock);
 57			return port;
 58		}
 59	}
 60	return NULL;		/* not found */
 61}
 62
 63
 64/* search for the next port - port is locked if found */
 65struct snd_seq_client_port *snd_seq_port_query_nearest(struct snd_seq_client *client,
 66						       struct snd_seq_port_info *pinfo)
 67{
 68	int num;
 69	struct snd_seq_client_port *port, *found;
 70	bool check_inactive = (pinfo->capability & SNDRV_SEQ_PORT_CAP_INACTIVE);
 71
 72	num = pinfo->addr.port;
 73	found = NULL;
 74	guard(read_lock)(&client->ports_lock);
 75	list_for_each_entry(port, &client->ports_list_head, list) {
 76		if ((port->capability & SNDRV_SEQ_PORT_CAP_INACTIVE) &&
 77		    !check_inactive)
 78			continue; /* skip inactive ports */
 79		if (port->addr.port < num)
 80			continue;
 81		if (port->addr.port == num) {
 82			found = port;
 83			break;
 84		}
 85		if (found == NULL || port->addr.port < found->addr.port)
 86			found = port;
 87	}
 88	if (found) {
 89		if (found->closing)
 90			found = NULL;
 91		else
 92			snd_use_lock_use(&found->use_lock);
 93	}
 94	return found;
 95}
 96
 97
 98/* initialize snd_seq_port_subs_info */
 99static void port_subs_info_init(struct snd_seq_port_subs_info *grp)
100{
101	INIT_LIST_HEAD(&grp->list_head);
102	grp->count = 0;
103	grp->exclusive = 0;
104	rwlock_init(&grp->list_lock);
105	init_rwsem(&grp->list_mutex);
106	grp->open = NULL;
107	grp->close = NULL;
108}
109
110
111/* create a port, port number or a negative error code is returned
112 * the caller needs to unref the port via snd_seq_port_unlock() appropriately
113 */
114int snd_seq_create_port(struct snd_seq_client *client, int port,
115			struct snd_seq_client_port **port_ret)
116{
117	struct snd_seq_client_port *new_port, *p;
118	int num;
119	
120	*port_ret = NULL;
121
122	/* sanity check */
123	if (snd_BUG_ON(!client))
124		return -EINVAL;
125
126	if (client->num_ports >= SNDRV_SEQ_MAX_PORTS) {
127		pr_warn("ALSA: seq: too many ports for client %d\n", client->number);
128		return -EINVAL;
129	}
130
131	/* create a new port */
132	new_port = kzalloc(sizeof(*new_port), GFP_KERNEL);
133	if (!new_port)
134		return -ENOMEM;	/* failure, out of memory */
135	/* init port data */
136	new_port->addr.client = client->number;
137	new_port->addr.port = -1;
138	new_port->owner = THIS_MODULE;
139	snd_use_lock_init(&new_port->use_lock);
140	port_subs_info_init(&new_port->c_src);
141	port_subs_info_init(&new_port->c_dest);
142	snd_use_lock_use(&new_port->use_lock);
143
144	num = max(port, 0);
145	guard(mutex)(&client->ports_mutex);
146	guard(write_lock_irq)(&client->ports_lock);
147	list_for_each_entry(p, &client->ports_list_head, list) {
148		if (p->addr.port == port) {
149			kfree(new_port);
150			return -EBUSY;
151		}
152		if (p->addr.port > num)
153			break;
154		if (port < 0) /* auto-probe mode */
155			num = p->addr.port + 1;
156	}
157	/* insert the new port */
158	list_add_tail(&new_port->list, &p->list);
159	client->num_ports++;
160	new_port->addr.port = num;	/* store the port number in the port */
161	sprintf(new_port->name, "port-%d", num);
162	*port_ret = new_port;
163
164	return num;
165}
166
167/* */
168static int subscribe_port(struct snd_seq_client *client,
169			  struct snd_seq_client_port *port,
170			  struct snd_seq_port_subs_info *grp,
171			  struct snd_seq_port_subscribe *info, int send_ack);
172static int unsubscribe_port(struct snd_seq_client *client,
173			    struct snd_seq_client_port *port,
174			    struct snd_seq_port_subs_info *grp,
175			    struct snd_seq_port_subscribe *info, int send_ack);
176
177
178static struct snd_seq_client_port *get_client_port(struct snd_seq_addr *addr,
179						   struct snd_seq_client **cp)
180{
181	*cp = snd_seq_client_use_ptr(addr->client);
182	if (!*cp)
183		return NULL;
184	return snd_seq_port_use_ptr(*cp, addr->port);
185}
186
187static void delete_and_unsubscribe_port(struct snd_seq_client *client,
188					struct snd_seq_client_port *port,
189					struct snd_seq_subscribers *subs,
190					bool is_src, bool ack);
191
192static inline struct snd_seq_subscribers *
193get_subscriber(struct list_head *p, bool is_src)
194{
195	if (is_src)
196		return list_entry(p, struct snd_seq_subscribers, src_list);
197	else
198		return list_entry(p, struct snd_seq_subscribers, dest_list);
199}
200
201/*
202 * remove all subscribers on the list
203 * this is called from port_delete, for each src and dest list.
204 */
205static void clear_subscriber_list(struct snd_seq_client *client,
206				  struct snd_seq_client_port *port,
207				  struct snd_seq_port_subs_info *grp,
208				  int is_src)
209{
210	struct list_head *p, *n;
211
212	list_for_each_safe(p, n, &grp->list_head) {
213		struct snd_seq_subscribers *subs;
214
215		subs = get_subscriber(p, is_src);
216		struct snd_seq_client *c __free(snd_seq_client) = NULL;
217		struct snd_seq_client_port *aport __free(snd_seq_port) =
218			is_src ?
219			get_client_port(&subs->info.dest, &c) :
220			get_client_port(&subs->info.sender, &c);
221		delete_and_unsubscribe_port(client, port, subs, is_src, false);
222
223		if (!aport) {
224			/* looks like the connected port is being deleted.
225			 * we decrease the counter, and when both ports are deleted
226			 * remove the subscriber info
227			 */
228			if (atomic_dec_and_test(&subs->ref_count))
229				kfree(subs);
230			continue;
231		}
232
233		/* ok we got the connected port */
234		delete_and_unsubscribe_port(c, aport, subs, !is_src, true);
235		kfree(subs);
236	}
237}
238
239/* delete port data */
240static int port_delete(struct snd_seq_client *client,
241		       struct snd_seq_client_port *port)
242{
243	/* set closing flag and wait for all port access are gone */
244	port->closing = 1;
245	snd_use_lock_sync(&port->use_lock); 
246
247	/* clear subscribers info */
248	clear_subscriber_list(client, port, &port->c_src, true);
249	clear_subscriber_list(client, port, &port->c_dest, false);
250
251	if (port->private_free)
252		port->private_free(port->private_data);
253
254	snd_BUG_ON(port->c_src.count != 0);
255	snd_BUG_ON(port->c_dest.count != 0);
256
257	kfree(port);
258	return 0;
259}
260
261
262/* delete a port with the given port id */
263int snd_seq_delete_port(struct snd_seq_client *client, int port)
264{
265	struct snd_seq_client_port *found = NULL, *p;
266
267	scoped_guard(mutex, &client->ports_mutex) {
268		guard(write_lock_irq)(&client->ports_lock);
269		list_for_each_entry(p, &client->ports_list_head, list) {
270			if (p->addr.port == port) {
271				/* ok found.  delete from the list at first */
272				list_del(&p->list);
273				client->num_ports--;
274				found = p;
275				break;
276			}
277		}
278	}
279	if (found)
280		return port_delete(client, found);
281	else
282		return -ENOENT;
283}
284
285/* delete the all ports belonging to the given client */
286int snd_seq_delete_all_ports(struct snd_seq_client *client)
287{
288	struct list_head deleted_list;
289	struct snd_seq_client_port *port, *tmp;
290	
291	/* move the port list to deleted_list, and
292	 * clear the port list in the client data.
293	 */
294	guard(mutex)(&client->ports_mutex);
295	scoped_guard(write_lock_irq, &client->ports_lock) {
296		if (!list_empty(&client->ports_list_head)) {
297			list_add(&deleted_list, &client->ports_list_head);
298			list_del_init(&client->ports_list_head);
299		} else {
300			INIT_LIST_HEAD(&deleted_list);
301		}
302		client->num_ports = 0;
303	}
304
305	/* remove each port in deleted_list */
306	list_for_each_entry_safe(port, tmp, &deleted_list, list) {
307		list_del(&port->list);
308		snd_seq_system_client_ev_port_exit(port->addr.client, port->addr.port);
309		port_delete(client, port);
310	}
311	return 0;
312}
313
314/* set port info fields */
315int snd_seq_set_port_info(struct snd_seq_client_port * port,
316			  struct snd_seq_port_info * info)
317{
318	if (snd_BUG_ON(!port || !info))
319		return -EINVAL;
320
321	/* set port name */
322	if (info->name[0])
323		strscpy(port->name, info->name, sizeof(port->name));
324	
325	/* set capabilities */
326	port->capability = info->capability;
327	
328	/* get port type */
329	port->type = info->type;
330
331	/* information about supported channels/voices */
332	port->midi_channels = info->midi_channels;
333	port->midi_voices = info->midi_voices;
334	port->synth_voices = info->synth_voices;
335
336	/* timestamping */
337	port->timestamping = (info->flags & SNDRV_SEQ_PORT_FLG_TIMESTAMP) ? 1 : 0;
338	port->time_real = (info->flags & SNDRV_SEQ_PORT_FLG_TIME_REAL) ? 1 : 0;
339	port->time_queue = info->time_queue;
340
341	/* UMP direction and group */
342	port->direction = info->direction;
343	port->ump_group = info->ump_group;
344	if (port->ump_group > SNDRV_UMP_MAX_GROUPS)
345		port->ump_group = 0;
346
347	/* fill default port direction */
348	if (!port->direction) {
349		if (info->capability & SNDRV_SEQ_PORT_CAP_READ)
350			port->direction |= SNDRV_SEQ_PORT_DIR_INPUT;
351		if (info->capability & SNDRV_SEQ_PORT_CAP_WRITE)
352			port->direction |= SNDRV_SEQ_PORT_DIR_OUTPUT;
353	}
354
355	port->is_midi1 = !!(info->flags & SNDRV_SEQ_PORT_FLG_IS_MIDI1);
356
357	return 0;
358}
359
360/* get port info fields */
361int snd_seq_get_port_info(struct snd_seq_client_port * port,
362			  struct snd_seq_port_info * info)
363{
364	if (snd_BUG_ON(!port || !info))
365		return -EINVAL;
366
367	/* get port name */
368	strscpy(info->name, port->name, sizeof(info->name));
369	
370	/* get capabilities */
371	info->capability = port->capability;
372
373	/* get port type */
374	info->type = port->type;
375
376	/* information about supported channels/voices */
377	info->midi_channels = port->midi_channels;
378	info->midi_voices = port->midi_voices;
379	info->synth_voices = port->synth_voices;
380
381	/* get subscriber counts */
382	info->read_use = port->c_src.count;
383	info->write_use = port->c_dest.count;
384	
385	/* timestamping */
386	info->flags = 0;
387	if (port->timestamping) {
388		info->flags |= SNDRV_SEQ_PORT_FLG_TIMESTAMP;
389		if (port->time_real)
390			info->flags |= SNDRV_SEQ_PORT_FLG_TIME_REAL;
391		info->time_queue = port->time_queue;
392	}
393
394	if (port->is_midi1)
395		info->flags |= SNDRV_SEQ_PORT_FLG_IS_MIDI1;
396
397	/* UMP direction and group */
398	info->direction = port->direction;
399	info->ump_group = port->ump_group;
400
401	return 0;
402}
403
404
405
406/*
407 * call callback functions (if any):
408 * the callbacks are invoked only when the first (for connection) or
409 * the last subscription (for disconnection) is done.  Second or later
410 * subscription results in increment of counter, but no callback is
411 * invoked.
412 * This feature is useful if these callbacks are associated with
413 * initialization or termination of devices (see seq_midi.c).
414 */
415
416static int subscribe_port(struct snd_seq_client *client,
417			  struct snd_seq_client_port *port,
418			  struct snd_seq_port_subs_info *grp,
419			  struct snd_seq_port_subscribe *info,
420			  int send_ack)
421{
422	int err = 0;
423
424	if (!try_module_get(port->owner))
425		return -EFAULT;
426	grp->count++;
427	if (grp->open && grp->count == 1) {
428		err = grp->open(port->private_data, info);
429		if (err < 0) {
430			module_put(port->owner);
431			grp->count--;
432		}
433	}
434	if (err >= 0 && send_ack && client->type == USER_CLIENT)
435		snd_seq_client_notify_subscription(port->addr.client, port->addr.port,
436						   info, SNDRV_SEQ_EVENT_PORT_SUBSCRIBED);
437
438	return err;
439}
440
441static int unsubscribe_port(struct snd_seq_client *client,
442			    struct snd_seq_client_port *port,
443			    struct snd_seq_port_subs_info *grp,
444			    struct snd_seq_port_subscribe *info,
445			    int send_ack)
446{
447	int err = 0;
448
449	if (! grp->count)
450		return -EINVAL;
451	grp->count--;
452	if (grp->close && grp->count == 0)
453		err = grp->close(port->private_data, info);
454	if (send_ack && client->type == USER_CLIENT)
455		snd_seq_client_notify_subscription(port->addr.client, port->addr.port,
456						   info, SNDRV_SEQ_EVENT_PORT_UNSUBSCRIBED);
457	module_put(port->owner);
458	return err;
459}
460
461
462
463/* check if both addresses are identical */
464static inline int addr_match(struct snd_seq_addr *r, struct snd_seq_addr *s)
465{
466	return (r->client == s->client) && (r->port == s->port);
467}
468
469/* check the two subscribe info match */
470/* if flags is zero, checks only sender and destination addresses */
471static int match_subs_info(struct snd_seq_port_subscribe *r,
472			   struct snd_seq_port_subscribe *s)
473{
474	if (addr_match(&r->sender, &s->sender) &&
475	    addr_match(&r->dest, &s->dest)) {
476		if (r->flags && r->flags == s->flags)
477			return r->queue == s->queue;
478		else if (! r->flags)
479			return 1;
480	}
481	return 0;
482}
483
484static int check_and_subscribe_port(struct snd_seq_client *client,
485				    struct snd_seq_client_port *port,
486				    struct snd_seq_subscribers *subs,
487				    bool is_src, bool exclusive, bool ack)
488{
489	struct snd_seq_port_subs_info *grp;
490	struct list_head *p;
491	struct snd_seq_subscribers *s;
492	int err;
493
494	grp = is_src ? &port->c_src : &port->c_dest;
495	guard(rwsem_write)(&grp->list_mutex);
496	if (exclusive) {
497		if (!list_empty(&grp->list_head))
498			return -EBUSY;
499	} else {
500		if (grp->exclusive)
501			return -EBUSY;
502		/* check whether already exists */
503		list_for_each(p, &grp->list_head) {
504			s = get_subscriber(p, is_src);
505			if (match_subs_info(&subs->info, &s->info))
506				return -EBUSY;
507		}
508	}
509
510	err = subscribe_port(client, port, grp, &subs->info, ack);
511	if (err < 0) {
512		grp->exclusive = 0;
513		return err;
514	}
515
516	/* add to list */
517	guard(write_lock_irq)(&grp->list_lock);
518	if (is_src)
519		list_add_tail(&subs->src_list, &grp->list_head);
520	else
521		list_add_tail(&subs->dest_list, &grp->list_head);
522	grp->exclusive = exclusive;
523	atomic_inc(&subs->ref_count);
524
525	return 0;
526}
527
528/* called with grp->list_mutex held */
529static void __delete_and_unsubscribe_port(struct snd_seq_client *client,
530					  struct snd_seq_client_port *port,
531					  struct snd_seq_subscribers *subs,
532					  bool is_src, bool ack)
533{
534	struct snd_seq_port_subs_info *grp;
535	struct list_head *list;
536	bool empty;
537
538	grp = is_src ? &port->c_src : &port->c_dest;
539	list = is_src ? &subs->src_list : &subs->dest_list;
540	scoped_guard(write_lock_irq, &grp->list_lock) {
541		empty = list_empty(list);
542		if (!empty)
543			list_del_init(list);
544		grp->exclusive = 0;
545	}
546
547	if (!empty)
548		unsubscribe_port(client, port, grp, &subs->info, ack);
549}
550
551static void delete_and_unsubscribe_port(struct snd_seq_client *client,
552					struct snd_seq_client_port *port,
553					struct snd_seq_subscribers *subs,
554					bool is_src, bool ack)
555{
556	struct snd_seq_port_subs_info *grp;
557
558	grp = is_src ? &port->c_src : &port->c_dest;
559	guard(rwsem_write)(&grp->list_mutex);
560	__delete_and_unsubscribe_port(client, port, subs, is_src, ack);
561}
562
563/* connect two ports */
564int snd_seq_port_connect(struct snd_seq_client *connector,
565			 struct snd_seq_client *src_client,
566			 struct snd_seq_client_port *src_port,
567			 struct snd_seq_client *dest_client,
568			 struct snd_seq_client_port *dest_port,
569			 struct snd_seq_port_subscribe *info)
570{
571	struct snd_seq_subscribers *subs;
572	bool exclusive;
573	int err;
574
575	subs = kzalloc(sizeof(*subs), GFP_KERNEL);
576	if (!subs)
577		return -ENOMEM;
578
579	subs->info = *info;
580	atomic_set(&subs->ref_count, 0);
581	INIT_LIST_HEAD(&subs->src_list);
582	INIT_LIST_HEAD(&subs->dest_list);
583
584	exclusive = !!(info->flags & SNDRV_SEQ_PORT_SUBS_EXCLUSIVE);
585
586	err = check_and_subscribe_port(src_client, src_port, subs, true,
587				       exclusive,
588				       connector->number != src_client->number);
589	if (err < 0)
590		goto error;
591	err = check_and_subscribe_port(dest_client, dest_port, subs, false,
592				       exclusive,
593				       connector->number != dest_client->number);
594	if (err < 0)
595		goto error_dest;
596
597	return 0;
598
599 error_dest:
600	delete_and_unsubscribe_port(src_client, src_port, subs, true,
601				    connector->number != src_client->number);
602 error:
603	kfree(subs);
604	return err;
605}
606
607/* remove the connection */
608int snd_seq_port_disconnect(struct snd_seq_client *connector,
609			    struct snd_seq_client *src_client,
610			    struct snd_seq_client_port *src_port,
611			    struct snd_seq_client *dest_client,
612			    struct snd_seq_client_port *dest_port,
613			    struct snd_seq_port_subscribe *info)
614{
615	struct snd_seq_port_subs_info *dest = &dest_port->c_dest;
616	struct snd_seq_subscribers *subs;
617	int err = -ENOENT;
618
619	/* always start from deleting the dest port for avoiding concurrent
620	 * deletions
621	 */
622	scoped_guard(rwsem_write, &dest->list_mutex) {
623		/* look for the connection */
624		list_for_each_entry(subs, &dest->list_head, dest_list) {
625			if (match_subs_info(info, &subs->info)) {
626				__delete_and_unsubscribe_port(dest_client, dest_port,
627							      subs, false,
628							      connector->number != dest_client->number);
629				err = 0;
630				break;
631			}
632		}
633	}
634	if (err < 0)
635		return err;
636
637	delete_and_unsubscribe_port(src_client, src_port, subs, true,
638				    connector->number != src_client->number);
639	kfree(subs);
640	return 0;
641}
642
643
644/* get matched subscriber */
645int snd_seq_port_get_subscription(struct snd_seq_port_subs_info *src_grp,
646				  struct snd_seq_addr *dest_addr,
647				  struct snd_seq_port_subscribe *subs)
648{
649	struct snd_seq_subscribers *s;
650	int err = -ENOENT;
651
652	guard(rwsem_read)(&src_grp->list_mutex);
653	list_for_each_entry(s, &src_grp->list_head, src_list) {
654		if (addr_match(dest_addr, &s->info.dest)) {
655			*subs = s->info;
656			err = 0;
657			break;
658		}
659	}
660	return err;
661}
662
663/*
664 * Attach a device driver that wants to receive events from the
665 * sequencer.  Returns the new port number on success.
666 * A driver that wants to receive the events converted to midi, will
667 * use snd_seq_midisynth_register_port().
668 */
669/* exported */
670int snd_seq_event_port_attach(int client,
671			      struct snd_seq_port_callback *pcbp,
672			      int cap, int type, int midi_channels,
673			      int midi_voices, char *portname)
674{
675	struct snd_seq_port_info portinfo;
676	int  ret;
677
678	/* Set up the port */
679	memset(&portinfo, 0, sizeof(portinfo));
680	portinfo.addr.client = client;
681	strscpy(portinfo.name, portname ? portname : "Unnamed port",
682		sizeof(portinfo.name));
683
684	portinfo.capability = cap;
685	portinfo.type = type;
686	portinfo.kernel = pcbp;
687	portinfo.midi_channels = midi_channels;
688	portinfo.midi_voices = midi_voices;
689
690	/* Create it */
691	ret = snd_seq_kernel_client_ctl(client,
692					SNDRV_SEQ_IOCTL_CREATE_PORT,
693					&portinfo);
694
695	if (ret >= 0)
696		ret = portinfo.addr.port;
697
698	return ret;
699}
700EXPORT_SYMBOL(snd_seq_event_port_attach);
701
702/*
703 * Detach the driver from a port.
704 */
705/* exported */
706int snd_seq_event_port_detach(int client, int port)
707{
708	struct snd_seq_port_info portinfo;
709	int  err;
710
711	memset(&portinfo, 0, sizeof(portinfo));
712	portinfo.addr.client = client;
713	portinfo.addr.port   = port;
714	err = snd_seq_kernel_client_ctl(client,
715					SNDRV_SEQ_IOCTL_DELETE_PORT,
716					&portinfo);
717
718	return err;
719}
720EXPORT_SYMBOL(snd_seq_event_port_detach);
~/ Read-only file preview.