1 /* 2 * compat ioctls for control API 3 * 4 * Copyright (c) by Takashi Iwai <tiwai@suse.de> 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation; either version 2 of the License, or 9 * (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program; if not, write to the Free Software 18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 19 */ 20 21 /* this file included from control.c */ 22 23 #include <linux/compat.h> 24 #include <linux/slab.h> 25 26 struct snd_ctl_elem_list32 { 27 u32 offset; 28 u32 space; 29 u32 used; 30 u32 count; 31 u32 pids; 32 unsigned char reserved[50]; 33 } /* don't set packed attribute here */; 34 35 static int snd_ctl_elem_list_compat(struct snd_card *card, 36 struct snd_ctl_elem_list32 __user *data32) 37 { 38 struct snd_ctl_elem_list __user *data; 39 compat_caddr_t ptr; 40 int err; 41 42 data = compat_alloc_user_space(sizeof(*data)); 43 44 /* offset, space, used, count */ 45 if (copy_in_user(data, data32, 4 * sizeof(u32))) 46 return -EFAULT; 47 /* pids */ 48 if (get_user(ptr, &data32->pids) || 49 put_user(compat_ptr(ptr), &data->pids)) 50 return -EFAULT; 51 err = snd_ctl_elem_list(card, data); 52 if (err < 0) 53 return err; 54 /* copy the result */ 55 if (copy_in_user(data32, data, 4 * sizeof(u32))) 56 return -EFAULT; 57 return 0; 58 } 59 60 /* 61 * control element info 62 * it uses union, so the things are not easy.. 63 */ 64 65 struct snd_ctl_elem_info32 { 66 struct snd_ctl_elem_id id; // the size of struct is same 67 s32 type; 68 u32 access; 69 u32 count; 70 s32 owner; 71 union { 72 struct { 73 s32 min; 74 s32 max; 75 s32 step; 76 } integer; 77 struct { 78 u64 min; 79 u64 max; 80 u64 step; 81 } integer64; 82 struct { 83 u32 items; 84 u32 item; 85 char name[64]; 86 } enumerated; 87 unsigned char reserved[128]; 88 } value; 89 unsigned char reserved[64]; 90 } __attribute__((packed)); 91 92 static int snd_ctl_elem_info_compat(struct snd_ctl_file *ctl, 93 struct snd_ctl_elem_info32 __user *data32) 94 { 95 struct snd_ctl_elem_info *data; 96 int err; 97 98 data = kzalloc(sizeof(*data), GFP_KERNEL); 99 if (! data) 100 return -ENOMEM; 101 102 err = -EFAULT; 103 /* copy id */ 104 if (copy_from_user(&data->id, &data32->id, sizeof(data->id))) 105 goto error; 106 /* we need to copy the item index. 107 * hope this doesn't break anything.. 108 */ 109 if (get_user(data->value.enumerated.item, &data32->value.enumerated.item)) 110 goto error; 111 112 snd_power_lock(ctl->card); 113 err = snd_power_wait(ctl->card, SNDRV_CTL_POWER_D0); 114 if (err >= 0) 115 err = snd_ctl_elem_info(ctl, data); 116 snd_power_unlock(ctl->card); 117 118 if (err < 0) 119 goto error; 120 /* restore info to 32bit */ 121 err = -EFAULT; 122 /* id, type, access, count */ 123 if (copy_to_user(&data32->id, &data->id, sizeof(data->id)) || 124 copy_to_user(&data32->type, &data->type, 3 * sizeof(u32))) 125 goto error; 126 if (put_user(data->owner, &data32->owner)) 127 goto error; 128 switch (data->type) { 129 case SNDRV_CTL_ELEM_TYPE_BOOLEAN: 130 case SNDRV_CTL_ELEM_TYPE_INTEGER: 131 if (put_user(data->value.integer.min, &data32->value.integer.min) || 132 put_user(data->value.integer.max, &data32->value.integer.max) || 133 put_user(data->value.integer.step, &data32->value.integer.step)) 134 goto error; 135 break; 136 case SNDRV_CTL_ELEM_TYPE_INTEGER64: 137 if (copy_to_user(&data32->value.integer64, 138 &data->value.integer64, 139 sizeof(data->value.integer64))) 140 goto error; 141 break; 142 case SNDRV_CTL_ELEM_TYPE_ENUMERATED: 143 if (copy_to_user(&data32->value.enumerated, 144 &data->value.enumerated, 145 sizeof(data->value.enumerated))) 146 goto error; 147 break; 148 default: 149 break; 150 } 151 err = 0; 152 error: 153 kfree(data); 154 return err; 155 } 156 157 /* read / write */ 158 struct snd_ctl_elem_value32 { 159 struct snd_ctl_elem_id id; 160 unsigned int indirect; /* bit-field causes misalignment */ 161 union { 162 s32 integer[128]; 163 unsigned char data[512]; 164 #ifndef CONFIG_X86_64 165 s64 integer64[64]; 166 #endif 167 } value; 168 unsigned char reserved[128]; 169 }; 170 171 172 /* get the value type and count of the control */ 173 static int get_ctl_type(struct snd_card *card, struct snd_ctl_elem_id *id, 174 int *countp) 175 { 176 struct snd_kcontrol *kctl; 177 struct snd_ctl_elem_info *info; 178 int err; 179 180 down_read(&card->controls_rwsem); 181 kctl = snd_ctl_find_id(card, id); 182 if (! kctl) { 183 up_read(&card->controls_rwsem); 184 return -ENXIO; 185 } 186 info = kzalloc(sizeof(*info), GFP_KERNEL); 187 if (info == NULL) { 188 up_read(&card->controls_rwsem); 189 return -ENOMEM; 190 } 191 info->id = *id; 192 err = kctl->info(kctl, info); 193 up_read(&card->controls_rwsem); 194 if (err >= 0) { 195 err = info->type; 196 *countp = info->count; 197 } 198 kfree(info); 199 return err; 200 } 201 202 static int get_elem_size(int type, int count) 203 { 204 switch (type) { 205 case SNDRV_CTL_ELEM_TYPE_INTEGER64: 206 return sizeof(s64) * count; 207 case SNDRV_CTL_ELEM_TYPE_ENUMERATED: 208 return sizeof(int) * count; 209 case SNDRV_CTL_ELEM_TYPE_BYTES: 210 return 512; 211 case SNDRV_CTL_ELEM_TYPE_IEC958: 212 return sizeof(struct snd_aes_iec958); 213 default: 214 return -1; 215 } 216 } 217 218 static int copy_ctl_value_from_user(struct snd_card *card, 219 struct snd_ctl_elem_value *data, 220 struct snd_ctl_elem_value32 __user *data32, 221 int *typep, int *countp) 222 { 223 int i, type, size; 224 int uninitialized_var(count); 225 unsigned int indirect; 226 227 if (copy_from_user(&data->id, &data32->id, sizeof(data->id))) 228 return -EFAULT; 229 if (get_user(indirect, &data32->indirect)) 230 return -EFAULT; 231 if (indirect) 232 return -EINVAL; 233 type = get_ctl_type(card, &data->id, &count); 234 if (type < 0) 235 return type; 236 237 if (type == SNDRV_CTL_ELEM_TYPE_BOOLEAN || 238 type == SNDRV_CTL_ELEM_TYPE_INTEGER) { 239 for (i = 0; i < count; i++) { 240 int val; 241 if (get_user(val, &data32->value.integer[i])) 242 return -EFAULT; 243 data->value.integer.value[i] = val; 244 } 245 } else { 246 size = get_elem_size(type, count); 247 if (size < 0) { 248 printk(KERN_ERR "snd_ioctl32_ctl_elem_value: unknown type %d\n", type); 249 return -EINVAL; 250 } 251 if (copy_from_user(data->value.bytes.data, 252 data32->value.data, size)) 253 return -EFAULT; 254 } 255 256 *typep = type; 257 *countp = count; 258 return 0; 259 } 260 261 /* restore the value to 32bit */ 262 static int copy_ctl_value_to_user(struct snd_ctl_elem_value32 __user *data32, 263 struct snd_ctl_elem_value *data, 264 int type, int count) 265 { 266 int i, size; 267 268 if (type == SNDRV_CTL_ELEM_TYPE_BOOLEAN || 269 type == SNDRV_CTL_ELEM_TYPE_INTEGER) { 270 for (i = 0; i < count; i++) { 271 int val; 272 val = data->value.integer.value[i]; 273 if (put_user(val, &data32->value.integer[i])) 274 return -EFAULT; 275 } 276 } else { 277 size = get_elem_size(type, count); 278 if (copy_to_user(data32->value.data, 279 data->value.bytes.data, size)) 280 return -EFAULT; 281 } 282 return 0; 283 } 284 285 static int snd_ctl_elem_read_user_compat(struct snd_card *card, 286 struct snd_ctl_elem_value32 __user *data32) 287 { 288 struct snd_ctl_elem_value *data; 289 int err, type, count; 290 291 data = kzalloc(sizeof(*data), GFP_KERNEL); 292 if (data == NULL) 293 return -ENOMEM; 294 295 if ((err = copy_ctl_value_from_user(card, data, data32, &type, &count)) < 0) 296 goto error; 297 298 snd_power_lock(card); 299 err = snd_power_wait(card, SNDRV_CTL_POWER_D0); 300 if (err >= 0) 301 err = snd_ctl_elem_read(card, data); 302 snd_power_unlock(card); 303 if (err >= 0) 304 err = copy_ctl_value_to_user(data32, data, type, count); 305 error: 306 kfree(data); 307 return err; 308 } 309 310 static int snd_ctl_elem_write_user_compat(struct snd_ctl_file *file, 311 struct snd_ctl_elem_value32 __user *data32) 312 { 313 struct snd_ctl_elem_value *data; 314 struct snd_card *card = file->card; 315 int err, type, count; 316 317 data = kzalloc(sizeof(*data), GFP_KERNEL); 318 if (data == NULL) 319 return -ENOMEM; 320 321 if ((err = copy_ctl_value_from_user(card, data, data32, &type, &count)) < 0) 322 goto error; 323 324 snd_power_lock(card); 325 err = snd_power_wait(card, SNDRV_CTL_POWER_D0); 326 if (err >= 0) 327 err = snd_ctl_elem_write(card, file, data); 328 snd_power_unlock(card); 329 if (err >= 0) 330 err = copy_ctl_value_to_user(data32, data, type, count); 331 error: 332 kfree(data); 333 return err; 334 } 335 336 /* add or replace a user control */ 337 static int snd_ctl_elem_add_compat(struct snd_ctl_file *file, 338 struct snd_ctl_elem_info32 __user *data32, 339 int replace) 340 { 341 struct snd_ctl_elem_info *data; 342 int err; 343 344 data = kzalloc(sizeof(*data), GFP_KERNEL); 345 if (! data) 346 return -ENOMEM; 347 348 err = -EFAULT; 349 /* id, type, access, count */ \ 350 if (copy_from_user(&data->id, &data32->id, sizeof(data->id)) || 351 copy_from_user(&data->type, &data32->type, 3 * sizeof(u32))) 352 goto error; 353 if (get_user(data->owner, &data32->owner) || 354 get_user(data->type, &data32->type)) 355 goto error; 356 switch (data->type) { 357 case SNDRV_CTL_ELEM_TYPE_BOOLEAN: 358 case SNDRV_CTL_ELEM_TYPE_INTEGER: 359 if (get_user(data->value.integer.min, &data32->value.integer.min) || 360 get_user(data->value.integer.max, &data32->value.integer.max) || 361 get_user(data->value.integer.step, &data32->value.integer.step)) 362 goto error; 363 break; 364 case SNDRV_CTL_ELEM_TYPE_INTEGER64: 365 if (copy_from_user(&data->value.integer64, 366 &data32->value.integer64, 367 sizeof(data->value.integer64))) 368 goto error; 369 break; 370 case SNDRV_CTL_ELEM_TYPE_ENUMERATED: 371 if (copy_from_user(&data->value.enumerated, 372 &data32->value.enumerated, 373 sizeof(data->value.enumerated))) 374 goto error; 375 break; 376 default: 377 break; 378 } 379 err = snd_ctl_elem_add(file, data, replace); 380 error: 381 kfree(data); 382 return err; 383 } 384 385 enum { 386 SNDRV_CTL_IOCTL_ELEM_LIST32 = _IOWR('U', 0x10, struct snd_ctl_elem_list32), 387 SNDRV_CTL_IOCTL_ELEM_INFO32 = _IOWR('U', 0x11, struct snd_ctl_elem_info32), 388 SNDRV_CTL_IOCTL_ELEM_READ32 = _IOWR('U', 0x12, struct snd_ctl_elem_value32), 389 SNDRV_CTL_IOCTL_ELEM_WRITE32 = _IOWR('U', 0x13, struct snd_ctl_elem_value32), 390 SNDRV_CTL_IOCTL_ELEM_ADD32 = _IOWR('U', 0x17, struct snd_ctl_elem_info32), 391 SNDRV_CTL_IOCTL_ELEM_REPLACE32 = _IOWR('U', 0x18, struct snd_ctl_elem_info32), 392 }; 393 394 static inline long snd_ctl_ioctl_compat(struct file *file, unsigned int cmd, unsigned long arg) 395 { 396 struct snd_ctl_file *ctl; 397 struct snd_kctl_ioctl *p; 398 void __user *argp = compat_ptr(arg); 399 int err; 400 401 ctl = file->private_data; 402 if (snd_BUG_ON(!ctl || !ctl->card)) 403 return -ENXIO; 404 405 switch (cmd) { 406 case SNDRV_CTL_IOCTL_PVERSION: 407 case SNDRV_CTL_IOCTL_CARD_INFO: 408 case SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS: 409 case SNDRV_CTL_IOCTL_POWER: 410 case SNDRV_CTL_IOCTL_POWER_STATE: 411 case SNDRV_CTL_IOCTL_ELEM_LOCK: 412 case SNDRV_CTL_IOCTL_ELEM_UNLOCK: 413 case SNDRV_CTL_IOCTL_ELEM_REMOVE: 414 case SNDRV_CTL_IOCTL_TLV_READ: 415 case SNDRV_CTL_IOCTL_TLV_WRITE: 416 case SNDRV_CTL_IOCTL_TLV_COMMAND: 417 return snd_ctl_ioctl(file, cmd, (unsigned long)argp); 418 case SNDRV_CTL_IOCTL_ELEM_LIST32: 419 return snd_ctl_elem_list_compat(ctl->card, argp); 420 case SNDRV_CTL_IOCTL_ELEM_INFO32: 421 return snd_ctl_elem_info_compat(ctl, argp); 422 case SNDRV_CTL_IOCTL_ELEM_READ32: 423 return snd_ctl_elem_read_user_compat(ctl->card, argp); 424 case SNDRV_CTL_IOCTL_ELEM_WRITE32: 425 return snd_ctl_elem_write_user_compat(ctl, argp); 426 case SNDRV_CTL_IOCTL_ELEM_ADD32: 427 return snd_ctl_elem_add_compat(ctl, argp, 0); 428 case SNDRV_CTL_IOCTL_ELEM_REPLACE32: 429 return snd_ctl_elem_add_compat(ctl, argp, 1); 430 } 431 432 down_read(&snd_ioctl_rwsem); 433 list_for_each_entry(p, &snd_control_compat_ioctls, list) { 434 if (p->fioctl) { 435 err = p->fioctl(ctl->card, ctl, cmd, arg); 436 if (err != -ENOIOCTLCMD) { 437 up_read(&snd_ioctl_rwsem); 438 return err; 439 } 440 } 441 } 442 up_read(&snd_ioctl_rwsem); 443 return -ENOIOCTLCMD; 444 } 445