|
@@ -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);
|