📄 seq_ports.c
字号:
/* * ALSA sequencer Ports * Copyright (c) 1998 by Frank van de Pol <fvdpol@coil.demon.nl> * Jaroslav Kysela <perex@suse.cz> * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */#include <sound/driver.h>#include <sound/core.h>#include <linux/slab.h>#include "seq_system.h"#include "seq_ports.h"#include "seq_clientmgr.h"/* registration of client ports *//* NOTE: the current implementation of the port structure as a linked list isnot optimal for clients that have many ports. For sending messages to allsubscribers of a port we first need to find the address of the portstructure, which means we have to traverse the list. A direct access table(array) would be better, but big preallocated arrays waste memory.Possible actions:1) leave it this way, a client does normaly does not have more than a fewports2) replace the linked list of ports by a array of pointers which isdynamicly kmalloced. When a port is added or deleted we can simply allocatea new array, copy the corresponding pointers, and delete the old one. Wethen only need a pointer to this array, and an integer that tells us howmuch elements are in array.*//* return pointer to port structure - port is locked if found */client_port_t *snd_seq_port_use_ptr(client_t *client, int num){ struct list_head *p; client_port_t *port; if (client == NULL) return NULL; read_lock(&client->ports_lock); list_for_each(p, &client->ports_list_head) { port = list_entry(p, client_port_t, list); if (port->addr.port == num) { if (port->closing) break; /* deleting now */ snd_use_lock_use(&port->use_lock); read_unlock(&client->ports_lock); return port; } } read_unlock(&client->ports_lock); return NULL; /* not found */}/* search for the next port - port is locked if found */client_port_t *snd_seq_port_query_nearest(client_t *client, snd_seq_port_info_t *pinfo){ int num; struct list_head *p; client_port_t *port, *found; num = pinfo->addr.port; found = NULL; read_lock(&client->ports_lock); list_for_each(p, &client->ports_list_head) { port = list_entry(p, client_port_t, list); if (port->addr.port < num) continue; if (port->addr.port == num) { found = port; break; } if (found == NULL || port->addr.port < found->addr.port) found = port; } if (found) { if (found->closing) found = NULL; else snd_use_lock_use(&found->use_lock); } read_unlock(&client->ports_lock); return found;}/* initialize port_subs_info_t */static void port_subs_info_init(port_subs_info_t *grp){ INIT_LIST_HEAD(&grp->list_head); grp->count = 0; grp->exclusive = 0; rwlock_init(&grp->list_lock); init_rwsem(&grp->list_mutex); grp->open = NULL; grp->close = NULL;}/* create a port, port number is returned (-1 on failure) */client_port_t *snd_seq_create_port(client_t *client, int port){ unsigned long flags; client_port_t *new_port; struct list_head *l; int num = -1; /* sanity check */ snd_assert(client, return NULL); if (client->num_ports >= SNDRV_SEQ_MAX_PORTS - 1) { snd_printk(KERN_WARNING "too many ports for client %d\n", client->number); return NULL; } /* create a new port */ new_port = kzalloc(sizeof(*new_port), GFP_KERNEL); if (! new_port) { snd_printd("malloc failed for registering client port\n"); return NULL; /* failure, out of memory */ } /* init port data */ new_port->addr.client = client->number; new_port->addr.port = -1; new_port->owner = THIS_MODULE; sprintf(new_port->name, "port-%d", num); snd_use_lock_init(&new_port->use_lock); port_subs_info_init(&new_port->c_src); port_subs_info_init(&new_port->c_dest); num = port >= 0 ? port : 0; down(&client->ports_mutex); write_lock_irqsave(&client->ports_lock, flags); list_for_each(l, &client->ports_list_head) { client_port_t *p = list_entry(l, client_port_t, list); if (p->addr.port > num) break; if (port < 0) /* auto-probe mode */ num = p->addr.port + 1; } /* insert the new port */ list_add_tail(&new_port->list, l); client->num_ports++; new_port->addr.port = num; /* store the port number in the port */ write_unlock_irqrestore(&client->ports_lock, flags); up(&client->ports_mutex); sprintf(new_port->name, "port-%d", num); return new_port;}/* */enum group_type_t { SRC_LIST, DEST_LIST};static int subscribe_port(client_t *client, client_port_t *port, port_subs_info_t *grp, snd_seq_port_subscribe_t *info, int send_ack);static int unsubscribe_port(client_t *client, client_port_t *port, port_subs_info_t *grp, snd_seq_port_subscribe_t *info, int send_ack);static client_port_t *get_client_port(snd_seq_addr_t *addr, client_t **cp){ client_port_t *p; *cp = snd_seq_client_use_ptr(addr->client); if (*cp) { p = snd_seq_port_use_ptr(*cp, addr->port); if (! p) { snd_seq_client_unlock(*cp); *cp = NULL; } return p; } return NULL;}/* * remove all subscribers on the list * this is called from port_delete, for each src and dest list. */static void clear_subscriber_list(client_t *client, client_port_t *port, port_subs_info_t *grp, int grptype){ struct list_head *p, *n; down_write(&grp->list_mutex); list_for_each_safe(p, n, &grp->list_head) { subscribers_t *subs; client_t *c; client_port_t *aport; if (grptype == SRC_LIST) { subs = list_entry(p, subscribers_t, src_list); aport = get_client_port(&subs->info.dest, &c); } else { subs = list_entry(p, subscribers_t, dest_list); aport = get_client_port(&subs->info.sender, &c); } list_del(p); unsubscribe_port(client, port, grp, &subs->info, 0); if (!aport) { /* looks like the connected port is being deleted. * we decrease the counter, and when both ports are deleted * remove the subscriber info */ if (atomic_dec_and_test(&subs->ref_count)) kfree(subs); } else { /* ok we got the connected port */ port_subs_info_t *agrp; agrp = (grptype == SRC_LIST) ? &aport->c_dest : &aport->c_src; down_write(&agrp->list_mutex); if (grptype == SRC_LIST) list_del(&subs->dest_list); else list_del(&subs->src_list); unsubscribe_port(c, aport, agrp, &subs->info, 1); kfree(subs); up_write(&agrp->list_mutex); snd_seq_port_unlock(aport); snd_seq_client_unlock(c); } } up_write(&grp->list_mutex);}/* delete port data */static int port_delete(client_t *client, client_port_t *port){ /* set closing flag and wait for all port access are gone */ port->closing = 1; snd_use_lock_sync(&port->use_lock); /* clear subscribers info */ clear_subscriber_list(client, port, &port->c_src, SRC_LIST); clear_subscriber_list(client, port, &port->c_dest, DEST_LIST); if (port->private_free) port->private_free(port->private_data); snd_assert(port->c_src.count == 0,); snd_assert(port->c_dest.count == 0,); kfree(port); return 0;}/* delete a port with the given port id */int snd_seq_delete_port(client_t *client, int port){ unsigned long flags; struct list_head *l; client_port_t *found = NULL; down(&client->ports_mutex); write_lock_irqsave(&client->ports_lock, flags); list_for_each(l, &client->ports_list_head) { client_port_t *p = list_entry(l, client_port_t, list); if (p->addr.port == port) { /* ok found. delete from the list at first */ list_del(l); client->num_ports--; found = p; break; } } write_unlock_irqrestore(&client->ports_lock, flags); up(&client->ports_mutex); if (found) return port_delete(client, found); else return -ENOENT;}/* delete the all ports belonging to the given client */int snd_seq_delete_all_ports(client_t *client){ unsigned long flags; struct list_head deleted_list, *p, *n; /* move the port list to deleted_list, and * clear the port list in the client data. */ down(&client->ports_mutex); write_lock_irqsave(&client->ports_lock, flags); if (! list_empty(&client->ports_list_head)) { __list_add(&deleted_list, client->ports_list_head.prev, client->ports_list_head.next); INIT_LIST_HEAD(&client->ports_list_head); } else { INIT_LIST_HEAD(&deleted_list); } client->num_ports = 0; write_unlock_irqrestore(&client->ports_lock, flags); /* remove each port in deleted_list */ list_for_each_safe(p, n, &deleted_list) { client_port_t *port = list_entry(p, client_port_t, list); list_del(p); snd_seq_system_client_ev_port_exit(port->addr.client, port->addr.port); port_delete(client, port); } up(&client->ports_mutex); return 0;}/* set port info fields */int snd_seq_set_port_info(client_port_t * port, snd_seq_port_info_t * info){ snd_assert(port && info, return -EINVAL);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -