diff options
author | Takashi Iwai <tiwai@suse.de> | 2015-03-23 08:14:02 -0400 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2015-03-23 08:14:02 -0400 |
commit | 3372dbdd8ca11f51be8c6a30b2bc79eb04c4a902 (patch) | |
tree | d4499bf5a5665b4820ffaf96bce55bf6b895195e /sound/core/control.c | |
parent | bc465aa9d045feb0e13b4a8f32cc33c1943f62d6 (diff) | |
parent | 967b1307b69b8ada8b331e01046ad1ef83742e99 (diff) |
Merge branch 'for-next' into topic/hda-core
Diffstat (limited to 'sound/core/control.c')
-rw-r--r-- | sound/core/control.c | 271 |
1 files changed, 157 insertions, 114 deletions
diff --git a/sound/core/control.c b/sound/core/control.c index eeb691d1911f..d677c27746e9 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 | ||
@@ -1161,84 +1182,102 @@ static void snd_ctl_elem_user_free(struct snd_kcontrol *kcontrol) | |||
1161 | static int snd_ctl_elem_add(struct snd_ctl_file *file, | 1182 | static int snd_ctl_elem_add(struct snd_ctl_file *file, |
1162 | struct snd_ctl_elem_info *info, int replace) | 1183 | struct snd_ctl_elem_info *info, int replace) |
1163 | { | 1184 | { |
1185 | /* The capacity of struct snd_ctl_elem_value.value.*/ | ||
1186 | static const unsigned int value_sizes[] = { | ||
1187 | [SNDRV_CTL_ELEM_TYPE_BOOLEAN] = sizeof(long), | ||
1188 | [SNDRV_CTL_ELEM_TYPE_INTEGER] = sizeof(long), | ||
1189 | [SNDRV_CTL_ELEM_TYPE_ENUMERATED] = sizeof(unsigned int), | ||
1190 | [SNDRV_CTL_ELEM_TYPE_BYTES] = sizeof(unsigned char), | ||
1191 | [SNDRV_CTL_ELEM_TYPE_IEC958] = sizeof(struct snd_aes_iec958), | ||
1192 | [SNDRV_CTL_ELEM_TYPE_INTEGER64] = sizeof(long long), | ||
1193 | }; | ||
1194 | static const unsigned int max_value_counts[] = { | ||
1195 | [SNDRV_CTL_ELEM_TYPE_BOOLEAN] = 128, | ||
1196 | [SNDRV_CTL_ELEM_TYPE_INTEGER] = 128, | ||
1197 | [SNDRV_CTL_ELEM_TYPE_ENUMERATED] = 128, | ||
1198 | [SNDRV_CTL_ELEM_TYPE_BYTES] = 512, | ||
1199 | [SNDRV_CTL_ELEM_TYPE_IEC958] = 1, | ||
1200 | [SNDRV_CTL_ELEM_TYPE_INTEGER64] = 64, | ||
1201 | }; | ||
1164 | struct snd_card *card = file->card; | 1202 | struct snd_card *card = file->card; |
1165 | struct snd_kcontrol kctl, *_kctl; | 1203 | struct snd_kcontrol *kctl; |
1204 | unsigned int count; | ||
1166 | unsigned int access; | 1205 | unsigned int access; |
1167 | long private_size; | 1206 | long private_size; |
1168 | struct user_element *ue; | 1207 | struct user_element *ue; |
1169 | int idx, err; | 1208 | int err; |
1170 | 1209 | ||
1171 | if (info->count < 1) | ||
1172 | return -EINVAL; | ||
1173 | if (!*info->id.name) | 1210 | if (!*info->id.name) |
1174 | return -EINVAL; | 1211 | return -EINVAL; |
1175 | if (strnlen(info->id.name, sizeof(info->id.name)) >= sizeof(info->id.name)) | 1212 | if (strnlen(info->id.name, sizeof(info->id.name)) >= sizeof(info->id.name)) |
1176 | return -EINVAL; | 1213 | 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 | 1214 | ||
1215 | /* Delete a control to replace them if needed. */ | ||
1184 | if (replace) { | 1216 | if (replace) { |
1217 | info->id.numid = 0; | ||
1185 | err = snd_ctl_remove_user_ctl(file, &info->id); | 1218 | err = snd_ctl_remove_user_ctl(file, &info->id); |
1186 | if (err) | 1219 | if (err) |
1187 | return err; | 1220 | return err; |
1188 | } | 1221 | } |
1189 | 1222 | ||
1190 | if (card->user_ctl_count >= MAX_USER_CONTROLS) | 1223 | /* |
1224 | * The number of userspace controls are counted control by control, | ||
1225 | * not element by element. | ||
1226 | */ | ||
1227 | if (card->user_ctl_count + 1 > MAX_USER_CONTROLS) | ||
1191 | return -ENOMEM; | 1228 | return -ENOMEM; |
1192 | 1229 | ||
1193 | memcpy(&kctl.id, &info->id, sizeof(info->id)); | 1230 | /* Check the number of elements for this userspace control. */ |
1194 | kctl.count = info->owner ? info->owner : 1; | 1231 | count = info->owner; |
1195 | access |= SNDRV_CTL_ELEM_ACCESS_USER; | 1232 | if (count == 0) |
1196 | if (info->type == SNDRV_CTL_ELEM_TYPE_ENUMERATED) | 1233 | count = 1; |
1197 | kctl.info = snd_ctl_elem_user_enum_info; | 1234 | |
1198 | else | 1235 | /* Arrange access permissions if needed. */ |
1199 | kctl.info = snd_ctl_elem_user_info; | 1236 | access = info->access; |
1200 | if (access & SNDRV_CTL_ELEM_ACCESS_READ) | 1237 | if (access == 0) |
1201 | kctl.get = snd_ctl_elem_user_get; | 1238 | access = SNDRV_CTL_ELEM_ACCESS_READWRITE; |
1202 | if (access & SNDRV_CTL_ELEM_ACCESS_WRITE) | 1239 | access &= (SNDRV_CTL_ELEM_ACCESS_READWRITE | |
1203 | kctl.put = snd_ctl_elem_user_put; | 1240 | SNDRV_CTL_ELEM_ACCESS_INACTIVE | |
1204 | if (access & SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE) { | 1241 | SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE); |
1205 | kctl.tlv.c = snd_ctl_elem_user_tlv; | 1242 | if (access & SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE) |
1206 | access |= SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK; | 1243 | access |= SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK; |
1207 | } | 1244 | access |= SNDRV_CTL_ELEM_ACCESS_USER; |
1208 | switch (info->type) { | 1245 | |
1209 | case SNDRV_CTL_ELEM_TYPE_BOOLEAN: | 1246 | /* |
1210 | case SNDRV_CTL_ELEM_TYPE_INTEGER: | 1247 | * Check information and calculate the size of data specific to |
1211 | private_size = sizeof(long); | 1248 | * this userspace control. |
1212 | if (info->count > 128) | 1249 | */ |
1213 | return -EINVAL; | 1250 | if (info->type < SNDRV_CTL_ELEM_TYPE_BOOLEAN || |
1214 | break; | 1251 | 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; | 1252 | return -EINVAL; |
1237 | } | 1253 | if (info->type == SNDRV_CTL_ELEM_TYPE_ENUMERATED && |
1238 | private_size *= info->count; | 1254 | info->value.enumerated.items == 0) |
1239 | ue = kzalloc(sizeof(struct user_element) + private_size, GFP_KERNEL); | 1255 | return -EINVAL; |
1240 | if (ue == NULL) | 1256 | if (info->count < 1 || |
1257 | info->count > max_value_counts[info->type]) | ||
1258 | return -EINVAL; | ||
1259 | private_size = value_sizes[info->type] * info->count; | ||
1260 | |||
1261 | /* | ||
1262 | * Keep memory object for this userspace control. After passing this | ||
1263 | * code block, the instance should be freed by snd_ctl_free_one(). | ||
1264 | * | ||
1265 | * Note that these elements in this control are locked. | ||
1266 | */ | ||
1267 | err = snd_ctl_new(&kctl, count, access, file); | ||
1268 | if (err < 0) | ||
1269 | return err; | ||
1270 | memcpy(&kctl->id, &info->id, sizeof(kctl->id)); | ||
1271 | kctl->private_data = kzalloc(sizeof(struct user_element) + private_size, | ||
1272 | GFP_KERNEL); | ||
1273 | if (kctl->private_data == NULL) { | ||
1274 | kfree(kctl); | ||
1241 | return -ENOMEM; | 1275 | return -ENOMEM; |
1276 | } | ||
1277 | kctl->private_free = snd_ctl_elem_user_free; | ||
1278 | |||
1279 | /* Set private data for this userspace control. */ | ||
1280 | ue = (struct user_element *)kctl->private_data; | ||
1242 | ue->card = card; | 1281 | ue->card = card; |
1243 | ue->info = *info; | 1282 | ue->info = *info; |
1244 | ue->info.access = 0; | 1283 | ue->info.access = 0; |
@@ -1247,21 +1286,25 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file, | |||
1247 | if (ue->info.type == SNDRV_CTL_ELEM_TYPE_ENUMERATED) { | 1286 | if (ue->info.type == SNDRV_CTL_ELEM_TYPE_ENUMERATED) { |
1248 | err = snd_ctl_elem_init_enum_names(ue); | 1287 | err = snd_ctl_elem_init_enum_names(ue); |
1249 | if (err < 0) { | 1288 | if (err < 0) { |
1250 | kfree(ue); | 1289 | snd_ctl_free_one(kctl); |
1251 | return err; | 1290 | return err; |
1252 | } | 1291 | } |
1253 | } | 1292 | } |
1254 | kctl.private_free = snd_ctl_elem_user_free; | 1293 | |
1255 | _kctl = snd_ctl_new(&kctl, access); | 1294 | /* Set callback functions. */ |
1256 | if (_kctl == NULL) { | 1295 | if (info->type == SNDRV_CTL_ELEM_TYPE_ENUMERATED) |
1257 | kfree(ue->priv_data); | 1296 | kctl->info = snd_ctl_elem_user_enum_info; |
1258 | kfree(ue); | 1297 | else |
1259 | return -ENOMEM; | 1298 | kctl->info = snd_ctl_elem_user_info; |
1260 | } | 1299 | if (access & SNDRV_CTL_ELEM_ACCESS_READ) |
1261 | _kctl->private_data = ue; | 1300 | kctl->get = snd_ctl_elem_user_get; |
1262 | for (idx = 0; idx < _kctl->count; idx++) | 1301 | if (access & SNDRV_CTL_ELEM_ACCESS_WRITE) |
1263 | _kctl->vd[idx].owner = file; | 1302 | kctl->put = snd_ctl_elem_user_put; |
1264 | err = snd_ctl_add(card, _kctl); | 1303 | if (access & SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE) |
1304 | kctl->tlv.c = snd_ctl_elem_user_tlv; | ||
1305 | |||
1306 | /* This function manage to free the instance on failure. */ | ||
1307 | err = snd_ctl_add(card, kctl); | ||
1265 | if (err < 0) | 1308 | if (err < 0) |
1266 | return err; | 1309 | return err; |
1267 | 1310 | ||