aboutsummaryrefslogtreecommitdiffstats
path: root/sound/core/control.c
diff options
context:
space:
mode:
authorJaroslav Kysela <perex@suse.cz>2006-07-05 11:34:51 -0400
committerJaroslav Kysela <perex@suse.cz>2006-09-23 04:37:26 -0400
commit8aa9b586e42099817163aba01d925c2660c4dbbe (patch)
treeb70172eafcb672074fda1858c7a9c5779a1132f8 /sound/core/control.c
parent6bbe13ecbbce4415a5a7959b3bc35b18313025e0 (diff)
[ALSA] Control API - more robust TLV implementation
- added callback option - added READ/WRITE/COMMAND flags to access member - added WRITE/COMMAND ioctls - added SNDRV_CTL_EVENT_MASK_TLV for TLV change notifications - added TLV support to ELEM_ADD ioctl Signed-off-by: Jaroslav Kysela <perex@suse.cz>
Diffstat (limited to 'sound/core/control.c')
-rw-r--r--sound/core/control.c139
1 files changed, 109 insertions, 30 deletions
diff --git a/sound/core/control.c b/sound/core/control.c
index f0c7272a2d48..31ad58154c06 100644
--- a/sound/core/control.c
+++ b/sound/core/control.c
@@ -236,12 +236,16 @@ struct snd_kcontrol *snd_ctl_new1(const struct snd_kcontrol_new *ncontrol,
236 kctl.id.index = ncontrol->index; 236 kctl.id.index = ncontrol->index;
237 kctl.count = ncontrol->count ? ncontrol->count : 1; 237 kctl.count = ncontrol->count ? ncontrol->count : 1;
238 access = ncontrol->access == 0 ? SNDRV_CTL_ELEM_ACCESS_READWRITE : 238 access = ncontrol->access == 0 ? SNDRV_CTL_ELEM_ACCESS_READWRITE :
239 (ncontrol->access & (SNDRV_CTL_ELEM_ACCESS_READWRITE|SNDRV_CTL_ELEM_ACCESS_INACTIVE| 239 (ncontrol->access & (SNDRV_CTL_ELEM_ACCESS_READWRITE|
240 SNDRV_CTL_ELEM_ACCESS_DINDIRECT|SNDRV_CTL_ELEM_ACCESS_INDIRECT)); 240 SNDRV_CTL_ELEM_ACCESS_INACTIVE|
241 SNDRV_CTL_ELEM_ACCESS_DINDIRECT|
242 SNDRV_CTL_ELEM_ACCESS_INDIRECT|
243 SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE|
244 SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK));
241 kctl.info = ncontrol->info; 245 kctl.info = ncontrol->info;
242 kctl.get = ncontrol->get; 246 kctl.get = ncontrol->get;
243 kctl.put = ncontrol->put; 247 kctl.put = ncontrol->put;
244 kctl.tlv = ncontrol->tlv; 248 kctl.tlv.p = ncontrol->tlv.p;
245 kctl.private_value = ncontrol->private_value; 249 kctl.private_value = ncontrol->private_value;
246 kctl.private_data = private_data; 250 kctl.private_data = private_data;
247 return snd_ctl_new(&kctl, access); 251 return snd_ctl_new(&kctl, access);
@@ -883,6 +887,8 @@ struct user_element {
883 struct snd_ctl_elem_info info; 887 struct snd_ctl_elem_info info;
884 void *elem_data; /* element data */ 888 void *elem_data; /* element data */
885 unsigned long elem_data_size; /* size of element data in bytes */ 889 unsigned long elem_data_size; /* size of element data in bytes */
890 void *tlv_data; /* TLV data */
891 unsigned long tlv_data_size; /* TLV data size */
886 void *priv_data; /* private data (like strings for enumerated type) */ 892 void *priv_data; /* private data (like strings for enumerated type) */
887 unsigned long priv_data_size; /* size of private data in bytes */ 893 unsigned long priv_data_size; /* size of private data in bytes */
888}; 894};
@@ -917,9 +923,46 @@ static int snd_ctl_elem_user_put(struct snd_kcontrol *kcontrol,
917 return change; 923 return change;
918} 924}
919 925
926static int snd_ctl_elem_user_tlv(struct snd_kcontrol *kcontrol,
927 int op_flag,
928 unsigned int size,
929 unsigned int __user *tlv)
930{
931 struct user_element *ue = kcontrol->private_data;
932 int change = 0;
933 void *new_data;
934
935 if (op_flag > 0) {
936 if (size > 1024 * 128) /* sane value */
937 return -EINVAL;
938 new_data = kmalloc(size, GFP_KERNEL);
939 if (new_data == NULL)
940 return -ENOMEM;
941 if (copy_from_user(new_data, tlv, size)) {
942 kfree(new_data);
943 return -EFAULT;
944 }
945 change = ue->tlv_data_size != size;
946 if (!change)
947 change = memcmp(ue->tlv_data, new_data, size);
948 kfree(ue->tlv_data);
949 ue->tlv_data = new_data;
950 ue->tlv_data_size = size;
951 } else {
952 if (size < ue->tlv_data_size)
953 return -ENOSPC;
954 if (copy_to_user(tlv, ue->tlv_data, ue->tlv_data_size))
955 return -EFAULT;
956 }
957 return change;
958}
959
920static void snd_ctl_elem_user_free(struct snd_kcontrol *kcontrol) 960static void snd_ctl_elem_user_free(struct snd_kcontrol *kcontrol)
921{ 961{
922 kfree(kcontrol->private_data); 962 struct user_element *ue = kcontrol->private_data;
963 if (ue->tlv_data)
964 kfree(ue->tlv_data);
965 kfree(ue);
923} 966}
924 967
925static int snd_ctl_elem_add(struct snd_ctl_file *file, 968static int snd_ctl_elem_add(struct snd_ctl_file *file,
@@ -938,7 +981,8 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file,
938 return -EINVAL; 981 return -EINVAL;
939 access = info->access == 0 ? SNDRV_CTL_ELEM_ACCESS_READWRITE : 982 access = info->access == 0 ? SNDRV_CTL_ELEM_ACCESS_READWRITE :
940 (info->access & (SNDRV_CTL_ELEM_ACCESS_READWRITE| 983 (info->access & (SNDRV_CTL_ELEM_ACCESS_READWRITE|
941 SNDRV_CTL_ELEM_ACCESS_INACTIVE)); 984 SNDRV_CTL_ELEM_ACCESS_INACTIVE|
985 SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE));
942 info->id.numid = 0; 986 info->id.numid = 0;
943 memset(&kctl, 0, sizeof(kctl)); 987 memset(&kctl, 0, sizeof(kctl));
944 down_write(&card->controls_rwsem); 988 down_write(&card->controls_rwsem);
@@ -964,6 +1008,10 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file,
964 kctl.get = snd_ctl_elem_user_get; 1008 kctl.get = snd_ctl_elem_user_get;
965 if (access & SNDRV_CTL_ELEM_ACCESS_WRITE) 1009 if (access & SNDRV_CTL_ELEM_ACCESS_WRITE)
966 kctl.put = snd_ctl_elem_user_put; 1010 kctl.put = snd_ctl_elem_user_put;
1011 if (access & SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE) {
1012 kctl.tlv.c = snd_ctl_elem_user_tlv;
1013 access |= SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK;
1014 }
967 switch (info->type) { 1015 switch (info->type) {
968 case SNDRV_CTL_ELEM_TYPE_BOOLEAN: 1016 case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
969 private_size = sizeof(char); 1017 private_size = sizeof(char);
@@ -1068,38 +1116,65 @@ static int snd_ctl_subscribe_events(struct snd_ctl_file *file, int __user *ptr)
1068 return 0; 1116 return 0;
1069} 1117}
1070 1118
1071static int snd_ctl_tlv_read(struct snd_card *card, 1119static int snd_ctl_tlv_ioctl(struct snd_ctl_file *file,
1072 struct snd_ctl_tlv __user *_tlv) 1120 struct snd_ctl_tlv __user *_tlv,
1121 int op_flag)
1073{ 1122{
1123 struct snd_card *card = file->card;
1074 struct snd_ctl_tlv tlv; 1124 struct snd_ctl_tlv tlv;
1075 struct snd_kcontrol *kctl; 1125 struct snd_kcontrol *kctl;
1126 struct snd_kcontrol_volatile *vd;
1076 unsigned int len; 1127 unsigned int len;
1077 int err = 0; 1128 int err = 0;
1078 1129
1079 if (copy_from_user(&tlv, _tlv, sizeof(tlv))) 1130 if (copy_from_user(&tlv, _tlv, sizeof(tlv)))
1080 return -EFAULT; 1131 return -EFAULT;
1081 if (tlv.length < sizeof(unsigned int) * 3) 1132 if (tlv.length < sizeof(unsigned int) * 3)
1082 return -EINVAL; 1133 return -EINVAL;
1083 down_read(&card->controls_rwsem); 1134 down_read(&card->controls_rwsem);
1084 kctl = snd_ctl_find_numid(card, tlv.numid); 1135 kctl = snd_ctl_find_numid(card, tlv.numid);
1085 if (kctl == NULL) { 1136 if (kctl == NULL) {
1086 err = -ENOENT; 1137 err = -ENOENT;
1087 goto __kctl_end; 1138 goto __kctl_end;
1088 } 1139 }
1089 if (kctl->tlv == NULL) { 1140 if (kctl->tlv.p == NULL) {
1090 err = -ENXIO; 1141 err = -ENXIO;
1091 goto __kctl_end; 1142 goto __kctl_end;
1092 } 1143 }
1093 len = kctl->tlv[1] + 2 * sizeof(unsigned int); 1144 vd = &kctl->vd[tlv.numid - kctl->id.numid];
1094 if (tlv.length < len) { 1145 if ((op_flag == 0 && (vd->access & SNDRV_CTL_ELEM_ACCESS_TLV_READ) == 0) ||
1095 err = -ENOMEM; 1146 (op_flag > 0 && (vd->access & SNDRV_CTL_ELEM_ACCESS_TLV_WRITE) == 0) ||
1096 goto __kctl_end; 1147 (op_flag < 0 && (vd->access & SNDRV_CTL_ELEM_ACCESS_TLV_COMMAND) == 0)) {
1097 } 1148 err = -ENXIO;
1098 if (copy_to_user(_tlv->tlv, kctl->tlv, len)) 1149 goto __kctl_end;
1099 err = -EFAULT; 1150 }
1151 if (vd->access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) {
1152 if (file && vd->owner != NULL && vd->owner != file) {
1153 err = -EPERM;
1154 goto __kctl_end;
1155 }
1156 err = kctl->tlv.c(kctl, op_flag, tlv.length, _tlv->tlv);
1157 if (err > 0) {
1158 up_read(&card->controls_rwsem);
1159 snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_TLV, &kctl->id);
1160 return 0;
1161 }
1162 } else {
1163 if (op_flag) {
1164 err = -ENXIO;
1165 goto __kctl_end;
1166 }
1167 len = kctl->tlv.p[1] + 2 * sizeof(unsigned int);
1168 if (tlv.length < len) {
1169 err = -ENOMEM;
1170 goto __kctl_end;
1171 }
1172 if (copy_to_user(_tlv->tlv, kctl->tlv.p, len))
1173 err = -EFAULT;
1174 }
1100 __kctl_end: 1175 __kctl_end:
1101 up_read(&card->controls_rwsem); 1176 up_read(&card->controls_rwsem);
1102 return err; 1177 return err;
1103} 1178}
1104 1179
1105static long snd_ctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 1180static long snd_ctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
@@ -1140,8 +1215,12 @@ static long snd_ctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg
1140 return snd_ctl_elem_remove(ctl, argp); 1215 return snd_ctl_elem_remove(ctl, argp);
1141 case SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS: 1216 case SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS:
1142 return snd_ctl_subscribe_events(ctl, ip); 1217 return snd_ctl_subscribe_events(ctl, ip);
1143 case SNDRV_CTL_IOCTL_TLV_READ: 1218 case SNDRV_CTL_IOCTL_TLV_READ:
1144 return snd_ctl_tlv_read(card, argp); 1219 return snd_ctl_tlv_ioctl(ctl, argp, 0);
1220 case SNDRV_CTL_IOCTL_TLV_WRITE:
1221 return snd_ctl_tlv_ioctl(ctl, argp, 1);
1222 case SNDRV_CTL_IOCTL_TLV_COMMAND:
1223 return snd_ctl_tlv_ioctl(ctl, argp, -1);
1145 case SNDRV_CTL_IOCTL_POWER: 1224 case SNDRV_CTL_IOCTL_POWER:
1146 return -ENOPROTOOPT; 1225 return -ENOPROTOOPT;
1147 case SNDRV_CTL_IOCTL_POWER_STATE: 1226 case SNDRV_CTL_IOCTL_POWER_STATE: