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 | ||
