📄 seq_clientmgr.c
字号:
if (snd_seq_ev_is_variable(&event)) { int extlen = event.data.ext.len & ~SNDRV_SEQ_EXT_MASK; if ((size_t)(extlen + len) > count) { /* back out, will get an error this time or next */ err = -EINVAL; break; } /* set user space pointer */ event.data.ext.len = extlen | SNDRV_SEQ_EXT_USRPTR; event.data.ext.ptr = (char*)buf + sizeof(snd_seq_event_t); len += extlen; /* increment data length */ } else {#if defined(CONFIG_SND_BIT32_EMUL) || defined(CONFIG_SND_BIT32_EMUL_MODULE) if (client->convert32 && snd_seq_ev_is_varusr(&event)) { void *ptr = compat_ptr(event.data.raw32.d[1]); event.data.ext.ptr = ptr; }#endif } /* ok, enqueue it */ err = snd_seq_client_enqueue_event(client, &event, file, !(file->f_flags & O_NONBLOCK), 0, 0); if (err < 0) break; __skip_event: /* Update pointers and counts */ count -= len; buf += len; written += len; } return written ? written : err;}/* * handle polling */static unsigned int snd_seq_poll(struct file *file, poll_table * wait){ client_t *client = (client_t *) file->private_data; unsigned int mask = 0; /* check client structures are in place */ snd_assert(client != NULL, return -ENXIO); if ((snd_seq_file_flags(file) & SNDRV_SEQ_LFLG_INPUT) && client->data.user.fifo) { /* check if data is available in the outqueue */ if (snd_seq_fifo_poll_wait(client->data.user.fifo, file, wait)) mask |= POLLIN | POLLRDNORM; } if (snd_seq_file_flags(file) & SNDRV_SEQ_LFLG_OUTPUT) { /* check if data is available in the pool */ if (!snd_seq_write_pool_allocated(client) || snd_seq_pool_poll_wait(client->pool, file, wait)) mask |= POLLOUT | POLLWRNORM; } return mask;}/*-----------------------------------------------------*//* SYSTEM_INFO ioctl() */static int snd_seq_ioctl_system_info(client_t *client, void __user *arg){ snd_seq_system_info_t info; memset(&info, 0, sizeof(info)); /* fill the info fields */ info.queues = SNDRV_SEQ_MAX_QUEUES; info.clients = SNDRV_SEQ_MAX_CLIENTS; info.ports = 256; /* fixed limit */ info.channels = 256; /* fixed limit */ info.cur_clients = client_usage.cur; info.cur_queues = snd_seq_queue_get_cur_queues(); if (copy_to_user(arg, &info, sizeof(info))) return -EFAULT; return 0;}/* RUNNING_MODE ioctl() */static int snd_seq_ioctl_running_mode(client_t *client, void __user *arg){ struct sndrv_seq_running_info info; client_t *cptr; int err = 0; if (copy_from_user(&info, arg, sizeof(info))) return -EFAULT; /* requested client number */ cptr = snd_seq_client_use_ptr(info.client); if (cptr == NULL) return -ENOENT; /* don't change !!! */#ifdef SNDRV_BIG_ENDIAN if (! info.big_endian) { err = -EINVAL; goto __err; }#else if (info.big_endian) { err = -EINVAL; goto __err; }#endif if (info.cpu_mode > sizeof(long)) { err = -EINVAL; goto __err; } cptr->convert32 = (info.cpu_mode < sizeof(long)); __err: snd_seq_client_unlock(cptr); return err;}/* CLIENT_INFO ioctl() */static void get_client_info(client_t *cptr, snd_seq_client_info_t *info){ info->client = cptr->number; /* fill the info fields */ info->type = cptr->type; strcpy(info->name, cptr->name); info->filter = cptr->filter; info->event_lost = cptr->event_lost; memcpy(info->event_filter, cptr->event_filter, 32); info->num_ports = cptr->num_ports; memset(info->reserved, 0, sizeof(info->reserved));}static int snd_seq_ioctl_get_client_info(client_t * client, void __user *arg){ client_t *cptr; snd_seq_client_info_t client_info; if (copy_from_user(&client_info, arg, sizeof(client_info))) return -EFAULT; /* requested client number */ cptr = snd_seq_client_use_ptr(client_info.client); if (cptr == NULL) return -ENOENT; /* don't change !!! */ get_client_info(cptr, &client_info); snd_seq_client_unlock(cptr); if (copy_to_user(arg, &client_info, sizeof(client_info))) return -EFAULT; return 0;}/* CLIENT_INFO ioctl() */static int snd_seq_ioctl_set_client_info(client_t * client, void __user *arg){ snd_seq_client_info_t client_info; if (copy_from_user(&client_info, arg, sizeof(client_info))) return -EFAULT; /* it is not allowed to set the info fields for an another client */ if (client->number != client_info.client) return -EPERM; /* also client type must be set now */ if (client->type != client_info.type) return -EINVAL; /* fill the info fields */ if (client_info.name[0]) strlcpy(client->name, client_info.name, sizeof(client->name)); client->filter = client_info.filter; client->event_lost = client_info.event_lost; memcpy(client->event_filter, client_info.event_filter, 32); return 0;}/* * CREATE PORT ioctl() */static int snd_seq_ioctl_create_port(client_t * client, void __user *arg){ client_port_t *port; snd_seq_port_info_t info; snd_seq_port_callback_t *callback; if (copy_from_user(&info, arg, sizeof(info))) return -EFAULT; /* it is not allowed to create the port for an another client */ if (info.addr.client != client->number) return -EPERM; port = snd_seq_create_port(client, (info.flags & SNDRV_SEQ_PORT_FLG_GIVEN_PORT) ? info.addr.port : -1); if (port == NULL) return -ENOMEM; if (client->type == USER_CLIENT && info.kernel) { snd_seq_delete_port(client, port->addr.port); return -EINVAL; } if (client->type == KERNEL_CLIENT) { if ((callback = info.kernel) != NULL) { if (callback->owner) port->owner = callback->owner; port->private_data = callback->private_data; port->private_free = callback->private_free; port->callback_all = callback->callback_all; port->event_input = callback->event_input; port->c_src.open = callback->subscribe; port->c_src.close = callback->unsubscribe; port->c_dest.open = callback->use; port->c_dest.close = callback->unuse; } } info.addr = port->addr; snd_seq_set_port_info(port, &info); snd_seq_system_client_ev_port_start(port->addr.client, port->addr.port); if (copy_to_user(arg, &info, sizeof(info))) return -EFAULT; return 0;}/* * DELETE PORT ioctl() */static int snd_seq_ioctl_delete_port(client_t * client, void __user *arg){ snd_seq_port_info_t info; int err; /* set passed parameters */ if (copy_from_user(&info, arg, sizeof(info))) return -EFAULT; /* it is not allowed to remove the port for an another client */ if (info.addr.client != client->number) return -EPERM; err = snd_seq_delete_port(client, info.addr.port); if (err >= 0) snd_seq_system_client_ev_port_exit(client->number, info.addr.port); return err;}/* * GET_PORT_INFO ioctl() (on any client) */static int snd_seq_ioctl_get_port_info(client_t *client, void __user *arg){ client_t *cptr; client_port_t *port; snd_seq_port_info_t 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; port = snd_seq_port_use_ptr(cptr, info.addr.port); if (port == NULL) { snd_seq_client_unlock(cptr); return -ENOENT; /* don't change */ } /* get port info */ 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;}/* * SET_PORT_INFO ioctl() (only ports on this/own client) */static int snd_seq_ioctl_set_port_info(client_t * client, void __user *arg){ client_port_t *port; snd_seq_port_info_t info; if (copy_from_user(&info, arg, sizeof(info))) return -EFAULT; if (info.addr.client != client->number) /* only set our own ports ! */ return -EPERM; port = snd_seq_port_use_ptr(client, info.addr.port); if (port) { snd_seq_set_port_info(port, &info); snd_seq_port_unlock(port); } return 0;}/* * port subscription (connection) */#define PERM_RD (SNDRV_SEQ_PORT_CAP_READ|SNDRV_SEQ_PORT_CAP_SUBS_READ)#define PERM_WR (SNDRV_SEQ_PORT_CAP_WRITE|SNDRV_SEQ_PORT_CAP_SUBS_WRITE)static int check_subscription_permission(client_t *client, client_port_t *sport, client_port_t *dport, snd_seq_port_subscribe_t *subs){ if (client->number != subs->sender.client && client->number != subs->dest.client) { /* connection by third client - check export permission */ if (check_port_perm(sport, SNDRV_SEQ_PORT_CAP_NO_EXPORT)) return -EPERM; if (check_port_perm(dport, SNDRV_SEQ_PORT_CAP_NO_EXPORT)) return -EPERM; } /* check read permission */ /* if sender or receiver is the subscribing client itself, * no permission check is necessary */ if (client->number != subs->sender.client) { if (! check_port_perm(sport, PERM_RD)) return -EPERM; } /* check write permission */ if (client->number != subs->dest.client) { if (! check_port_perm(dport, PERM_WR)) return -EPERM; } return 0;}/* * send an subscription notify event to user client: * client must be user client. */int snd_seq_client_notify_subscription(int client, int port, snd_seq_port_subscribe_t *info, int evtype){ snd_seq_event_t event; memset(&event, 0, sizeof(event)); event.type = evtype; event.data.connect.dest = info->dest; event.data.connect.sender = info->sender; return snd_seq_system_notify(client, port, &event); /* non-atomic */}/* * add to port's subscription list IOCTL interface */static int snd_seq_ioctl_subscribe_port(client_t * client, void __user *arg){ int result = -EINVAL; client_t *receiver = NULL, *sender = NULL; client_port_t *sport = NULL, *dport = NULL; snd_seq_port_subscribe_t subs; if (copy_from_user(&subs, arg, sizeof(subs))) return -EFAULT; if ((receiver = snd_seq_client_use_ptr(subs.dest.client)) == NULL) goto __end; if ((sender = snd_seq_client_use_ptr(subs.sender.client)) == NULL) goto __end; if ((sport = snd_seq_port_use_ptr(sender, subs.sender.port)) == NULL) goto __end; if ((dport = snd_seq_port_use_ptr(receiver, subs.dest.port)) == NULL) goto __end; result = check_subscription_permission(client, sport, dport, &subs); if (result < 0) goto __end; /* connect them */ result = snd_seq_port_connect(client, sender, sport, receiver, dport, &subs); if (! result) /* broadcast announce */ snd_seq_client_notify_subscription(SNDRV_SEQ_ADDRESS_SUBSCRIBERS, 0, &subs, SNDRV_SEQ_EVENT_PORT_SUBSCRIBED); __end: if (sport) snd_seq_port_unlock(sport); if (dport) snd_seq_port_unlock(dport); if (sender) snd_seq_client_unlock(sender); if (receiver) snd_seq_client_unlock(receiver); return result;}/* * remove from port's subscription list */static int snd_seq_ioctl_unsubscribe_port(client_t * client, void __user *arg){ int result = -ENXIO; client_t *receiver = NULL, *sender = NULL; client_port_t *sport = NULL, *dport = NULL; snd_seq_port_subscribe_t subs; if (copy_from_user(&subs, arg, sizeof(subs))) return -EFAULT; if ((receiver = snd_seq_client_use_ptr(subs.dest.client)) == NULL) goto __end; if ((sender = snd_seq_client_use_ptr(subs.sender.client)) == NULL) goto __end; if ((sport = snd_seq_port_use_ptr(sender, subs.sender.port)) == NULL) goto __end; if ((dport = snd_seq_port_use_ptr(receiver, subs.dest.port)) == NULL) goto __end; result = check_subscription_permission(client, sport, dport, &subs); if (result < 0) goto __end; result = snd_seq_port_disconnect(client, sender, sport, receiver, dport, &subs); if (! result) /* broadcast announce */ snd_seq_client_notify_subscription(SNDRV_SEQ_ADDRESS_SUBSCRIBERS, 0, &subs, SNDRV_SEQ_EVENT_PORT_UNSUBSCRIBED); __end: if (sport) snd_seq_port_unlock(sport); if (dport) snd_seq_port_unlock(dport); if (sender) snd_seq_client_unlock(sender); if (receiver) snd_seq_client_unlock(receiver); return result;}/* CREATE_QUEUE ioctl() */static int snd_seq_ioctl_create_queue(client_t *client, void __user *arg){ snd_seq_queue_info_t info; int result; queue_t *q; if (copy_from_user(&info, arg, sizeof(info))) return -EFAULT; result = snd_seq_queue_alloc(client->number, info.locked, info.flags); if (result < 0) return result; q = queueptr(result); if (q == NULL) return -EINVAL; info.queue = q->queue; info.locked = q->locked; info.owner = q->owner; /* set queue name */ if (! info.name[0]) snprintf(info.name, sizeof(info.name), "Queue-%d", q->queue); strlcpy(q->name, info.name, sizeof(q->name)); queuefree(q); if (copy_to_user(arg, &info, sizeof(info))) return -EFAULT; return 0;}/* DELETE_QUEUE ioctl() */static int snd_seq_ioctl_delete_queue(client_t *client, void __user *arg){ snd_seq_queue_info_t info; if (copy_from_user(&info, arg, sizeof(info))) return -EFAULT;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -