|
14 | 14 | #include <linux/kmod.h> |
15 | 15 |
|
16 | 16 | #include <sound/seq_kernel.h> |
| 17 | +#include <sound/ump.h> |
17 | 18 | #include "seq_clientmgr.h" |
18 | 19 | #include "seq_memory.h" |
19 | 20 | #include "seq_queue.h" |
@@ -71,6 +72,10 @@ static int snd_seq_deliver_single_event(struct snd_seq_client *client, |
71 | 72 | struct snd_seq_event *event, |
72 | 73 | int filter, int atomic, int hop); |
73 | 74 |
|
| 75 | +#if IS_ENABLED(CONFIG_SND_SEQ_UMP) |
| 76 | +static void free_ump_info(struct snd_seq_client *client); |
| 77 | +#endif |
| 78 | + |
74 | 79 | /* |
75 | 80 | */ |
76 | 81 | static inline unsigned short snd_seq_file_flags(struct file *file) |
@@ -382,6 +387,9 @@ static int snd_seq_release(struct inode *inode, struct file *file) |
382 | 387 | seq_free_client(client); |
383 | 388 | if (client->data.user.fifo) |
384 | 389 | snd_seq_fifo_delete(&client->data.user.fifo); |
| 390 | +#if IS_ENABLED(CONFIG_SND_SEQ_UMP) |
| 391 | + free_ump_info(client); |
| 392 | +#endif |
385 | 393 | put_pid(client->data.user.owner); |
386 | 394 | kfree(client); |
387 | 395 | } |
@@ -1282,7 +1290,6 @@ static int snd_seq_ioctl_set_client_info(struct snd_seq_client *client, |
1282 | 1290 | if (client->user_pversion >= SNDRV_PROTOCOL_VERSION(1, 0, 3)) |
1283 | 1291 | client->midi_version = client_info->midi_version; |
1284 | 1292 | memcpy(client->event_filter, client_info->event_filter, 32); |
1285 | | - |
1286 | 1293 | return 0; |
1287 | 1294 | } |
1288 | 1295 |
|
@@ -2087,6 +2094,108 @@ static int snd_seq_ioctl_query_next_port(struct snd_seq_client *client, |
2087 | 2094 | return 0; |
2088 | 2095 | } |
2089 | 2096 |
|
| 2097 | +#if IS_ENABLED(CONFIG_SND_SEQ_UMP) |
| 2098 | +#define NUM_UMP_INFOS (SNDRV_UMP_MAX_BLOCKS + 1) |
| 2099 | + |
| 2100 | +static void free_ump_info(struct snd_seq_client *client) |
| 2101 | +{ |
| 2102 | + int i; |
| 2103 | + |
| 2104 | + if (!client->ump_info) |
| 2105 | + return; |
| 2106 | + for (i = 0; i < NUM_UMP_INFOS; i++) |
| 2107 | + kfree(client->ump_info[i]); |
| 2108 | + kfree(client->ump_info); |
| 2109 | + client->ump_info = NULL; |
| 2110 | +} |
| 2111 | + |
| 2112 | +static void terminate_ump_info_strings(void *p, int type) |
| 2113 | +{ |
| 2114 | + if (type == SNDRV_SEQ_CLIENT_UMP_INFO_ENDPOINT) { |
| 2115 | + struct snd_ump_endpoint_info *ep = p; |
| 2116 | + ep->name[sizeof(ep->name) - 1] = 0; |
| 2117 | + } else { |
| 2118 | + struct snd_ump_block_info *bp = p; |
| 2119 | + bp->name[sizeof(bp->name) - 1] = 0; |
| 2120 | + } |
| 2121 | +} |
| 2122 | + |
| 2123 | +/* UMP-specific ioctls -- called directly without data copy */ |
| 2124 | +static int snd_seq_ioctl_client_ump_info(struct snd_seq_client *caller, |
| 2125 | + unsigned int cmd, |
| 2126 | + unsigned long arg) |
| 2127 | +{ |
| 2128 | + struct snd_seq_client_ump_info __user *argp = |
| 2129 | + (struct snd_seq_client_ump_info __user *)arg; |
| 2130 | + struct snd_seq_client *cptr; |
| 2131 | + int client, type, err = 0; |
| 2132 | + size_t size; |
| 2133 | + void *p; |
| 2134 | + |
| 2135 | + if (get_user(client, &argp->client) || get_user(type, &argp->type)) |
| 2136 | + return -EFAULT; |
| 2137 | + if (cmd == SNDRV_SEQ_IOCTL_SET_CLIENT_UMP_INFO && |
| 2138 | + caller->number != client) |
| 2139 | + return -EPERM; |
| 2140 | + if (type < 0 || type >= NUM_UMP_INFOS) |
| 2141 | + return -EINVAL; |
| 2142 | + if (type == SNDRV_SEQ_CLIENT_UMP_INFO_ENDPOINT) |
| 2143 | + size = sizeof(struct snd_ump_endpoint_info); |
| 2144 | + else |
| 2145 | + size = sizeof(struct snd_ump_block_info); |
| 2146 | + cptr = snd_seq_client_use_ptr(client); |
| 2147 | + if (!cptr) |
| 2148 | + return -ENOENT; |
| 2149 | + |
| 2150 | + mutex_lock(&cptr->ioctl_mutex); |
| 2151 | + if (!cptr->midi_version) { |
| 2152 | + err = -EBADFD; |
| 2153 | + goto error; |
| 2154 | + } |
| 2155 | + |
| 2156 | + if (cmd == SNDRV_SEQ_IOCTL_GET_CLIENT_UMP_INFO) { |
| 2157 | + if (!cptr->ump_info) |
| 2158 | + p = NULL; |
| 2159 | + else |
| 2160 | + p = cptr->ump_info[type]; |
| 2161 | + if (!p) { |
| 2162 | + err = -ENODEV; |
| 2163 | + goto error; |
| 2164 | + } |
| 2165 | + if (copy_to_user(argp->info, p, size)) { |
| 2166 | + err = -EFAULT; |
| 2167 | + goto error; |
| 2168 | + } |
| 2169 | + } else { |
| 2170 | + if (cptr->type != USER_CLIENT) { |
| 2171 | + err = -EBADFD; |
| 2172 | + goto error; |
| 2173 | + } |
| 2174 | + if (!cptr->ump_info) { |
| 2175 | + cptr->ump_info = kcalloc(NUM_UMP_INFOS, |
| 2176 | + sizeof(void *), GFP_KERNEL); |
| 2177 | + if (!cptr->ump_info) { |
| 2178 | + err = -ENOMEM; |
| 2179 | + goto error; |
| 2180 | + } |
| 2181 | + } |
| 2182 | + p = memdup_user(argp->info, size); |
| 2183 | + if (IS_ERR(p)) { |
| 2184 | + err = PTR_ERR(p); |
| 2185 | + goto error; |
| 2186 | + } |
| 2187 | + kfree(cptr->ump_info[type]); |
| 2188 | + terminate_ump_info_strings(p, type); |
| 2189 | + cptr->ump_info[type] = p; |
| 2190 | + } |
| 2191 | + |
| 2192 | + error: |
| 2193 | + mutex_unlock(&cptr->ioctl_mutex); |
| 2194 | + snd_seq_client_unlock(cptr); |
| 2195 | + return err; |
| 2196 | +} |
| 2197 | +#endif |
| 2198 | + |
2090 | 2199 | /* -------------------------------------------------------- */ |
2091 | 2200 |
|
2092 | 2201 | static const struct ioctl_handler { |
@@ -2157,6 +2266,15 @@ static long snd_seq_ioctl(struct file *file, unsigned int cmd, |
2157 | 2266 | if (snd_BUG_ON(!client)) |
2158 | 2267 | return -ENXIO; |
2159 | 2268 |
|
| 2269 | +#if IS_ENABLED(CONFIG_SND_SEQ_UMP) |
| 2270 | + /* exception - handling large data */ |
| 2271 | + switch (cmd) { |
| 2272 | + case SNDRV_SEQ_IOCTL_GET_CLIENT_UMP_INFO: |
| 2273 | + case SNDRV_SEQ_IOCTL_SET_CLIENT_UMP_INFO: |
| 2274 | + return snd_seq_ioctl_client_ump_info(client, cmd, arg); |
| 2275 | + } |
| 2276 | +#endif |
| 2277 | + |
2160 | 2278 | for (handler = ioctl_handlers; handler->cmd > 0; ++handler) { |
2161 | 2279 | if (handler->cmd == cmd) |
2162 | 2280 | break; |
|
0 commit comments