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