.. | .. |
---|
| 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, |
---|