声卡控制之kcontrol

一个芯片里有多个寄存器

一个寄存器里面某些位表示某个功能。

用一个kcontrol来表示某个功能,当打开、设置某个功能的时候,来操作某个kcontrol就可以了。

一个声卡有多个kcontrol

一个kcontrol对应一个功能,比如调整音量、开关、录音等等

一个kcontrol里面有函数来设置功能

下面进行代码分析:

/sound/soc/codecs/Wm8960.c

wm8960_probe
    snd_soc_add_codec_controls(codec, wm8960_snd_controls, ARRAY_SIZE(wm8960_snd_controls));
        struct snd_card *card = codec->card->snd_card;
        snd_soc_add_controls(card, codec->dev, controls, num_controls,codec->name_prefix, codec);
            for (i = 0; i < num_controls; i++) {
                const struct snd_kcontrol_new *control = &controls[i];
                //snd_soc_new:会使用snd_kcontrol_new构造出snd_kcontrol
                
                err = snd_ctl_add(card, snd_soc_cnew(control, data,control->name, prefix));
                            list_add_tail(&kcontrol->list, &card->controls);//将snd_kcontrol添加到card->controls链表中
        }
    }

一个snd_card里面有一个controls链表,里面会有一堆的snd_kcontrol

 snd_kcontrol结构体如下:

struct snd_kcontrol {
    struct list_head list;        /* list of controls */
    struct snd_ctl_elem_id id;
    unsigned int count;        /* count of same elements */
    snd_kcontrol_info_t *info; //获得kcontrol的信息
    snd_kcontrol_get_t *get;   //获得kcontrol的值(即寄存器中某些位的值)
    snd_kcontrol_put_t *put;   //设置kcontrol的值
    union {
        snd_kcontrol_tlv_rw_t *c;
        const unsigned int *p;
    } tlv;
    unsigned long private_value;  //这里面的东西供info, get, put函数使用
    void *private_data;           //这里面的东西供info, get, put函数使用,里面可能含有相应寄存器的地址、操作的位
    void (*private_free)(struct snd_kcontrol *kcontrol);
    struct snd_kcontrol_volatile vd[0];    /* volatile data */
};

snd_ctl_elem_id结构体如下:

struct snd_ctl_elem_id {
    unsigned int numid;        /* numeric identifier, zero = invalid */
    snd_ctl_elem_iface_t iface;    /* interface identifier */
    unsigned int device;        /* device/client number */
    unsigned int subdevice;        /* subdevice (substream) number */
    unsigned char name[44];        /* ASCII name of item */
    unsigned int index;        /* index of item */
};

问:
snd_kcontrol的值由谁提供
snd_kcontrol对应功能,对应寄存器,因此应该由芯片驱动程序提供。
芯片驱动中有一系列的snd_kcontrol_new,使用函数snd_soc_cnew来构造snd_kcontrol,然后使用函数snd_ctl_add将snd_kcontrol添加到声卡中。
在snd_kcontrol_new结构体中肯定有:info get put 函数,比如说:

static const struct snd_kcontrol_new wm8960_snd_controls[] = {
SOC_DOUBLE_R_TLV("Capture Volume", WM8960_LINVOL, WM8960_RINVOL,
         0, 63, 0, adc_tlv),
}

#define SOC_DOUBLE_R_TLV(xname, reg_left, reg_right, xshift, xmax, xinvert, tlv_array) 
{    .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),
    .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |
         SNDRV_CTL_ELEM_ACCESS_READWRITE,
    .tlv.p = (tlv_array), 
    .info = snd_soc_info_volsw, 
    .get = snd_soc_get_volsw, .put = snd_soc_put_volsw, 
    .private_value = SOC_DOUBLE_R_VALUE(reg_left, reg_right, xshift, 
                        xmax, xinvert) }

使用宏SOC_DOUBLE_R_TLV创建了一个snd_kcontrol_new结构体,该结构体中的值用来构造snd_kcontrol。构造出snd_kcontrol后,就直接放入到声卡中。
以后想操作某个功能时,就可以操作对应的snd_kcontrol,调用里面的info函数获得信息, get函数获得寄存器的值,put函数来设置相应的值。
在开发板上执行tinymix,可以列出所有的snd_kcontrol,它有如下几项:
ctl
type
num
name
value
比如说,要读取或设置capture volume
snd_kcontrol_new中的名字最终会存放在snd_kcontrol的id的name数组中

使用名字进行操作:
tinymix "Capture Volume" //读
tinymix "Capture Volume" 10 //设置

也可以使用序号进行操作:
tinymix 0 //读
tinymix 0 20 //写
问题:如何根据id来找到对应的snd_kcontrol
snd_kcontrol里面有一个snd_ctl_elem_id id, id中有个numid

总结:
1)如何构造snd_kcontrol_new
这些宏中包括:
名字:xname
左声道的寄存器:reg_left
右声道的寄存器:reg_right
寄存器是从哪一位开始:xshift
最大值:xmax
是否反转:xinvert
使用一系列的宏来构造snd_kcontrol_new,这些宏里面有默认的info函数,get、put函数。这些函数会根据私有数据来读取或设置寄存器中的相应位。
宏的原材料来源于芯片手册,读芯片手册,确定寄存器的地址、位数(可以算出最大值)

2)如何使用snd_kcontrol

在应用程序中:
open("/dev/snd/controlC0"),打开底层的设备节点。对应的file_operations结构体在control.c中的snd_ctl_ioctl函数

static const struct file_operations snd_ctl_f_ops =
{
    .owner =    THIS_MODULE,
    .read =        snd_ctl_read,
    .open =        snd_ctl_open,
    .release =    snd_ctl_release,
    .llseek =    no_llseek,
    .poll =        snd_ctl_poll,
    .unlocked_ioctl =    snd_ctl_ioctl,
    .compat_ioctl =    snd_ctl_ioctl_compat,
    .fasync =    snd_ctl_fasync,
};
static long snd_ctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
    switch (cmd) {
    ...
    case SNDRV_CTL_IOCTL_ELEM_INFO:
            return snd_ctl_elem_info_user(ctl, argp);
    case SNDRV_CTL_IOCTL_ELEM_READ:
            return snd_ctl_elem_read_user(card, argp);
    case SNDRV_CTL_IOCTL_ELEM_WRITE:
            return snd_ctl_elem_write_user(ctl, argp);
    ...
    }
}
snd_ctl_elem_info_user
    snd_ctl_elem_info(ctl, &info)
        struct snd_kcontrol *kctl;
        kctl = snd_ctl_find_id(card, &info->id);//根据id从card中找到snd_kcontrol
        kctl->info(kctl, info);//调用snd_kcontrol中的info函数,因为snd_kcontrol又是由snd_kcontrol_new构造而成,从而调用snd_kcontrol_new中的info函数,即那些宏中的info函数

snd_ctl_elem_read_user
    snd_ctl_elem_read(card, control)
        struct snd_kcontrol *kctl;
        kctl = snd_ctl_find_id(card, &control->id);
         kctl->get(kctl, control); //调用宏中的get函数
         
snd_ctl_elem_write_user
    snd_ctl_elem_write(card, file, control)
        struct snd_kcontrol *kctl;
        kctl = snd_ctl_find_id(card, &control->id);
        kctl->put(kctl, control);

如果想看应用程序的话,可以参考文件tinymixer.c

原文地址:https://www.cnblogs.com/-glb/p/14391462.html