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 25 struct sndrv_ctl_elem_list32 { 26 u32 offset; 27 u32 space; 28 u32 used; 29 u32 count; 30 u32 pids; 31 unsigned char reserved[50]; 32 } /* don't set packed attribute here */; 33 34 static int snd_ctl_elem_list_compat(snd_card_t *card, struct sndrv_ctl_elem_list32 __user *data32) 35 { 36 struct sndrv_ctl_elem_list __user *data; 37 compat_caddr_t ptr; 38 int err; 39 40 data = compat_alloc_user_space(sizeof(*data)); 41 42 /* offset, space, used, count */ 43 if (copy_in_user(data, data32, 4 * sizeof(u32))) 44 return -EFAULT; 45 /* pids */ 46 if (get_user(ptr, &data32->pids) || 47 put_user(compat_ptr(ptr), &data->pids)) 48 return -EFAULT; 49 err = snd_ctl_elem_list(card, data); 50 if (err < 0) 51 return err; 52 /* copy the result */ 53 if (copy_in_user(data32, data, 4 * sizeof(u32))) 54 return -EFAULT; 55 return 0; 56 } 57 58 /* 59 * control element info 60 * it uses union, so the things are not easy.. 61 */ 62 63 struct sndrv_ctl_elem_info32 { 64 struct sndrv_ctl_elem_id id; // the size of struct is same 65 s32 type; 66 u32 access; 67 u32 count; 68 s32 owner; 69 union { 70 struct { 71 s32 min; 72 s32 max; 73 s32 step; 74 } integer; 75 struct { 76 u64 min; 77 u64 max; 78 u64 step; 79 } integer64; 80 struct { 81 u32 items; 82 u32 item; 83 char name[64]; 84 } enumerated; 85 unsigned char reserved[128]; 86 } value; 87 unsigned char reserved[64]; 88 } __attribute__((packed)); 89 90 static int snd_ctl_elem_info_compat(snd_ctl_file_t *ctl, struct sndrv_ctl_elem_info32 __user *data32) 91 { 92 struct sndrv_ctl_elem_info *data; 93 int err; 94 95 data = kcalloc(1, sizeof(*data), GFP_KERNEL); 96 if (! data) 97 return -ENOMEM; 98 99 err = -EFAULT; 100 /* copy id */ 101 if (copy_from_user(&data->id, &data32->id, sizeof(data->id))) 102 goto error; 103 /* we need to copy the item index. 104 * hope this doesn't break anything.. 105 */ 106 if (get_user(data->value.enumerated.item, &data32->value.enumerated.item)) 107 goto error; 108 err = snd_ctl_elem_info(ctl, data); 109 if (err < 0) 110 goto error; 111 /* restore info to 32bit */ 112 err = -EFAULT; 113 /* id, type, access, count */ 114 if (copy_to_user(&data32->id, &data->id, sizeof(data->id)) || 115 copy_to_user(&data32->type, &data->type, 3 * sizeof(u32))) 116 goto error; 117 if (put_user(data->owner, &data32->owner)) 118 goto error; 119 switch (data->type) { 120 case SNDRV_CTL_ELEM_TYPE_BOOLEAN: 121 case SNDRV_CTL_ELEM_TYPE_INTEGER: 122 if (put_user(data->value.integer.min, &data32->value.integer.min) || 123 put_user(data->value.integer.max, &data32->value.integer.max) || 124 put_user(data->value.integer.step, &data32->value.integer.step)) 125 goto error; 126 break; 127 case SNDRV_CTL_ELEM_TYPE_INTEGER64: 128 if (copy_to_user(&data32->value.integer64, 129 &data->value.integer64, 130 sizeof(data->value.integer64))) 131 goto error; 132 break; 133 case SNDRV_CTL_ELEM_TYPE_ENUMERATED: 134 if (copy_to_user(&data32->value.enumerated, 135 &data->value.enumerated, 136 sizeof(data->value.enumerated))) 137 goto error; 138 break; 139 default: 140 break; 141 } 142 err = 0; 143 error: 144 kfree(data); 145 return err; 146 } 147 148 /* read / write */ 149 struct sndrv_ctl_elem_value32 { 150 struct sndrv_ctl_elem_id id; 151 unsigned int indirect; /* bit-field causes misalignment */ 152 union { 153 s32 integer[128]; 154 unsigned char data[512]; 155 #ifndef CONFIG_X86_64 156 s64 integer64[64]; 157 #endif 158 } value; 159 unsigned char reserved[128]; 160 }; 161 162 163 /* get the value type and count of the control */ 164 static int get_ctl_type(snd_card_t *card, snd_ctl_elem_id_t *id, int *countp) 165 { 166 snd_kcontrol_t *kctl; 167 snd_ctl_elem_info_t info; 168 int err; 169 170 down_read(&card->controls_rwsem); 171 kctl = snd_ctl_find_id(card, id); 172 if (! kctl) { 173 up_read(&card->controls_rwsem); 174 return -ENXIO; 175 } 176 info.id = *id; 177 err = kctl->info(kctl, &info); 178 up_read(&card->controls_rwsem); 179 if (err >= 0) { 180 err = info.type; 181 *countp = info.count; 182 } 183 return err; 184 } 185 186 static int get_elem_size(int type, int count) 187 { 188 switch (type) { 189 case SNDRV_CTL_ELEM_TYPE_INTEGER64: 190 return sizeof(s64) * count; 191 case SNDRV_CTL_ELEM_TYPE_ENUMERATED: 192 return sizeof(int) * count; 193 case SNDRV_CTL_ELEM_TYPE_BYTES: 194 return 512; 195 case SNDRV_CTL_ELEM_TYPE_IEC958: 196 return sizeof(struct sndrv_aes_iec958); 197 default: 198 return -1; 199 } 200 } 201 202 static int copy_ctl_value_from_user(snd_card_t *card, 203 struct sndrv_ctl_elem_value *data, 204 struct sndrv_ctl_elem_value32 __user *data32, 205 int *typep, int *countp) 206 { 207 int i, type, count, size; 208 unsigned int indirect; 209 210 if (copy_from_user(&data->id, &data32->id, sizeof(data->id))) 211 return -EFAULT; 212 if (get_user(indirect, &data32->indirect)) 213 return -EFAULT; 214 if (indirect) 215 return -EINVAL; 216 type = get_ctl_type(card, &data->id, &count); 217 if (type < 0) 218 return type; 219 220 if (type == SNDRV_CTL_ELEM_TYPE_BOOLEAN || 221 type == SNDRV_CTL_ELEM_TYPE_INTEGER) { 222 for (i = 0; i < count; i++) { 223 int val; 224 if (get_user(val, &data32->value.integer[i])) 225 return -EFAULT; 226 data->value.integer.value[i] = val; 227 } 228 } else { 229 size = get_elem_size(type, count); 230 if (size < 0) { 231 printk(KERN_ERR "snd_ioctl32_ctl_elem_value: unknown type %d\n", type); 232 return -EINVAL; 233 } 234 if (copy_from_user(data->value.bytes.data, 235 data32->value.data, size)) 236 return -EFAULT; 237 } 238 239 *typep = type; 240 *countp = count; 241 return 0; 242 } 243 244 /* restore the value to 32bit */ 245 static int copy_ctl_value_to_user(struct sndrv_ctl_elem_value32 __user *data32, 246 struct sndrv_ctl_elem_value *data, 247 int type, int count) 248 { 249 int i, size; 250 251 if (type == SNDRV_CTL_ELEM_TYPE_BOOLEAN || 252 type == SNDRV_CTL_ELEM_TYPE_INTEGER) { 253 for (i = 0; i < count; i++) { 254 int val; 255 val = data->value.integer.value[i]; 256 if (put_user(val, &data32->value.integer[i])) 257 return -EFAULT; 258 } 259 } else { 260 size = get_elem_size(type, count); 261 if (copy_to_user(data32->value.data, 262 data->value.bytes.data, size)) 263 return -EFAULT; 264 } 265 return 0; 266 } 267 268 static int snd_ctl_elem_read_user_compat(snd_card_t *card, 269 struct sndrv_ctl_elem_value32 __user *data32) 270 { 271 struct sndrv_ctl_elem_value *data; 272 int err, type, count; 273 274 data = kcalloc(1, sizeof(*data), GFP_KERNEL); 275 if (data == NULL) 276 return -ENOMEM; 277 278 if ((err = copy_ctl_value_from_user(card, data, data32, &type, &count)) < 0) 279 goto error; 280 if ((err = snd_ctl_elem_read(card, data)) < 0) 281 goto error; 282 err = copy_ctl_value_to_user(data32, data, type, count); 283 error: 284 kfree(data); 285 return err; 286 } 287 288 static int snd_ctl_elem_write_user_compat(snd_ctl_file_t *file, 289 struct sndrv_ctl_elem_value32 __user *data32) 290 { 291 struct sndrv_ctl_elem_value *data; 292 int err, type, count; 293 294 data = kcalloc(1, sizeof(*data), GFP_KERNEL); 295 if (data == NULL) 296 return -ENOMEM; 297 298 if ((err = copy_ctl_value_from_user(file->card, data, data32, &type, &count)) < 0) 299 goto error; 300 if ((err = snd_ctl_elem_write(file->card, file, data)) < 0) 301 goto error; 302 err = copy_ctl_value_to_user(data32, data, type, count); 303 error: 304 kfree(data); 305 return err; 306 } 307 308 /* add or replace a user control */ 309 static int snd_ctl_elem_add_compat(snd_ctl_file_t *file, 310 struct sndrv_ctl_elem_info32 __user *data32, 311 int replace) 312 { 313 struct sndrv_ctl_elem_info *data; 314 int err; 315 316 data = kcalloc(1, sizeof(*data), GFP_KERNEL); 317 if (! data) 318 return -ENOMEM; 319 320 err = -EFAULT; 321 /* id, type, access, count */ \ 322 if (copy_from_user(&data->id, &data32->id, sizeof(data->id)) || 323 copy_from_user(&data->type, &data32->type, 3 * sizeof(u32))) 324 goto error; 325 if (get_user(data->owner, &data32->owner) || 326 get_user(data->type, &data32->type)) 327 goto error; 328 switch (data->type) { 329 case SNDRV_CTL_ELEM_TYPE_BOOLEAN: 330 case SNDRV_CTL_ELEM_TYPE_INTEGER: 331 if (get_user(data->value.integer.min, &data32->value.integer.min) || 332 get_user(data->value.integer.max, &data32->value.integer.max) || 333 get_user(data->value.integer.step, &data32->value.integer.step)) 334 goto error; 335 break; 336 case SNDRV_CTL_ELEM_TYPE_INTEGER64: 337 if (copy_from_user(&data->value.integer64, 338 &data32->value.integer64, 339 sizeof(data->value.integer64))) 340 goto error; 341 break; 342 case SNDRV_CTL_ELEM_TYPE_ENUMERATED: 343 if (copy_from_user(&data->value.enumerated, 344 &data32->value.enumerated, 345 sizeof(data->value.enumerated))) 346 goto error; 347 break; 348 default: 349 break; 350 } 351 err = snd_ctl_elem_add(file, data, replace); 352 error: 353 kfree(data); 354 return err; 355 } 356 357 enum { 358 SNDRV_CTL_IOCTL_ELEM_LIST32 = _IOWR('U', 0x10, struct sndrv_ctl_elem_list32), 359 SNDRV_CTL_IOCTL_ELEM_INFO32 = _IOWR('U', 0x11, struct sndrv_ctl_elem_info32), 360 SNDRV_CTL_IOCTL_ELEM_READ32 = _IOWR('U', 0x12, struct sndrv_ctl_elem_value32), 361 SNDRV_CTL_IOCTL_ELEM_WRITE32 = _IOWR('U', 0x13, struct sndrv_ctl_elem_value32), 362 SNDRV_CTL_IOCTL_ELEM_ADD32 = _IOWR('U', 0x17, struct sndrv_ctl_elem_info32), 363 SNDRV_CTL_IOCTL_ELEM_REPLACE32 = _IOWR('U', 0x18, struct sndrv_ctl_elem_info32), 364 }; 365 366 static inline long snd_ctl_ioctl_compat(struct file *file, unsigned int cmd, unsigned long arg) 367 { 368 snd_ctl_file_t *ctl; 369 struct list_head *list; 370 void __user *argp = compat_ptr(arg); 371 int err; 372 373 ctl = file->private_data; 374 snd_assert(ctl && ctl->card, return -ENXIO); 375 376 switch (cmd) { 377 case SNDRV_CTL_IOCTL_PVERSION: 378 case SNDRV_CTL_IOCTL_CARD_INFO: 379 case SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS: 380 case SNDRV_CTL_IOCTL_POWER: 381 case SNDRV_CTL_IOCTL_POWER_STATE: 382 case SNDRV_CTL_IOCTL_ELEM_LOCK: 383 case SNDRV_CTL_IOCTL_ELEM_UNLOCK: 384 return snd_ctl_ioctl(file, cmd, (unsigned long)argp); 385 case SNDRV_CTL_IOCTL_ELEM_LIST32: 386 return snd_ctl_elem_list_compat(ctl->card, argp); 387 case SNDRV_CTL_IOCTL_ELEM_INFO32: 388 return snd_ctl_elem_info_compat(ctl, argp); 389 case SNDRV_CTL_IOCTL_ELEM_READ32: 390 return snd_ctl_elem_read_user_compat(ctl->card, argp); 391 case SNDRV_CTL_IOCTL_ELEM_WRITE32: 392 return snd_ctl_elem_write_user_compat(ctl, argp); 393 case SNDRV_CTL_IOCTL_ELEM_ADD32: 394 return snd_ctl_elem_add_compat(ctl, argp, 0); 395 case SNDRV_CTL_IOCTL_ELEM_REPLACE32: 396 return snd_ctl_elem_add_compat(ctl, argp, 1); 397 } 398 399 down_read(&snd_ioctl_rwsem); 400 list_for_each(list, &snd_control_compat_ioctls) { 401 snd_kctl_ioctl_t *p = list_entry(list, snd_kctl_ioctl_t, list); 402 if (p->fioctl) { 403 err = p->fioctl(ctl->card, ctl, cmd, arg); 404 if (err != -ENOIOCTLCMD) { 405 up_read(&snd_ioctl_rwsem); 406 return err; 407 } 408 } 409 } 410 up_read(&snd_ioctl_rwsem); 411 return -ENOIOCTLCMD; 412 } 413