root/sound/core/seq/seq_ports.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. snd_seq_port_use_ptr
  2. snd_seq_port_query_nearest
  3. port_subs_info_init
  4. snd_seq_create_port
  5. get_client_port
  6. get_subscriber
  7. clear_subscriber_list
  8. port_delete
  9. snd_seq_delete_port
  10. snd_seq_delete_all_ports
  11. snd_seq_set_port_info
  12. snd_seq_get_port_info
  13. subscribe_port
  14. unsubscribe_port
  15. addr_match
  16. match_subs_info
  17. check_and_subscribe_port
  18. delete_and_unsubscribe_port
  19. snd_seq_port_connect
  20. snd_seq_port_disconnect
  21. snd_seq_port_get_subscription
  22. snd_seq_event_port_attach
  23. snd_seq_event_port_detach

   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 
  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 */
  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  */
 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 
 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 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  */
 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 */
 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 */
 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 */
 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 */
 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 */
 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 
 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 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 */
 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 
 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 
 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 */
 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 */
 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 */
 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 */
 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 */
 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);

/* [<][>][^][v][top][bottom][index][help] */