Branch data Line data Source code
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 : 26 : struct snd_seq_client_port *snd_seq_port_use_ptr(struct snd_seq_client *client,
45 : : int num)
46 : : {
47 : 26 : struct snd_seq_client_port *port;
48 : :
49 [ + - ]: 26 : if (client == NULL)
50 : : return NULL;
51 : 26 : read_lock(&client->ports_lock);
52 [ + - ]: 52 : list_for_each_entry(port, &client->ports_list_head, list) {
53 [ + + ]: 52 : if (port->addr.port == num) {
54 [ + - ]: 26 : if (port->closing)
55 : : break; /* deleting now */
56 : 26 : snd_use_lock_use(&port->use_lock);
57 : 26 : read_unlock(&client->ports_lock);
58 : 26 : return port;
59 : : }
60 : : }
61 : 0 : read_unlock(&client->ports_lock);
62 : 0 : return NULL; /* not found */
63 : : }
64 : :
65 : :
66 : : /* search for the next port - port is locked if found */
67 : 0 : struct snd_seq_client_port *snd_seq_port_query_nearest(struct snd_seq_client *client,
68 : : struct snd_seq_port_info *pinfo)
69 : : {
70 : 0 : int num;
71 : 0 : struct snd_seq_client_port *port, *found;
72 : :
73 : 0 : num = pinfo->addr.port;
74 : 0 : found = NULL;
75 : 0 : read_lock(&client->ports_lock);
76 [ # # ]: 0 : list_for_each_entry(port, &client->ports_list_head, list) {
77 [ # # ]: 0 : if (port->addr.port < num)
78 : 0 : continue;
79 [ # # ]: 0 : if (port->addr.port == num) {
80 : : found = port;
81 : : break;
82 : : }
83 [ # # # # ]: 0 : if (found == NULL || port->addr.port < found->addr.port)
84 : 0 : found = port;
85 : : }
86 [ # # ]: 0 : if (found) {
87 [ # # ]: 0 : if (found->closing)
88 : : found = NULL;
89 : : else
90 : 0 : snd_use_lock_use(&found->use_lock);
91 : : }
92 : 0 : read_unlock(&client->ports_lock);
93 : 0 : return found;
94 : : }
95 : :
96 : :
97 : : /* initialize snd_seq_port_subs_info */
98 : 78 : static void port_subs_info_init(struct snd_seq_port_subs_info *grp)
99 : : {
100 : 78 : INIT_LIST_HEAD(&grp->list_head);
101 : 78 : grp->count = 0;
102 : 78 : grp->exclusive = 0;
103 : 39 : rwlock_init(&grp->list_lock);
104 : 39 : init_rwsem(&grp->list_mutex);
105 : 78 : grp->open = NULL;
106 : 78 : 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 : : */
113 : 39 : struct snd_seq_client_port *snd_seq_create_port(struct snd_seq_client *client,
114 : : int port)
115 : : {
116 : 39 : struct snd_seq_client_port *new_port, *p;
117 : 39 : int num = -1;
118 : :
119 : : /* sanity check */
120 [ + - ]: 39 : if (snd_BUG_ON(!client))
121 : : return NULL;
122 : :
123 [ - + ]: 39 : if (client->num_ports >= SNDRV_SEQ_MAX_PORTS) {
124 : 0 : pr_warn("ALSA: seq: too many ports for client %d\n", client->number);
125 : 0 : return NULL;
126 : : }
127 : :
128 : : /* create a new port */
129 : 39 : new_port = kzalloc(sizeof(*new_port), GFP_KERNEL);
130 [ + - ]: 39 : if (!new_port)
131 : : return NULL; /* failure, out of memory */
132 : : /* init port data */
133 : 39 : new_port->addr.client = client->number;
134 : 39 : new_port->addr.port = -1;
135 : 39 : new_port->owner = THIS_MODULE;
136 : 39 : sprintf(new_port->name, "port-%d", num);
137 : 39 : snd_use_lock_init(&new_port->use_lock);
138 : 39 : port_subs_info_init(&new_port->c_src);
139 : 39 : port_subs_info_init(&new_port->c_dest);
140 : 39 : snd_use_lock_use(&new_port->use_lock);
141 : :
142 : 39 : num = port >= 0 ? port : 0;
143 : 39 : mutex_lock(&client->ports_mutex);
144 : 39 : write_lock_irq(&client->ports_lock);
145 [ + + ]: 52 : list_for_each_entry(p, &client->ports_list_head, list) {
146 [ + - ]: 13 : if (p->addr.port > num)
147 : : break;
148 [ - + ]: 13 : if (port < 0) /* auto-probe mode */
149 : 0 : num = p->addr.port + 1;
150 : : }
151 : : /* insert the new port */
152 : 39 : list_add_tail(&new_port->list, &p->list);
153 : 39 : client->num_ports++;
154 : 39 : new_port->addr.port = num; /* store the port number in the port */
155 : 39 : sprintf(new_port->name, "port-%d", num);
156 : 39 : write_unlock_irq(&client->ports_lock);
157 : 39 : mutex_unlock(&client->ports_mutex);
158 : :
159 : 39 : 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 : :
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 *
195 : 0 : get_subscriber(struct list_head *p, bool is_src)
196 : : {
197 : 0 : if (is_src)
198 : 0 : return list_entry(p, struct snd_seq_subscribers, src_list);
199 : : else
200 : 0 : 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 : : */
207 : 0 : 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 : 0 : struct list_head *p, *n;
213 : :
214 [ # # ]: 0 : list_for_each_safe(p, n, &grp->list_head) {
215 : 0 : struct snd_seq_subscribers *subs;
216 : 0 : struct snd_seq_client *c;
217 : 0 : struct snd_seq_client_port *aport;
218 : :
219 [ # # ]: 0 : subs = get_subscriber(p, is_src);
220 [ # # ]: 0 : if (is_src)
221 : 0 : aport = get_client_port(&subs->info.dest, &c);
222 : : else
223 : 0 : aport = get_client_port(&subs->info.sender, &c);
224 : 0 : delete_and_unsubscribe_port(client, port, subs, is_src, false);
225 : :
226 [ # # ]: 0 : 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 [ # # ]: 0 : if (atomic_dec_and_test(&subs->ref_count))
232 : 0 : kfree(subs);
233 : 0 : continue;
234 : : }
235 : :
236 : : /* ok we got the connected port */
237 : 0 : delete_and_unsubscribe_port(c, aport, subs, !is_src, true);
238 : 0 : kfree(subs);
239 : 0 : snd_seq_port_unlock(aport);
240 : 0 : snd_seq_client_unlock(c);
241 : : }
242 : 0 : }
243 : :
244 : : /* delete port data */
245 : 0 : 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 : 0 : port->closing = 1;
250 : 0 : snd_use_lock_sync(&port->use_lock);
251 : :
252 : : /* clear subscribers info */
253 : 0 : clear_subscriber_list(client, port, &port->c_src, true);
254 : 0 : clear_subscriber_list(client, port, &port->c_dest, false);
255 : :
256 [ # # ]: 0 : if (port->private_free)
257 : 0 : port->private_free(port->private_data);
258 : :
259 : 0 : snd_BUG_ON(port->c_src.count != 0);
260 : 0 : snd_BUG_ON(port->c_dest.count != 0);
261 : :
262 : 0 : kfree(port);
263 : 0 : return 0;
264 : : }
265 : :
266 : :
267 : : /* delete a port with the given port id */
268 : 0 : int snd_seq_delete_port(struct snd_seq_client *client, int port)
269 : : {
270 : 0 : struct snd_seq_client_port *found = NULL, *p;
271 : :
272 : 0 : mutex_lock(&client->ports_mutex);
273 : 0 : write_lock_irq(&client->ports_lock);
274 [ # # ]: 0 : list_for_each_entry(p, &client->ports_list_head, list) {
275 [ # # ]: 0 : if (p->addr.port == port) {
276 : : /* ok found. delete from the list at first */
277 : 0 : list_del(&p->list);
278 : 0 : client->num_ports--;
279 : 0 : found = p;
280 : 0 : break;
281 : : }
282 : : }
283 : 0 : write_unlock_irq(&client->ports_lock);
284 : 0 : mutex_unlock(&client->ports_mutex);
285 [ # # ]: 0 : if (found)
286 : 0 : return port_delete(client, found);
287 : : else
288 : : return -ENOENT;
289 : : }
290 : :
291 : : /* delete the all ports belonging to the given client */
292 : 0 : int snd_seq_delete_all_ports(struct snd_seq_client *client)
293 : : {
294 : 0 : struct list_head deleted_list;
295 : 0 : 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 : 0 : mutex_lock(&client->ports_mutex);
301 : 0 : write_lock_irq(&client->ports_lock);
302 [ # # ]: 0 : if (! list_empty(&client->ports_list_head)) {
303 : 0 : list_add(&deleted_list, &client->ports_list_head);
304 : 0 : list_del_init(&client->ports_list_head);
305 : : } else {
306 : 0 : INIT_LIST_HEAD(&deleted_list);
307 : : }
308 : 0 : client->num_ports = 0;
309 : 0 : write_unlock_irq(&client->ports_lock);
310 : :
311 : : /* remove each port in deleted_list */
312 [ # # ]: 0 : list_for_each_entry_safe(port, tmp, &deleted_list, list) {
313 : 0 : list_del(&port->list);
314 : 0 : snd_seq_system_client_ev_port_exit(port->addr.client, port->addr.port);
315 : 0 : port_delete(client, port);
316 : : }
317 : 0 : mutex_unlock(&client->ports_mutex);
318 : 0 : return 0;
319 : : }
320 : :
321 : : /* set port info fields */
322 : 39 : int snd_seq_set_port_info(struct snd_seq_client_port * port,
323 : : struct snd_seq_port_info * info)
324 : : {
325 [ + - ]: 39 : if (snd_BUG_ON(!port || !info))
326 : : return -EINVAL;
327 : :
328 : : /* set port name */
329 [ + - ]: 39 : if (info->name[0])
330 : 39 : strlcpy(port->name, info->name, sizeof(port->name));
331 : :
332 : : /* set capabilities */
333 : 39 : port->capability = info->capability;
334 : :
335 : : /* get port type */
336 : 39 : port->type = info->type;
337 : :
338 : : /* information about supported channels/voices */
339 : 39 : port->midi_channels = info->midi_channels;
340 : 39 : port->midi_voices = info->midi_voices;
341 : 39 : port->synth_voices = info->synth_voices;
342 : :
343 : : /* timestamping */
344 : 39 : port->timestamping = (info->flags & SNDRV_SEQ_PORT_FLG_TIMESTAMP) ? 1 : 0;
345 : 39 : port->time_real = (info->flags & SNDRV_SEQ_PORT_FLG_TIME_REAL) ? 1 : 0;
346 : 39 : port->time_queue = info->time_queue;
347 : :
348 : 39 : return 0;
349 : : }
350 : :
351 : : /* get port info fields */
352 : 0 : int snd_seq_get_port_info(struct snd_seq_client_port * port,
353 : : struct snd_seq_port_info * info)
354 : : {
355 [ # # ]: 0 : if (snd_BUG_ON(!port || !info))
356 : : return -EINVAL;
357 : :
358 : : /* get port name */
359 : 0 : strlcpy(info->name, port->name, sizeof(info->name));
360 : :
361 : : /* get capabilities */
362 : 0 : info->capability = port->capability;
363 : :
364 : : /* get port type */
365 : 0 : info->type = port->type;
366 : :
367 : : /* information about supported channels/voices */
368 : 0 : info->midi_channels = port->midi_channels;
369 : 0 : info->midi_voices = port->midi_voices;
370 : 0 : info->synth_voices = port->synth_voices;
371 : :
372 : : /* get subscriber counts */
373 : 0 : info->read_use = port->c_src.count;
374 : 0 : info->write_use = port->c_dest.count;
375 : :
376 : : /* timestamping */
377 : 0 : info->flags = 0;
378 [ # # ]: 0 : if (port->timestamping) {
379 : 0 : info->flags |= SNDRV_SEQ_PORT_FLG_TIMESTAMP;
380 [ # # ]: 0 : if (port->time_real)
381 : 0 : info->flags |= SNDRV_SEQ_PORT_FLG_TIME_REAL;
382 : 0 : 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 : :
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 : :
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 */
448 : 0 : static inline int addr_match(struct snd_seq_addr *r, struct snd_seq_addr *s)
449 : : {
450 [ # # # # : 0 : 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 */
455 : 0 : static int match_subs_info(struct snd_seq_port_subscribe *r,
456 : : struct snd_seq_port_subscribe *s)
457 : : {
458 [ # # ]: 0 : if (addr_match(&r->sender, &s->sender) &&
459 [ # # ]: 0 : addr_match(&r->dest, &s->dest)) {
460 [ # # # # ]: 0 : if (r->flags && r->flags == s->flags)
461 : 0 : return r->queue == s->queue;
462 [ # # ]: 0 : else if (! r->flags)
463 : 0 : return 1;
464 : : }
465 : : return 0;
466 : : }
467 : :
468 : 0 : 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 : 0 : struct snd_seq_port_subs_info *grp;
474 : 0 : struct list_head *p;
475 : 0 : struct snd_seq_subscribers *s;
476 : 0 : int err;
477 : :
478 [ # # ]: 0 : grp = is_src ? &port->c_src : &port->c_dest;
479 : 0 : err = -EBUSY;
480 : 0 : down_write(&grp->list_mutex);
481 [ # # ]: 0 : if (exclusive) {
482 [ # # ]: 0 : if (!list_empty(&grp->list_head))
483 : 0 : goto __error;
484 : : } else {
485 [ # # ]: 0 : if (grp->exclusive)
486 : 0 : goto __error;
487 : : /* check whether already exists */
488 [ # # ]: 0 : list_for_each(p, &grp->list_head) {
489 [ # # ]: 0 : s = get_subscriber(p, is_src);
490 [ # # ]: 0 : if (match_subs_info(&subs->info, &s->info))
491 : 0 : goto __error;
492 : : }
493 : : }
494 : :
495 : 0 : err = subscribe_port(client, port, grp, &subs->info, ack);
496 [ # # ]: 0 : if (err < 0) {
497 : 0 : grp->exclusive = 0;
498 : 0 : goto __error;
499 : : }
500 : :
501 : : /* add to list */
502 : 0 : write_lock_irq(&grp->list_lock);
503 [ # # ]: 0 : if (is_src)
504 : 0 : list_add_tail(&subs->src_list, &grp->list_head);
505 : : else
506 : 0 : list_add_tail(&subs->dest_list, &grp->list_head);
507 : 0 : grp->exclusive = exclusive;
508 : 0 : atomic_inc(&subs->ref_count);
509 : 0 : write_unlock_irq(&grp->list_lock);
510 : 0 : err = 0;
511 : :
512 : 0 : __error:
513 : 0 : up_write(&grp->list_mutex);
514 : 0 : return err;
515 : : }
516 : :
517 : 0 : 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 : 0 : struct snd_seq_port_subs_info *grp;
523 : 0 : struct list_head *list;
524 : 0 : bool empty;
525 : :
526 [ # # ]: 0 : grp = is_src ? &port->c_src : &port->c_dest;
527 [ # # ]: 0 : list = is_src ? &subs->src_list : &subs->dest_list;
528 : 0 : down_write(&grp->list_mutex);
529 : 0 : write_lock_irq(&grp->list_lock);
530 [ # # ]: 0 : empty = list_empty(list);
531 [ # # ]: 0 : if (!empty)
532 : 0 : list_del_init(list);
533 : 0 : grp->exclusive = 0;
534 : 0 : write_unlock_irq(&grp->list_lock);
535 : :
536 [ # # ]: 0 : if (!empty)
537 : 0 : unsubscribe_port(client, port, grp, &subs->info, ack);
538 : 0 : up_write(&grp->list_mutex);
539 : 0 : }
540 : :
541 : : /* connect two ports */
542 : 0 : 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 : 0 : struct snd_seq_subscribers *subs;
550 : 0 : bool exclusive;
551 : 0 : int err;
552 : :
553 : 0 : subs = kzalloc(sizeof(*subs), GFP_KERNEL);
554 [ # # ]: 0 : if (!subs)
555 : : return -ENOMEM;
556 : :
557 : 0 : subs->info = *info;
558 : 0 : atomic_set(&subs->ref_count, 0);
559 : 0 : INIT_LIST_HEAD(&subs->src_list);
560 : 0 : INIT_LIST_HEAD(&subs->dest_list);
561 : :
562 : 0 : exclusive = !!(info->flags & SNDRV_SEQ_PORT_SUBS_EXCLUSIVE);
563 : :
564 : 0 : err = check_and_subscribe_port(src_client, src_port, subs, true,
565 : : exclusive,
566 : 0 : connector->number != src_client->number);
567 [ # # ]: 0 : if (err < 0)
568 : 0 : goto error;
569 : 0 : err = check_and_subscribe_port(dest_client, dest_port, subs, false,
570 : : exclusive,
571 : 0 : connector->number != dest_client->number);
572 [ # # ]: 0 : if (err < 0)
573 : 0 : goto error_dest;
574 : :
575 : : return 0;
576 : :
577 : : error_dest:
578 : 0 : delete_and_unsubscribe_port(src_client, src_port, subs, true,
579 : 0 : connector->number != src_client->number);
580 : 0 : error:
581 : 0 : kfree(subs);
582 : 0 : return err;
583 : : }
584 : :
585 : : /* remove the connection */
586 : 0 : 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 : 0 : struct snd_seq_port_subs_info *src = &src_port->c_src;
594 : 0 : struct snd_seq_subscribers *subs;
595 : 0 : int err = -ENOENT;
596 : :
597 : 0 : down_write(&src->list_mutex);
598 : : /* look for the connection */
599 [ # # ]: 0 : list_for_each_entry(subs, &src->list_head, src_list) {
600 [ # # ]: 0 : if (match_subs_info(info, &subs->info)) {
601 : 0 : atomic_dec(&subs->ref_count); /* mark as not ready */
602 : 0 : err = 0;
603 : 0 : break;
604 : : }
605 : : }
606 : 0 : up_write(&src->list_mutex);
607 [ # # ]: 0 : if (err < 0)
608 : : return err;
609 : :
610 : 0 : delete_and_unsubscribe_port(src_client, src_port, subs, true,
611 : 0 : connector->number != src_client->number);
612 : 0 : delete_and_unsubscribe_port(dest_client, dest_port, subs, false,
613 : 0 : connector->number != dest_client->number);
614 : 0 : kfree(subs);
615 : 0 : return 0;
616 : : }
617 : :
618 : :
619 : : /* get matched subscriber */
620 : 0 : 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 : 0 : struct snd_seq_subscribers *s;
625 : 0 : int err = -ENOENT;
626 : :
627 : 0 : down_read(&src_grp->list_mutex);
628 [ # # ]: 0 : list_for_each_entry(s, &src_grp->list_head, src_list) {
629 [ # # ]: 0 : if (addr_match(dest_addr, &s->info.dest)) {
630 : 0 : *subs = s->info;
631 : 0 : err = 0;
632 : 0 : break;
633 : : }
634 : : }
635 : 0 : up_read(&src_grp->list_mutex);
636 : 0 : 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 */
646 : 0 : 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 : 0 : struct snd_seq_port_info portinfo;
652 : 0 : int ret;
653 : :
654 : : /* Set up the port */
655 : 0 : memset(&portinfo, 0, sizeof(portinfo));
656 : 0 : portinfo.addr.client = client;
657 [ # # ]: 0 : strlcpy(portinfo.name, portname ? portname : "Unnamed port",
658 : : sizeof(portinfo.name));
659 : :
660 : 0 : portinfo.capability = cap;
661 : 0 : portinfo.type = type;
662 : 0 : portinfo.kernel = pcbp;
663 : 0 : portinfo.midi_channels = midi_channels;
664 : 0 : portinfo.midi_voices = midi_voices;
665 : :
666 : : /* Create it */
667 : 0 : ret = snd_seq_kernel_client_ctl(client,
668 : : SNDRV_SEQ_IOCTL_CREATE_PORT,
669 : : &portinfo);
670 : :
671 [ # # ]: 0 : if (ret >= 0)
672 : 0 : ret = portinfo.addr.port;
673 : :
674 : 0 : return ret;
675 : : }
676 : : EXPORT_SYMBOL(snd_seq_event_port_attach);
677 : :
678 : : /*
679 : : * Detach the driver from a port.
680 : : */
681 : : /* exported */
682 : 0 : int snd_seq_event_port_detach(int client, int port)
683 : : {
684 : 0 : struct snd_seq_port_info portinfo;
685 : 0 : int err;
686 : :
687 : 0 : memset(&portinfo, 0, sizeof(portinfo));
688 : 0 : portinfo.addr.client = client;
689 : 0 : portinfo.addr.port = port;
690 : 0 : err = snd_seq_kernel_client_ctl(client,
691 : : SNDRV_SEQ_IOCTL_DELETE_PORT,
692 : : &portinfo);
693 : :
694 : 0 : return err;
695 : : }
696 : : EXPORT_SYMBOL(snd_seq_event_port_detach);
|