aboutsummaryrefslogtreecommitdiffstats
path: root/sound/core
diff options
context:
space:
mode:
authorTakashi Sakamoto <o-takashi@sakamocchi.jp>2016-08-12 21:13:34 -0400
committerTakashi Iwai <tiwai@suse.de>2016-08-22 05:11:03 -0400
commit8ce8eb601c71d4eec4c83ac2398a2cb847f4ef4d (patch)
tree2d7897ca14af7eb173f7af9656166a30cfef2485 /sound/core
parent77dfa8d3196a0cd219dfd6c65e4ff5a3e696fd8c (diff)
ALSA: seq: add an alternative way to handle ioctl requests
ALSA sequencer is designed with two types of clients; application and kernel. Operations for each ioctl command should handle data in both of user space and kernel space, while current implementation just allows them to handle data in user space. Data in kernel space is handled with change of address limit of running tasks. This commit adds a new table to map ioctl commands to corresponding functions. The functions get data in kernel space. Helper functions to operate kernel and application clients seek entries from the table. Especially, the helper function for application is responsible for coping from user space to kernel space or vise versa. Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp> Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/core')
-rw-r--r--sound/core/seq/seq_clientmgr.c76
-rw-r--r--sound/core/seq/seq_compat.c5
2 files changed, 80 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,
2168 2168
2169/* -------------------------------------------------------- */ 2169/* -------------------------------------------------------- */
2170 2170
2171static const struct ioctl_handler {
2172 unsigned int cmd;
2173 int (*func)(struct snd_seq_client *client, void *arg);
2174} ioctl_handlers[] = {
2175 { 0, NULL },
2176};
2177
2171static struct seq_ioctl_table { 2178static struct seq_ioctl_table {
2172 unsigned int cmd; 2179 unsigned int cmd;
2173 int (*func)(struct snd_seq_client *client, void __user * arg); 2180 int (*func)(struct snd_seq_client *client, void __user * arg);
@@ -2204,6 +2211,63 @@ static struct seq_ioctl_table {
2204 { 0, NULL }, 2211 { 0, NULL },
2205}; 2212};
2206 2213
2214static long seq_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
2215{
2216 struct snd_seq_client *client = file->private_data;
2217 /* To use kernel stack for ioctl data. */
2218 union ioctl_arg {
2219 int pversion;
2220 int client_id;
2221 struct snd_seq_system_info system_info;
2222 struct snd_seq_running_info running_info;
2223 struct snd_seq_client_info client_info;
2224 struct snd_seq_port_info port_info;
2225 struct snd_seq_port_subscribe port_subscribe;
2226 struct snd_seq_queue_info queue_info;
2227 struct snd_seq_queue_status queue_status;
2228 struct snd_seq_queue_tempo tempo;
2229 struct snd_seq_queue_timer queue_timer;
2230 struct snd_seq_queue_client queue_client;
2231 struct snd_seq_client_pool client_pool;
2232 struct snd_seq_remove_events remove_events;
2233 struct snd_seq_query_subs query_subs;
2234 } buf = {0};
2235 const struct ioctl_handler *handler;
2236 unsigned long size;
2237 int err;
2238
2239 if (snd_BUG_ON(!client))
2240 return -ENXIO;
2241
2242 for (handler = ioctl_handlers; handler->cmd > 0; ++handler) {
2243 if (handler->cmd == cmd)
2244 break;
2245 }
2246 if (handler->cmd == 0)
2247 return -ENOTTY;
2248 /*
2249 * All of ioctl commands for ALSA sequencer get an argument of size
2250 * within 13 bits. We can safely pick up the size from the command.
2251 */
2252 size = _IOC_SIZE(handler->cmd);
2253 if (_IOC_DIR(handler->cmd) & IOC_IN) {
2254 if (copy_from_user(&buf, (const void __user *)arg, size))
2255 return -EFAULT;
2256 }
2257
2258 err = handler->func(client, &buf);
2259 if (err >= 0) {
2260 /* Some commands includes a bug in 'dir' field. */
2261 if (handler->cmd == SNDRV_SEQ_IOCTL_SET_QUEUE_CLIENT ||
2262 handler->cmd == SNDRV_SEQ_IOCTL_SET_CLIENT_POOL ||
2263 (_IOC_DIR(handler->cmd) & IOC_OUT))
2264 if (copy_to_user((void __user *)arg, &buf, size))
2265 return -EFAULT;
2266 }
2267
2268 return err;
2269}
2270
2207static int snd_seq_do_ioctl(struct snd_seq_client *client, unsigned int cmd, 2271static int snd_seq_do_ioctl(struct snd_seq_client *client, unsigned int cmd,
2208 void __user *arg) 2272 void __user *arg)
2209{ 2273{
@@ -2234,9 +2298,12 @@ static long snd_seq_ioctl(struct file *file, unsigned int cmd, unsigned long arg
2234{ 2298{
2235 struct snd_seq_client *client = file->private_data; 2299 struct snd_seq_client *client = file->private_data;
2236 2300
2301 if (seq_ioctl(file, cmd, arg) >= 0)
2302 return 0;
2303
2237 if (snd_BUG_ON(!client)) 2304 if (snd_BUG_ON(!client))
2238 return -ENXIO; 2305 return -ENXIO;
2239 2306
2240 return snd_seq_do_ioctl(client, cmd, (void __user *) arg); 2307 return snd_seq_do_ioctl(client, cmd, (void __user *) arg);
2241} 2308}
2242 2309
@@ -2437,6 +2504,7 @@ EXPORT_SYMBOL(snd_seq_kernel_client_dispatch);
2437 */ 2504 */
2438int snd_seq_kernel_client_ctl(int clientid, unsigned int cmd, void *arg) 2505int snd_seq_kernel_client_ctl(int clientid, unsigned int cmd, void *arg)
2439{ 2506{
2507 const struct ioctl_handler *handler;
2440 struct snd_seq_client *client; 2508 struct snd_seq_client *client;
2441 mm_segment_t fs; 2509 mm_segment_t fs;
2442 int result; 2510 int result;
@@ -2444,6 +2512,12 @@ int snd_seq_kernel_client_ctl(int clientid, unsigned int cmd, void *arg)
2444 client = clientptr(clientid); 2512 client = clientptr(clientid);
2445 if (client == NULL) 2513 if (client == NULL)
2446 return -ENXIO; 2514 return -ENXIO;
2515
2516 for (handler = ioctl_handlers; handler->cmd > 0; ++handler) {
2517 if (handler->cmd == cmd)
2518 return handler->func(client, arg);
2519 }
2520
2447 fs = snd_enter_user(); 2521 fs = snd_enter_user();
2448 result = snd_seq_do_ioctl(client, cmd, (void __force __user *)arg); 2522 result = snd_seq_do_ioctl(client, cmd, (void __force __user *)arg);
2449 snd_leave_user(fs); 2523 snd_leave_user(fs);
diff --git a/sound/core/seq/seq_compat.c b/sound/core/seq/seq_compat.c
index 65175902a68a..4cfc50584218 100644
--- a/sound/core/seq/seq_compat.c
+++ b/sound/core/seq/seq_compat.c
@@ -59,6 +59,9 @@ static int snd_seq_call_port_info_ioctl(struct snd_seq_client *client, unsigned
59 goto error; 59 goto error;
60 data->kernel = NULL; 60 data->kernel = NULL;
61 61
62 if (snd_seq_kernel_client_ctl(client->number, cmd, &data) >= 0)
63 return 0;
64
62 fs = snd_enter_user(); 65 fs = snd_enter_user();
63 err = snd_seq_do_ioctl(client, cmd, data); 66 err = snd_seq_do_ioctl(client, cmd, data);
64 snd_leave_user(fs); 67 snd_leave_user(fs);
@@ -123,6 +126,8 @@ static long snd_seq_ioctl_compat(struct file *file, unsigned int cmd, unsigned l
123 case SNDRV_SEQ_IOCTL_GET_SUBSCRIPTION: 126 case SNDRV_SEQ_IOCTL_GET_SUBSCRIPTION:
124 case SNDRV_SEQ_IOCTL_QUERY_NEXT_CLIENT: 127 case SNDRV_SEQ_IOCTL_QUERY_NEXT_CLIENT:
125 case SNDRV_SEQ_IOCTL_RUNNING_MODE: 128 case SNDRV_SEQ_IOCTL_RUNNING_MODE:
129 if (seq_ioctl(file, cmd, arg) >= 0)
130 return 0;
126 return snd_seq_do_ioctl(client, cmd, argp); 131 return snd_seq_do_ioctl(client, cmd, argp);
127 case SNDRV_SEQ_IOCTL_CREATE_PORT32: 132 case SNDRV_SEQ_IOCTL_CREATE_PORT32:
128 return snd_seq_call_port_info_ioctl(client, SNDRV_SEQ_IOCTL_CREATE_PORT, argp); 133 return snd_seq_call_port_info_ioctl(client, SNDRV_SEQ_IOCTL_CREATE_PORT, argp);