diff options
Diffstat (limited to 'sound/core/control.c')
-rw-r--r-- | sound/core/control.c | 80 |
1 files changed, 45 insertions, 35 deletions
diff --git a/sound/core/control.c b/sound/core/control.c index 9aa15bfc7936..649d3217590e 100644 --- a/sound/core/control.c +++ b/sound/core/control.c | |||
@@ -348,6 +348,40 @@ static int snd_ctl_find_hole(struct snd_card *card, unsigned int count) | |||
348 | return 0; | 348 | return 0; |
349 | } | 349 | } |
350 | 350 | ||
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) | ||
353 | { | ||
354 | struct snd_ctl_elem_id id; | ||
355 | unsigned int idx; | ||
356 | unsigned int count; | ||
357 | |||
358 | id = kcontrol->id; | ||
359 | if (id.index > UINT_MAX - kcontrol->count) | ||
360 | return -EINVAL; | ||
361 | |||
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; | ||
367 | } | ||
368 | |||
369 | if (snd_ctl_find_hole(card, kcontrol->count) < 0) | ||
370 | return -ENOMEM; | ||
371 | |||
372 | list_add_tail(&kcontrol->list, &card->controls); | ||
373 | card->controls_count += kcontrol->count; | ||
374 | kcontrol->id.numid = card->last_numid + 1; | ||
375 | card->last_numid += kcontrol->count; | ||
376 | |||
377 | id = kcontrol->id; | ||
378 | count = kcontrol->count; | ||
379 | for (idx = 0; idx < count; idx++, id.index++, id.numid++) | ||
380 | snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_ADD, &id); | ||
381 | |||
382 | return 0; | ||
383 | } | ||
384 | |||
351 | /** | 385 | /** |
352 | * snd_ctl_add - add the control instance to the card | 386 | * snd_ctl_add - add the control instance to the card |
353 | * @card: the card instance | 387 | * @card: the card instance |
@@ -364,45 +398,18 @@ static int snd_ctl_find_hole(struct snd_card *card, unsigned int count) | |||
364 | */ | 398 | */ |
365 | int snd_ctl_add(struct snd_card *card, struct snd_kcontrol *kcontrol) | 399 | int snd_ctl_add(struct snd_card *card, struct snd_kcontrol *kcontrol) |
366 | { | 400 | { |
367 | struct snd_ctl_elem_id id; | ||
368 | unsigned int idx; | ||
369 | unsigned int count; | ||
370 | int err = -EINVAL; | 401 | int err = -EINVAL; |
371 | 402 | ||
372 | if (! kcontrol) | 403 | if (! kcontrol) |
373 | return err; | 404 | return err; |
374 | if (snd_BUG_ON(!card || !kcontrol->info)) | 405 | if (snd_BUG_ON(!card || !kcontrol->info)) |
375 | goto error; | 406 | goto error; |
376 | id = kcontrol->id; | ||
377 | if (id.index > UINT_MAX - kcontrol->count) | ||
378 | goto error; | ||
379 | 407 | ||
380 | down_write(&card->controls_rwsem); | 408 | down_write(&card->controls_rwsem); |
381 | if (snd_ctl_find_id(card, &id)) { | 409 | err = __snd_ctl_add(card, kcontrol); |
382 | up_write(&card->controls_rwsem); | ||
383 | dev_err(card->dev, "control %i:%i:%i:%s:%i is already present\n", | ||
384 | id.iface, | ||
385 | id.device, | ||
386 | id.subdevice, | ||
387 | id.name, | ||
388 | id.index); | ||
389 | err = -EBUSY; | ||
390 | goto error; | ||
391 | } | ||
392 | if (snd_ctl_find_hole(card, kcontrol->count) < 0) { | ||
393 | up_write(&card->controls_rwsem); | ||
394 | err = -ENOMEM; | ||
395 | goto error; | ||
396 | } | ||
397 | list_add_tail(&kcontrol->list, &card->controls); | ||
398 | card->controls_count += kcontrol->count; | ||
399 | kcontrol->id.numid = card->last_numid + 1; | ||
400 | card->last_numid += kcontrol->count; | ||
401 | id = kcontrol->id; | ||
402 | count = kcontrol->count; | ||
403 | up_write(&card->controls_rwsem); | 410 | up_write(&card->controls_rwsem); |
404 | for (idx = 0; idx < count; idx++, id.index++, id.numid++) | 411 | if (err < 0) |
405 | snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_ADD, &id); | 412 | goto error; |
406 | return 0; | 413 | return 0; |
407 | 414 | ||
408 | error: | 415 | error: |
@@ -1361,9 +1368,12 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file, | |||
1361 | kctl->tlv.c = snd_ctl_elem_user_tlv; | 1368 | kctl->tlv.c = snd_ctl_elem_user_tlv; |
1362 | 1369 | ||
1363 | /* This function manage to free the instance on failure. */ | 1370 | /* This function manage to free the instance on failure. */ |
1364 | err = snd_ctl_add(card, kctl); | 1371 | down_write(&card->controls_rwsem); |
1365 | if (err < 0) | 1372 | err = __snd_ctl_add(card, kctl); |
1366 | return err; | 1373 | if (err < 0) { |
1374 | snd_ctl_free_one(kctl); | ||
1375 | goto unlock; | ||
1376 | } | ||
1367 | offset = snd_ctl_get_ioff(kctl, &info->id); | 1377 | offset = snd_ctl_get_ioff(kctl, &info->id); |
1368 | snd_ctl_build_ioff(&info->id, kctl, offset); | 1378 | snd_ctl_build_ioff(&info->id, kctl, offset); |
1369 | /* | 1379 | /* |
@@ -1374,10 +1384,10 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file, | |||
1374 | * which locks the element. | 1384 | * which locks the element. |
1375 | */ | 1385 | */ |
1376 | 1386 | ||
1377 | down_write(&card->controls_rwsem); | ||
1378 | card->user_ctl_count++; | 1387 | card->user_ctl_count++; |
1379 | up_write(&card->controls_rwsem); | ||
1380 | 1388 | ||
1389 | unlock: | ||
1390 | up_write(&card->controls_rwsem); | ||
1381 | return 0; | 1391 | return 0; |
1382 | } | 1392 | } |
1383 | 1393 | ||