blob: a4a7a14697b35192a291aa3de3144073bc81dc7b [file] [log] [blame]
/*
* hda-emu - simple HD-audio codec emulator for debugging snd-hda-intel driver
*
* Simple cut-out version of ALSA control API handlers
*
* Copyright (c) Takashi Iwai <tiwai@suse.de>
*
* This driver is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This driver is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <sound/core.h>
#include <sound/control.h>
#include "hda-log.h"
static int snd_ctl_list_elems;
LIST_HEAD(snd_ctl_list_head); /* exported */
struct snd_kcontrol *snd_ctl_new1(const struct snd_kcontrol_new *knew,
void *private_data)
{
struct snd_kcontrol *kctl;
kctl = calloc(1, sizeof(*kctl));
if (!kctl)
return NULL;
kctl->id.iface = knew->iface;
kctl->id.device = knew->device;
kctl->id.subdevice = knew->subdevice;
kctl->id.index = knew->index;
if (knew->name)
strcpy(kctl->id.name, knew->name);
kctl->count = knew->count ? knew->count : 1;
kctl->vd[0].access = knew->access ?
knew->access : SNDRV_CTL_ELEM_ACCESS_READWRITE;
kctl->info = knew->info;
kctl->get = knew->get;
kctl->put = knew->put;
memcpy(&kctl->tlv, &knew->tlv, sizeof(kctl->tlv));
kctl->private_value = knew->private_value;
kctl->private_data = private_data;
INIT_LIST_HEAD(&kctl->list);
return kctl;
}
void snd_ctl_free_one(struct snd_kcontrol *kctl)
{
if (kctl) {
if (kctl->private_free)
kctl->private_free(kctl);
free(kctl);
}
}
int snd_ctl_add(struct snd_card *card, struct snd_kcontrol *kctl)
{
if (snd_ctl_find_id(card, &kctl->id)) {
hda_log(HDA_LOG_ERR, "Control element %s:%d already exists!\n",
kctl->id.name, kctl->id.index);
return -EBUSY;
}
kctl->id.numid = snd_ctl_list_elems + 1;
snd_ctl_list_elems += kctl->count;
list_add_tail(&kctl->list, &snd_ctl_list_head);
hda_log(HDA_LOG_INFO, "CTRL: add: %s:%d\n",
kctl->id.name, kctl->id.index);
return 0;
}
int snd_ctl_remove(struct snd_card *card, struct snd_kcontrol *kctl)
{
hda_log(HDA_LOG_INFO, "CTRL: remove: %s:%d\n",
kctl->id.name, kctl->id.index);
list_del(&kctl->list);
snd_ctl_free_one(kctl);
return 0;
}
int snd_ctl_remove_id(struct snd_card *card, struct snd_ctl_elem_id *id)
{
struct snd_kcontrol *kctl = snd_ctl_find_id(card, id);
if (!kctl)
return -ENODEV;
return snd_ctl_remove(card, kctl);
}
int snd_ctl_rename_id(struct snd_card *card, struct snd_ctl_elem_id *src_id,
struct snd_ctl_elem_id *dst_id)
{
struct snd_kcontrol *kctl = snd_ctl_find_id(card, src_id);
if (!kctl)
return -ENODEV;
kctl->id = *dst_id;
return 0;
}
struct snd_kcontrol *snd_ctl_find_numid(struct snd_card *card, unsigned int numid)
{
struct snd_kcontrol *kctl;
list_for_each_entry(kctl, &snd_ctl_list_head, list) {
if (kctl->id.numid <= numid && kctl->id.numid + kctl->count > numid)
return kctl;
}
return NULL;
}
struct snd_kcontrol *snd_ctl_find_id(struct snd_card *card,
struct snd_ctl_elem_id *id)
{
struct snd_kcontrol *kctl;
list_for_each_entry(kctl, &snd_ctl_list_head, list) {
if (kctl->id.iface == id->iface &&
kctl->id.device == id->device &&
kctl->id.subdevice == id->subdevice &&
kctl->id.index == id->index &&
!strcmp(kctl->id.name, id->name))
return kctl;
}
return NULL;
}
#ifndef HAVE_BOOLEAN_INFO
int snd_ctl_boolean_mono_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
uinfo->count = 1;
uinfo->value.integer.min = 0;
uinfo->value.integer.max = 1;
return 0;
}
int snd_ctl_boolean_stereo_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
uinfo->count = 2;
uinfo->value.integer.min = 0;
uinfo->value.integer.max = 1;
return 0;
}
#endif
int snd_ctl_enum_info(struct snd_ctl_elem_info *info, unsigned int channels,
unsigned int items, const char *const names[])
{
info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
info->count = channels;
info->value.enumerated.items = items;
if (!items)
return 0;
if (info->value.enumerated.item >= items)
info->value.enumerated.item = items - 1;
if (strlen(names[info->value.enumerated.item]) >= sizeof(info->value.enumerated.name))
hda_log(HDA_LOG_WARN, "ALSA: too long item name '%s'\n", names[info->value.enumerated.item]);
strlcpy(info->value.enumerated.name,
names[info->value.enumerated.item],
sizeof(info->value.enumerated.name));
return 0;
}
int snd_ctl_activate_id(struct snd_card *card, struct snd_ctl_elem_id *id,
int active)
{
struct snd_kcontrol *kctl;
unsigned int index_offset;
int ret;
kctl = snd_ctl_find_id(card, id);
if (!kctl)
return -ENOENT;
index_offset = snd_ctl_get_ioff(kctl, &kctl->id);
ret = 0;
if (active) {
if (!(kctl->vd[0].access & SNDRV_CTL_ELEM_ACCESS_INACTIVE))
goto unlock;
kctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
} else {
if (kctl->vd[0].access & SNDRV_CTL_ELEM_ACCESS_INACTIVE)
goto unlock;
kctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE;
}
ret = 1;
unlock:
if (ret > 0) {
hda_log(HDA_LOG_INFO, "Control element %s:%d active changed to %s\n",
kctl->id.name, kctl->id.index,
(kctl->vd[0].access & SNDRV_CTL_ELEM_ACCESS_INACTIVE) ? "inactive" : "active");
}
return ret;
}
void snd_ctl_notify(struct snd_card *card, unsigned int mask,
struct snd_ctl_elem_id *id)
{
struct snd_kcontrol *kctl;
kctl = snd_ctl_find_id(card, id);
if (!kctl)
return;
hda_log(HDA_LOG_INFO, "CTL Notify: %s:%d, mask=%d\n",
kctl->id.name, kctl->id.index, mask);
}