diff options
Diffstat (limited to 'sound/core/control.c')
-rw-r--r-- | sound/core/control.c | 315 |
1 files changed, 195 insertions, 120 deletions
diff --git a/sound/core/control.c b/sound/core/control.c index eeb691d1911f..be5b97cd8dc3 100644 --- a/sound/core/control.c +++ b/sound/core/control.c | |||
@@ -192,36 +192,41 @@ void snd_ctl_notify(struct snd_card *card, unsigned int mask, | |||
192 | EXPORT_SYMBOL(snd_ctl_notify); | 192 | EXPORT_SYMBOL(snd_ctl_notify); |
193 | 193 | ||
194 | /** | 194 | /** |
195 | * snd_ctl_new - create a control instance from the template | 195 | * snd_ctl_new - create a new control instance with some elements |
196 | * @control: the control template | 196 | * @kctl: the pointer to store new control instance |
197 | * @access: the default control access | 197 | * @count: the number of elements in this control |
198 | * @access: the default access flags for elements in this control | ||
199 | * @file: given when locking these elements | ||
198 | * | 200 | * |
199 | * Allocates a new struct snd_kcontrol instance and copies the given template | 201 | * Allocates a memory object for a new control instance. The instance has |
200 | * to the new instance. It does not copy volatile data (access). | 202 | * elements as many as the given number (@count). Each element has given |
203 | * access permissions (@access). Each element is locked when @file is given. | ||
201 | * | 204 | * |
202 | * Return: The pointer of the new instance, or %NULL on failure. | 205 | * Return: 0 on success, error code on failure |
203 | */ | 206 | */ |
204 | static struct snd_kcontrol *snd_ctl_new(struct snd_kcontrol *control, | 207 | static int snd_ctl_new(struct snd_kcontrol **kctl, unsigned int count, |
205 | unsigned int access) | 208 | unsigned int access, struct snd_ctl_file *file) |
206 | { | 209 | { |
207 | struct snd_kcontrol *kctl; | 210 | unsigned int size; |
208 | unsigned int idx; | 211 | unsigned int idx; |
209 | 212 | ||
210 | if (snd_BUG_ON(!control || !control->count)) | 213 | if (count == 0 || count > MAX_CONTROL_COUNT) |
211 | return NULL; | 214 | return -EINVAL; |
212 | 215 | ||
213 | if (control->count > MAX_CONTROL_COUNT) | 216 | size = sizeof(struct snd_kcontrol); |
214 | return NULL; | 217 | size += sizeof(struct snd_kcontrol_volatile) * count; |
215 | 218 | ||
216 | kctl = kzalloc(sizeof(*kctl) + sizeof(struct snd_kcontrol_volatile) * control->count, GFP_KERNEL); | 219 | *kctl = kzalloc(size, GFP_KERNEL); |
217 | if (kctl == NULL) { | 220 | if (!*kctl) |
218 | pr_err("ALSA: Cannot allocate control instance\n"); | 221 | return -ENOMEM; |
219 | return NULL; | 222 | |
223 | for (idx = 0; idx < count; idx++) { | ||
224 | (*kctl)->vd[idx].access = access; | ||
225 | (*kctl)->vd[idx].owner = file; | ||
220 | } | 226 | } |
221 | *kctl = *control; | 227 | (*kctl)->count = count; |
222 | for (idx = 0; idx < kctl->count; idx++) | 228 | |
223 | kctl->vd[idx].access = access; | 229 | return 0; |
224 | return kctl; | ||
225 | } | 230 | } |
226 | 231 | ||
227 | /** | 232 | /** |
@@ -238,37 +243,53 @@ static struct snd_kcontrol *snd_ctl_new(struct snd_kcontrol *control, | |||
238 | struct snd_kcontrol *snd_ctl_new1(const struct snd_kcontrol_new *ncontrol, | 243 | struct snd_kcontrol *snd_ctl_new1(const struct snd_kcontrol_new *ncontrol, |
239 | void *private_data) | 244 | void *private_data) |
240 | { | 245 | { |
241 | struct snd_kcontrol kctl; | 246 | struct snd_kcontrol *kctl; |
247 | unsigned int count; | ||
242 | unsigned int access; | 248 | unsigned int access; |
249 | int err; | ||
243 | 250 | ||
244 | if (snd_BUG_ON(!ncontrol || !ncontrol->info)) | 251 | if (snd_BUG_ON(!ncontrol || !ncontrol->info)) |
245 | return NULL; | 252 | return NULL; |
246 | memset(&kctl, 0, sizeof(kctl)); | 253 | |
247 | kctl.id.iface = ncontrol->iface; | 254 | count = ncontrol->count; |
248 | kctl.id.device = ncontrol->device; | 255 | if (count == 0) |
249 | kctl.id.subdevice = ncontrol->subdevice; | 256 | count = 1; |
257 | |||
258 | access = ncontrol->access; | ||
259 | if (access == 0) | ||
260 | access = SNDRV_CTL_ELEM_ACCESS_READWRITE; | ||
261 | access &= (SNDRV_CTL_ELEM_ACCESS_READWRITE | | ||
262 | SNDRV_CTL_ELEM_ACCESS_VOLATILE | | ||
263 | SNDRV_CTL_ELEM_ACCESS_INACTIVE | | ||
264 | SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE | | ||
265 | SNDRV_CTL_ELEM_ACCESS_TLV_COMMAND | | ||
266 | SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK); | ||
267 | |||
268 | err = snd_ctl_new(&kctl, count, access, NULL); | ||
269 | if (err < 0) | ||
270 | return NULL; | ||
271 | |||
272 | /* The 'numid' member is decided when calling snd_ctl_add(). */ | ||
273 | kctl->id.iface = ncontrol->iface; | ||
274 | kctl->id.device = ncontrol->device; | ||
275 | kctl->id.subdevice = ncontrol->subdevice; | ||
250 | if (ncontrol->name) { | 276 | if (ncontrol->name) { |
251 | strlcpy(kctl.id.name, ncontrol->name, sizeof(kctl.id.name)); | 277 | strlcpy(kctl->id.name, ncontrol->name, sizeof(kctl->id.name)); |
252 | if (strcmp(ncontrol->name, kctl.id.name) != 0) | 278 | if (strcmp(ncontrol->name, kctl->id.name) != 0) |
253 | pr_warn("ALSA: Control name '%s' truncated to '%s'\n", | 279 | pr_warn("ALSA: Control name '%s' truncated to '%s'\n", |
254 | ncontrol->name, kctl.id.name); | 280 | ncontrol->name, kctl->id.name); |
255 | } | 281 | } |
256 | kctl.id.index = ncontrol->index; | 282 | kctl->id.index = ncontrol->index; |
257 | kctl.count = ncontrol->count ? ncontrol->count : 1; | 283 | |
258 | access = ncontrol->access == 0 ? SNDRV_CTL_ELEM_ACCESS_READWRITE : | 284 | kctl->info = ncontrol->info; |
259 | (ncontrol->access & (SNDRV_CTL_ELEM_ACCESS_READWRITE| | 285 | kctl->get = ncontrol->get; |
260 | SNDRV_CTL_ELEM_ACCESS_VOLATILE| | 286 | kctl->put = ncontrol->put; |
261 | SNDRV_CTL_ELEM_ACCESS_INACTIVE| | 287 | kctl->tlv.p = ncontrol->tlv.p; |
262 | SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE| | 288 | |
263 | SNDRV_CTL_ELEM_ACCESS_TLV_COMMAND| | 289 | kctl->private_value = ncontrol->private_value; |
264 | SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK)); | 290 | kctl->private_data = private_data; |
265 | kctl.info = ncontrol->info; | 291 | |
266 | kctl.get = ncontrol->get; | 292 | return kctl; |
267 | kctl.put = ncontrol->put; | ||
268 | kctl.tlv.p = ncontrol->tlv.p; | ||
269 | kctl.private_value = ncontrol->private_value; | ||
270 | kctl.private_data = private_data; | ||
271 | return snd_ctl_new(&kctl, access); | ||
272 | } | 293 | } |
273 | EXPORT_SYMBOL(snd_ctl_new1); | 294 | EXPORT_SYMBOL(snd_ctl_new1); |
274 | 295 | ||
@@ -557,6 +578,7 @@ error: | |||
557 | * | 578 | * |
558 | * Finds the control instance with the given id, and activate or | 579 | * Finds the control instance with the given id, and activate or |
559 | * inactivate the control together with notification, if changed. | 580 | * inactivate the control together with notification, if changed. |
581 | * The given ID data is filled with full information. | ||
560 | * | 582 | * |
561 | * Return: 0 if unchanged, 1 if changed, or a negative error code on failure. | 583 | * Return: 0 if unchanged, 1 if changed, or a negative error code on failure. |
562 | */ | 584 | */ |
@@ -586,6 +608,7 @@ int snd_ctl_activate_id(struct snd_card *card, struct snd_ctl_elem_id *id, | |||
586 | goto unlock; | 608 | goto unlock; |
587 | vd->access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE; | 609 | vd->access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE; |
588 | } | 610 | } |
611 | snd_ctl_build_ioff(id, kctl, index_offset); | ||
589 | ret = 1; | 612 | ret = 1; |
590 | unlock: | 613 | unlock: |
591 | up_write(&card->controls_rwsem); | 614 | up_write(&card->controls_rwsem); |
@@ -1017,8 +1040,12 @@ static int snd_ctl_elem_user_info(struct snd_kcontrol *kcontrol, | |||
1017 | struct snd_ctl_elem_info *uinfo) | 1040 | struct snd_ctl_elem_info *uinfo) |
1018 | { | 1041 | { |
1019 | struct user_element *ue = kcontrol->private_data; | 1042 | struct user_element *ue = kcontrol->private_data; |
1043 | unsigned int offset; | ||
1020 | 1044 | ||
1045 | offset = snd_ctl_get_ioff(kcontrol, &uinfo->id); | ||
1021 | *uinfo = ue->info; | 1046 | *uinfo = ue->info; |
1047 | snd_ctl_build_ioff(&uinfo->id, kcontrol, offset); | ||
1048 | |||
1022 | return 0; | 1049 | return 0; |
1023 | } | 1050 | } |
1024 | 1051 | ||
@@ -1028,10 +1055,13 @@ static int snd_ctl_elem_user_enum_info(struct snd_kcontrol *kcontrol, | |||
1028 | struct user_element *ue = kcontrol->private_data; | 1055 | struct user_element *ue = kcontrol->private_data; |
1029 | const char *names; | 1056 | const char *names; |
1030 | unsigned int item; | 1057 | unsigned int item; |
1058 | unsigned int offset; | ||
1031 | 1059 | ||
1032 | item = uinfo->value.enumerated.item; | 1060 | item = uinfo->value.enumerated.item; |
1033 | 1061 | ||
1062 | offset = snd_ctl_get_ioff(kcontrol, &uinfo->id); | ||
1034 | *uinfo = ue->info; | 1063 | *uinfo = ue->info; |
1064 | snd_ctl_build_ioff(&uinfo->id, kcontrol, offset); | ||
1035 | 1065 | ||
1036 | item = min(item, uinfo->value.enumerated.items - 1); | 1066 | item = min(item, uinfo->value.enumerated.items - 1); |
1037 | uinfo->value.enumerated.item = item; | 1067 | uinfo->value.enumerated.item = item; |
@@ -1078,7 +1108,7 @@ static int snd_ctl_elem_user_tlv(struct snd_kcontrol *kcontrol, | |||
1078 | int change = 0; | 1108 | int change = 0; |
1079 | void *new_data; | 1109 | void *new_data; |
1080 | 1110 | ||
1081 | if (op_flag > 0) { | 1111 | if (op_flag == SNDRV_CTL_TLV_OP_WRITE) { |
1082 | if (size > 1024 * 128) /* sane value */ | 1112 | if (size > 1024 * 128) /* sane value */ |
1083 | return -EINVAL; | 1113 | return -EINVAL; |
1084 | 1114 | ||
@@ -1161,84 +1191,103 @@ static void snd_ctl_elem_user_free(struct snd_kcontrol *kcontrol) | |||
1161 | static int snd_ctl_elem_add(struct snd_ctl_file *file, | 1191 | static int snd_ctl_elem_add(struct snd_ctl_file *file, |
1162 | struct snd_ctl_elem_info *info, int replace) | 1192 | struct snd_ctl_elem_info *info, int replace) |
1163 | { | 1193 | { |
1194 | /* The capacity of struct snd_ctl_elem_value.value.*/ | ||
1195 | static const unsigned int value_sizes[] = { | ||
1196 | [SNDRV_CTL_ELEM_TYPE_BOOLEAN] = sizeof(long), | ||
1197 | [SNDRV_CTL_ELEM_TYPE_INTEGER] = sizeof(long), | ||
1198 | [SNDRV_CTL_ELEM_TYPE_ENUMERATED] = sizeof(unsigned int), | ||
1199 | [SNDRV_CTL_ELEM_TYPE_BYTES] = sizeof(unsigned char), | ||
1200 | [SNDRV_CTL_ELEM_TYPE_IEC958] = sizeof(struct snd_aes_iec958), | ||
1201 | [SNDRV_CTL_ELEM_TYPE_INTEGER64] = sizeof(long long), | ||
1202 | }; | ||
1203 | static const unsigned int max_value_counts[] = { | ||
1204 | [SNDRV_CTL_ELEM_TYPE_BOOLEAN] = 128, | ||
1205 | [SNDRV_CTL_ELEM_TYPE_INTEGER] = 128, | ||
1206 | [SNDRV_CTL_ELEM_TYPE_ENUMERATED] = 128, | ||
1207 | [SNDRV_CTL_ELEM_TYPE_BYTES] = 512, | ||
1208 | [SNDRV_CTL_ELEM_TYPE_IEC958] = 1, | ||
1209 | [SNDRV_CTL_ELEM_TYPE_INTEGER64] = 64, | ||
1210 | }; | ||
1164 | struct snd_card *card = file->card; | 1211 | struct snd_card *card = file->card; |
1165 | struct snd_kcontrol kctl, *_kctl; | 1212 | struct snd_kcontrol *kctl; |
1213 | unsigned int count; | ||
1166 | unsigned int access; | 1214 | unsigned int access; |
1167 | long private_size; | 1215 | long private_size; |
1168 | struct user_element *ue; | 1216 | struct user_element *ue; |
1169 | int idx, err; | 1217 | unsigned int offset; |
1218 | int err; | ||
1170 | 1219 | ||
1171 | if (info->count < 1) | ||
1172 | return -EINVAL; | ||
1173 | if (!*info->id.name) | 1220 | if (!*info->id.name) |
1174 | return -EINVAL; | 1221 | return -EINVAL; |
1175 | if (strnlen(info->id.name, sizeof(info->id.name)) >= sizeof(info->id.name)) | 1222 | if (strnlen(info->id.name, sizeof(info->id.name)) >= sizeof(info->id.name)) |
1176 | return -EINVAL; | 1223 | return -EINVAL; |
1177 | access = info->access == 0 ? SNDRV_CTL_ELEM_ACCESS_READWRITE : | ||
1178 | (info->access & (SNDRV_CTL_ELEM_ACCESS_READWRITE| | ||
1179 | SNDRV_CTL_ELEM_ACCESS_INACTIVE| | ||
1180 | SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE)); | ||
1181 | info->id.numid = 0; | ||
1182 | memset(&kctl, 0, sizeof(kctl)); | ||
1183 | 1224 | ||
1225 | /* Delete a control to replace them if needed. */ | ||
1184 | if (replace) { | 1226 | if (replace) { |
1227 | info->id.numid = 0; | ||
1185 | err = snd_ctl_remove_user_ctl(file, &info->id); | 1228 | err = snd_ctl_remove_user_ctl(file, &info->id); |
1186 | if (err) | 1229 | if (err) |
1187 | return err; | 1230 | return err; |
1188 | } | 1231 | } |
1189 | 1232 | ||
1190 | if (card->user_ctl_count >= MAX_USER_CONTROLS) | 1233 | /* |
1234 | * The number of userspace controls are counted control by control, | ||
1235 | * not element by element. | ||
1236 | */ | ||
1237 | if (card->user_ctl_count + 1 > MAX_USER_CONTROLS) | ||
1191 | return -ENOMEM; | 1238 | return -ENOMEM; |
1192 | 1239 | ||
1193 | memcpy(&kctl.id, &info->id, sizeof(info->id)); | 1240 | /* Check the number of elements for this userspace control. */ |
1194 | kctl.count = info->owner ? info->owner : 1; | 1241 | count = info->owner; |
1195 | access |= SNDRV_CTL_ELEM_ACCESS_USER; | 1242 | if (count == 0) |
1196 | if (info->type == SNDRV_CTL_ELEM_TYPE_ENUMERATED) | 1243 | count = 1; |
1197 | kctl.info = snd_ctl_elem_user_enum_info; | 1244 | |
1198 | else | 1245 | /* Arrange access permissions if needed. */ |
1199 | kctl.info = snd_ctl_elem_user_info; | 1246 | access = info->access; |
1200 | if (access & SNDRV_CTL_ELEM_ACCESS_READ) | 1247 | if (access == 0) |
1201 | kctl.get = snd_ctl_elem_user_get; | 1248 | access = SNDRV_CTL_ELEM_ACCESS_READWRITE; |
1202 | if (access & SNDRV_CTL_ELEM_ACCESS_WRITE) | 1249 | access &= (SNDRV_CTL_ELEM_ACCESS_READWRITE | |
1203 | kctl.put = snd_ctl_elem_user_put; | 1250 | SNDRV_CTL_ELEM_ACCESS_INACTIVE | |
1204 | if (access & SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE) { | 1251 | SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE); |
1205 | kctl.tlv.c = snd_ctl_elem_user_tlv; | 1252 | if (access & SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE) |
1206 | access |= SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK; | 1253 | access |= SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK; |
1207 | } | 1254 | access |= SNDRV_CTL_ELEM_ACCESS_USER; |
1208 | switch (info->type) { | 1255 | |
1209 | case SNDRV_CTL_ELEM_TYPE_BOOLEAN: | 1256 | /* |
1210 | case SNDRV_CTL_ELEM_TYPE_INTEGER: | 1257 | * Check information and calculate the size of data specific to |
1211 | private_size = sizeof(long); | 1258 | * this userspace control. |
1212 | if (info->count > 128) | 1259 | */ |
1213 | return -EINVAL; | 1260 | if (info->type < SNDRV_CTL_ELEM_TYPE_BOOLEAN || |
1214 | break; | 1261 | info->type > SNDRV_CTL_ELEM_TYPE_INTEGER64) |
1215 | case SNDRV_CTL_ELEM_TYPE_INTEGER64: | ||
1216 | private_size = sizeof(long long); | ||
1217 | if (info->count > 64) | ||
1218 | return -EINVAL; | ||
1219 | break; | ||
1220 | case SNDRV_CTL_ELEM_TYPE_ENUMERATED: | ||
1221 | private_size = sizeof(unsigned int); | ||
1222 | if (info->count > 128 || info->value.enumerated.items == 0) | ||
1223 | return -EINVAL; | ||
1224 | break; | ||
1225 | case SNDRV_CTL_ELEM_TYPE_BYTES: | ||
1226 | private_size = sizeof(unsigned char); | ||
1227 | if (info->count > 512) | ||
1228 | return -EINVAL; | ||
1229 | break; | ||
1230 | case SNDRV_CTL_ELEM_TYPE_IEC958: | ||
1231 | private_size = sizeof(struct snd_aes_iec958); | ||
1232 | if (info->count != 1) | ||
1233 | return -EINVAL; | ||
1234 | break; | ||
1235 | default: | ||
1236 | return -EINVAL; | 1262 | return -EINVAL; |
1237 | } | 1263 | if (info->type == SNDRV_CTL_ELEM_TYPE_ENUMERATED && |
1238 | private_size *= info->count; | 1264 | info->value.enumerated.items == 0) |
1239 | ue = kzalloc(sizeof(struct user_element) + private_size, GFP_KERNEL); | 1265 | return -EINVAL; |
1240 | if (ue == NULL) | 1266 | if (info->count < 1 || |
1267 | info->count > max_value_counts[info->type]) | ||
1268 | return -EINVAL; | ||
1269 | private_size = value_sizes[info->type] * info->count; | ||
1270 | |||
1271 | /* | ||
1272 | * Keep memory object for this userspace control. After passing this | ||
1273 | * code block, the instance should be freed by snd_ctl_free_one(). | ||
1274 | * | ||
1275 | * Note that these elements in this control are locked. | ||
1276 | */ | ||
1277 | err = snd_ctl_new(&kctl, count, access, file); | ||
1278 | if (err < 0) | ||
1279 | return err; | ||
1280 | memcpy(&kctl->id, &info->id, sizeof(kctl->id)); | ||
1281 | kctl->private_data = kzalloc(sizeof(struct user_element) + private_size, | ||
1282 | GFP_KERNEL); | ||
1283 | if (kctl->private_data == NULL) { | ||
1284 | kfree(kctl); | ||
1241 | return -ENOMEM; | 1285 | return -ENOMEM; |
1286 | } | ||
1287 | kctl->private_free = snd_ctl_elem_user_free; | ||
1288 | |||
1289 | /* Set private data for this userspace control. */ | ||
1290 | ue = (struct user_element *)kctl->private_data; | ||
1242 | ue->card = card; | 1291 | ue->card = card; |
1243 | ue->info = *info; | 1292 | ue->info = *info; |
1244 | ue->info.access = 0; | 1293 | ue->info.access = 0; |
@@ -1247,23 +1296,36 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file, | |||
1247 | if (ue->info.type == SNDRV_CTL_ELEM_TYPE_ENUMERATED) { | 1296 | if (ue->info.type == SNDRV_CTL_ELEM_TYPE_ENUMERATED) { |
1248 | err = snd_ctl_elem_init_enum_names(ue); | 1297 | err = snd_ctl_elem_init_enum_names(ue); |
1249 | if (err < 0) { | 1298 | if (err < 0) { |
1250 | kfree(ue); | 1299 | snd_ctl_free_one(kctl); |
1251 | return err; | 1300 | return err; |
1252 | } | 1301 | } |
1253 | } | 1302 | } |
1254 | kctl.private_free = snd_ctl_elem_user_free; | 1303 | |
1255 | _kctl = snd_ctl_new(&kctl, access); | 1304 | /* Set callback functions. */ |
1256 | if (_kctl == NULL) { | 1305 | if (info->type == SNDRV_CTL_ELEM_TYPE_ENUMERATED) |
1257 | kfree(ue->priv_data); | 1306 | kctl->info = snd_ctl_elem_user_enum_info; |
1258 | kfree(ue); | 1307 | else |
1259 | return -ENOMEM; | 1308 | kctl->info = snd_ctl_elem_user_info; |
1260 | } | 1309 | if (access & SNDRV_CTL_ELEM_ACCESS_READ) |
1261 | _kctl->private_data = ue; | 1310 | kctl->get = snd_ctl_elem_user_get; |
1262 | for (idx = 0; idx < _kctl->count; idx++) | 1311 | if (access & SNDRV_CTL_ELEM_ACCESS_WRITE) |
1263 | _kctl->vd[idx].owner = file; | 1312 | kctl->put = snd_ctl_elem_user_put; |
1264 | err = snd_ctl_add(card, _kctl); | 1313 | if (access & SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE) |
1314 | kctl->tlv.c = snd_ctl_elem_user_tlv; | ||
1315 | |||
1316 | /* This function manage to free the instance on failure. */ | ||
1317 | err = snd_ctl_add(card, kctl); | ||
1265 | if (err < 0) | 1318 | if (err < 0) |
1266 | return err; | 1319 | return err; |
1320 | offset = snd_ctl_get_ioff(kctl, &info->id); | ||
1321 | snd_ctl_build_ioff(&info->id, kctl, offset); | ||
1322 | /* | ||
1323 | * Here we cannot fill any field for the number of elements added by | ||
1324 | * this operation because there're no specific fields. The usage of | ||
1325 | * 'owner' field for this purpose may cause any bugs to userspace | ||
1326 | * applications because the field originally means PID of a process | ||
1327 | * which locks the element. | ||
1328 | */ | ||
1267 | 1329 | ||
1268 | down_write(&card->controls_rwsem); | 1330 | down_write(&card->controls_rwsem); |
1269 | card->user_ctl_count++; | 1331 | card->user_ctl_count++; |
@@ -1276,9 +1338,19 @@ static int snd_ctl_elem_add_user(struct snd_ctl_file *file, | |||
1276 | struct snd_ctl_elem_info __user *_info, int replace) | 1338 | struct snd_ctl_elem_info __user *_info, int replace) |
1277 | { | 1339 | { |
1278 | struct snd_ctl_elem_info info; | 1340 | struct snd_ctl_elem_info info; |
1341 | int err; | ||
1342 | |||
1279 | if (copy_from_user(&info, _info, sizeof(info))) | 1343 | if (copy_from_user(&info, _info, sizeof(info))) |
1280 | return -EFAULT; | 1344 | return -EFAULT; |
1281 | return snd_ctl_elem_add(file, &info, replace); | 1345 | err = snd_ctl_elem_add(file, &info, replace); |
1346 | if (err < 0) | ||
1347 | return err; | ||
1348 | if (copy_to_user(_info, &info, sizeof(info))) { | ||
1349 | snd_ctl_remove_user_ctl(file, &info.id); | ||
1350 | return -EFAULT; | ||
1351 | } | ||
1352 | |||
1353 | return 0; | ||
1282 | } | 1354 | } |
1283 | 1355 | ||
1284 | static int snd_ctl_elem_remove(struct snd_ctl_file *file, | 1356 | static int snd_ctl_elem_remove(struct snd_ctl_file *file, |
@@ -1338,9 +1410,12 @@ static int snd_ctl_tlv_ioctl(struct snd_ctl_file *file, | |||
1338 | goto __kctl_end; | 1410 | goto __kctl_end; |
1339 | } | 1411 | } |
1340 | vd = &kctl->vd[tlv.numid - kctl->id.numid]; | 1412 | vd = &kctl->vd[tlv.numid - kctl->id.numid]; |
1341 | if ((op_flag == 0 && (vd->access & SNDRV_CTL_ELEM_ACCESS_TLV_READ) == 0) || | 1413 | if ((op_flag == SNDRV_CTL_TLV_OP_READ && |
1342 | (op_flag > 0 && (vd->access & SNDRV_CTL_ELEM_ACCESS_TLV_WRITE) == 0) || | 1414 | (vd->access & SNDRV_CTL_ELEM_ACCESS_TLV_READ) == 0) || |
1343 | (op_flag < 0 && (vd->access & SNDRV_CTL_ELEM_ACCESS_TLV_COMMAND) == 0)) { | 1415 | (op_flag == SNDRV_CTL_TLV_OP_WRITE && |
1416 | (vd->access & SNDRV_CTL_ELEM_ACCESS_TLV_WRITE) == 0) || | ||
1417 | (op_flag == SNDRV_CTL_TLV_OP_CMD && | ||
1418 | (vd->access & SNDRV_CTL_ELEM_ACCESS_TLV_COMMAND) == 0)) { | ||
1344 | err = -ENXIO; | 1419 | err = -ENXIO; |
1345 | goto __kctl_end; | 1420 | goto __kctl_end; |
1346 | } | 1421 | } |
@@ -1357,7 +1432,7 @@ static int snd_ctl_tlv_ioctl(struct snd_ctl_file *file, | |||
1357 | return 0; | 1432 | return 0; |
1358 | } | 1433 | } |
1359 | } else { | 1434 | } else { |
1360 | if (op_flag) { | 1435 | if (op_flag != SNDRV_CTL_TLV_OP_READ) { |
1361 | err = -ENXIO; | 1436 | err = -ENXIO; |
1362 | goto __kctl_end; | 1437 | goto __kctl_end; |
1363 | } | 1438 | } |