summaryrefslogtreecommitdiff
path: root/sound/core/seq/seq_clientmgr.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/core/seq/seq_clientmgr.c')
-rw-r--r--sound/core/seq/seq_clientmgr.c76
1 files changed, 75 insertions, 1 deletions
diff --git a/sound/core/seq/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c
index 37590f8320a3..cf370031cfd5 100644
--- a/sound/core/seq/seq_clientmgr.c
+++ b/sound/core/seq/seq_clientmgr.c
@@ -2168,6 +2168,13 @@ static int snd_seq_ioctl_query_next_port(struct snd_seq_client *client,
/* -------------------------------------------------------- */
+static const struct ioctl_handler {
+ unsigned int cmd;
+ int (*func)(struct snd_seq_client *client, void *arg);
+} ioctl_handlers[] = {
+ { 0, NULL },
+};
+
static struct seq_ioctl_table {
unsigned int cmd;
int (*func)(struct snd_seq_client *client, void __user * arg);
@@ -2204,6 +2211,63 @@ static struct seq_ioctl_table {
{ 0, NULL },
};
+static long seq_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct snd_seq_client *client = file->private_data;
+ /* To use kernel stack for ioctl data. */
+ union ioctl_arg {
+ int pversion;
+ int client_id;
+ struct snd_seq_system_info system_info;
+ struct snd_seq_running_info running_info;
+ struct snd_seq_client_info client_info;
+ struct snd_seq_port_info port_info;
+ struct snd_seq_port_subscribe port_subscribe;
+ struct snd_seq_queue_info queue_info;
+ struct snd_seq_queue_status queue_status;
+ struct snd_seq_queue_tempo tempo;
+ struct snd_seq_queue_timer queue_timer;
+ struct snd_seq_queue_client queue_client;
+ struct snd_seq_client_pool client_pool;
+ struct snd_seq_remove_events remove_events;
+ struct snd_seq_query_subs query_subs;
+ } buf = {0};
+ const struct ioctl_handler *handler;
+ unsigned long size;
+ int err;
+
+ if (snd_BUG_ON(!client))
+ return -ENXIO;
+
+ for (handler = ioctl_handlers; handler->cmd > 0; ++handler) {
+ if (handler->cmd == cmd)
+ break;
+ }
+ if (handler->cmd == 0)
+ return -ENOTTY;
+ /*
+ * All of ioctl commands for ALSA sequencer get an argument of size
+ * within 13 bits. We can safely pick up the size from the command.
+ */
+ size = _IOC_SIZE(handler->cmd);
+ if (_IOC_DIR(handler->cmd) & IOC_IN) {
+ if (copy_from_user(&buf, (const void __user *)arg, size))
+ return -EFAULT;
+ }
+
+ err = handler->func(client, &buf);
+ if (err >= 0) {
+ /* Some commands includes a bug in 'dir' field. */
+ if (handler->cmd == SNDRV_SEQ_IOCTL_SET_QUEUE_CLIENT ||
+ handler->cmd == SNDRV_SEQ_IOCTL_SET_CLIENT_POOL ||
+ (_IOC_DIR(handler->cmd) & IOC_OUT))
+ if (copy_to_user((void __user *)arg, &buf, size))
+ return -EFAULT;
+ }
+
+ return err;
+}
+
static int snd_seq_do_ioctl(struct snd_seq_client *client, unsigned int cmd,
void __user *arg)
{
@@ -2234,9 +2298,12 @@ static long snd_seq_ioctl(struct file *file, unsigned int cmd, unsigned long arg
{
struct snd_seq_client *client = file->private_data;
+ if (seq_ioctl(file, cmd, arg) >= 0)
+ return 0;
+
if (snd_BUG_ON(!client))
return -ENXIO;
-
+
return snd_seq_do_ioctl(client, cmd, (void __user *) arg);
}
@@ -2437,6 +2504,7 @@ EXPORT_SYMBOL(snd_seq_kernel_client_dispatch);
*/
int snd_seq_kernel_client_ctl(int clientid, unsigned int cmd, void *arg)
{
+ const struct ioctl_handler *handler;
struct snd_seq_client *client;
mm_segment_t fs;
int result;
@@ -2444,6 +2512,12 @@ int snd_seq_kernel_client_ctl(int clientid, unsigned int cmd, void *arg)
client = clientptr(clientid);
if (client == NULL)
return -ENXIO;
+
+ for (handler = ioctl_handlers; handler->cmd > 0; ++handler) {
+ if (handler->cmd == cmd)
+ return handler->func(client, arg);
+ }
+
fs = snd_enter_user();
result = snd_seq_do_ioctl(client, cmd, (void __force __user *)arg);
snd_leave_user(fs);