zoukankan      html  css  js  c++  java
  • 声卡控制之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

  • 相关阅读:
    2019-2020-1 20199324《Linux内核原理与分析》第七周作业
    2019-2020-1 20199324《Linux内核原理与分析》第六周作业
    2019-2020-1 20199324《Linux内核原理与分析》第五周作业
    介绍一个比较了各种浏览器对于HTML5 等标准支持程度的网站
    JaveScript 中的正则表达式
    Windows中查看进程的资源消耗(cpu, Disk,Memory,NetWork)
    Windows中通过命令行启动打开Service 管理工具
    删除Widows 启动项中的信息
    LAMP中添加多虚拟主机
    多线程的安全问题
  • 原文地址:https://www.cnblogs.com/-glb/p/14391462.html
Copyright © 2011-2022 走看看