📄 seq_clientmgr.c
字号:
*/static int snd_seq_ioctl_query_subs(struct snd_seq_client *client, void __user *arg){ int result = -ENXIO; struct snd_seq_client *cptr = NULL; struct snd_seq_client_port *port = NULL; struct snd_seq_query_subs subs; struct snd_seq_port_subs_info *group; struct list_head *p; int i; if (copy_from_user(&subs, arg, sizeof(subs))) return -EFAULT; if ((cptr = snd_seq_client_use_ptr(subs.root.client)) == NULL) goto __end; if ((port = snd_seq_port_use_ptr(cptr, subs.root.port)) == NULL) goto __end; switch (subs.type) { case SNDRV_SEQ_QUERY_SUBS_READ: group = &port->c_src; break; case SNDRV_SEQ_QUERY_SUBS_WRITE: group = &port->c_dest; break; default: goto __end; } down_read(&group->list_mutex); /* search for the subscriber */ subs.num_subs = group->count; i = 0; result = -ENOENT; list_for_each(p, &group->list_head) { if (i++ == subs.index) { /* found! */ struct snd_seq_subscribers *s; if (subs.type == SNDRV_SEQ_QUERY_SUBS_READ) { s = list_entry(p, struct snd_seq_subscribers, src_list); subs.addr = s->info.dest; } else { s = list_entry(p, struct snd_seq_subscribers, dest_list); subs.addr = s->info.sender; } subs.flags = s->info.flags; subs.queue = s->info.queue; result = 0; break; } } up_read(&group->list_mutex); __end: if (port) snd_seq_port_unlock(port); if (cptr) snd_seq_client_unlock(cptr); if (result >= 0) { if (copy_to_user(arg, &subs, sizeof(subs))) return -EFAULT; } return result;}/* * query next client */static int snd_seq_ioctl_query_next_client(struct snd_seq_client *client, void __user *arg){ struct snd_seq_client *cptr = NULL; struct snd_seq_client_info info; if (copy_from_user(&info, arg, sizeof(info))) return -EFAULT; /* search for next client */ info.client++; if (info.client < 0) info.client = 0; for (; info.client < SNDRV_SEQ_MAX_CLIENTS; info.client++) { cptr = snd_seq_client_use_ptr(info.client); if (cptr) break; /* found */ } if (cptr == NULL) return -ENOENT; get_client_info(cptr, &info); snd_seq_client_unlock(cptr); if (copy_to_user(arg, &info, sizeof(info))) return -EFAULT; return 0;}/* * query next port */static int snd_seq_ioctl_query_next_port(struct snd_seq_client *client, void __user *arg){ struct snd_seq_client *cptr; struct snd_seq_client_port *port = NULL; struct snd_seq_port_info info; if (copy_from_user(&info, arg, sizeof(info))) return -EFAULT; cptr = snd_seq_client_use_ptr(info.addr.client); if (cptr == NULL) return -ENXIO; /* search for next port */ info.addr.port++; port = snd_seq_port_query_nearest(cptr, &info); if (port == NULL) { snd_seq_client_unlock(cptr); return -ENOENT; } /* get port info */ info.addr = port->addr; snd_seq_get_port_info(port, &info); snd_seq_port_unlock(port); snd_seq_client_unlock(cptr); if (copy_to_user(arg, &info, sizeof(info))) return -EFAULT; return 0;}/* -------------------------------------------------------- */static struct seq_ioctl_table { unsigned int cmd; int (*func)(struct snd_seq_client *client, void __user * arg);} ioctl_tables[] = { { SNDRV_SEQ_IOCTL_SYSTEM_INFO, snd_seq_ioctl_system_info }, { SNDRV_SEQ_IOCTL_RUNNING_MODE, snd_seq_ioctl_running_mode }, { SNDRV_SEQ_IOCTL_GET_CLIENT_INFO, snd_seq_ioctl_get_client_info }, { SNDRV_SEQ_IOCTL_SET_CLIENT_INFO, snd_seq_ioctl_set_client_info }, { SNDRV_SEQ_IOCTL_CREATE_PORT, snd_seq_ioctl_create_port }, { SNDRV_SEQ_IOCTL_DELETE_PORT, snd_seq_ioctl_delete_port }, { SNDRV_SEQ_IOCTL_GET_PORT_INFO, snd_seq_ioctl_get_port_info }, { SNDRV_SEQ_IOCTL_SET_PORT_INFO, snd_seq_ioctl_set_port_info }, { SNDRV_SEQ_IOCTL_SUBSCRIBE_PORT, snd_seq_ioctl_subscribe_port }, { SNDRV_SEQ_IOCTL_UNSUBSCRIBE_PORT, snd_seq_ioctl_unsubscribe_port }, { SNDRV_SEQ_IOCTL_CREATE_QUEUE, snd_seq_ioctl_create_queue }, { SNDRV_SEQ_IOCTL_DELETE_QUEUE, snd_seq_ioctl_delete_queue }, { SNDRV_SEQ_IOCTL_GET_QUEUE_INFO, snd_seq_ioctl_get_queue_info }, { SNDRV_SEQ_IOCTL_SET_QUEUE_INFO, snd_seq_ioctl_set_queue_info }, { SNDRV_SEQ_IOCTL_GET_NAMED_QUEUE, snd_seq_ioctl_get_named_queue }, { SNDRV_SEQ_IOCTL_GET_QUEUE_STATUS, snd_seq_ioctl_get_queue_status }, { SNDRV_SEQ_IOCTL_GET_QUEUE_TEMPO, snd_seq_ioctl_get_queue_tempo }, { SNDRV_SEQ_IOCTL_SET_QUEUE_TEMPO, snd_seq_ioctl_set_queue_tempo }, { SNDRV_SEQ_IOCTL_GET_QUEUE_TIMER, snd_seq_ioctl_get_queue_timer }, { SNDRV_SEQ_IOCTL_SET_QUEUE_TIMER, snd_seq_ioctl_set_queue_timer }, { SNDRV_SEQ_IOCTL_GET_QUEUE_CLIENT, snd_seq_ioctl_get_queue_client }, { SNDRV_SEQ_IOCTL_SET_QUEUE_CLIENT, snd_seq_ioctl_set_queue_client }, { SNDRV_SEQ_IOCTL_GET_CLIENT_POOL, snd_seq_ioctl_get_client_pool }, { SNDRV_SEQ_IOCTL_SET_CLIENT_POOL, snd_seq_ioctl_set_client_pool }, { SNDRV_SEQ_IOCTL_GET_SUBSCRIPTION, snd_seq_ioctl_get_subscription }, { SNDRV_SEQ_IOCTL_QUERY_NEXT_CLIENT, snd_seq_ioctl_query_next_client }, { SNDRV_SEQ_IOCTL_QUERY_NEXT_PORT, snd_seq_ioctl_query_next_port }, { SNDRV_SEQ_IOCTL_REMOVE_EVENTS, snd_seq_ioctl_remove_events }, { SNDRV_SEQ_IOCTL_QUERY_SUBS, snd_seq_ioctl_query_subs }, { 0, NULL },};static int snd_seq_do_ioctl(struct snd_seq_client *client, unsigned int cmd, void __user *arg){ struct seq_ioctl_table *p; switch (cmd) { case SNDRV_SEQ_IOCTL_PVERSION: /* return sequencer version number */ return put_user(SNDRV_SEQ_VERSION, (int __user *)arg) ? -EFAULT : 0; case SNDRV_SEQ_IOCTL_CLIENT_ID: /* return the id of this client */ return put_user(client->number, (int __user *)arg) ? -EFAULT : 0; } if (! arg) return -EFAULT; for (p = ioctl_tables; p->cmd; p++) { if (p->cmd == cmd) return p->func(client, arg); } snd_printd("seq unknown ioctl() 0x%x (type='%c', number=0x%2x)\n", cmd, _IOC_TYPE(cmd), _IOC_NR(cmd)); return -ENOTTY;}static long snd_seq_ioctl(struct file *file, unsigned int cmd, unsigned long arg){ struct snd_seq_client *client = file->private_data; snd_assert(client != NULL, return -ENXIO); return snd_seq_do_ioctl(client, cmd, (void __user *) arg);}#ifdef CONFIG_COMPAT#include "seq_compat.c"#else#define snd_seq_ioctl_compat NULL#endif/* -------------------------------------------------------- *//* exported to kernel modules */int snd_seq_create_kernel_client(struct snd_card *card, int client_index, const char *name_fmt, ...){ struct snd_seq_client *client; va_list args; snd_assert(! in_interrupt(), return -EBUSY); if (card && client_index >= SNDRV_SEQ_CLIENTS_PER_CARD) return -EINVAL; if (card == NULL && client_index >= SNDRV_SEQ_GLOBAL_CLIENTS) return -EINVAL; if (mutex_lock_interruptible(®ister_mutex)) return -ERESTARTSYS; if (card) { client_index += SNDRV_SEQ_GLOBAL_CLIENTS + card->number * SNDRV_SEQ_CLIENTS_PER_CARD; if (client_index >= SNDRV_SEQ_DYNAMIC_CLIENTS_BEGIN) client_index = -1; } /* empty write queue as default */ client = seq_create_client1(client_index, 0); if (client == NULL) { mutex_unlock(®ister_mutex); return -EBUSY; /* failure code */ } usage_alloc(&client_usage, 1); client->accept_input = 1; client->accept_output = 1; va_start(args, name_fmt); vsnprintf(client->name, sizeof(client->name), name_fmt, args); va_end(args); client->type = KERNEL_CLIENT; mutex_unlock(®ister_mutex); /* make others aware this new client */ snd_seq_system_client_ev_client_start(client->number); /* return client number to caller */ return client->number;}/* exported to kernel modules */int snd_seq_delete_kernel_client(int client){ struct snd_seq_client *ptr; snd_assert(! in_interrupt(), return -EBUSY); ptr = clientptr(client); if (ptr == NULL) return -EINVAL; seq_free_client(ptr); kfree(ptr); return 0;}/* skeleton to enqueue event, called from snd_seq_kernel_client_enqueue * and snd_seq_kernel_client_enqueue_blocking */static int kernel_client_enqueue(int client, struct snd_seq_event *ev, struct file *file, int blocking, int atomic, int hop){ struct snd_seq_client *cptr; int result; snd_assert(ev != NULL, return -EINVAL); if (ev->type == SNDRV_SEQ_EVENT_NONE) return 0; /* ignore this */ if (ev->type == SNDRV_SEQ_EVENT_KERNEL_ERROR) return -EINVAL; /* quoted events can't be enqueued */ /* fill in client number */ ev->source.client = client; if (check_event_type_and_length(ev)) return -EINVAL; cptr = snd_seq_client_use_ptr(client); if (cptr == NULL) return -EINVAL; if (! cptr->accept_output) result = -EPERM; else /* send it */ result = snd_seq_client_enqueue_event(cptr, ev, file, blocking, atomic, hop); snd_seq_client_unlock(cptr); return result;}/* * exported, called by kernel clients to enqueue events (w/o blocking) * * RETURN VALUE: zero if succeed, negative if error */int snd_seq_kernel_client_enqueue(int client, struct snd_seq_event * ev, int atomic, int hop){ return kernel_client_enqueue(client, ev, NULL, 0, atomic, hop);}/* * exported, called by kernel clients to enqueue events (with blocking) * * RETURN VALUE: zero if succeed, negative if error */int snd_seq_kernel_client_enqueue_blocking(int client, struct snd_seq_event * ev, struct file *file, int atomic, int hop){ return kernel_client_enqueue(client, ev, file, 1, atomic, hop);}/* * exported, called by kernel clients to dispatch events directly to other * clients, bypassing the queues. Event time-stamp will be updated. * * RETURN VALUE: negative = delivery failed, * zero, or positive: the number of delivered events */int snd_seq_kernel_client_dispatch(int client, struct snd_seq_event * ev, int atomic, int hop){ struct snd_seq_client *cptr; int result; snd_assert(ev != NULL, return -EINVAL); /* fill in client number */ ev->queue = SNDRV_SEQ_QUEUE_DIRECT; ev->source.client = client; if (check_event_type_and_length(ev)) return -EINVAL; cptr = snd_seq_client_use_ptr(client); if (cptr == NULL) return -EINVAL; if (!cptr->accept_output) result = -EPERM; else result = snd_seq_deliver_event(cptr, ev, atomic, hop); snd_seq_client_unlock(cptr); return result;}/* * exported, called by kernel clients to perform same functions as with * userland ioctl() */int snd_seq_kernel_client_ctl(int clientid, unsigned int cmd, void *arg){ struct snd_seq_client *client; mm_segment_t fs; int result; client = clientptr(clientid); if (client == NULL) return -ENXIO; fs = snd_enter_user(); result = snd_seq_do_ioctl(client, cmd, (void __user *)arg); snd_leave_user(fs); return result;}/* exported (for OSS emulator) */int snd_seq_kernel_client_write_poll(int clientid, struct file *file, poll_table *wait){ struct snd_seq_client *client; client = clientptr(clientid); if (client == NULL) return -ENXIO; if (! snd_seq_write_pool_allocated(client)) return 1; if (snd_seq_pool_poll_wait(client->pool, file, wait)) return 1; return 0;}/*---------------------------------------------------------------------------*/#ifdef CONFIG_PROC_FS/* * /proc interface */static void snd_seq_info_dump_subscribers(struct snd_info_buffer *buffer, struct snd_seq_port_subs_info *group, int is_src, char *msg){ struct list_head *p; struct snd_seq_subscribers *s; int count = 0; down_read(&group->list_mutex); if (list_empty(&group->list_head)) { up_read(&group->list_mutex); return; } snd_iprintf(buffer, msg); list_for_each(p, &group->list_head) { if (is_src) s = list_entry(p, struct snd_seq_subscribers, src_list); else s = list_entry(p, struct snd_seq_subscribers, dest_list); if (count++) snd_iprintf(buffer, ", "); snd_iprintf(buffer, "%d:%d", is_src ? s->info.dest.client : s->info.sender.client, is_src ? s->info.dest.port : s->info.sender.port); if (s->info.flags & SNDRV_SEQ_PORT_SUBS_TIMESTAMP) snd_iprintf(buffer, "[%c:%d]", ((s->info.flags & SNDRV_SEQ_PORT_SUBS_TIME_REAL) ? 'r' : 't'), s->info.queue); if (group->exclusive) snd_iprintf(buffer, "[ex]"); } up_read(&group->list_mutex); snd_iprintf(buffer, "\n");}#define FLAG_PERM_RD(perm) ((perm) & SNDRV_SEQ_PORT_CAP_READ ? ((perm) & SNDRV_SEQ_PORT_CAP_SUBS_READ ? 'R' : 'r') : '-')#define FLAG_PERM_WR(perm) ((perm) & SNDRV_SEQ_PORT_CAP_WRITE ? ((perm) & SNDRV_SEQ_PORT_CAP_SUBS_WRITE ? 'W' : 'w') : '-')#define FLAG_PERM_EX(perm) ((perm) & SNDRV_SEQ_PORT_CAP_NO_EXPORT ? '-' : 'e')#define FLAG_PERM_DUPLEX(perm) ((perm) & SNDRV_SEQ_PORT_CAP_DUPLEX ? 'X' : '-')static void snd_seq_info_dump_ports(struct snd_info_buffer *buffer, struct snd_seq_client *client){ struct list_head *l; mutex_lock(&client->ports_mutex); list_for_each(l, &client->ports_list_head) { struct snd_seq_client_port *p = list_entry(l, struct snd_seq_client_port, list); snd_iprintf(buffer, " Port %3d : \"%s\" (%c%c%c%c)\n", p->addr.port, p->name, FLAG_PERM_RD(p->capability), FLAG_PERM_WR(p->capability), FLAG_PERM_EX(p->capability), FLAG_PERM_DUPLEX(p->capability)); snd_seq_info_dump_subscribers(buffer, &p->c_src, 1, " Connecting To: "); snd_seq_info_dump_subscribers(buffer, &p->c_dest, 0, " Connected From: "); } mutex_unlock(&client->ports_mutex);}void snd_seq_info_pool(struct snd_info_buffer *buffer, struct snd_seq_pool *pool, char *space);/* exported to seq_info.c */void snd_seq_info_clients_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer){ int c; struct snd_seq_client *client; snd_iprintf(buffer, "Client info\n"); snd_iprintf(buffer, " cur clients : %d\n", client_usage.cur); snd_iprintf(buffer, " peak clients : %d\n", client_usage.peak); snd_iprintf(buffer, " max clients : %d\n", SNDRV_SEQ_MAX_CLIENTS); snd_iprintf(buffer, "\n"); /* list t
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -