alsa_control_interface
控制接口对于许多开关(switch)和调节器(slider)应用广泛,它能被用户空间存取,从而读写CODEC相关寄存器
alsa的架构是清晰了,但是一直不明白,alsa的控制接口是如何被上层调用的
staticint wm8903_probe(struct platform_device *pdev)
{
structsnd_soc_device *socdev = platform_get_drvdata(pdev);
intret = 0;
if(!wm8903_codec) {
dev_err(&pdev->dev,"I2C device not yet probed\n");
gotoerr;
}
socdev->card->codec= wm8903_codec;
/*register pcms */
ret= snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
if(ret < 0) {
dev_err(&pdev->dev,"failed to create pcms\n");
gotoerr;
}
snd_soc_add_controls(socdev->card->codec,wm8903_snd_controls,
ARRAY_SIZE(wm8903_snd_controls));
wm8903_add_widgets(socdev->card->codec);
returnret;
err:
returnret;
}
可以看到在上面的snd_soc_add_controls这个函数就是在添加控制接口
staticconststruct snd_kcontrol_new wm8903_snd_controls[]
看下snd_kcontrol这个结构是什么样子的??
structsnd_kcontrol_new {
snd_ctl_elem_iface_tiface; /* interface identifier */
unsignedint device; /* device/client number */
unsignedint subdevice; /* subdevice (substream) number */
unsignedchar *name; /* ASCII name of item */
unsignedint index; /* index of item */
unsignedint access; /* access rights */
unsignedint 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;
constunsigned int *p;
}tlv;
unsignedlong private_value;
};
现在说说上面的这个结构里面的每一个成员到底是什么意思??
iface:定义了control的类型,形式为SND_CTL_ELEM_IFACE_XXX,对于mixter是SND_CTL_ELEM_IFACE_MIXER,对于不属于mixer的全局控制,使用CARD;如果关联到某类设备,则是PCM、RAWMIDI、TIMER或SEQUENCER。在这里,我们主要关注mixer。
name:字段是名称标识,这个字段非常重要,因为control的作用由名称来区分,对于名称相同的control,则使用index区分。下面会详细介绍上层应用如何根据name名称标识来找到底层相应的control。name定义的标准是“SOURCEDIRECTION 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宏:
我们这里遇到的宏有如下几类:
SOC_SINGLE("LeftInput PGA Switch", WM8903_ANALOGUE_LEFT_INPUT_0,7, 1, 1)
SOC_ENUM("DRCCompressor Slope R0", drc_slope_r0)
SOC_DOUBLE_R_TLV("DigitalPlayback Volume", WM8903_DAC_DIGITAL_VOLUME_LEFT,
WM8903_DAC_DIGITAL_VOLUME_RIGHT,1, 120, 0, digital_tlv),
下面分别看下以上三种类型的宏的定义是什么样子的:
#defineSOC_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) }
#defineWM8903_ANALOGUE_LEFT_INPUT_0 0x2C
上面的宏是MIXER
我们可以看到这个register是0x2c
上面宏的意思是:对寄存器0x2c的位便宜的第7位,可以设置的最大值是max也就是1,我们查询下datasheet后,发现第7位主要用来设置LeftInput PGA Mute 的,并且这一位只能有1或者0,因为最大位就是1,这里的invert的中文解释是反转,听同事讲解是一般默认情况下,1是open,0是close的这个invert如果是1的话就是和默认的相反的,如果是0的话,就是不反转的,是和默认是一样的
看下下一个宏:SOC_ENUM
SOC_ENUM("DRCCompressor Slope R0", drc_slope_r0)
staticconst structsoc_enum drc_slope_r0 =
SOC_ENUM_SINGLE(WM8903_DRC_2,3, 6, drc_slope_text);
#defineSOC_ENUM_SINGLE(xreg, xshift, xmax, xtexts) \
SOC_ENUM_DOUBLE(xreg,xshift, xshift, xmax, xtexts)
#defineSOC_ENUM_DOUBLE(xreg, xshift_l, xshift_r, xmax, xtexts) \
{ .reg= xreg, .shift_l = xshift_l, .shift_r = xshift_r, \
.max= xmax, .texts = xtexts }
#defineWM8903_DRC_2 0x2A
#defineSOC_ENUM(xname, xenum) \
{ .iface= SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname,\
.info= snd_soc_info_enum_double, \
.get= snd_soc_get_enum_double, .put = snd_soc_put_enum_double, \
.private_value= (unsignedlong)&xenum}
最后操作寄存器的值就是0x2A
这里的偏移是3,最大值就6,说明我们设置的值的种类最多是6种,我们查询下datasheet下:
可以发现从第三位开始,可以最多设置6种,这个应该是起压缩的作用,从description可以看出,compressorslope
接下来是下一个宏:
SOC_DOUBLE_R_TLV("DigitalPlayback Volume", WM8903_DAC_DIGITAL_VOLUME_LEFT,
WM8903_DAC_DIGITAL_VOLUME_RIGHT,1, 120, 0, digital_tlv),
#defineSOC_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= (unsignedlong)&(structsoc_mixer_control) \
{.reg= reg_left, .rreg = reg_right, .shift = xshift, \
.max= xmax, .platform_max = xmax, .invert = xinvert} }
上面的这个宏其实和其他的宏类似,但是这是对两个集训起reg_lift和reg_right进行操作,codec芯片中的左右声道一般差不多,所以就需要这个宏的存在
从上面可以看出,这两个寄存器的配置是一样的
我们看下Machine里面是如何添加每一个control的
snd_soc_add_controls(socdev->card->codec,wm8903_snd_controls,
ARRAY_SIZE(wm8903_snd_controls));
/**
* snd_soc_add_controls - add anarray of controls to a codec.
* Convienience function to add alist of controls. Many codecs were
* duplicating this code.
*
* @codec: codec to add controlsto
* @controls: array of controls toadd
* @num_controls: number ofelements in the array
*
* Return 0 for success, elseerror.
*/
intsnd_soc_add_controls(structsnd_soc_codec *codec,
conststructsnd_kcontrol_new *controls, intnum_controls)
{
structsnd_card *card = codec->card;
interr, i;
for(i = 0; i < num_controls; i++) {
conststructsnd_kcontrol_new *control = &controls[i];
err= snd_ctl_add(card, snd_soc_cnew(control, codec, NULL));
if(err < 0) {
dev_err(codec->dev,"%s: Failed to add %s\n",
codec->name,control->name);
returnerr;
}
}
return0;
}
EXPORT_SYMBOL_GPL(snd_soc_add_controls);
从上面的函数可以看出,我们会遍历出每一个control接口,然后进行添加
调用err= snd_ctl_add(card, snd_soc_cnew(control, codec, NULL));进行添加
在调用这个添加函数之前,调用snd_soc_cnew
看下这个函数是如何实现的:
/**
* snd_soc_cnew - create newcontrol
* @_template: control template
* @data: control private data
* @long_name: control long name
*
* Create a new mixer control froma template control.
*
* Returns 0 for success, elseerror.
*/
structsnd_kcontrol *snd_soc_cnew(conststructsnd_kcontrol_new *_template,
void*data, char*long_name)
{
structsnd_kcontrol_new template;
memcpy(&template,_template, sizeof(template));
if(long_name)
template.name= long_name;
template.index= 0;
returnsnd_ctl_new1(&template, data);
}
EXPORT_SYMBOL_GPL(snd_soc_cnew);
tracecode
跳入:snd_ctl_new1
/**
* snd_ctl_new1 - create a controlinstance from the template
* @ncontrol: the initializationrecord
* @private_data: the private datato set
*
* Allocates a new structsnd_kcontrol instance and initialize from the given
* template. When the accessfield of ncontrol is 0, it's assumed as
* READWRITE access. When thecount field is 0, it's assumes as one.
*
* Returns the pointer of thenewly generated instance, or NULL on failure.
*/
structsnd_kcontrol *snd_ctl_new1(conststructsnd_kcontrol_new *ncontrol,
void*private_data)
{
structsnd_kcontrol kctl;
unsignedintaccess;
if(snd_BUG_ON(!ncontrol || !ncontrol->info))
returnNULL;
memset(&kctl,0, sizeof(kctl));
kctl.id.iface= ncontrol->iface;
kctl.id.device= ncontrol->device;
kctl.id.subdevice= ncontrol->subdevice;
if(ncontrol->name){
strlcpy(kctl.id.name,ncontrol->name,sizeof(kctl.id.name));
if(strcmp(ncontrol->name, kctl.id.name) != 0)
snd_printk(KERN_WARNING
"Control name '%s' truncated to'%s'\n",
ncontrol->name, kctl.id.name);
}
kctl.id.index= ncontrol->index;
kctl.count= ncontrol->count? ncontrol->count: 1;
access= ncontrol->access== 0 ? SNDRV_CTL_ELEM_ACCESS_READWRITE :
(ncontrol->access& (SNDRV_CTL_ELEM_ACCESS_READWRITE|
SNDRV_CTL_ELEM_ACCESS_INACTIVE|
SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE|
SNDRV_CTL_ELEM_ACCESS_TLV_COMMAND|
SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK));
kctl.info= ncontrol->info;
kctl.get= ncontrol->get;
kctl.put= ncontrol->put;
kctl.tlv.p= ncontrol->tlv.p;
kctl.private_value= ncontrol->private_value;
kctl.private_data= private_data;
returnsnd_ctl_new(&kctl, access);
}
EXPORT_SYMBOL(snd_ctl_new1);
上面会进行structsnd_kcontrol 结构的创建,我们传进来的是structsnd_kcontrol_new结构类型的控制结构
structsnd_kcontrol {
structlist_head list; /* list of controls */
structsnd_ctl_elem_id id;
unsignedintcount; /* 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;
constunsignedint*p;
}tlv;
unsignedlongprivate_value;
void*private_data;
void(*private_free)(structsnd_kcontrol *kcontrol);
structsnd_kcontrol_volatile vd[0]; /* volatiledata */
};
上面会通过我们传进来的structsnd_kcontrol_new 类型的指针里面的值,进行为structsnd_kcontrol 赋值
最终就转化为kctl了,这里需要注意下,就是kctl.count,我们的ncontrol里面是没有count的,我在添加的宏里面没有找到count的赋值,所以这里测count就是1,还有一点就是kctl.id.index= ncontrol->index;我们这里添加的index的值都是0的,所以kctl.id的值都是0的
tracecode:snd_ctl_new
/**
* snd_ctl_new - create a controlinstance from the template
* @control: the control template
* @access: the default controlaccess
*
* Allocates a new structsnd_kcontrol instance and copies the given template
* to the new instance. It doesnot copy volatile data (access).
*
* Returns the pointer of the newinstance, or NULL on failure.
*/
staticstructsnd_kcontrol *snd_ctl_new(structsnd_kcontrol *control,
unsignedintaccess)
{
structsnd_kcontrol *kctl;
unsignedintidx;
if(snd_BUG_ON(!control || !control->count))
returnNULL;
if(control->count> MAX_CONTROL_COUNT)
returnNULL;
kctl= kzalloc(sizeof(*kctl)+ sizeof(structsnd_kcontrol_volatile) * control->count, GFP_KERNEL);
if(kctl == NULL) {
snd_printk(KERN_ERR"Cannot allocate controlinstance\n");
returnNULL;
}
*kctl= *control;
for(idx = 0; idx < kctl->count;idx++)
kctl->vd[idx].access= access;
returnkctl;
}
上面是为kctl进行进一步的初始化。然后将初始化好的kctl直接返回
返回到:err= snd_ctl_add(card, snd_soc_cnew(control, codec, NULL));
通过上面的分析,我们可以知道snd_soc_cnew返回的是structsnd_kcontrol 结构
接下来是添加的过程:snd_ctl_add
函数的原型如下:
/**
* snd_ctl_add - add the controlinstance to the card
* @card: the card instance
* @kcontrol: the control instanceto add
*
* Adds the control instancecreated via snd_ctl_new() or
* snd_ctl_new1() to the givencard. Assigns also an unique
* numid used for fast search.
*
* Returns zero if successful, ora negative error code on failure.
*
* It frees automatically thecontrol which cannot be added.
*/
intrt(structsnd_card *card, structsnd_kcontrol *kcontrol)
{
structsnd_ctl_elem_id id;
unsignedintidx;
interr = -EINVAL;
if(! kcontrol)
returnerr;
if(snd_BUG_ON(!card || !kcontrol->info))
gotoerror;
id= kcontrol->id;
down_write(&card->controls_rwsem);
if(snd_ctl_find_id(card, &id)) {
up_write(&card->controls_rwsem);
snd_printd(KERN_ERR"control %i:%i:%i:%s:%i is alreadypresent\n",
id.iface,
id.device,
id.subdevice,
id.name,
id.index);
err= -EBUSY;
gotoerror;
}
if(snd_ctl_find_hole(card, kcontrol->count)< 0) {
up_write(&card->controls_rwsem);
err= -ENOMEM;
gotoerror;
}
list_add_tail(&kcontrol->list,&card->controls);
card->controls_count+= kcontrol->count;
kcontrol->id.numid= card->last_numid+ 1;
card->last_numid+= kcontrol->count;
up_write(&card->controls_rwsem);
for(idx = 0; idx < kcontrol->count;idx++, id.index++,id.numid++)
snd_ctl_notify(card,SNDRV_CTL_EVENT_MASK_ADD, &id);
return0;
error:
snd_ctl_free_one(kcontrol);
returnerr;
}
EXPORT_SYMBOL(snd_ctl_add);
看下if语句里面的snd_ctl_find_id函数,这个函数还是挺重要的
看下snd_ctl_find_id函数的原型
/**
* snd_ctl_find_id - find thecontrol instance with the given id
* @card: the card instance
* @id: the id to search
*
* Finds the control instance withthe given id from the card.
*
* Returns the pointer of theinstance if found, or NULL if not.
*
* The caller must downcard->controls_rwsem before calling this function
* (if the race condition canhappen).
*/
structsnd_kcontrol *snd_ctl_find_id(structsnd_card *card,
structsnd_ctl_elem_id *id)
{
structsnd_kcontrol *kctl;
if(snd_BUG_ON(!card || !id))
returnNULL;
if(id->numid!= 0)
returnsnd_ctl_find_numid(card, id->numid);
list_for_each_entry(kctl,&card->controls, list){
if(kctl->id.iface != id->iface)
continue;
if(kctl->id.device != id->device)
continue;
if(kctl->id.subdevice != id->subdevice)
continue;
if(strncmp(kctl->id.name, id->name, sizeof(kctl->id.name)))
continue;
if(kctl->id.index > id->index)
continue;
if(kctl->id.index + kctl->count <= id->index)
continue;
returnkctl;
}
returnNULL;
}
EXPORT_SYMBOL(snd_ctl_find_id);
我们首先先判断这个kctl->id->numid是否有值,我们这个里面还没有值,所以snd_ctl_find_numid不会执行的
接下来会就会遍历card->controls链表,这里这个链表里面还没有值,因为我们添加的control还没有添加进去,所以直接返回NULL,其实snd_ctl_find_numid还是很重要的,上层就是通过mumid找到对应的control的,接下来会讲解,现在返回到intrt(structsnd_card *card, structsnd_kcontrol *kcontrol)
现在讲解到snd_ctl_find_hole(card,kcontrol->count):
staticintsnd_ctl_find_hole(structsnd_card *card, unsignedintcount)
{
unsignedintlast_numid, iter = 100000;
last_numid= card->last_numid;
while(last_numid != snd_ctl_hole_check(card, count)) {
if(--iter == 0) {
/*this situation is very unlikely */
snd_printk(KERN_ERR"unable to allocate new controlnumid\n");
return-ENOMEM;
}
last_numid= card->last_numid;
}
return0;
}
trace code:snd_ctl_hole_check(card, count
staticunsignedintsnd_ctl_hole_check(structsnd_card *card,
unsignedintcount)
{
structsnd_kcontrol *kctl;
list_for_each_entry(kctl,&card->controls, list){
if((kctl->id.numid <= card->last_numid &&
kctl->id.numid + kctl->count > card->last_numid) ||
(kctl->id.numid <= card->last_numid + count - 1 &&
kctl->id.numid + kctl->count > card->last_numid +count - 1))
returncard->last_numid = kctl->id.numid + kctl->count - 1;
}
returncard->last_numid;
}
同样的的道理,因为我们的card->controls链表是没有值的,所以直接返回
接下来是:list_add_tail(&kcontrol->list,&card->controls);
看到没有,这里才开始将我们的创加的kcontrol添加到card->controls链表里面去,所以上面是遍历不到kcontrol的
continuetrace code:
card->controls_count+= kcontrol->count;
kcontrol->id.numid= card->last_numid+ 1;
card->last_numid+= kcontrol->count;
上面的代码很重要,因为
我讲解下上面到底在完成什么工作??
还记得我在上面讲解的吗?kcontrol->count的值是1,应该说每个control的count都是1
1、card->controls_count+=kcontrol->count 就是记录添加到card->controls链表链表里面的control的数目
2、kcontrol->id.numid= card->last_numid+ 1;用于记录这个control的numid
3、card->last_numid+= kcontrol->count;改变上一次的card->last_numid,因为这个时候,已经添加了新的control了,数目也增加了,所以需要改变下
接下来是:
for(idx = 0; idx < kcontrol->count;idx++, id.index++,id.numid++)
snd_ctl_notify(card,SNDRV_CTL_EVENT_MASK_ADD, &id);
voidsnd_ctl_notify(structsnd_card *card, unsignedintmask,
structsnd_ctl_elem_id *id)
{
unsignedlongflags;
structsnd_ctl_file *ctl;
structsnd_kctl_event *ev;
if(snd_BUG_ON(!card || !id))
return;
read_lock(&card->ctl_files_rwlock);
#ifdefined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE)
card->mixer_oss_change_count++;
#endif
list_for_each_entry(ctl,&card->ctl_files, list){
if(!ctl->subscribed)
continue;
spin_lock_irqsave(&ctl->read_lock,flags);
list_for_each_entry(ev,&ctl->events, list) {
if(ev->id.numid == id->numid) {
ev->mask|= mask;
goto_found;
}
}
ev= kzalloc(sizeof(*ev),GFP_ATOMIC);
if(ev) {
ev->id= *id;
ev->mask= mask;
list_add_tail(&ev->list,&ctl->events);
}else{
snd_printk(KERN_ERR"No memory available to allocateevent\n");
}
_found:
wake_up(&ctl->change_sleep);
spin_unlock_irqrestore(&ctl->read_lock,flags);
kill_fasync(&ctl->fasync,SIGIO, POLL_IN);
}
read_unlock(&card->ctl_files_rwlock);
}
EXPORT_SYMBOL(snd_ctl_notify);
上面的函数,其实是没有起任何作用的因为在caard->ctl_files是没有任何值的,我是没有找到,我在kernel/sound/下面是没有找到初始化ctl_files和添加ctl_files的动作
好了,这个添加control:snd_soc_add_controls的函数已经讲解完了
接下来说下,上层是如何通过结构,操作底层的
这里需要提前调用下整个声卡测注册函数:
就在staticvoidsnd_soc_instantiate_card(structsnd_soc_card *card)
{
...............
…..............省略
…..............
ret= snd_card_register(codec->card);
...............
…..............省略
…..............
}
看到上面的函数没有??当声卡的所有组件都创建成功后,也就是前期的工作都完成后,那么就会调用上面的函数进行整个声卡设备的创建
看下这个函数的原型
/**
* snd_card_register - registerthe soundcard
* @card: soundcard structure
*
* This function registers allthe devices assigned to the soundcard.
* Until calling this, the ALSAcontrol interface is blocked from the
* external accesses. Thus, youshould call this function at the end
* of the initialization of thecard.
*
* Returns zero otherwise anegative error code if the registrain failed.
*/
intsnd_card_register(structsnd_card *card)
{
interr;
if(snd_BUG_ON(!card))
return-EINVAL;
#ifndefCONFIG_SYSFS_DEPRECATED
if(!card->card_dev){
card->card_dev= device_create(sound_class, card->dev,
MKDEV(0, 0), card,
"card%i",card->number);
if(IS_ERR(card->card_dev))
card->card_dev= NULL;
}
#endif
if((err = snd_device_register_all(card)) < 0)
returnerr;
mutex_lock(&snd_card_mutex);
if(snd_cards[card->number]){
/*already registered */
mutex_unlock(&snd_card_mutex);
return0;
}
snd_card_set_id_no_lock(card,card->id[0]== '\0' ?NULL : card->id);
snd_cards[card->number]= card;
mutex_unlock(&snd_card_mutex);
init_info_for_card(card);
#ifdefined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE)
if(snd_mixer_oss_notify_callback)
snd_mixer_oss_notify_callback(card,SND_MIXER_OSS_NOTIFY_REGISTER);
#endif
#ifndefCONFIG_SYSFS_DEPRECATED
if(card->card_dev){
err= device_create_file(card->card_dev,&card_id_attrs);
if(err < 0)
returnerr;
err= device_create_file(card->card_dev,&card_number_attrs);
if(err < 0)
returnerr;
}
#endif
return0;
}
EXPORT_SYMBOL(snd_card_register);
continuetrace code:
snd_device_register_all(card)
/*
* register all the devices on thecard.
* called from init.c
*/
intsnd_device_register_all(structsnd_card *card)
{
structsnd_device *dev;
interr;
if(snd_BUG_ON(!card))
return-ENXIO;
list_for_each_entry(dev,&card->devices, list){
if(dev->state == SNDRV_DEV_BUILD && dev->ops->dev_register){
if((err = dev->ops->dev_register(dev)) < 0)
returnerr;
dev->state= SNDRV_DEV_REGISTERED;
}
}
return0;
}
上面就会遍历声卡结构里面的deviceds链表,然后调用对应的device->ops->dev_register进行这个component的创建
先知道这些,上面以后会详细讲解的
还有一点需要知道的就是:
在wm8903_probe函数里面
staticint wm8903_probe(structplatform_device *pdev)
{
…...............
…...............省略
…...............
ret= snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
…...............
…...............省略
…...............
}
因为我们的control的系统调用和上面的函数有关联,所以我就提下上面的函数
tracecode:
/**
* snd_soc_new_pcms - create newsound card and pcms
* @socdev: the SoC audio device
* @idx: ALSA card index
* @xid: card identification
*
* Create a new sound card basedupon the codec and interface pcms.
*
* Returns 0 for success, elseerror.
*/
intsnd_soc_new_pcms(structsnd_soc_device *socdev, intidx, constchar*xid)
{
structsnd_soc_card *card = socdev->card;
structsnd_soc_codec *codec = card->codec;
intret, i;
mutex_lock(&codec->mutex);
/*register a sound card */
ret= snd_card_create(idx, xid, codec->owner,0, &codec->card);
if(ret < 0) {
printk(KERN_ERR"asoc: can't create sound card forcodec %s\n",
codec->name);
mutex_unlock(&codec->mutex);
returnret;
}
codec->socdev= socdev;
codec->card->dev= socdev->dev;
codec->card->private_data= codec;
strncpy(codec->card->driver,codec->name,sizeof(codec->card->driver));
/*create the pcms */
for(i = 0; i < card->num_links;i++) {
ret= soc_new_pcm(socdev, &card->dai_link[i],i);
if(ret < 0) {
printk(KERN_ERR"asoc: can't create pcm %s\n",
card->dai_link[i].stream_name);
mutex_unlock(&codec->mutex);
returnret;
}
/*Check for codec->ac97 to handle the ac97.c fun */
if(card->dai_link[i].codec_dai->ac97_control&& codec->ac97){
snd_ac97_dev_add_pdata(codec->ac97,
card->dai_link[i].cpu_dai->ac97_pdata);
}
}
mutex_unlock(&codec->mutex);
returnret;
}
EXPORT_SYMBOL_GPL(snd_soc_new_pcms);
我们这里只需要关注ret= snd_card_create(idx, xid, codec->owner,0, &codec->card);函数就可以了
continuetrace code:
intsnd_card_create(int idx,const char*xid,
structmodule *module, intextra_size,
structsnd_card **card_ret)
{
structsnd_card *card;
interr, idx2;
if(snd_BUG_ON(!card_ret))
return-EINVAL;
*card_ret= NULL;
if(extra_size < 0)
extra_size= 0;
card= kzalloc(sizeof(*card)+ extra_size, GFP_KERNEL);
…..........................
…..........................省略
…..........................
err= snd_ctl_create(card);
if(err < 0) {
snd_printk(KERN_ERR"unable to register controlminors\n");
goto__error;
}
err= snd_info_card_create(card);
if(err < 0) {
snd_printk(KERN_ERR"unable to create card info\n");
goto__error_ctl;
}
if(extra_size > 0)
card->private_data= (char*)card + sizeof(structsnd_card);
*card_ret= card;
return0;
__error_ctl:
snd_device_free_all(card,SNDRV_DEV_CMD_PRE);
__error:
kfree(card);
returnerr;
}
EXPORT_SYMBOL(snd_card_create);
上面主要完成声卡结构的创建和声卡control的创建
在上面我们这里看下err= snd_ctl_create(card);函数
看下上面函数的原型
/*
* create control core:
* called from init.c
*/
intsnd_ctl_create(structsnd_card *card)
{
staticstructsnd_device_ops ops = {
.dev_free= snd_ctl_dev_free,
.dev_register= snd_ctl_dev_register,
.dev_disconnect= snd_ctl_dev_disconnect,
};
if(snd_BUG_ON(!card))
return-ENXIO;
returnsnd_device_new(card, SNDRV_DEV_CONTROL, card, &ops);
}
看到上面的ops没有,我在上面所说的做后调用声卡里面的component的ops里面的注册函数,这里是control,所以在注册control的时候就会调用snd_ctl_dev_register进行注册
继续将上面的代码讲解完
trace snd_device_new
/**
* snd_device_new - create an ALSAdevice component
* @card: the card instance
* @type: the device type,SNDRV_DEV_XXX
* @device_data: the data pointerof this device
* @ops: the operator table
*
* Creates a new device componentfor the given data pointer.
* The device will be assigned tothe card and managed together
* by the card.
*
* The data pointer plays a roleas the identifier, too, so the
* pointer address must be uniqueand unchanged.
*
* Returns zero if successful, ora negative error code on failure.
*/
intsnd_device_new(structsnd_card *card, snd_device_type_ttype,
void*device_data, structsnd_device_ops *ops)
{
structsnd_device *dev;
if(snd_BUG_ON(!card || !device_data || !ops))
return-ENXIO;
dev= kzalloc(sizeof(*dev),GFP_KERNEL);
if(dev == NULL) {
snd_printk(KERN_ERR"Cannot allocate device\n");
return-ENOMEM;
}
dev->card= card;
dev->type= type;
dev->state= SNDRV_DEV_BUILD;
dev->device_data= device_data;
dev->ops= ops;
list_add(&dev->list,&card->devices); /*add to the head of list */
return0;
}
EXPORT_SYMBOL(snd_device_new);
snd_device_new函数完成的功能就是创建controldevice ,并将创建的device添加到card->devices链表里面
经过上面的一些相关知识的讲解,我想下面对讲解control的系统调用就好理解了:
这里我们需要讲解声卡的control的注册函数:snd_ctl_dev_register(也就是controldevice 的ops->dev_register 函数)
函数的原型如下:
/*
* registration of the controldevice
*/
staticintsnd_ctl_dev_register(structsnd_device *device)
{
structsnd_card *card = device->device_data;
interr, cardnum;
charname[16];
if(snd_BUG_ON(!card))
return-ENXIO;
cardnum= card->number;
if(snd_BUG_ON(cardnum < 0 || cardnum >= SNDRV_CARDS))
return-ENXIO;
sprintf(name,"controlC%i",cardnum);
if((err = snd_register_device(SNDRV_DEVICE_TYPE_CONTROL, card, -1,
&snd_ctl_f_ops, card, name)) < 0)
returnerr;
return0;
}
看下snd_ctl_f_ops:
staticconststructfile_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,
};
这个注册函数主要调用的是snd_register_device
continuetrace code:
/**
* snd_register_device - Registerthe ALSA device file for the card
* @type: the device type,SNDRV_DEVICE_TYPE_XXX
* @card: the card instance
* @dev: the device index
* @f_ops: the file operations
* @private_data: user pointer forf_ops->open()
* @name: the device file name
*
* Registers an ALSA device filefor the given card.
* The operators have to be set inreg parameter.
*
* This function uses the card'sdevice pointer to link to the
* correct &struct device.
*
* Returns zero if successful, ora negative error code on failure.
*/
staticinlineintsnd_register_device(inttype, structsnd_card *card, intdev,
conststructfile_operations *f_ops,
void*private_data,
constchar*name)
{
returnsnd_register_device_for_dev(type, card, dev, f_ops,
private_data, name,
snd_card_get_device_link(card));
}
trace code :
/**
* snd_register_device_for_dev -Register the ALSA device file for the card
* @type: the device type,SNDRV_DEVICE_TYPE_XXX
* @card: the card instance
* @dev: the device index
* @f_ops: the file operations
* @private_data: user pointer forf_ops->open()
* @name: the device file name
* @device: the &struct deviceto link this new device to
*
* Registers an ALSA device filefor the given card.
* The operators have to be set inreg parameter.
*
* Returns zero if successful, ora negative error code on failure.
*/
intsnd_register_device_for_dev(inttype, structsnd_card *card, intdev,
conststructfile_operations *f_ops,
void*private_data,
constchar*name, structdevice *device)
{
intminor;
structsnd_minor *preg;
if(snd_BUG_ON(!name))
return-EINVAL;
preg= kmalloc(sizeof*preg, GFP_KERNEL);
if(preg == NULL)
return-ENOMEM;
preg->type= type;
preg->card= card ? card->number: -1;
preg->device= dev;
preg->f_ops= f_ops;
preg->private_data= private_data;
mutex_lock(&sound_mutex);
#ifdefCONFIG_SND_DYNAMIC_MINORS
minor= snd_find_free_minor();
#else
minor= snd_kernel_minor(type, card, dev);
if(minor >= 0 && snd_minors[minor])
minor= -EBUSY;
#endif
if(minor < 0) {
mutex_unlock(&sound_mutex);
kfree(preg);
returnminor;
}
snd_minors[minor]= preg;
preg->dev= device_create(sound_class, device, MKDEV(major, minor),
private_data, "%s",name);
if(IS_ERR(preg->dev)){
snd_minors[minor]= NULL;
mutex_unlock(&sound_mutex);
minor= PTR_ERR(preg->dev);
kfree(preg);
returnminor;
}
mutex_unlock(&sound_mutex);
return0;
}
EXPORT_SYMBOL(snd_register_device_for_dev);
看到没有上面的device_create(sound_class,device, MKDEV(major, minor),就是在创建control的devicefile ,在soundclass 下面 sound_class= class_create(THIS_MODULE,"sound");这是设备模型里面的基础知识,我们进入pad后可以看到control对应的device
知道我们创建了设备文件,在Linux下,设备都是以文件访问的,我们这里的control设备也是不例外的,当上层的系统调用操作这个设备的时候,最终就会调用这个设备的file_operation里面的操作函数进行操作
我们看下上面的file_operation里面的成员方法
从用户的角度看的话,需要操作设备的时候,要先打开设备,然后再进行操作设备,比如read、write、ioctl等一些操作
先看下open函数:snd_ctl_open
staticint snd_ctl_open(structinode *inode, struct file *file)
{
unsignedlongflags;
structsnd_card *card;
structsnd_ctl_file *ctl;
interr;
err= nonseekable_open(inode, file);
if(err < 0)
returnerr;
card= snd_lookup_minor_data(iminor(inode), SNDRV_DEVICE_TYPE_CONTROL);
if(!card) {
err= -ENODEV;
goto__error1;
}
err= snd_card_file_add(card, file);
if(err < 0) {
err= -ENODEV;
goto__error1;
}
if(!try_module_get(card->module)){
err= -EFAULT;
goto__error2;
}
ctl= kzalloc(sizeof(*ctl),GFP_KERNEL);
if(ctl == NULL) {
err= -ENOMEM;
goto__error;
}
INIT_LIST_HEAD(&ctl->events);
init_waitqueue_head(&ctl->change_sleep);
spin_lock_init(&ctl->read_lock);
ctl->card= card;
ctl->prefer_pcm_subdevice= -1;
ctl->prefer_rawmidi_subdevice= -1;
ctl->pid= get_pid(task_pid(current));
file->private_data= ctl;
write_lock_irqsave(&card->ctl_files_rwlock,flags);
list_add_tail(&ctl->list,&card->ctl_files);
write_unlock_irqrestore(&card->ctl_files_rwlock,flags);
return0;
__error:
module_put(card->module);
__error2:
snd_card_file_remove(card,file);
__error1:
returnerr;
}
1、在open函数里面主要做了创建structsnd_ctl_file 结构的动作,并创建好的ctl赋值到file->private_data
2、将初始化好的ctl值,添加到card->ctl_files链表里面
上面的open函数大概的就是这样,接下来,我们看下我们重点:.unlocked_ioctl= snd_ctl_ioctl,
ioctl函数,是我们在驱动里面经常需要实现的函数,上层只要下cmd命令就可以控制设备了
函数的原型如下:
staticlongsnd_ctl_ioctl(structfile *file, unsignedintcmd, unsignedlongarg)
{
structsnd_ctl_file *ctl;
structsnd_card *card;
structsnd_kctl_ioctl *p;
void__user *argp = (void__user *)arg;
int__user *ip = argp;
interr;
ctl= file->private_data;
card= ctl->card;
if(snd_BUG_ON(!card))
return-ENXIO;
switch(cmd) {
caseSNDRV_CTL_IOCTL_PVERSION:
returnput_user(SNDRV_CTL_VERSION, ip) ? -EFAULT : 0;
caseSNDRV_CTL_IOCTL_CARD_INFO:
returnsnd_ctl_card_info(card, ctl, cmd, argp);
caseSNDRV_CTL_IOCTL_ELEM_LIST:
returnsnd_ctl_elem_list(card, argp);
caseSNDRV_CTL_IOCTL_ELEM_INFO:
returnsnd_ctl_elem_info_user(ctl, argp);
caseSNDRV_CTL_IOCTL_ELEM_READ:
returnsnd_ctl_elem_read_user(card, argp);
caseSNDRV_CTL_IOCTL_ELEM_WRITE:
returnsnd_ctl_elem_write_user(ctl, argp);
caseSNDRV_CTL_IOCTL_ELEM_LOCK:
returnsnd_ctl_elem_lock(ctl, argp);
caseSNDRV_CTL_IOCTL_ELEM_UNLOCK:
returnsnd_ctl_elem_unlock(ctl, argp);
caseSNDRV_CTL_IOCTL_ELEM_ADD:
returnsnd_ctl_elem_add_user(ctl, argp, 0);
caseSNDRV_CTL_IOCTL_ELEM_REPLACE:
returnsnd_ctl_elem_add_user(ctl, argp, 1);
caseSNDRV_CTL_IOCTL_ELEM_REMOVE:
returnsnd_ctl_elem_remove(ctl, argp);
caseSNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS:
returnsnd_ctl_subscribe_events(ctl, ip);
caseSNDRV_CTL_IOCTL_TLV_READ:
returnsnd_ctl_tlv_ioctl(ctl, argp, 0);
caseSNDRV_CTL_IOCTL_TLV_WRITE:
returnsnd_ctl_tlv_ioctl(ctl, argp, 1);
caseSNDRV_CTL_IOCTL_TLV_COMMAND:
returnsnd_ctl_tlv_ioctl(ctl, argp, -1);
caseSNDRV_CTL_IOCTL_POWER:
return-ENOPROTOOPT;
caseSNDRV_CTL_IOCTL_POWER_STATE:
#ifdefCONFIG_PM
returnput_user(card->power_state, ip) ? -EFAULT : 0;
#else
returnput_user(SNDRV_CTL_POWER_D0, ip) ? -EFAULT : 0;
#endif
}
down_read(&snd_ioctl_rwsem);
list_for_each_entry(p,&snd_control_ioctls, list){
err= p->fioctl(card, ctl, cmd, arg);
if(err != -ENOIOCTLCMD) {
up_read(&snd_ioctl_rwsem);
returnerr;
}
}
up_read(&snd_ioctl_rwsem);
snd_printdd("unknownioctl = 0x%x\n", cmd);
return-ENOTTY;
}
有很多的case我选择其中一个case进行讲解:
caseSNDRV_CTL_IOCTL_ELEM_WRITE:
returnsnd_ctl_elem_write_user(ctl, argp);
上面的函数,前面会取出private_data的值,我们还记得private的值是哪里来的吗?是在我们的open函数里面进行赋值的
然后trace case SNDRV_CTL_IOCTL_ELEM_WRITE
staticintsnd_ctl_elem_write_user(structsnd_ctl_file *file,
structsnd_ctl_elem_value __user *_control)
{
structsnd_ctl_elem_value *control;
structsnd_card *card;
intresult;
control= memdup_user(_control, sizeof(*control));
if(IS_ERR(control))
returnPTR_ERR(control);
card= file->card;
snd_power_lock(card);
result= snd_power_wait(card, SNDRV_CTL_POWER_D0);
if(result >= 0)
result= snd_ctl_elem_write(card, file, control);
snd_power_unlock(card);
if(result >= 0)
if(copy_to_user(_control, control, sizeof(*control)))
result= -EFAULT;
kfree(control);
returnresult;
}
会将用户空间的arg参数传递过去,因为是用户空间的数据,内核空间不能直接使用,所以需要进行转化下,才可以使用,相当于copyfrom user,这里看下用户空间传递的参数的类型是什么类型的??
structsnd_ctl_elem_value *control
structsnd_ctl_elem_value {
structsnd_ctl_elem_id id; /*W: element ID */
unsignedintindirect:1; /* W: indirect access - obsoleted */
union{
union{
longvalue[128];
long*value_ptr; /*obsoleted */
}integer;
union{
longlongvalue[64];
longlong*value_ptr; /*obsoleted */
}integer64;
union{
unsignedintitem[128];
unsignedint*item_ptr; /*obsoleted */
}enumerated;
union{
unsignedchardata[512];
unsignedchar*data_ptr; /*obsoleted */
}bytes;
structsnd_aes_iec958 iec958;
}value; /*RO */
structtimespec tstamp;
unsignedcharreserved[128-sizeof(structtimespec)];
};
看见没有,用户空间需要传递的参数的结构就是上面
tracecode:
result= snd_ctl_elem_write(card, file, control);
staticint snd_ctl_elem_write(structsnd_card *card, structsnd_ctl_file *file,
structsnd_ctl_elem_value *control)
{
structsnd_kcontrol *kctl;
structsnd_kcontrol_volatile *vd;
unsignedintindex_offset;
intresult;
down_read(&card->controls_rwsem);
kctl= snd_ctl_find_id(card, &control->id);
if(kctl == NULL) {
result= -ENOENT;
}else{
index_offset= snd_ctl_get_ioff(kctl, &control->id);
vd= &kctl->vd[index_offset];
if(!(vd->access& SNDRV_CTL_ELEM_ACCESS_WRITE) ||
kctl->put== NULL ||
(file && vd->owner&& vd->owner!= file)) {
result= -EPERM;
}else{
snd_ctl_build_ioff(&control->id,kctl, index_offset);
result= kctl->put(kctl,control);
}
if(result > 0) {
up_read(&card->controls_rwsem);
snd_ctl_notify(card,SNDRV_CTL_EVENT_MASK_VALUE,
&control->id);
return0;
}
}
up_read(&card->controls_rwsem);
returnresult;
}
我们通过传递control->id给给snd_ctl_find函数,其实我们就是通过这个id->numid找到对应的control的
接下来我们需要tracecode :snd_ctl_find_id
snd_ctl_find_id这个函数,不知道,你们还是否记得,我们在添加control的时候,也遇到过,只是那个时候遍历的control链表还是空的,还没有添加进去,忘记的可以回过头来再看下
/**
* snd_ctl_find_id - find thecontrol instance with the given id
* @card: the card instance
* @id: the id to search
*
* Finds the control instance withthe given id from the card.
*
* Returns the pointer of theinstance if found, or NULL if not.
*
* The caller must downcard->controls_rwsem before calling this function
* (if the race condition canhappen).
*/
structsnd_kcontrol *snd_ctl_find_id(structsnd_card *card,
structsnd_ctl_elem_id *id)
{
structsnd_kcontrol *kctl;
if(snd_BUG_ON(!card || !id))
returnNULL;
if(id->numid!= 0)
returnsnd_ctl_find_numid(card, id->numid);
list_for_each_entry(kctl,&card->controls, list){
if(kctl->id.iface != id->iface)
continue;
if(kctl->id.device != id->device)
continue;
if(kctl->id.subdevice != id->subdevice)
continue;
if(strncmp(kctl->id.name, id->name, sizeof(kctl->id.name)))
continue;
if(kctl->id.index > id->index)
continue;
if(kctl->id.index + kctl->count <= id->index)
continue;
returnkctl;
}
returnNULL;
}
EXPORT_SYMBOL(snd_ctl_find_id);
continuetrace code:snd_ctl_find_numid(card, id->numid)
/**
* snd_ctl_find_numid - find thecontrol instance with the given number-id
* @card: the card instance
* @numid: the number-id to search
*
* Finds the control instance withthe given number-id from the card.
*
* Returns the pointer of theinstance if found, or NULL if not.
*
* The caller must downcard->controls_rwsem before calling this function
* (if the race condition canhappen).
*/
structsnd_kcontrol *snd_ctl_find_numid(structsnd_card *card, unsignedintnumid)
{
structsnd_kcontrol *kctl;
if(snd_BUG_ON(!card || !numid))
returnNULL;
list_for_each_entry(kctl,&card->controls, list){
if(kctl->id.numid <= numid && kctl->id.numid +kctl->count > numid)
returnkctl;
}
returnNULL;
}
EXPORT_SYMBOL(snd_ctl_find_numid);
上面代码有没有看到,这里就是思想,我们驱动中会遍历声卡中的每一个control,通过numid找到对用的kctl,找到相应的kctl后,然后直接返回相对应的kctl
再次返回到:staticintsnd_ctl_elem_write(structsnd_card *card, structsnd_ctl_file *file, structsnd_ctl_elem_value *control)
我们通过numid找到对应的control以后,继续执行下面的代码
通过判断我们添加的control的acess是否是write权限
然后调用我们的kctl->put 函数
我们需要找到我们在驱动里面实现的control 的put函数
/*Headphones */
SOC_DOUBLE_R("HeadphoneSwitch",
WM8903_ANALOGUE_OUT1_LEFT, WM8903_ANALOGUE_OUT1_RIGHT,
8, 1, 1),
#defineSOC_DOUBLE_R(xname, reg_left, reg_right, xshift, xmax, xinvert) \
{ .iface= SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
.info= snd_soc_info_volsw_2r, \
.get= snd_soc_get_volsw_2r, .put = snd_soc_put_volsw_2r, \
.private_value= (unsignedlong)&(structsoc_mixer_control) \
{.reg= reg_left, .rreg = reg_right, .shift = xshift, \
.max= xmax, .platform_max = xmax, .invert = xinvert} }
上面的put函数就是snd_soc_put_volsw_2r
trace上面的函数是如何实现的???
/**
* snd_soc_put_volsw_2r - doublemixer set callback
* @kcontrol: mixer control
* @ucontrol: control elementinformation
*
* Callback to set the value of adouble mixer control that spans 2 registers.
*
* Returns 0 for success.
*/
intsnd_soc_put_volsw_2r(structsnd_kcontrol *kcontrol,
structsnd_ctl_elem_value *ucontrol)
{
structsoc_mixer_control *mc =
(structsoc_mixer_control *)kcontrol->private_value;
structsnd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
unsignedintreg = mc->reg;
unsignedintreg2 = mc->rreg;
unsignedintshift = mc->shift;
intmax = mc->max;
unsignedintmask = (1 << fls(max)) - 1;
unsignedintinvert = mc->invert;
interr;
unsignedintval, 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_locked(codec, reg, val_mask, val);
if(err < 0)
returnerr;
err= snd_soc_update_bits_locked(codec, reg2, val_mask, val2);
returnerr;
}
EXPORT_SYMBOL_GPL(snd_soc_put_volsw_2r);
tracecode:
下面会调用
err= snd_soc_update_bits_locked(codec, reg, val_mask, val);真正的操作codec的寄存器
/**
* snd_soc_update_bits_locked -update codec register bits
* @codec: audio codec
* @reg: codec register
* @mask: register mask
* @value: new value
*
* Writes new register value, andtakes the codec mutex.
*
* Returns 1 for change else 0.
*/
intsnd_soc_update_bits_locked(structsnd_soc_codec *codec,
unsignedshortreg, unsignedintmask,
unsignedintvalue)
{
intchange;
mutex_lock(&codec->mutex);
change= snd_soc_update_bits(codec, reg, mask, value);
mutex_unlock(&codec->mutex);
returnchange;
}
EXPORT_SYMBOL_GPL(snd_soc_update_bits_locked);
到这里我们还不能停止,我们不防继续追踪下代码,我们的snd_soc_update_bits函数
/**
* snd_soc_update_bits - updatecodec register bits
* @codec: audio codec
* @reg: codec register
* @mask: register mask
* @value: new value
*
* Writes new register value.
*
* Returns 1 for change else 0.
*/
intsnd_soc_update_bits(structsnd_soc_codec *codec, unsignedshortreg,
unsignedintmask, unsignedintvalue)
{
intchange;
unsignedintold, new;
old= snd_soc_read(codec, reg);
new= (old & ~mask) | value;
change= old != new;
if(change)
snd_soc_write(codec,reg, new);
returnchange;
}
EXPORT_SYMBOL_GPL(snd_soc_update_bits);
上面会进行判断,如果你这次操作的工作和以前的是一样的话,那么就没有必要重复操作codec了,如果不一样的话,才会去操作codec
看到没有,我们读操作用的是snd_soc_read
写操作用的是:snd_soc_write
/*codec IO */
staticinlineunsignedintsnd_soc_read(structsnd_soc_codec *codec,
unsignedintreg)
{
returncodec->read(codec,reg);
}
staticinlineunsignedintsnd_soc_write(structsnd_soc_codec *codec,
unsignedintreg, unsignedintval)
{
returncodec->write(codec,reg, val);
}
最终调用的是codec里面的read和write方法
这里我们需要查找这个read和write的方法
最终我们在codecdriver里面找到了这两个方法的赋值
codecdriver path :/kernel/driver/sound/soc/codec/wm8903.c里面
这里只是暂时提下,在codecdriver 里面会详细讲解这个driver
在这个driver的probe函数里面会有一句:ret= snd_soc_codec_set_cache_io(codec, 8, 16, SND_SOC_I2C);
/**
* snd_soc_codec_set_cache_io: Setup standard I/O functions.
*
* @codec: CODEC to configure.
* @type: Type of cache.
* @addr_bits: Number of bits ofregister address data.
* @data_bits: Number of bits ofdata per register.
* @control: Control bus used.
*
* Register formats are frequentlyshared between many I2C and SPI
* devices. In order to promotecode reuse the ASoC core provides
* some standard implementationsof CODEC read and write operations
* which can be set up using thisfunction.
*
* The caller is responsible forallocating and initialising the
* actual cache.
*
* Note that at present this codecannot be used by CODECs with
* volatile registers.
*/
intsnd_soc_codec_set_cache_io(structsnd_soc_codec *codec,
intaddr_bits, intdata_bits,
enumsnd_soc_control_type control)
{
inti;
for(i = 0; i < ARRAY_SIZE(io_types); i++)
if(io_types[i].addr_bits== addr_bits &&
io_types[i].data_bits== data_bits)
break;
if(i == ARRAY_SIZE(io_types)) {
printk(KERN_ERR
"No I/O functions for %d bitaddress %d bit data\n",
addr_bits, data_bits);
return-EINVAL;
}
codec->write= io_types[i].write;
codec->read= io_types[i].read;
switch(control) {
caseSND_SOC_CUSTOM:
break;
caseSND_SOC_I2C:
#ifdefined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) &&defined(MODULE))
codec->hw_write= (hw_write_t)i2c_master_send;
#endif
if(io_types[i].i2c_read)
codec->hw_read= io_types[i].i2c_read;
break;
caseSND_SOC_SPI:
if(io_types[i].spi_write)
codec->hw_write= io_types[i].spi_write;
break;
}
return0;
}
EXPORT_SYMBOL_GPL(snd_soc_codec_set_cache_io);
io_types[] = {
{
.addr_bits= 4, .data_bits= 12,
.write= snd_soc_4_12_write, .read= snd_soc_4_12_read,
.spi_write= snd_soc_4_12_spi_write,
},
{
.addr_bits= 7, .data_bits= 9,
.write= snd_soc_7_9_write, .read= snd_soc_7_9_read,
.spi_write= snd_soc_7_9_spi_write,
},
{
.addr_bits= 8, .data_bits= 8,
.write= snd_soc_8_8_write, .read= snd_soc_8_8_read,
.i2c_read= snd_soc_8_8_read_i2c,
},
{
.addr_bits= 8, .data_bits= 16,
.write= snd_soc_8_16_write, .read= snd_soc_8_16_read,
.i2c_read= snd_soc_8_16_read_i2c,
},
{
.addr_bits= 16, .data_bits= 8,
.write= snd_soc_16_8_write, .read= snd_soc_16_8_read,
.i2c_read= snd_soc_16_8_read_i2c,
.spi_write= snd_soc_16_8_spi_write,
},
{
.addr_bits= 16, .data_bits= 16,
.write= snd_soc_16_16_write, .read= snd_soc_16_16_read,
.i2c_read= snd_soc_16_16_read_i2c,
},
};
我们传递是8和16,所以我们找到对应的read和write方法
.write= snd_soc_8_16_write, .read= snd_soc_8_16_read,
.i2c_read= snd_soc_8_16_read_i2c,
至于上面两个方法是如何实现的,因为在codecdriver 里面也会调用上面的方法,在那里会详细讲解,这里只需要最终调用的是codec里面的方法进行操作就可以了。到这里alsa的控制接口的讲解就已经讲解完了,从上层到底层,应该说比较详细的