diff options
Diffstat (limited to 'sound/pci')
-rw-r--r-- | sound/pci/emu10k1/emufx.c | 100 |
1 files changed, 79 insertions, 21 deletions
diff --git a/sound/pci/emu10k1/emufx.c b/sound/pci/emu10k1/emufx.c index d8e8db89535f..7b173c496953 100644 --- a/sound/pci/emu10k1/emufx.c +++ b/sound/pci/emu10k1/emufx.c | |||
@@ -655,13 +655,66 @@ snd_emu10k1_look_for_ctl(struct snd_emu10k1 *emu, struct snd_ctl_elem_id *id) | |||
655 | return NULL; | 655 | return NULL; |
656 | } | 656 | } |
657 | 657 | ||
658 | #define MAX_TLV_SIZE 256 | ||
659 | |||
660 | static unsigned int *copy_tlv(unsigned int __user *_tlv) | ||
661 | { | ||
662 | unsigned int data[2]; | ||
663 | unsigned int *tlv; | ||
664 | |||
665 | if (!_tlv) | ||
666 | return NULL; | ||
667 | if (copy_from_user(data, _tlv, sizeof(data))) | ||
668 | return NULL; | ||
669 | if (data[1] >= MAX_TLV_SIZE) | ||
670 | return NULL; | ||
671 | tlv = kmalloc(data[1] * 4 + sizeof(data), GFP_KERNEL); | ||
672 | if (!tlv) | ||
673 | return NULL; | ||
674 | memcpy(tlv, data, sizeof(data)); | ||
675 | if (copy_from_user(tlv + 2, _tlv + 2, data[1])) { | ||
676 | kfree(tlv); | ||
677 | return NULL; | ||
678 | } | ||
679 | return tlv; | ||
680 | } | ||
681 | |||
682 | static int copy_gctl(struct snd_emu10k1 *emu, | ||
683 | struct snd_emu10k1_fx8010_control_gpr *gctl, | ||
684 | struct snd_emu10k1_fx8010_control_gpr __user *_gctl, | ||
685 | int idx) | ||
686 | { | ||
687 | struct snd_emu10k1_fx8010_control_old_gpr __user *octl; | ||
688 | |||
689 | if (emu->support_tlv) | ||
690 | return copy_from_user(gctl, &_gctl[idx], sizeof(*gctl)); | ||
691 | octl = (struct snd_emu10k1_fx8010_control_old_gpr __user *)_gctl; | ||
692 | if (copy_from_user(gctl, &octl[idx], sizeof(*octl))) | ||
693 | return -EFAULT; | ||
694 | gctl->tlv = NULL; | ||
695 | return 0; | ||
696 | } | ||
697 | |||
698 | static int copy_gctl_to_user(struct snd_emu10k1 *emu, | ||
699 | struct snd_emu10k1_fx8010_control_gpr __user *_gctl, | ||
700 | struct snd_emu10k1_fx8010_control_gpr *gctl, | ||
701 | int idx) | ||
702 | { | ||
703 | struct snd_emu10k1_fx8010_control_old_gpr __user *octl; | ||
704 | |||
705 | if (emu->support_tlv) | ||
706 | return copy_to_user(&_gctl[idx], gctl, sizeof(*gctl)); | ||
707 | |||
708 | octl = (struct snd_emu10k1_fx8010_control_old_gpr __user *)_gctl; | ||
709 | return copy_to_user(&octl[idx], gctl, sizeof(*octl)); | ||
710 | } | ||
711 | |||
658 | static int snd_emu10k1_verify_controls(struct snd_emu10k1 *emu, | 712 | static int snd_emu10k1_verify_controls(struct snd_emu10k1 *emu, |
659 | struct snd_emu10k1_fx8010_code *icode) | 713 | struct snd_emu10k1_fx8010_code *icode) |
660 | { | 714 | { |
661 | unsigned int i; | 715 | unsigned int i; |
662 | struct snd_ctl_elem_id __user *_id; | 716 | struct snd_ctl_elem_id __user *_id; |
663 | struct snd_ctl_elem_id id; | 717 | struct snd_ctl_elem_id id; |
664 | struct snd_emu10k1_fx8010_control_gpr __user *_gctl; | ||
665 | struct snd_emu10k1_fx8010_control_gpr *gctl; | 718 | struct snd_emu10k1_fx8010_control_gpr *gctl; |
666 | int err; | 719 | int err; |
667 | 720 | ||
@@ -676,9 +729,8 @@ static int snd_emu10k1_verify_controls(struct snd_emu10k1 *emu, | |||
676 | if (! gctl) | 729 | if (! gctl) |
677 | return -ENOMEM; | 730 | return -ENOMEM; |
678 | err = 0; | 731 | err = 0; |
679 | for (i = 0, _gctl = icode->gpr_add_controls; | 732 | for (i = 0; i < icode->gpr_add_control_count; i++) { |
680 | i < icode->gpr_add_control_count; i++, _gctl++) { | 733 | if (copy_gctl(emu, gctl, icode->gpr_add_controls, i)) { |
681 | if (copy_from_user(gctl, _gctl, sizeof(*gctl))) { | ||
682 | err = -EFAULT; | 734 | err = -EFAULT; |
683 | goto __error; | 735 | goto __error; |
684 | } | 736 | } |
@@ -697,10 +749,9 @@ static int snd_emu10k1_verify_controls(struct snd_emu10k1 *emu, | |||
697 | goto __error; | 749 | goto __error; |
698 | } | 750 | } |
699 | } | 751 | } |
700 | for (i = 0, _gctl = icode->gpr_list_controls; | 752 | for (i = 0; i < icode->gpr_list_control_count; i++) { |
701 | i < icode->gpr_list_control_count; i++, _gctl++) { | ||
702 | /* FIXME: we need to check the WRITE access */ | 753 | /* FIXME: we need to check the WRITE access */ |
703 | if (copy_from_user(gctl, _gctl, sizeof(*gctl))) { | 754 | if (copy_gctl(emu, gctl, icode->gpr_list_controls, i)) { |
704 | err = -EFAULT; | 755 | err = -EFAULT; |
705 | goto __error; | 756 | goto __error; |
706 | } | 757 | } |
@@ -718,13 +769,14 @@ static void snd_emu10k1_ctl_private_free(struct snd_kcontrol *kctl) | |||
718 | kctl->private_value = 0; | 769 | kctl->private_value = 0; |
719 | list_del(&ctl->list); | 770 | list_del(&ctl->list); |
720 | kfree(ctl); | 771 | kfree(ctl); |
772 | if (kctl->tlv.p) | ||
773 | kfree(kctl->tlv.p); | ||
721 | } | 774 | } |
722 | 775 | ||
723 | static int snd_emu10k1_add_controls(struct snd_emu10k1 *emu, | 776 | static int snd_emu10k1_add_controls(struct snd_emu10k1 *emu, |
724 | struct snd_emu10k1_fx8010_code *icode) | 777 | struct snd_emu10k1_fx8010_code *icode) |
725 | { | 778 | { |
726 | unsigned int i, j; | 779 | unsigned int i, j; |
727 | struct snd_emu10k1_fx8010_control_gpr __user *_gctl; | ||
728 | struct snd_emu10k1_fx8010_control_gpr *gctl; | 780 | struct snd_emu10k1_fx8010_control_gpr *gctl; |
729 | struct snd_emu10k1_fx8010_ctl *ctl, *nctl; | 781 | struct snd_emu10k1_fx8010_ctl *ctl, *nctl; |
730 | struct snd_kcontrol_new knew; | 782 | struct snd_kcontrol_new knew; |
@@ -740,9 +792,8 @@ static int snd_emu10k1_add_controls(struct snd_emu10k1 *emu, | |||
740 | goto __error; | 792 | goto __error; |
741 | } | 793 | } |
742 | 794 | ||
743 | for (i = 0, _gctl = icode->gpr_add_controls; | 795 | for (i = 0; i < icode->gpr_add_control_count; i++) { |
744 | i < icode->gpr_add_control_count; i++, _gctl++) { | 796 | if (copy_gctl(emu, gctl, icode->gpr_add_controls, i)) { |
745 | if (copy_from_user(gctl, _gctl, sizeof(*gctl))) { | ||
746 | err = -EFAULT; | 797 | err = -EFAULT; |
747 | goto __error; | 798 | goto __error; |
748 | } | 799 | } |
@@ -763,11 +814,10 @@ static int snd_emu10k1_add_controls(struct snd_emu10k1 *emu, | |||
763 | knew.device = gctl->id.device; | 814 | knew.device = gctl->id.device; |
764 | knew.subdevice = gctl->id.subdevice; | 815 | knew.subdevice = gctl->id.subdevice; |
765 | knew.info = snd_emu10k1_gpr_ctl_info; | 816 | knew.info = snd_emu10k1_gpr_ctl_info; |
766 | if (gctl->tlv.p) { | 817 | knew.tlv.p = copy_tlv(gctl->tlv); |
767 | knew.tlv.p = gctl->tlv.p; | 818 | if (knew.tlv.p) |
768 | knew.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | | 819 | knew.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | |
769 | SNDRV_CTL_ELEM_ACCESS_TLV_READ; | 820 | SNDRV_CTL_ELEM_ACCESS_TLV_READ; |
770 | } | ||
771 | knew.get = snd_emu10k1_gpr_ctl_get; | 821 | knew.get = snd_emu10k1_gpr_ctl_get; |
772 | knew.put = snd_emu10k1_gpr_ctl_put; | 822 | knew.put = snd_emu10k1_gpr_ctl_put; |
773 | memset(nctl, 0, sizeof(*nctl)); | 823 | memset(nctl, 0, sizeof(*nctl)); |
@@ -785,12 +835,14 @@ static int snd_emu10k1_add_controls(struct snd_emu10k1 *emu, | |||
785 | ctl = kmalloc(sizeof(*ctl), GFP_KERNEL); | 835 | ctl = kmalloc(sizeof(*ctl), GFP_KERNEL); |
786 | if (ctl == NULL) { | 836 | if (ctl == NULL) { |
787 | err = -ENOMEM; | 837 | err = -ENOMEM; |
838 | kfree(knew.tlv.p); | ||
788 | goto __error; | 839 | goto __error; |
789 | } | 840 | } |
790 | knew.private_value = (unsigned long)ctl; | 841 | knew.private_value = (unsigned long)ctl; |
791 | *ctl = *nctl; | 842 | *ctl = *nctl; |
792 | if ((err = snd_ctl_add(emu->card, kctl = snd_ctl_new1(&knew, emu))) < 0) { | 843 | if ((err = snd_ctl_add(emu->card, kctl = snd_ctl_new1(&knew, emu))) < 0) { |
793 | kfree(ctl); | 844 | kfree(ctl); |
845 | kfree(knew.tlv.p); | ||
794 | goto __error; | 846 | goto __error; |
795 | } | 847 | } |
796 | kctl->private_free = snd_emu10k1_ctl_private_free; | 848 | kctl->private_free = snd_emu10k1_ctl_private_free; |
@@ -841,7 +893,6 @@ static int snd_emu10k1_list_controls(struct snd_emu10k1 *emu, | |||
841 | unsigned int i = 0, j; | 893 | unsigned int i = 0, j; |
842 | unsigned int total = 0; | 894 | unsigned int total = 0; |
843 | struct snd_emu10k1_fx8010_control_gpr *gctl; | 895 | struct snd_emu10k1_fx8010_control_gpr *gctl; |
844 | struct snd_emu10k1_fx8010_control_gpr __user *_gctl; | ||
845 | struct snd_emu10k1_fx8010_ctl *ctl; | 896 | struct snd_emu10k1_fx8010_ctl *ctl; |
846 | struct snd_ctl_elem_id *id; | 897 | struct snd_ctl_elem_id *id; |
847 | struct list_head *list; | 898 | struct list_head *list; |
@@ -850,11 +901,11 @@ static int snd_emu10k1_list_controls(struct snd_emu10k1 *emu, | |||
850 | if (! gctl) | 901 | if (! gctl) |
851 | return -ENOMEM; | 902 | return -ENOMEM; |
852 | 903 | ||
853 | _gctl = icode->gpr_list_controls; | ||
854 | list_for_each(list, &emu->fx8010.gpr_ctl) { | 904 | list_for_each(list, &emu->fx8010.gpr_ctl) { |
855 | ctl = emu10k1_gpr_ctl(list); | 905 | ctl = emu10k1_gpr_ctl(list); |
856 | total++; | 906 | total++; |
857 | if (_gctl && i < icode->gpr_list_control_count) { | 907 | if (icode->gpr_list_controls && |
908 | i < icode->gpr_list_control_count) { | ||
858 | memset(gctl, 0, sizeof(*gctl)); | 909 | memset(gctl, 0, sizeof(*gctl)); |
859 | id = &ctl->kcontrol->id; | 910 | id = &ctl->kcontrol->id; |
860 | gctl->id.iface = id->iface; | 911 | gctl->id.iface = id->iface; |
@@ -871,11 +922,11 @@ static int snd_emu10k1_list_controls(struct snd_emu10k1 *emu, | |||
871 | gctl->min = ctl->min; | 922 | gctl->min = ctl->min; |
872 | gctl->max = ctl->max; | 923 | gctl->max = ctl->max; |
873 | gctl->translation = ctl->translation; | 924 | gctl->translation = ctl->translation; |
874 | if (copy_to_user(_gctl, gctl, sizeof(*gctl))) { | 925 | if (copy_gctl_to_user(emu, icode->gpr_list_controls, |
926 | gctl, i)) { | ||
875 | kfree(gctl); | 927 | kfree(gctl); |
876 | return -EFAULT; | 928 | return -EFAULT; |
877 | } | 929 | } |
878 | _gctl++; | ||
879 | i++; | 930 | i++; |
880 | } | 931 | } |
881 | } | 932 | } |
@@ -1026,7 +1077,7 @@ snd_emu10k1_init_mono_control(struct snd_emu10k1_fx8010_control_gpr *ctl, | |||
1026 | ctl->gpr[0] = gpr + 0; ctl->value[0] = defval; | 1077 | ctl->gpr[0] = gpr + 0; ctl->value[0] = defval; |
1027 | ctl->min = 0; | 1078 | ctl->min = 0; |
1028 | ctl->max = 100; | 1079 | ctl->max = 100; |
1029 | ctl->tlv.p = snd_emu10k1_db_scale1; | 1080 | ctl->tlv = snd_emu10k1_db_scale1; |
1030 | ctl->translation = EMU10K1_GPR_TRANSLATION_TABLE100; | 1081 | ctl->translation = EMU10K1_GPR_TRANSLATION_TABLE100; |
1031 | } | 1082 | } |
1032 | 1083 | ||
@@ -1041,7 +1092,7 @@ snd_emu10k1_init_stereo_control(struct snd_emu10k1_fx8010_control_gpr *ctl, | |||
1041 | ctl->gpr[1] = gpr + 1; ctl->value[1] = defval; | 1092 | ctl->gpr[1] = gpr + 1; ctl->value[1] = defval; |
1042 | ctl->min = 0; | 1093 | ctl->min = 0; |
1043 | ctl->max = 100; | 1094 | ctl->max = 100; |
1044 | ctl->tlv.p = snd_emu10k1_db_scale1; | 1095 | ctl->tlv = snd_emu10k1_db_scale1; |
1045 | ctl->translation = EMU10K1_GPR_TRANSLATION_TABLE100; | 1096 | ctl->translation = EMU10K1_GPR_TRANSLATION_TABLE100; |
1046 | } | 1097 | } |
1047 | 1098 | ||
@@ -1566,7 +1617,9 @@ A_OP(icode, &ptr, iMAC0, A_GPR(var), A_GPR(var), A_GPR(vol), A_EXTIN(input)) | |||
1566 | seg = snd_enter_user(); | 1617 | seg = snd_enter_user(); |
1567 | icode->gpr_add_control_count = nctl; | 1618 | icode->gpr_add_control_count = nctl; |
1568 | icode->gpr_add_controls = (struct snd_emu10k1_fx8010_control_gpr __user *)controls; | 1619 | icode->gpr_add_controls = (struct snd_emu10k1_fx8010_control_gpr __user *)controls; |
1620 | emu->support_tlv = 1; /* support TLV */ | ||
1569 | err = snd_emu10k1_icode_poke(emu, icode); | 1621 | err = snd_emu10k1_icode_poke(emu, icode); |
1622 | emu->support_tlv = 0; /* clear again */ | ||
1570 | snd_leave_user(seg); | 1623 | snd_leave_user(seg); |
1571 | 1624 | ||
1572 | __err: | 1625 | __err: |
@@ -2183,7 +2236,9 @@ static int __devinit _snd_emu10k1_init_efx(struct snd_emu10k1 *emu) | |||
2183 | seg = snd_enter_user(); | 2236 | seg = snd_enter_user(); |
2184 | icode->gpr_add_control_count = i; | 2237 | icode->gpr_add_control_count = i; |
2185 | icode->gpr_add_controls = (struct snd_emu10k1_fx8010_control_gpr __user *)controls; | 2238 | icode->gpr_add_controls = (struct snd_emu10k1_fx8010_control_gpr __user *)controls; |
2239 | emu->support_tlv = 1; /* support TLV */ | ||
2186 | err = snd_emu10k1_icode_poke(emu, icode); | 2240 | err = snd_emu10k1_icode_poke(emu, icode); |
2241 | emu->support_tlv = 0; /* clear again */ | ||
2187 | snd_leave_user(seg); | 2242 | snd_leave_user(seg); |
2188 | if (err >= 0) | 2243 | if (err >= 0) |
2189 | err = snd_emu10k1_ipcm_poke(emu, ipcm); | 2244 | err = snd_emu10k1_ipcm_poke(emu, ipcm); |
@@ -2327,6 +2382,9 @@ static int snd_emu10k1_fx8010_ioctl(struct snd_hwdep * hw, struct file *file, un | |||
2327 | int res; | 2382 | int res; |
2328 | 2383 | ||
2329 | switch (cmd) { | 2384 | switch (cmd) { |
2385 | case SNDRV_EMU10K1_IOCTL_PVERSION: | ||
2386 | emu->support_tlv = 1; | ||
2387 | return put_user(SNDRV_EMU10K1_VERSION, (int __user *)argp); | ||
2330 | case SNDRV_EMU10K1_IOCTL_INFO: | 2388 | case SNDRV_EMU10K1_IOCTL_INFO: |
2331 | info = kmalloc(sizeof(*info), GFP_KERNEL); | 2389 | info = kmalloc(sizeof(*info), GFP_KERNEL); |
2332 | if (!info) | 2390 | if (!info) |