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 u64 names_ptr; 87 u32 names_length; 88 } enumerated; 89 unsigned char reserved[128]; 90 } value; 91 unsigned char reserved[64]; 92 } __attribute__((packed)); 93 94 static int snd_ctl_elem_info_compat(struct snd_ctl_file *ctl, 95 struct snd_ctl_elem_info32 __user *data32) 96 { 97 struct snd_ctl_elem_info *data; 98 int err; 99 100 data = kzalloc(sizeof(*data), GFP_KERNEL); 101 if (! data) 102 return -ENOMEM; 103 104 err = -EFAULT; 105 /* copy id */ 106 if (copy_from_user(&data->id, &data32->id, sizeof(data->id))) 107 goto error; 108 /* we need to copy the item index. 109 * hope this doesn't break anything.. 110 */ 111 if (get_user(data->value.enumerated.item, &data32->value.enumerated.item)) 112 goto error; 113 114 err = snd_power_wait(ctl->card, SNDRV_CTL_POWER_D0); 115 if (err < 0) 116 goto error; 117 err = snd_ctl_elem_info(ctl, data); 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 #ifdef CONFIG_X86_X32 172 /* x32 has a different alignment for 64bit values from ia32 */ 173 struct snd_ctl_elem_value_x32 { 174 struct snd_ctl_elem_id id; 175 unsigned int indirect; /* bit-field causes misalignment */ 176 union { 177 s32 integer[128]; 178 unsigned char data[512]; 179 s64 integer64[64]; 180 } value; 181 unsigned char reserved[128]; 182 }; 183 #endif /* CONFIG_X86_X32 */ 184 185 /* get the value type and count of the control */ 186 static int get_ctl_type(struct snd_card *card, struct snd_ctl_elem_id *id, 187 int *countp) 188 { 189 struct snd_kcontrol *kctl; 190 struct snd_ctl_elem_info *info; 191 int err; 192 193 down_read(&card->controls_rwsem); 194 kctl = snd_ctl_find_id(card, id); 195 if (! kctl) { 196 up_read(&card->controls_rwsem); 197 return -ENOENT; 198 } 199 info = kzalloc(sizeof(*info), GFP_KERNEL); 200 if (info == NULL) { 201 up_read(&card->controls_rwsem); 202 return -ENOMEM; 203 } 204 info->id = *id; 205 err = kctl->info(kctl, info); 206 up_read(&card->controls_rwsem); 207 if (err >= 0) { 208 err = info->type; 209 *countp = info->count; 210 } 211 kfree(info); 212 return err; 213 } 214 215 static int get_elem_size(int type, int count) 216 { 217 switch (type) { 218 case SNDRV_CTL_ELEM_TYPE_INTEGER64: 219 return sizeof(s64) * count; 220 case SNDRV_CTL_ELEM_TYPE_ENUMERATED: 221 return sizeof(int) * count; 222 case SNDRV_CTL_ELEM_TYPE_BYTES: 223 return 512; 224 case SNDRV_CTL_ELEM_TYPE_IEC958: 225 return sizeof(struct snd_aes_iec958); 226 default: 227 return -1; 228 } 229 } 230 231 static int copy_ctl_value_from_user(struct snd_card *card, 232 struct snd_ctl_elem_value *data, 233 void __user *userdata, 234 void __user *valuep, 235 int *typep, int *countp) 236 { 237 struct snd_ctl_elem_value32 __user *data32 = userdata; 238 int i, type, size; 239 int uninitialized_var(count); 240 unsigned int indirect; 241 242 if (copy_from_user(&data->id, &data32->id, sizeof(data->id))) 243 return -EFAULT; 244 if (get_user(indirect, &data32->indirect)) 245 return -EFAULT; 246 if (indirect) 247 return -EINVAL; 248 type = get_ctl_type(card, &data->id, &count); 249 if (type < 0) 250 return type; 251 252 if (type == SNDRV_CTL_ELEM_TYPE_BOOLEAN || 253 type == SNDRV_CTL_ELEM_TYPE_INTEGER) { 254 for (i = 0; i < count; i++) { 255 s32 __user *intp = valuep; 256 int val; 257 if (get_user(val, &intp[i])) 258 return -EFAULT; 259 data->value.integer.value[i] = val; 260 } 261 } else { 262 size = get_elem_size(type, count); 263 if (size < 0) { 264 dev_err(card->dev, "snd_ioctl32_ctl_elem_value: unknown type %d\n", type); 265 return -EINVAL; 266 } 267 if (copy_from_user(data->value.bytes.data, valuep, size)) 268 return -EFAULT; 269 } 270 271 *typep = type; 272 *countp = count; 273 return 0; 274 } 275 276 /* restore the value to 32bit */ 277 static int copy_ctl_value_to_user(void __user *userdata, 278 void __user *valuep, 279 struct snd_ctl_elem_value *data, 280 int type, int count) 281 { 282 int i, size; 283 284 if (type == SNDRV_CTL_ELEM_TYPE_BOOLEAN || 285 type == SNDRV_CTL_ELEM_TYPE_INTEGER) { 286 for (i = 0; i < count; i++) { 287 s32 __user *intp = valuep; 288 int val; 289 val = data->value.integer.value[i]; 290 if (put_user(val, &intp[i])) 291 return -EFAULT; 292 } 293 } else { 294 size = get_elem_size(type, count); 295 if (copy_to_user(valuep, data->value.bytes.data, size)) 296 return -EFAULT; 297 } 298 return 0; 299 } 300 301 static int ctl_elem_read_user(struct snd_card *card, 302 void __user *userdata, void __user *valuep) 303 { 304 struct snd_ctl_elem_value *data; 305 int err, type, count; 306 307 data = kzalloc(sizeof(*data), GFP_KERNEL); 308 if (data == NULL) 309 return -ENOMEM; 310 311 err = copy_ctl_value_from_user(card, data, userdata, valuep, 312 &type, &count); 313 if (err < 0) 314 goto error; 315 316 err = snd_power_wait(card, SNDRV_CTL_POWER_D0); 317 if (err < 0) 318 goto error; 319 err = snd_ctl_elem_read(card, data); 320 if (err < 0) 321 goto error; 322 err = copy_ctl_value_to_user(userdata, valuep, data, type, count); 323 error: 324 kfree(data); 325 return err; 326 } 327 328 static int ctl_elem_write_user(struct snd_ctl_file *file, 329 void __user *userdata, void __user *valuep) 330 { 331 struct snd_ctl_elem_value *data; 332 struct snd_card *card = file->card; 333 int err, type, count; 334 335 data = kzalloc(sizeof(*data), GFP_KERNEL); 336 if (data == NULL) 337 return -ENOMEM; 338 339 err = copy_ctl_value_from_user(card, data, userdata, valuep, 340 &type, &count); 341 if (err < 0) 342 goto error; 343 344 err = snd_power_wait(card, SNDRV_CTL_POWER_D0); 345 if (err < 0) 346 goto error; 347 err = snd_ctl_elem_write(card, file, data); 348 if (err < 0) 349 goto error; 350 err = copy_ctl_value_to_user(userdata, valuep, data, type, count); 351 error: 352 kfree(data); 353 return err; 354 } 355 356 static int snd_ctl_elem_read_user_compat(struct snd_card *card, 357 struct snd_ctl_elem_value32 __user *data32) 358 { 359 return ctl_elem_read_user(card, data32, &data32->value); 360 } 361 362 static int snd_ctl_elem_write_user_compat(struct snd_ctl_file *file, 363 struct snd_ctl_elem_value32 __user *data32) 364 { 365 return ctl_elem_write_user(file, data32, &data32->value); 366 } 367 368 #ifdef CONFIG_X86_X32 369 static int snd_ctl_elem_read_user_x32(struct snd_card *card, 370 struct snd_ctl_elem_value_x32 __user *data32) 371 { 372 return ctl_elem_read_user(card, data32, &data32->value); 373 } 374 375 static int snd_ctl_elem_write_user_x32(struct snd_ctl_file *file, 376 struct snd_ctl_elem_value_x32 __user *data32) 377 { 378 return ctl_elem_write_user(file, data32, &data32->value); 379 } 380 #endif /* CONFIG_X86_X32 */ 381 382 /* add or replace a user control */ 383 static int snd_ctl_elem_add_compat(struct snd_ctl_file *file, 384 struct snd_ctl_elem_info32 __user *data32, 385 int replace) 386 { 387 struct snd_ctl_elem_info *data; 388 int err; 389 390 data = kzalloc(sizeof(*data), GFP_KERNEL); 391 if (! data) 392 return -ENOMEM; 393 394 err = -EFAULT; 395 /* id, type, access, count */ \ 396 if (copy_from_user(&data->id, &data32->id, sizeof(data->id)) || 397 copy_from_user(&data->type, &data32->type, 3 * sizeof(u32))) 398 goto error; 399 if (get_user(data->owner, &data32->owner) || 400 get_user(data->type, &data32->type)) 401 goto error; 402 switch (data->type) { 403 case SNDRV_CTL_ELEM_TYPE_BOOLEAN: 404 case SNDRV_CTL_ELEM_TYPE_INTEGER: 405 if (get_user(data->value.integer.min, &data32->value.integer.min) || 406 get_user(data->value.integer.max, &data32->value.integer.max) || 407 get_user(data->value.integer.step, &data32->value.integer.step)) 408 goto error; 409 break; 410 case SNDRV_CTL_ELEM_TYPE_INTEGER64: 411 if (copy_from_user(&data->value.integer64, 412 &data32->value.integer64, 413 sizeof(data->value.integer64))) 414 goto error; 415 break; 416 case SNDRV_CTL_ELEM_TYPE_ENUMERATED: 417 if (copy_from_user(&data->value.enumerated, 418 &data32->value.enumerated, 419 sizeof(data->value.enumerated))) 420 goto error; 421 data->value.enumerated.names_ptr = 422 (uintptr_t)compat_ptr(data->value.enumerated.names_ptr); 423 break; 424 default: 425 break; 426 } 427 err = snd_ctl_elem_add(file, data, replace); 428 error: 429 kfree(data); 430 return err; 431 } 432 433 enum { 434 SNDRV_CTL_IOCTL_ELEM_LIST32 = _IOWR('U', 0x10, struct snd_ctl_elem_list32), 435 SNDRV_CTL_IOCTL_ELEM_INFO32 = _IOWR('U', 0x11, struct snd_ctl_elem_info32), 436 SNDRV_CTL_IOCTL_ELEM_READ32 = _IOWR('U', 0x12, struct snd_ctl_elem_value32), 437 SNDRV_CTL_IOCTL_ELEM_WRITE32 = _IOWR('U', 0x13, struct snd_ctl_elem_value32), 438 SNDRV_CTL_IOCTL_ELEM_ADD32 = _IOWR('U', 0x17, struct snd_ctl_elem_info32), 439 SNDRV_CTL_IOCTL_ELEM_REPLACE32 = _IOWR('U', 0x18, struct snd_ctl_elem_info32), 440 #ifdef CONFIG_X86_X32 441 SNDRV_CTL_IOCTL_ELEM_READ_X32 = _IOWR('U', 0x12, struct snd_ctl_elem_value_x32), 442 SNDRV_CTL_IOCTL_ELEM_WRITE_X32 = _IOWR('U', 0x13, struct snd_ctl_elem_value_x32), 443 #endif /* CONFIG_X86_X32 */ 444 }; 445 446 static inline long snd_ctl_ioctl_compat(struct file *file, unsigned int cmd, unsigned long arg) 447 { 448 struct snd_ctl_file *ctl; 449 struct snd_kctl_ioctl *p; 450 void __user *argp = compat_ptr(arg); 451 int err; 452 453 ctl = file->private_data; 454 if (snd_BUG_ON(!ctl || !ctl->card)) 455 return -ENXIO; 456 457 switch (cmd) { 458 case SNDRV_CTL_IOCTL_PVERSION: 459 case SNDRV_CTL_IOCTL_CARD_INFO: 460 case SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS: 461 case SNDRV_CTL_IOCTL_POWER: 462 case SNDRV_CTL_IOCTL_POWER_STATE: 463 case SNDRV_CTL_IOCTL_ELEM_LOCK: 464 case SNDRV_CTL_IOCTL_ELEM_UNLOCK: 465 case SNDRV_CTL_IOCTL_ELEM_REMOVE: 466 case SNDRV_CTL_IOCTL_TLV_READ: 467 case SNDRV_CTL_IOCTL_TLV_WRITE: 468 case SNDRV_CTL_IOCTL_TLV_COMMAND: 469 return snd_ctl_ioctl(file, cmd, (unsigned long)argp); 470 case SNDRV_CTL_IOCTL_ELEM_LIST32: 471 return snd_ctl_elem_list_compat(ctl->card, argp); 472 case SNDRV_CTL_IOCTL_ELEM_INFO32: 473 return snd_ctl_elem_info_compat(ctl, argp); 474 case SNDRV_CTL_IOCTL_ELEM_READ32: 475 return snd_ctl_elem_read_user_compat(ctl->card, argp); 476 case SNDRV_CTL_IOCTL_ELEM_WRITE32: 477 return snd_ctl_elem_write_user_compat(ctl, argp); 478 case SNDRV_CTL_IOCTL_ELEM_ADD32: 479 return snd_ctl_elem_add_compat(ctl, argp, 0); 480 case SNDRV_CTL_IOCTL_ELEM_REPLACE32: 481 return snd_ctl_elem_add_compat(ctl, argp, 1); 482 #ifdef CONFIG_X86_X32 483 case SNDRV_CTL_IOCTL_ELEM_READ_X32: 484 return snd_ctl_elem_read_user_x32(ctl->card, argp); 485 case SNDRV_CTL_IOCTL_ELEM_WRITE_X32: 486 return snd_ctl_elem_write_user_x32(ctl, argp); 487 #endif /* CONFIG_X86_X32 */ 488 } 489 490 down_read(&snd_ioctl_rwsem); 491 list_for_each_entry(p, &snd_control_compat_ioctls, list) { 492 if (p->fioctl) { 493 err = p->fioctl(ctl->card, ctl, cmd, arg); 494 if (err != -ENOIOCTLCMD) { 495 up_read(&snd_ioctl_rwsem); 496 return err; 497 } 498 } 499 } 500 up_read(&snd_ioctl_rwsem); 501 return -ENOIOCTLCMD; 502 } 503