zoukankan      html  css  js  c++  java
  • snd_kcontrol探究

    control控制接口


    控制接口对于许多开关(switch)和调节器(slider)应用广泛,它能被用户空间存取,从而读写CODEC相关寄存器。control的主要用于mixer。它用snd_kcontrol_new结构体描述。


    snd_kcontrol_new


    1. struct snd_kcontrol_new {    
    2.     snd_ctl_elem_iface_t iface; /* interface identifier */    
    3.     unsigned int device;        /* device/client number */    
    4.     unsigned int subdevice;     /* subdevice (substream) number */    
    5.     unsigned char *name;        /* ASCII name of item */    
    6.     unsigned int index;     /* index of item */    
    7.     unsigned int access;        /* access rights */    
    8.     unsigned int count;     /* count of same elements */    
    9.     snd_kcontrol_info_t *info;    
    10.     snd_kcontrol_get_t *get;    
    11.     snd_kcontrol_put_t *put;    
    12.     union {    
    13.         snd_kcontrol_tlv_rw_t *c;    
    14.         const unsigned int *p;    
    15.     } tlv;    
    16.     unsigned long private_value;    
    17. };  


    iface字段 定义了control的类型,形式为SNDRV_CTL_ELEM_IFACE_XXX,对于mixer是SNDRV_CTL_ELEM_IFACE_MIXER,对于不属于mixer的全局控制,使用CARD;如果关联到某类设备,则是PCM、RAWMIDI、TIMER或SEQUENCER。在这里,我们主要关注mixer。

    name字段 是名称标识,这个字段非常重要,因为control的作用由名称来区分,对于名称相同的control,则使用index区分。下面会详细介绍 上层应用 如何根据 name名称标识 来找到 底层相应的control。

    name定义的标准是“SOURCE DIRECTION FUNCTION”即“源 方向 功能”,SOURCE定义了control的源,如“Master”、“PCM”等;DIRECTION 则为“Playback”、“Capture”等,如果DIRECTION忽略,意味着Playback和capture双向;FUNCTION则可以是“Switch”、“Volume”和“Route”等。

    上层也可以根据numid来找到对应的control,snd_ctl_find_id()也是优先判断上层是否传递了numid,是则直接返回这个numid对应的control。用户层设置numid和control的关联时,可用alsa-lib的snd_mixer_selem_set_enum_item()函数。snd_kcontrol_new结构体并没有numid这个成员,是因为numid是系统自动管理的,原则是该control的注册次序,保存到snd_ctl_elem_value结构体中。

    access字段是访问控制权限。SNDRV_CTL_ELEM_ACCESS_READ意味着只读,这时put()函数不必实现;SNDRV_CTL_ELEM_ACCESS_WRITE意味着只写,这时get()函数不必实现。若control值频繁变化,则需定义 VOLATILE标志。当control处于非激活状态时,应设置INACTIVE标志。

    private_value字段包含1个长整型值,可以通过它给info()、get()和put()函数传递参数。


    kcontrol宏


    在早期的ALSA创建一个新的control需要实现snd_kcontrol_new中的info、get和put这三个成员函数。现在较新版本的ALSA均定义了一些宏,如:

    1. #define SOC_SINGLE(xname, reg, shift, max, invert) /    
    2. {   .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, /    
    3.     .info = snd_soc_info_volsw, .get = snd_soc_get_volsw,/    
    4.     .put = snd_soc_put_volsw, /    
    5.     .private_value =  SOC_SINGLE_VALUE(reg, shift, max, invert) }   
    这个宏的对象是MIXER,对寄存器reg的位偏移shift可以设置0-max的数值。

    又如:

    1. #define SOC_DOUBLE_R_TLV(xname, reg_left, reg_right, xshift, xmax, xinvert, tlv_array) /    
    2. {   .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),/    
    3.     .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |/    
    4.          SNDRV_CTL_ELEM_ACCESS_READWRITE,/    
    5.     .tlv.p = (tlv_array), /    
    6.     .info = snd_soc_info_volsw_2r, /    
    7.     .get = snd_soc_get_volsw_2r, .put = snd_soc_put_volsw_2r, /    
    8.     .private_value = (unsigned long)&(struct soc_mixer_control) /    
    9.         {.reg = reg_left, .rreg = reg_right, .shift = xshift, /    
    10.         .max = xmax, .invert = xinvert} }  
    这个宏与刚才类似,但是它是对两个寄存器reg_left和reg_right进行同一操作,Codec芯片中左右声道的寄存器配置一般来说是差不多的,这就是这个宏存在的意义。

    例如我们一个Playback Volume的kcontrol接口这样定义:

    SOC_DOUBLE_R_TLV("Playback Volume", REG_VOL_L, REG_VOL_R, 0, 192, 0, digital_tlv)

    我们仅仅需要将Volume寄存器地址及位偏移,最大值填进去即可,当然这些数据要从Codec的datasheet取得。这里Volume寄存器地址是REG_VOL_L(左声道)和REG_VOL_R(右声道),位偏移为0,DAC Digital Gain范围是0-192(steps)。


    触发过程


    为了探讨这些kcontrol是如何触发的,我们以SOC_DOUBLE_R_TLV的put函数为例说明:

    1. /**  
    2.  * snd_soc_put_volsw_2r - double mixer set callback  
    3.  * @kcontrol: mixer control  
    4.  * @ucontrol: control element information  
    5.  *  
    6.  * Callback to set the value of a double mixer control that spans 2 registers.  
    7.  *  
    8.  * Returns 0 for success.  
    9.  */    
    10. int snd_soc_put_volsw_2r(struct snd_kcontrol *kcontrol,    
    11.     struct snd_ctl_elem_value *ucontrol)    
    12. {    
    13.     struct soc_mixer_control *mc =    
    14.         (struct soc_mixer_control *)kcontrol->private_value;    
    15.     struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);    
    16.     unsigned int reg = mc->reg;    
    17.     unsigned int reg2 = mc->rreg;    
    18.     unsigned int shift = mc->shift;    
    19.     int max = mc->max;    
    20.     unsigned int mask = (1 << fls(max)) - 1;    
    21.     unsigned int invert = mc->invert;    
    22.     int err;    
    23.     unsigned int val, val2, val_mask;    
    24.     
    25.     val_mask = mask << shift;    
    26.     val = (ucontrol->value.integer.value[0] & mask);    
    27.     val2 = (ucontrol->value.integer.value[1] & mask);    
    28.      
    29.     if (invert) {    
    30.         val = max - val;    
    31.         val2 = max - val2;    
    32.     }     
    33.     
    34.     val = val << shift;    
    35.     val2 = val2 << shift;    
    36.     
    37.     err = snd_soc_update_bits(codec, reg, val_mask, val);    
    38.     if (err < 0)    
    39.         return err;    
    40.     
    41.     err = snd_soc_update_bits(codec, reg2, val_mask, val2);    
    42.     return err;    
    43. }  
    struct snd_ctl_elem_value *ucontrol:从用户层传递下来的,这个也可以从命名看出来(kcontrol-kernel control,ucontrol-user control);

    shift是位偏移,而位掩码mask是通过宏SOC_DOUBLE_R_TLV中的xmax运算得到:unsigned int mask = (1 << fls(max)) - 1;

    调用snd_soc_update_bits()->snd_soc_write()将ucontrol的value送到CODEC的寄存器上。

    snd_soc_put_volsw_2r()作为一个callback函数,用户层要设置某些功能时,如改变Playback Volume:

    #amixer cset numid=3,iface=MIXER,name='Playback Volume' 100

    注:amixer相关用法见:http://hi.baidu.com/serial_story/blog/item/c4e826d82a562f3f32fa1c31.html


    到内核层时,会遍历一个节点类型为struct snd_kcontrol *的链表,找到kcontrol.id.numid与3相匹配的kctl(这个过程见snd_ctl_find_id()函数),然后调用kctl.put()函数将100写到Playback Volume寄存器中。当然如果上层没有提供numid,则可根据name找到kcontrol.id.name相匹配的kctl。详细见snd_ctl_find_id函数:

    1. /**  
    2.  * snd_ctl_find_id - find the control instance with the given id  
    3.  * @card: the card instance  
    4.  * @id: the id to search  
    5.  *  
    6.  * Finds the control instance with the given id from the card.  
    7.  *  
    8.  * Returns the pointer of the instance if found, or NULL if not.  
    9.  *  
    10.  * The caller must down card->controls_rwsem before calling this function  
    11.  * (if the race condition can happen).  
    12.  */    
    13. struct snd_kcontrol *snd_ctl_find_id(struct snd_card *card,    
    14.                      struct snd_ctl_elem_id *id)    
    15. {    
    16.     struct snd_kcontrol *kctl;    
    17.     
    18.     if (snd_BUG_ON(!card || !id))    
    19.         return NULL;    
    20.     if (id->numid != 0)    
    21.         return snd_ctl_find_numid(card, id->numid);    
    22.     list_for_each_entry(kctl, &card->controls, list) {    
    23.         if (kctl->id.iface != id->iface)    
    24.             continue;    
    25.         if (kctl->id.device != id->device)    
    26.             continue;    
    27.         if (kctl->id.subdevice != id->subdevice)    
    28.             continue;    
    29.         if (strncmp(kctl->id.name, id->name, sizeof(kctl->id.name)))    
    30.             continue;    
    31.         if (kctl->id.index > id->index)    
    32.             continue;    
    33.         if (kctl->id.index + kctl->count <= id->index)    
    34.             continue;    
    35.         return kctl;    
    36.     }    
    37.     return NULL;    
    38. }   

    从上往下的大致流程:

    1. amixer-用户层    
    2.   |->snd_ctl_ioctl-系统调用    
    3.        |->snd_ctl_elem_write_user-内核钩子函数    
    4.             |->snd_ctl_elem_wirte-    
    5.                  |->snd_ctl_find_id-遍历kcontrol链表找到name字段匹配的kctl    
    6.                  |->kctl->put()-调用kctl的成员函数put()    
    7.                       |->snd_soc_put_volsw_2r  





  • 相关阅读:
    [原]POJ1141 Brackets Sequence (dp动态规划,递归)
    [转]10分钟入门python
    [原]sdut2605 A^X mod P 山东省第四届ACM省赛(打表,快速幂模思想,哈希)
    [原]SQL_实验2.1.3 清华大学出版社
    [原]sdut2624 Contest Print Server (大水+大坑)山东省第四届ACM省赛
    [原]hdu2191 悼念512汶川大地震遇难同胞——珍惜现在,感恩生活 (这个只是题目名字) (多重背包)
    快速幂运算
    山东省acm省赛 I Sequence(动态规划)
    [ACM] 携程预赛第一场 括号匹配 (动态规划)
    [ACM] poj 1141 Brackets Sequence (动态规划)
  • 原文地址:https://www.cnblogs.com/liulaolaiu/p/11744605.html
Copyright © 2011-2022 走看看