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