ALSA之snd_kcontrol探究
来源:互联网 发布:淘宝链接跳转淘宝app 编辑:程序博客网 时间:2024/04/30 01:34
转自http://blog.csdn.net/sepnic/article/details/6150723#comments
control控制接口
控制接口对于许多开关(switch)和调节器(slider)应用广泛,它能被用户空间存取,从而读写CODEC相关寄存器。control的主要用于mixer。它用snd_kcontrol_new结构体描述。
snd_kcontrol_new
- struct snd_kcontrol_new {
- snd_ctl_elem_iface_t iface; /* interface identifier */
- unsigned int device; /* device/client number */
- unsigned int subdevice; /* subdevice (substream) number */
- unsigned char *name; /* ASCII name of item */
- unsigned int index; /* index of item */
- unsigned int access; /* access rights */
- unsigned int count; /* count of same elements */
- snd_kcontrol_info_t *info;
- snd_kcontrol_get_t *get;
- snd_kcontrol_put_t *put;
- union {
- snd_kcontrol_tlv_rw_t *c;
- const unsigned int *p;
- } tlv;
- unsigned long private_value;
- };
struct snd_kcontrol_new { snd_ctl_elem_iface_t iface; /* interface identifier */ unsigned int device; /* device/client number */ unsigned int subdevice; /* subdevice (substream) number */ unsigned char *name; /* ASCII name of item */ unsigned int index; /* index of item */ unsigned int access; /* access rights */ unsigned int count; /* count of same elements */ snd_kcontrol_info_t *info; snd_kcontrol_get_t *get; snd_kcontrol_put_t *put; union { snd_kcontrol_tlv_rw_t *c; const unsigned int *p; } tlv; unsigned long private_value; };
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均定义了一些宏,如:
- #define SOC_SINGLE(xname, reg, shift, max, invert) /
- { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, /
- .info = snd_soc_info_volsw, .get = snd_soc_get_volsw,/
- .put = snd_soc_put_volsw, /
- .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert) }
#define SOC_SINGLE(xname, reg, shift, max, invert) / { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, / .info = snd_soc_info_volsw, .get = snd_soc_get_volsw,/ .put = snd_soc_put_volsw, / .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert) }这个宏的对象是MIXER,对寄存器reg的位偏移shift可以设置0-max的数值。
又如:
- #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_2r, /
- .get = snd_soc_get_volsw_2r, .put = snd_soc_put_volsw_2r, /
- .private_value = (unsigned long)&(struct soc_mixer_control) /
- {.reg = reg_left, .rreg = reg_right, .shift = xshift, /
- .max = xmax, .invert = xinvert} }
#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_2r, / .get = snd_soc_get_volsw_2r, .put = snd_soc_put_volsw_2r, / .private_value = (unsigned long)&(struct soc_mixer_control) / {.reg = reg_left, .rreg = reg_right, .shift = xshift, / .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函数为例说明:
- /**
- * snd_soc_put_volsw_2r - double mixer set callback
- * @kcontrol: mixer control
- * @ucontrol: control element information
- *
- * Callback to set the value of a double mixer control that spans 2 registers.
- *
- * Returns 0 for success.
- */
- int snd_soc_put_volsw_2r(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
- {
- struct soc_mixer_control *mc =
- (struct soc_mixer_control *)kcontrol->private_value;
- struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
- unsigned int reg = mc->reg;
- unsigned int reg2 = mc->rreg;
- unsigned int shift = mc->shift;
- int max = mc->max;
- unsigned int mask = (1 << fls(max)) - 1;
- unsigned int invert = mc->invert;
- int err;
- unsigned int val, val2, val_mask;
- val_mask = mask << shift;
- val = (ucontrol->value.integer.value[0] & mask);
- val2 = (ucontrol->value.integer.value[1] & mask);
- if (invert) {
- val = max - val;
- val2 = max - val2;
- }
- val = val << shift;
- val2 = val2 << shift;
- err = snd_soc_update_bits(codec, reg, val_mask, val);
- if (err < 0)
- return err;
- err = snd_soc_update_bits(codec, reg2, val_mask, val2);
- return err;
- }
/** * snd_soc_put_volsw_2r - double mixer set callback * @kcontrol: mixer control * @ucontrol: control element information * * Callback to set the value of a double mixer control that spans 2 registers. * * Returns 0 for success. */ int snd_soc_put_volsw_2r(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); unsigned int reg = mc->reg; unsigned int reg2 = mc->rreg; unsigned int shift = mc->shift; int max = mc->max; unsigned int mask = (1 << fls(max)) - 1; unsigned int invert = mc->invert; int err; unsigned int val, val2, val_mask; val_mask = mask << shift; val = (ucontrol->value.integer.value[0] & mask); val2 = (ucontrol->value.integer.value[1] & mask); if (invert) { val = max - val; val2 = max - val2; } val = val << shift; val2 = val2 << shift; err = snd_soc_update_bits(codec, reg, val_mask, val); if (err < 0) return err; err = snd_soc_update_bits(codec, reg2, val_mask, val2); return err; }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。
从上往下的大致流程:
- amixer-用户层
- |->snd_ctl_ioctl-系统调用
- |->snd_ctl_elem_write_user-内核钩子函数
- |->snd_ctl_elem_wirte-
- |->snd_ctl_find_id-遍历kcontrol链表找到name字段匹配的kctl
- |->kctl->put()-调用kctl的成员函数put()
- |->snd_soc_put_volsw_2r
- ALSA之snd_kcontrol探究
- ALSA之snd_kcontrol探究
- snd_kcontrol探究
- snd_kcontrol探究
- snd_kcontrol探究
- snd_kcontrol探究
- snd_kcontrol探究
- snd_kcontrol探究
- snd_kcontrol探究
- snd_kcontrol探究
- snd_kcontrol探究
- ALSA驱动之-------ALSA架构简介
- 【ALSA】使用ALSA進行音訊資料擷取 之其二
- ALSA驱动之-------ALSA架构简介
- ALSA之CODEC分析
- ALSA之PCM分析
- ALSA之PCM分析
- ALSA之CODEC分析
- Dithering-视觉的奇特现象
- 写给自己的 自考北大计算机与应用专业
- 自己对Java中==与equals比较的理解
- linux kill命令参数及用法详解--linux终止进程命令
- Dither
- ALSA之snd_kcontrol探究
- Top K算法详细解析—百度面试
- 下载centOS-->torrent文件
- JavaIO流(7)
- 从浏览器“罢工”中,感知到自己的错误认识
- 第二周 任务3 输入x计算并输出分段函数F(x)的值
- C/C++ 的位运算
- UDK游戏引擎新手一定可以遇到的细节方面的问题!
- ubuntu中文版man手册配置方法