snd_kcontrol的分析
2011-06-24 19:54:45
本文参考了:AZURE的文章《snd_kcontrol探究》
http://blog.csdn.net/sepnic/archive/2011/01/18/6150723.aspx
用snd_kcontrol_new结构体了codec可供控制的部分,包括:通道切换(switch/mixer),音量调整(volume)。
下面根据具体代码分析一下snd_kcontrol_new的构建和使用。
TLV:具体指的是什么? 是Tag,Length,Value(标记、长度、值)吗?
iface字段定义了control的类型,形式为SNDRV_CTL_ELEM_IFACE_XXX,对于mixer是SNDRV_CTL_ELEM_IFACE_MIXER,对于不属于mixer的全局控制,使用CARD;如果关联到某类设备,则是PCM、RAWMIDI、TIMER或SEQUENCER。在这里,我们主要关注mixer。
name字段是名称标识:name定义的标准是“SOURCE DIRECTION FUNCTION”即“源 方向 功能”:
SOURCE定义了control的源,如“Master”、“PCM”等;
DIRECTION 则为“Playback”、“Capture”等,如果DIRECTION忽略,意味着Playback和capture双向;
FUNCTION则可以是“Switch”、“Volume”和“Route”等。
【???】名字这样设置的原因??难道alsa_lib中做了名字的解析?
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均定义了一些宏(可能是因为寄存器的操作有一定的共性,抽象出来比较简炼),如:
例如我们一个Playback Volume的kcontrol接口这样定义:
SOC_DOUBLE_R_TLV("Playback Volume", REG_VOL_L, REG_VOL_R, 0, 192, 0, digital_tlv)
以这个control为例,改变Playback Volume到100,使用一下命令:
通过上面给定的Volume寄存器地址及位偏移,以及音量值,填写寄存器。
从数据手册得知:Volume寄存器地址REG_VOL_L(左声道)和REG_VOL_R(右声道),音量bit filed的位偏移(shift)是0,DAC Digital Gain范围是0-192(steps).
位掩码mask是通过宏SOC_DOUBLE_R_TLV中的xmax运算得到:unsigned int mask = (1 << fls(max)) - 1;
到内核层时,会遍历一个节点类型为struct snd_kcontrol *的链表,找到kcontrol.id.numid与3相匹配的kctl(这个过程见snd_ctl_find_id()函数),然后调用
kctl.put()--snd_soc_put_volsw_2r用snd_soc_update_bits()->snd_soc_write()--->codec->write
将100写到Playback Volume寄存器中。
从上往下的大致流程:
amixer-用户层
|->snd_ctl_ioctl-系统调用
|->snd_ctl_elem_write_user-内核钩子函数
|->snd_ctl_elem_wirte-
|->snd_ctl_find_id-遍历kcontrol链表找到与给定id相匹配的kctl
|->kctl->put()-调用kctl的成员函数put()
|->snd_soc_put_volsw_2r
总结:
从上面可以看出,alsa drvier 到alsa_lib和alsa_utils,它用了很多心思,一大堆代码,希望使用者知道的尽量少,做的尽量少。
但结构的过于复杂和不直观,实际上会让使用者花费大量精力来理解它的思路。
据传google正在重做中间层的那些库,包括alsa_lib。
但alsa依然潇洒的活在linux内核里。这个世界如此的寂寞。别人无法理解,是因为它不懂欣赏。那就孤独的前行吧。