| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * Routines for driver control interface |
|---|
| 3 | 4 | * Copyright (c) by Jaroslav Kysela <perex@perex.cz> |
|---|
| 4 | | - * |
|---|
| 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 | 5 | */ |
|---|
| 21 | 6 | |
|---|
| 22 | 7 | #include <linux/threads.h> |
|---|
| .. | .. |
|---|
| 26 | 11 | #include <linux/vmalloc.h> |
|---|
| 27 | 12 | #include <linux/time.h> |
|---|
| 28 | 13 | #include <linux/mm.h> |
|---|
| 14 | +#include <linux/math64.h> |
|---|
| 29 | 15 | #include <linux/sched/signal.h> |
|---|
| 30 | 16 | #include <sound/core.h> |
|---|
| 31 | 17 | #include <sound/minors.h> |
|---|
| .. | .. |
|---|
| 54 | 40 | struct snd_ctl_file *ctl; |
|---|
| 55 | 41 | int i, err; |
|---|
| 56 | 42 | |
|---|
| 57 | | - err = nonseekable_open(inode, file); |
|---|
| 43 | + err = stream_open(inode, file); |
|---|
| 58 | 44 | if (err < 0) |
|---|
| 59 | 45 | return err; |
|---|
| 60 | 46 | |
|---|
| .. | .. |
|---|
| 164 | 150 | return; |
|---|
| 165 | 151 | if (card->shutdown) |
|---|
| 166 | 152 | return; |
|---|
| 167 | | - read_lock(&card->ctl_files_rwlock); |
|---|
| 153 | + read_lock_irqsave(&card->ctl_files_rwlock, flags); |
|---|
| 168 | 154 | #if IS_ENABLED(CONFIG_SND_MIXER_OSS) |
|---|
| 169 | 155 | card->mixer_oss_change_count++; |
|---|
| 170 | 156 | #endif |
|---|
| 171 | 157 | list_for_each_entry(ctl, &card->ctl_files, list) { |
|---|
| 172 | 158 | if (!ctl->subscribed) |
|---|
| 173 | 159 | continue; |
|---|
| 174 | | - spin_lock_irqsave(&ctl->read_lock, flags); |
|---|
| 160 | + spin_lock(&ctl->read_lock); |
|---|
| 175 | 161 | list_for_each_entry(ev, &ctl->events, list) { |
|---|
| 176 | 162 | if (ev->id.numid == id->numid) { |
|---|
| 177 | 163 | ev->mask |= mask; |
|---|
| .. | .. |
|---|
| 188 | 174 | } |
|---|
| 189 | 175 | _found: |
|---|
| 190 | 176 | wake_up(&ctl->change_sleep); |
|---|
| 191 | | - spin_unlock_irqrestore(&ctl->read_lock, flags); |
|---|
| 177 | + spin_unlock(&ctl->read_lock); |
|---|
| 192 | 178 | kill_fasync(&ctl->fasync, SIGIO, POLL_IN); |
|---|
| 193 | 179 | } |
|---|
| 194 | | - read_unlock(&card->ctl_files_rwlock); |
|---|
| 180 | + read_unlock_irqrestore(&card->ctl_files_rwlock, flags); |
|---|
| 195 | 181 | } |
|---|
| 196 | 182 | EXPORT_SYMBOL(snd_ctl_notify); |
|---|
| 197 | 183 | |
|---|
| .. | .. |
|---|
| 211 | 197 | static int snd_ctl_new(struct snd_kcontrol **kctl, unsigned int count, |
|---|
| 212 | 198 | unsigned int access, struct snd_ctl_file *file) |
|---|
| 213 | 199 | { |
|---|
| 214 | | - unsigned int size; |
|---|
| 215 | 200 | unsigned int idx; |
|---|
| 216 | 201 | |
|---|
| 217 | 202 | if (count == 0 || count > MAX_CONTROL_COUNT) |
|---|
| 218 | 203 | return -EINVAL; |
|---|
| 219 | 204 | |
|---|
| 220 | | - size = sizeof(struct snd_kcontrol); |
|---|
| 221 | | - size += sizeof(struct snd_kcontrol_volatile) * count; |
|---|
| 222 | | - |
|---|
| 223 | | - *kctl = kzalloc(size, GFP_KERNEL); |
|---|
| 205 | + *kctl = kzalloc(struct_size(*kctl, vd, count), GFP_KERNEL); |
|---|
| 224 | 206 | if (!*kctl) |
|---|
| 225 | 207 | return -ENOMEM; |
|---|
| 226 | 208 | |
|---|
| .. | .. |
|---|
| 267 | 249 | SNDRV_CTL_ELEM_ACCESS_INACTIVE | |
|---|
| 268 | 250 | SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE | |
|---|
| 269 | 251 | SNDRV_CTL_ELEM_ACCESS_TLV_COMMAND | |
|---|
| 270 | | - SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK); |
|---|
| 252 | + SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK | |
|---|
| 253 | + SNDRV_CTL_ELEM_ACCESS_SKIP_CHECK); |
|---|
| 271 | 254 | |
|---|
| 272 | 255 | err = snd_ctl_new(&kctl, count, access, NULL); |
|---|
| 273 | 256 | if (err < 0) |
|---|
| .. | .. |
|---|
| 348 | 331 | return 0; |
|---|
| 349 | 332 | } |
|---|
| 350 | 333 | |
|---|
| 351 | | -/* add a new kcontrol object; call with card->controls_rwsem locked */ |
|---|
| 352 | | -static int __snd_ctl_add(struct snd_card *card, struct snd_kcontrol *kcontrol) |
|---|
| 334 | +enum snd_ctl_add_mode { |
|---|
| 335 | + CTL_ADD_EXCLUSIVE, CTL_REPLACE, CTL_ADD_ON_REPLACE, |
|---|
| 336 | +}; |
|---|
| 337 | + |
|---|
| 338 | +/* add/replace a new kcontrol object; call with card->controls_rwsem locked */ |
|---|
| 339 | +static int __snd_ctl_add_replace(struct snd_card *card, |
|---|
| 340 | + struct snd_kcontrol *kcontrol, |
|---|
| 341 | + enum snd_ctl_add_mode mode) |
|---|
| 353 | 342 | { |
|---|
| 354 | 343 | struct snd_ctl_elem_id id; |
|---|
| 355 | 344 | unsigned int idx; |
|---|
| 356 | 345 | unsigned int count; |
|---|
| 346 | + struct snd_kcontrol *old; |
|---|
| 347 | + int err; |
|---|
| 357 | 348 | |
|---|
| 358 | 349 | id = kcontrol->id; |
|---|
| 359 | 350 | if (id.index > UINT_MAX - kcontrol->count) |
|---|
| 360 | 351 | return -EINVAL; |
|---|
| 361 | 352 | |
|---|
| 362 | | - if (snd_ctl_find_id(card, &id)) { |
|---|
| 363 | | - dev_err(card->dev, |
|---|
| 364 | | - "control %i:%i:%i:%s:%i is already present\n", |
|---|
| 365 | | - id.iface, id.device, id.subdevice, id.name, id.index); |
|---|
| 366 | | - return -EBUSY; |
|---|
| 353 | + old = snd_ctl_find_id(card, &id); |
|---|
| 354 | + if (!old) { |
|---|
| 355 | + if (mode == CTL_REPLACE) |
|---|
| 356 | + return -EINVAL; |
|---|
| 357 | + } else { |
|---|
| 358 | + if (mode == CTL_ADD_EXCLUSIVE) { |
|---|
| 359 | + dev_err(card->dev, |
|---|
| 360 | + "control %i:%i:%i:%s:%i is already present\n", |
|---|
| 361 | + id.iface, id.device, id.subdevice, id.name, |
|---|
| 362 | + id.index); |
|---|
| 363 | + return -EBUSY; |
|---|
| 364 | + } |
|---|
| 365 | + |
|---|
| 366 | + err = snd_ctl_remove(card, old); |
|---|
| 367 | + if (err < 0) |
|---|
| 368 | + return err; |
|---|
| 367 | 369 | } |
|---|
| 368 | 370 | |
|---|
| 369 | 371 | if (snd_ctl_find_hole(card, kcontrol->count) < 0) |
|---|
| .. | .. |
|---|
| 382 | 384 | return 0; |
|---|
| 383 | 385 | } |
|---|
| 384 | 386 | |
|---|
| 387 | +static int snd_ctl_add_replace(struct snd_card *card, |
|---|
| 388 | + struct snd_kcontrol *kcontrol, |
|---|
| 389 | + enum snd_ctl_add_mode mode) |
|---|
| 390 | +{ |
|---|
| 391 | + int err = -EINVAL; |
|---|
| 392 | + |
|---|
| 393 | + if (! kcontrol) |
|---|
| 394 | + return err; |
|---|
| 395 | + if (snd_BUG_ON(!card || !kcontrol->info)) |
|---|
| 396 | + goto error; |
|---|
| 397 | + |
|---|
| 398 | + down_write(&card->controls_rwsem); |
|---|
| 399 | + err = __snd_ctl_add_replace(card, kcontrol, mode); |
|---|
| 400 | + up_write(&card->controls_rwsem); |
|---|
| 401 | + if (err < 0) |
|---|
| 402 | + goto error; |
|---|
| 403 | + return 0; |
|---|
| 404 | + |
|---|
| 405 | + error: |
|---|
| 406 | + snd_ctl_free_one(kcontrol); |
|---|
| 407 | + return err; |
|---|
| 408 | +} |
|---|
| 409 | + |
|---|
| 385 | 410 | /** |
|---|
| 386 | 411 | * snd_ctl_add - add the control instance to the card |
|---|
| 387 | 412 | * @card: the card instance |
|---|
| .. | .. |
|---|
| 398 | 423 | */ |
|---|
| 399 | 424 | int snd_ctl_add(struct snd_card *card, struct snd_kcontrol *kcontrol) |
|---|
| 400 | 425 | { |
|---|
| 401 | | - int err = -EINVAL; |
|---|
| 402 | | - |
|---|
| 403 | | - if (! kcontrol) |
|---|
| 404 | | - return err; |
|---|
| 405 | | - if (snd_BUG_ON(!card || !kcontrol->info)) |
|---|
| 406 | | - goto error; |
|---|
| 407 | | - |
|---|
| 408 | | - down_write(&card->controls_rwsem); |
|---|
| 409 | | - err = __snd_ctl_add(card, kcontrol); |
|---|
| 410 | | - up_write(&card->controls_rwsem); |
|---|
| 411 | | - if (err < 0) |
|---|
| 412 | | - goto error; |
|---|
| 413 | | - return 0; |
|---|
| 414 | | - |
|---|
| 415 | | - error: |
|---|
| 416 | | - snd_ctl_free_one(kcontrol); |
|---|
| 417 | | - return err; |
|---|
| 426 | + return snd_ctl_add_replace(card, kcontrol, CTL_ADD_EXCLUSIVE); |
|---|
| 418 | 427 | } |
|---|
| 419 | 428 | EXPORT_SYMBOL(snd_ctl_add); |
|---|
| 420 | 429 | |
|---|
| .. | .. |
|---|
| 435 | 444 | int snd_ctl_replace(struct snd_card *card, struct snd_kcontrol *kcontrol, |
|---|
| 436 | 445 | bool add_on_replace) |
|---|
| 437 | 446 | { |
|---|
| 438 | | - struct snd_ctl_elem_id id; |
|---|
| 439 | | - unsigned int count; |
|---|
| 440 | | - unsigned int idx; |
|---|
| 441 | | - struct snd_kcontrol *old; |
|---|
| 442 | | - int ret; |
|---|
| 443 | | - |
|---|
| 444 | | - if (!kcontrol) |
|---|
| 445 | | - return -EINVAL; |
|---|
| 446 | | - if (snd_BUG_ON(!card || !kcontrol->info)) { |
|---|
| 447 | | - ret = -EINVAL; |
|---|
| 448 | | - goto error; |
|---|
| 449 | | - } |
|---|
| 450 | | - id = kcontrol->id; |
|---|
| 451 | | - down_write(&card->controls_rwsem); |
|---|
| 452 | | - old = snd_ctl_find_id(card, &id); |
|---|
| 453 | | - if (!old) { |
|---|
| 454 | | - if (add_on_replace) |
|---|
| 455 | | - goto add; |
|---|
| 456 | | - up_write(&card->controls_rwsem); |
|---|
| 457 | | - ret = -EINVAL; |
|---|
| 458 | | - goto error; |
|---|
| 459 | | - } |
|---|
| 460 | | - ret = snd_ctl_remove(card, old); |
|---|
| 461 | | - if (ret < 0) { |
|---|
| 462 | | - up_write(&card->controls_rwsem); |
|---|
| 463 | | - goto error; |
|---|
| 464 | | - } |
|---|
| 465 | | -add: |
|---|
| 466 | | - if (snd_ctl_find_hole(card, kcontrol->count) < 0) { |
|---|
| 467 | | - up_write(&card->controls_rwsem); |
|---|
| 468 | | - ret = -ENOMEM; |
|---|
| 469 | | - goto error; |
|---|
| 470 | | - } |
|---|
| 471 | | - list_add_tail(&kcontrol->list, &card->controls); |
|---|
| 472 | | - card->controls_count += kcontrol->count; |
|---|
| 473 | | - kcontrol->id.numid = card->last_numid + 1; |
|---|
| 474 | | - card->last_numid += kcontrol->count; |
|---|
| 475 | | - id = kcontrol->id; |
|---|
| 476 | | - count = kcontrol->count; |
|---|
| 477 | | - up_write(&card->controls_rwsem); |
|---|
| 478 | | - for (idx = 0; idx < count; idx++, id.index++, id.numid++) |
|---|
| 479 | | - snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_ADD, &id); |
|---|
| 480 | | - return 0; |
|---|
| 481 | | - |
|---|
| 482 | | -error: |
|---|
| 483 | | - snd_ctl_free_one(kcontrol); |
|---|
| 484 | | - return ret; |
|---|
| 447 | + return snd_ctl_add_replace(card, kcontrol, |
|---|
| 448 | + add_on_replace ? CTL_ADD_ON_REPLACE : CTL_REPLACE); |
|---|
| 485 | 449 | } |
|---|
| 486 | 450 | EXPORT_SYMBOL(snd_ctl_replace); |
|---|
| 487 | 451 | |
|---|
| .. | .. |
|---|
| 753 | 717 | } |
|---|
| 754 | 718 | |
|---|
| 755 | 719 | static int snd_ctl_elem_list(struct snd_card *card, |
|---|
| 756 | | - struct snd_ctl_elem_list __user *_list) |
|---|
| 720 | + struct snd_ctl_elem_list *list) |
|---|
| 757 | 721 | { |
|---|
| 758 | | - struct snd_ctl_elem_list list; |
|---|
| 759 | 722 | struct snd_kcontrol *kctl; |
|---|
| 760 | 723 | struct snd_ctl_elem_id id; |
|---|
| 761 | 724 | unsigned int offset, space, jidx; |
|---|
| 762 | 725 | int err = 0; |
|---|
| 763 | 726 | |
|---|
| 764 | | - if (copy_from_user(&list, _list, sizeof(list))) |
|---|
| 765 | | - return -EFAULT; |
|---|
| 766 | | - offset = list.offset; |
|---|
| 767 | | - space = list.space; |
|---|
| 727 | + offset = list->offset; |
|---|
| 728 | + space = list->space; |
|---|
| 768 | 729 | |
|---|
| 769 | 730 | down_read(&card->controls_rwsem); |
|---|
| 770 | | - list.count = card->controls_count; |
|---|
| 771 | | - list.used = 0; |
|---|
| 731 | + list->count = card->controls_count; |
|---|
| 732 | + list->used = 0; |
|---|
| 772 | 733 | if (space > 0) { |
|---|
| 773 | 734 | list_for_each_entry(kctl, &card->controls, list) { |
|---|
| 774 | 735 | if (offset >= kctl->count) { |
|---|
| .. | .. |
|---|
| 777 | 738 | } |
|---|
| 778 | 739 | for (jidx = offset; jidx < kctl->count; jidx++) { |
|---|
| 779 | 740 | snd_ctl_build_ioff(&id, kctl, jidx); |
|---|
| 780 | | - if (copy_to_user(list.pids + list.used, &id, |
|---|
| 741 | + if (copy_to_user(list->pids + list->used, &id, |
|---|
| 781 | 742 | sizeof(id))) { |
|---|
| 782 | 743 | err = -EFAULT; |
|---|
| 783 | 744 | goto out; |
|---|
| 784 | 745 | } |
|---|
| 785 | | - list.used++; |
|---|
| 746 | + list->used++; |
|---|
| 786 | 747 | if (!--space) |
|---|
| 787 | 748 | goto out; |
|---|
| 788 | 749 | } |
|---|
| .. | .. |
|---|
| 791 | 752 | } |
|---|
| 792 | 753 | out: |
|---|
| 793 | 754 | up_read(&card->controls_rwsem); |
|---|
| 794 | | - if (!err && copy_to_user(_list, &list, sizeof(list))) |
|---|
| 795 | | - err = -EFAULT; |
|---|
| 796 | 755 | return err; |
|---|
| 797 | 756 | } |
|---|
| 798 | 757 | |
|---|
| 799 | | -static bool validate_element_member_dimension(struct snd_ctl_elem_info *info) |
|---|
| 758 | +static int snd_ctl_elem_list_user(struct snd_card *card, |
|---|
| 759 | + struct snd_ctl_elem_list __user *_list) |
|---|
| 800 | 760 | { |
|---|
| 801 | | - unsigned int members; |
|---|
| 802 | | - unsigned int i; |
|---|
| 761 | + struct snd_ctl_elem_list list; |
|---|
| 762 | + int err; |
|---|
| 803 | 763 | |
|---|
| 804 | | - if (info->dimen.d[0] == 0) |
|---|
| 805 | | - return true; |
|---|
| 764 | + if (copy_from_user(&list, _list, sizeof(list))) |
|---|
| 765 | + return -EFAULT; |
|---|
| 766 | + err = snd_ctl_elem_list(card, &list); |
|---|
| 767 | + if (err) |
|---|
| 768 | + return err; |
|---|
| 769 | + if (copy_to_user(_list, &list, sizeof(list))) |
|---|
| 770 | + return -EFAULT; |
|---|
| 806 | 771 | |
|---|
| 807 | | - members = 1; |
|---|
| 808 | | - for (i = 0; i < ARRAY_SIZE(info->dimen.d); ++i) { |
|---|
| 809 | | - if (info->dimen.d[i] == 0) |
|---|
| 810 | | - break; |
|---|
| 811 | | - members *= info->dimen.d[i]; |
|---|
| 812 | | - |
|---|
| 813 | | - /* |
|---|
| 814 | | - * info->count should be validated in advance, to guarantee |
|---|
| 815 | | - * calculation soundness. |
|---|
| 816 | | - */ |
|---|
| 817 | | - if (members > info->count) |
|---|
| 818 | | - return false; |
|---|
| 819 | | - } |
|---|
| 820 | | - |
|---|
| 821 | | - for (++i; i < ARRAY_SIZE(info->dimen.d); ++i) { |
|---|
| 822 | | - if (info->dimen.d[i] > 0) |
|---|
| 823 | | - return false; |
|---|
| 824 | | - } |
|---|
| 825 | | - |
|---|
| 826 | | - return members == info->count; |
|---|
| 772 | + return 0; |
|---|
| 827 | 773 | } |
|---|
| 828 | 774 | |
|---|
| 829 | | -static int snd_ctl_elem_info(struct snd_ctl_file *ctl, |
|---|
| 830 | | - struct snd_ctl_elem_info *info) |
|---|
| 775 | +/* Check whether the given kctl info is valid */ |
|---|
| 776 | +static int snd_ctl_check_elem_info(struct snd_card *card, |
|---|
| 777 | + const struct snd_ctl_elem_info *info) |
|---|
| 831 | 778 | { |
|---|
| 832 | | - struct snd_card *card = ctl->card; |
|---|
| 833 | | - struct snd_kcontrol *kctl; |
|---|
| 779 | + static const unsigned int max_value_counts[] = { |
|---|
| 780 | + [SNDRV_CTL_ELEM_TYPE_BOOLEAN] = 128, |
|---|
| 781 | + [SNDRV_CTL_ELEM_TYPE_INTEGER] = 128, |
|---|
| 782 | + [SNDRV_CTL_ELEM_TYPE_ENUMERATED] = 128, |
|---|
| 783 | + [SNDRV_CTL_ELEM_TYPE_BYTES] = 512, |
|---|
| 784 | + [SNDRV_CTL_ELEM_TYPE_IEC958] = 1, |
|---|
| 785 | + [SNDRV_CTL_ELEM_TYPE_INTEGER64] = 64, |
|---|
| 786 | + }; |
|---|
| 787 | + |
|---|
| 788 | + if (info->type < SNDRV_CTL_ELEM_TYPE_BOOLEAN || |
|---|
| 789 | + info->type > SNDRV_CTL_ELEM_TYPE_INTEGER64) { |
|---|
| 790 | + if (card) |
|---|
| 791 | + dev_err(card->dev, |
|---|
| 792 | + "control %i:%i:%i:%s:%i: invalid type %d\n", |
|---|
| 793 | + info->id.iface, info->id.device, |
|---|
| 794 | + info->id.subdevice, info->id.name, |
|---|
| 795 | + info->id.index, info->type); |
|---|
| 796 | + return -EINVAL; |
|---|
| 797 | + } |
|---|
| 798 | + if (info->type == SNDRV_CTL_ELEM_TYPE_ENUMERATED && |
|---|
| 799 | + info->value.enumerated.items == 0) { |
|---|
| 800 | + if (card) |
|---|
| 801 | + dev_err(card->dev, |
|---|
| 802 | + "control %i:%i:%i:%s:%i: zero enum items\n", |
|---|
| 803 | + info->id.iface, info->id.device, |
|---|
| 804 | + info->id.subdevice, info->id.name, |
|---|
| 805 | + info->id.index); |
|---|
| 806 | + return -EINVAL; |
|---|
| 807 | + } |
|---|
| 808 | + if (info->count > max_value_counts[info->type]) { |
|---|
| 809 | + if (card) |
|---|
| 810 | + dev_err(card->dev, |
|---|
| 811 | + "control %i:%i:%i:%s:%i: invalid count %d\n", |
|---|
| 812 | + info->id.iface, info->id.device, |
|---|
| 813 | + info->id.subdevice, info->id.name, |
|---|
| 814 | + info->id.index, info->count); |
|---|
| 815 | + return -EINVAL; |
|---|
| 816 | + } |
|---|
| 817 | + |
|---|
| 818 | + return 0; |
|---|
| 819 | +} |
|---|
| 820 | + |
|---|
| 821 | +/* The capacity of struct snd_ctl_elem_value.value.*/ |
|---|
| 822 | +static const unsigned int value_sizes[] = { |
|---|
| 823 | + [SNDRV_CTL_ELEM_TYPE_BOOLEAN] = sizeof(long), |
|---|
| 824 | + [SNDRV_CTL_ELEM_TYPE_INTEGER] = sizeof(long), |
|---|
| 825 | + [SNDRV_CTL_ELEM_TYPE_ENUMERATED] = sizeof(unsigned int), |
|---|
| 826 | + [SNDRV_CTL_ELEM_TYPE_BYTES] = sizeof(unsigned char), |
|---|
| 827 | + [SNDRV_CTL_ELEM_TYPE_IEC958] = sizeof(struct snd_aes_iec958), |
|---|
| 828 | + [SNDRV_CTL_ELEM_TYPE_INTEGER64] = sizeof(long long), |
|---|
| 829 | +}; |
|---|
| 830 | + |
|---|
| 831 | +#ifdef CONFIG_SND_CTL_VALIDATION |
|---|
| 832 | +/* fill the remaining snd_ctl_elem_value data with the given pattern */ |
|---|
| 833 | +static void fill_remaining_elem_value(struct snd_ctl_elem_value *control, |
|---|
| 834 | + struct snd_ctl_elem_info *info, |
|---|
| 835 | + u32 pattern) |
|---|
| 836 | +{ |
|---|
| 837 | + size_t offset = value_sizes[info->type] * info->count; |
|---|
| 838 | + |
|---|
| 839 | + offset = (offset + sizeof(u32) - 1) / sizeof(u32); |
|---|
| 840 | + memset32((u32 *)control->value.bytes.data + offset, pattern, |
|---|
| 841 | + sizeof(control->value) / sizeof(u32) - offset); |
|---|
| 842 | +} |
|---|
| 843 | + |
|---|
| 844 | +/* check whether the given integer ctl value is valid */ |
|---|
| 845 | +static int sanity_check_int_value(struct snd_card *card, |
|---|
| 846 | + const struct snd_ctl_elem_value *control, |
|---|
| 847 | + const struct snd_ctl_elem_info *info, |
|---|
| 848 | + int i) |
|---|
| 849 | +{ |
|---|
| 850 | + long long lval, lmin, lmax, lstep; |
|---|
| 851 | + u64 rem; |
|---|
| 852 | + |
|---|
| 853 | + switch (info->type) { |
|---|
| 854 | + default: |
|---|
| 855 | + case SNDRV_CTL_ELEM_TYPE_BOOLEAN: |
|---|
| 856 | + lval = control->value.integer.value[i]; |
|---|
| 857 | + lmin = 0; |
|---|
| 858 | + lmax = 1; |
|---|
| 859 | + lstep = 0; |
|---|
| 860 | + break; |
|---|
| 861 | + case SNDRV_CTL_ELEM_TYPE_INTEGER: |
|---|
| 862 | + lval = control->value.integer.value[i]; |
|---|
| 863 | + lmin = info->value.integer.min; |
|---|
| 864 | + lmax = info->value.integer.max; |
|---|
| 865 | + lstep = info->value.integer.step; |
|---|
| 866 | + break; |
|---|
| 867 | + case SNDRV_CTL_ELEM_TYPE_INTEGER64: |
|---|
| 868 | + lval = control->value.integer64.value[i]; |
|---|
| 869 | + lmin = info->value.integer64.min; |
|---|
| 870 | + lmax = info->value.integer64.max; |
|---|
| 871 | + lstep = info->value.integer64.step; |
|---|
| 872 | + break; |
|---|
| 873 | + case SNDRV_CTL_ELEM_TYPE_ENUMERATED: |
|---|
| 874 | + lval = control->value.enumerated.item[i]; |
|---|
| 875 | + lmin = 0; |
|---|
| 876 | + lmax = info->value.enumerated.items - 1; |
|---|
| 877 | + lstep = 0; |
|---|
| 878 | + break; |
|---|
| 879 | + } |
|---|
| 880 | + |
|---|
| 881 | + if (lval < lmin || lval > lmax) { |
|---|
| 882 | + dev_err(card->dev, |
|---|
| 883 | + "control %i:%i:%i:%s:%i: value out of range %lld (%lld/%lld) at count %i\n", |
|---|
| 884 | + control->id.iface, control->id.device, |
|---|
| 885 | + control->id.subdevice, control->id.name, |
|---|
| 886 | + control->id.index, lval, lmin, lmax, i); |
|---|
| 887 | + return -EINVAL; |
|---|
| 888 | + } |
|---|
| 889 | + if (lstep) { |
|---|
| 890 | + div64_u64_rem(lval, lstep, &rem); |
|---|
| 891 | + if (rem) { |
|---|
| 892 | + dev_err(card->dev, |
|---|
| 893 | + "control %i:%i:%i:%s:%i: unaligned value %lld (step %lld) at count %i\n", |
|---|
| 894 | + control->id.iface, control->id.device, |
|---|
| 895 | + control->id.subdevice, control->id.name, |
|---|
| 896 | + control->id.index, lval, lstep, i); |
|---|
| 897 | + return -EINVAL; |
|---|
| 898 | + } |
|---|
| 899 | + } |
|---|
| 900 | + |
|---|
| 901 | + return 0; |
|---|
| 902 | +} |
|---|
| 903 | + |
|---|
| 904 | +/* perform sanity checks to the given snd_ctl_elem_value object */ |
|---|
| 905 | +static int sanity_check_elem_value(struct snd_card *card, |
|---|
| 906 | + const struct snd_ctl_elem_value *control, |
|---|
| 907 | + const struct snd_ctl_elem_info *info, |
|---|
| 908 | + u32 pattern) |
|---|
| 909 | +{ |
|---|
| 910 | + size_t offset; |
|---|
| 911 | + int i, ret = 0; |
|---|
| 912 | + u32 *p; |
|---|
| 913 | + |
|---|
| 914 | + switch (info->type) { |
|---|
| 915 | + case SNDRV_CTL_ELEM_TYPE_BOOLEAN: |
|---|
| 916 | + case SNDRV_CTL_ELEM_TYPE_INTEGER: |
|---|
| 917 | + case SNDRV_CTL_ELEM_TYPE_INTEGER64: |
|---|
| 918 | + case SNDRV_CTL_ELEM_TYPE_ENUMERATED: |
|---|
| 919 | + for (i = 0; i < info->count; i++) { |
|---|
| 920 | + ret = sanity_check_int_value(card, control, info, i); |
|---|
| 921 | + if (ret < 0) |
|---|
| 922 | + return ret; |
|---|
| 923 | + } |
|---|
| 924 | + break; |
|---|
| 925 | + default: |
|---|
| 926 | + break; |
|---|
| 927 | + } |
|---|
| 928 | + |
|---|
| 929 | + /* check whether the remaining area kept untouched */ |
|---|
| 930 | + offset = value_sizes[info->type] * info->count; |
|---|
| 931 | + offset = (offset + sizeof(u32) - 1) / sizeof(u32); |
|---|
| 932 | + p = (u32 *)control->value.bytes.data + offset; |
|---|
| 933 | + for (; offset < sizeof(control->value) / sizeof(u32); offset++, p++) { |
|---|
| 934 | + if (*p != pattern) { |
|---|
| 935 | + ret = -EINVAL; |
|---|
| 936 | + break; |
|---|
| 937 | + } |
|---|
| 938 | + *p = 0; /* clear the checked area */ |
|---|
| 939 | + } |
|---|
| 940 | + |
|---|
| 941 | + return ret; |
|---|
| 942 | +} |
|---|
| 943 | +#else |
|---|
| 944 | +static inline void fill_remaining_elem_value(struct snd_ctl_elem_value *control, |
|---|
| 945 | + struct snd_ctl_elem_info *info, |
|---|
| 946 | + u32 pattern) |
|---|
| 947 | +{ |
|---|
| 948 | +} |
|---|
| 949 | + |
|---|
| 950 | +static inline int sanity_check_elem_value(struct snd_card *card, |
|---|
| 951 | + struct snd_ctl_elem_value *control, |
|---|
| 952 | + struct snd_ctl_elem_info *info, |
|---|
| 953 | + u32 pattern) |
|---|
| 954 | +{ |
|---|
| 955 | + return 0; |
|---|
| 956 | +} |
|---|
| 957 | +#endif |
|---|
| 958 | + |
|---|
| 959 | +static int __snd_ctl_elem_info(struct snd_card *card, |
|---|
| 960 | + struct snd_kcontrol *kctl, |
|---|
| 961 | + struct snd_ctl_elem_info *info, |
|---|
| 962 | + struct snd_ctl_file *ctl) |
|---|
| 963 | +{ |
|---|
| 834 | 964 | struct snd_kcontrol_volatile *vd; |
|---|
| 835 | 965 | unsigned int index_offset; |
|---|
| 836 | 966 | int result; |
|---|
| 837 | 967 | |
|---|
| 838 | | - down_read(&card->controls_rwsem); |
|---|
| 839 | | - kctl = snd_ctl_find_id(card, &info->id); |
|---|
| 840 | | - if (kctl == NULL) { |
|---|
| 841 | | - up_read(&card->controls_rwsem); |
|---|
| 842 | | - return -ENOENT; |
|---|
| 843 | | - } |
|---|
| 844 | 968 | #ifdef CONFIG_SND_DEBUG |
|---|
| 845 | 969 | info->access = 0; |
|---|
| 846 | 970 | #endif |
|---|
| .. | .. |
|---|
| 859 | 983 | } else { |
|---|
| 860 | 984 | info->owner = -1; |
|---|
| 861 | 985 | } |
|---|
| 986 | + if (!snd_ctl_skip_validation(info) && |
|---|
| 987 | + snd_ctl_check_elem_info(card, info) < 0) |
|---|
| 988 | + result = -EINVAL; |
|---|
| 862 | 989 | } |
|---|
| 990 | + return result; |
|---|
| 991 | +} |
|---|
| 992 | + |
|---|
| 993 | +static int snd_ctl_elem_info(struct snd_ctl_file *ctl, |
|---|
| 994 | + struct snd_ctl_elem_info *info) |
|---|
| 995 | +{ |
|---|
| 996 | + struct snd_card *card = ctl->card; |
|---|
| 997 | + struct snd_kcontrol *kctl; |
|---|
| 998 | + int result; |
|---|
| 999 | + |
|---|
| 1000 | + down_read(&card->controls_rwsem); |
|---|
| 1001 | + kctl = snd_ctl_find_id(card, &info->id); |
|---|
| 1002 | + if (kctl == NULL) |
|---|
| 1003 | + result = -ENOENT; |
|---|
| 1004 | + else |
|---|
| 1005 | + result = __snd_ctl_elem_info(card, kctl, info, ctl); |
|---|
| 863 | 1006 | up_read(&card->controls_rwsem); |
|---|
| 864 | 1007 | return result; |
|---|
| 865 | 1008 | } |
|---|
| .. | .. |
|---|
| 878 | 1021 | result = snd_ctl_elem_info(ctl, &info); |
|---|
| 879 | 1022 | if (result < 0) |
|---|
| 880 | 1023 | return result; |
|---|
| 1024 | + /* drop internal access flags */ |
|---|
| 1025 | + info.access &= ~SNDRV_CTL_ELEM_ACCESS_SKIP_CHECK; |
|---|
| 881 | 1026 | if (copy_to_user(_info, &info, sizeof(info))) |
|---|
| 882 | 1027 | return -EFAULT; |
|---|
| 883 | 1028 | return result; |
|---|
| .. | .. |
|---|
| 889 | 1034 | struct snd_kcontrol *kctl; |
|---|
| 890 | 1035 | struct snd_kcontrol_volatile *vd; |
|---|
| 891 | 1036 | unsigned int index_offset; |
|---|
| 1037 | + struct snd_ctl_elem_info info; |
|---|
| 1038 | + const u32 pattern = 0xdeadbeef; |
|---|
| 1039 | + int ret; |
|---|
| 892 | 1040 | |
|---|
| 893 | 1041 | kctl = snd_ctl_find_id(card, &control->id); |
|---|
| 894 | 1042 | if (kctl == NULL) |
|---|
| .. | .. |
|---|
| 900 | 1048 | return -EPERM; |
|---|
| 901 | 1049 | |
|---|
| 902 | 1050 | snd_ctl_build_ioff(&control->id, kctl, index_offset); |
|---|
| 903 | | - return kctl->get(kctl, control); |
|---|
| 1051 | + |
|---|
| 1052 | +#ifdef CONFIG_SND_CTL_VALIDATION |
|---|
| 1053 | + /* info is needed only for validation */ |
|---|
| 1054 | + memset(&info, 0, sizeof(info)); |
|---|
| 1055 | + info.id = control->id; |
|---|
| 1056 | + ret = __snd_ctl_elem_info(card, kctl, &info, NULL); |
|---|
| 1057 | + if (ret < 0) |
|---|
| 1058 | + return ret; |
|---|
| 1059 | +#endif |
|---|
| 1060 | + |
|---|
| 1061 | + if (!snd_ctl_skip_validation(&info)) |
|---|
| 1062 | + fill_remaining_elem_value(control, &info, pattern); |
|---|
| 1063 | + ret = kctl->get(kctl, control); |
|---|
| 1064 | + if (ret < 0) |
|---|
| 1065 | + return ret; |
|---|
| 1066 | + if (!snd_ctl_skip_validation(&info) && |
|---|
| 1067 | + sanity_check_elem_value(card, control, &info, pattern) < 0) { |
|---|
| 1068 | + dev_err(card->dev, |
|---|
| 1069 | + "control %i:%i:%i:%s:%i: access overflow\n", |
|---|
| 1070 | + control->id.iface, control->id.device, |
|---|
| 1071 | + control->id.subdevice, control->id.name, |
|---|
| 1072 | + control->id.index); |
|---|
| 1073 | + return -EINVAL; |
|---|
| 1074 | + } |
|---|
| 1075 | + return ret; |
|---|
| 904 | 1076 | } |
|---|
| 905 | 1077 | |
|---|
| 906 | 1078 | static int snd_ctl_elem_read_user(struct snd_card *card, |
|---|
| .. | .. |
|---|
| 1241 | 1413 | static int snd_ctl_elem_add(struct snd_ctl_file *file, |
|---|
| 1242 | 1414 | struct snd_ctl_elem_info *info, int replace) |
|---|
| 1243 | 1415 | { |
|---|
| 1244 | | - /* The capacity of struct snd_ctl_elem_value.value.*/ |
|---|
| 1245 | | - static const unsigned int value_sizes[] = { |
|---|
| 1246 | | - [SNDRV_CTL_ELEM_TYPE_BOOLEAN] = sizeof(long), |
|---|
| 1247 | | - [SNDRV_CTL_ELEM_TYPE_INTEGER] = sizeof(long), |
|---|
| 1248 | | - [SNDRV_CTL_ELEM_TYPE_ENUMERATED] = sizeof(unsigned int), |
|---|
| 1249 | | - [SNDRV_CTL_ELEM_TYPE_BYTES] = sizeof(unsigned char), |
|---|
| 1250 | | - [SNDRV_CTL_ELEM_TYPE_IEC958] = sizeof(struct snd_aes_iec958), |
|---|
| 1251 | | - [SNDRV_CTL_ELEM_TYPE_INTEGER64] = sizeof(long long), |
|---|
| 1252 | | - }; |
|---|
| 1253 | | - static const unsigned int max_value_counts[] = { |
|---|
| 1254 | | - [SNDRV_CTL_ELEM_TYPE_BOOLEAN] = 128, |
|---|
| 1255 | | - [SNDRV_CTL_ELEM_TYPE_INTEGER] = 128, |
|---|
| 1256 | | - [SNDRV_CTL_ELEM_TYPE_ENUMERATED] = 128, |
|---|
| 1257 | | - [SNDRV_CTL_ELEM_TYPE_BYTES] = 512, |
|---|
| 1258 | | - [SNDRV_CTL_ELEM_TYPE_IEC958] = 1, |
|---|
| 1259 | | - [SNDRV_CTL_ELEM_TYPE_INTEGER64] = 64, |
|---|
| 1260 | | - }; |
|---|
| 1261 | 1416 | struct snd_card *card = file->card; |
|---|
| 1262 | 1417 | struct snd_kcontrol *kctl; |
|---|
| 1263 | 1418 | unsigned int count; |
|---|
| .. | .. |
|---|
| 1309 | 1464 | * Check information and calculate the size of data specific to |
|---|
| 1310 | 1465 | * this userspace control. |
|---|
| 1311 | 1466 | */ |
|---|
| 1312 | | - if (info->type < SNDRV_CTL_ELEM_TYPE_BOOLEAN || |
|---|
| 1313 | | - info->type > SNDRV_CTL_ELEM_TYPE_INTEGER64) |
|---|
| 1314 | | - return -EINVAL; |
|---|
| 1315 | | - if (info->type == SNDRV_CTL_ELEM_TYPE_ENUMERATED && |
|---|
| 1316 | | - info->value.enumerated.items == 0) |
|---|
| 1317 | | - return -EINVAL; |
|---|
| 1318 | | - if (info->count < 1 || |
|---|
| 1319 | | - info->count > max_value_counts[info->type]) |
|---|
| 1320 | | - return -EINVAL; |
|---|
| 1321 | | - if (!validate_element_member_dimension(info)) |
|---|
| 1467 | + /* pass NULL to card for suppressing error messages */ |
|---|
| 1468 | + err = snd_ctl_check_elem_info(NULL, info); |
|---|
| 1469 | + if (err < 0) |
|---|
| 1470 | + return err; |
|---|
| 1471 | + /* user-space control doesn't allow zero-size data */ |
|---|
| 1472 | + if (info->count < 1) |
|---|
| 1322 | 1473 | return -EINVAL; |
|---|
| 1323 | 1474 | private_size = value_sizes[info->type] * info->count; |
|---|
| 1324 | 1475 | |
|---|
| .. | .. |
|---|
| 1369 | 1520 | |
|---|
| 1370 | 1521 | /* This function manage to free the instance on failure. */ |
|---|
| 1371 | 1522 | down_write(&card->controls_rwsem); |
|---|
| 1372 | | - err = __snd_ctl_add(card, kctl); |
|---|
| 1523 | + err = __snd_ctl_add_replace(card, kctl, CTL_ADD_EXCLUSIVE); |
|---|
| 1373 | 1524 | if (err < 0) { |
|---|
| 1374 | 1525 | snd_ctl_free_one(kctl); |
|---|
| 1375 | 1526 | goto unlock; |
|---|
| .. | .. |
|---|
| 1564 | 1715 | case SNDRV_CTL_IOCTL_CARD_INFO: |
|---|
| 1565 | 1716 | return snd_ctl_card_info(card, ctl, cmd, argp); |
|---|
| 1566 | 1717 | case SNDRV_CTL_IOCTL_ELEM_LIST: |
|---|
| 1567 | | - return snd_ctl_elem_list(card, argp); |
|---|
| 1718 | + return snd_ctl_elem_list_user(card, argp); |
|---|
| 1568 | 1719 | case SNDRV_CTL_IOCTL_ELEM_INFO: |
|---|
| 1569 | 1720 | return snd_ctl_elem_info_user(ctl, argp); |
|---|
| 1570 | 1721 | case SNDRV_CTL_IOCTL_ELEM_READ: |
|---|
| .. | .. |
|---|
| 1774 | 1925 | |
|---|
| 1775 | 1926 | #ifdef CONFIG_COMPAT |
|---|
| 1776 | 1927 | /** |
|---|
| 1777 | | - * snd_ctl_unregister_ioctl - de-register the device-specific compat 32bit |
|---|
| 1778 | | - * control-ioctls |
|---|
| 1928 | + * snd_ctl_unregister_ioctl_compat - de-register the device-specific compat |
|---|
| 1929 | + * 32bit control-ioctls |
|---|
| 1779 | 1930 | * @fcn: ioctl callback function to unregister |
|---|
| 1780 | 1931 | */ |
|---|
| 1781 | 1932 | int snd_ctl_unregister_ioctl_compat(snd_kctl_ioctl_func_t fcn) |
|---|
| .. | .. |
|---|
| 1800 | 1951 | { |
|---|
| 1801 | 1952 | struct snd_ctl_file *kctl; |
|---|
| 1802 | 1953 | int subdevice = -1; |
|---|
| 1954 | + unsigned long flags; |
|---|
| 1803 | 1955 | |
|---|
| 1804 | | - read_lock(&card->ctl_files_rwlock); |
|---|
| 1956 | + read_lock_irqsave(&card->ctl_files_rwlock, flags); |
|---|
| 1805 | 1957 | list_for_each_entry(kctl, &card->ctl_files, list) { |
|---|
| 1806 | 1958 | if (kctl->pid == task_pid(current)) { |
|---|
| 1807 | 1959 | subdevice = kctl->preferred_subdevice[type]; |
|---|
| .. | .. |
|---|
| 1809 | 1961 | break; |
|---|
| 1810 | 1962 | } |
|---|
| 1811 | 1963 | } |
|---|
| 1812 | | - read_unlock(&card->ctl_files_rwlock); |
|---|
| 1964 | + read_unlock_irqrestore(&card->ctl_files_rwlock, flags); |
|---|
| 1813 | 1965 | return subdevice; |
|---|
| 1814 | 1966 | } |
|---|
| 1815 | 1967 | EXPORT_SYMBOL_GPL(snd_ctl_get_preferred_subdevice); |
|---|
| .. | .. |
|---|
| 1858 | 2010 | { |
|---|
| 1859 | 2011 | struct snd_card *card = device->device_data; |
|---|
| 1860 | 2012 | struct snd_ctl_file *ctl; |
|---|
| 2013 | + unsigned long flags; |
|---|
| 1861 | 2014 | |
|---|
| 1862 | | - read_lock(&card->ctl_files_rwlock); |
|---|
| 2015 | + read_lock_irqsave(&card->ctl_files_rwlock, flags); |
|---|
| 1863 | 2016 | list_for_each_entry(ctl, &card->ctl_files, list) { |
|---|
| 1864 | 2017 | wake_up(&ctl->change_sleep); |
|---|
| 1865 | 2018 | kill_fasync(&ctl->fasync, SIGIO, POLL_ERR); |
|---|
| 1866 | 2019 | } |
|---|
| 1867 | | - read_unlock(&card->ctl_files_rwlock); |
|---|
| 2020 | + read_unlock_irqrestore(&card->ctl_files_rwlock, flags); |
|---|
| 1868 | 2021 | |
|---|
| 1869 | 2022 | return snd_unregister_device(&card->ctl_dev); |
|---|
| 1870 | 2023 | } |
|---|
| .. | .. |
|---|
| 1893 | 2046 | */ |
|---|
| 1894 | 2047 | int snd_ctl_create(struct snd_card *card) |
|---|
| 1895 | 2048 | { |
|---|
| 1896 | | - static struct snd_device_ops ops = { |
|---|
| 2049 | + static const struct snd_device_ops ops = { |
|---|
| 1897 | 2050 | .dev_free = snd_ctl_dev_free, |
|---|
| 1898 | 2051 | .dev_register = snd_ctl_dev_register, |
|---|
| 1899 | 2052 | .dev_disconnect = snd_ctl_dev_disconnect, |
|---|