ALsa Control 从上层到驱动的详解

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,对于mixterSND_CTL_ELEM_IFACE_MIXER,对于不属于mixer的全局控制,使用CARD;如果关联到某类设备,则是PCMRAWMIDITIMERSEQUENCER。在这里,我们主要关注mixer


name字段是名称标识,这个字段非常重要,因为control的作用由名称来区分,对于名称相同的control,则使用index区分。下面会详细介绍上层应用如何根据name名称标识来找到底层相应的controlname定义的标准是“SOURCEDIRECTION FUNCTION”即“源方向 功能”,SOURCE定义了control的源,如“Master”、“PCM”等;DIRECTION则为“Playback”、“Capture”等,如果DIRECTION忽略,意味着Playbackcapture双向;FUNCTION则可以是“Switch”、“Volume”和“Route”等。上层也可以根据numid来找到对应的controlsnd_ctl_find_id()也是优先判断上层是否传递了numid,是则直接返回这个numid对应的control。用户层设置numidcontrol的关联时,可用alsa-libsnd_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

我们可以看到这个register0x2c



上面宏的意思是:对寄存器0x2c的位便宜的第7位,可以设置的最大值是max也就是1,我们查询下datasheet后,发现第7位主要用来设置LeftInput PGA Mute 的,并且这一位只能有1或者0,因为最大位就是1,这里的invert的中文解释是反转,听同事讲解是一般默认情况下,1open0close的这个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_liftreg_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,应该说每个controlcount都是1

1card->controls_count+=kcontrol->count 就是记录添加到card->controls链表链表里面的control的数目

2kcontrol->id.numid= card->last_numid+ 1;用于记录这个controlnumid

3card->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的动作


好了,这个添加controlsnd_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没有,我在上面所说的做后调用声卡里面的componentops里面的注册函数,这里是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),就是在创建controldevicefile ,soundclass 下面 sound_class= class_create(THIS_MODULE,"sound");这是设备模型里面的基础知识,我们进入pad后可以看到control对应的device






知道我们创建了设备文件,在Linux下,设备都是以文件访问的,我们这里的control设备也是不例外的,当上层的系统调用操作这个设备的时候,最终就会调用这个设备的file_operation里面的操作函数进行操作


我们看下上面的file_operation里面的成员方法

从用户的角度看的话,需要操作设备的时候,要先打开设备,然后再进行操作设备,比如readwriteioctl等一些操作


先看下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以后,继续执行下面的代码


通过判断我们添加的controlacess是否是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里面的readwrite方法

这里我们需要查找这个readwrite的方法


最终我们在codecdriver里面找到了这两个方法的赋值

codecdriver path :/kernel/driver/sound/soc/codec/wm8903.c里面



这里只是暂时提下,在codecdriver 里面会详细讲解这个driver


在这个driverprobe函数里面会有一句: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,

},

};

我们传递是816,所以我们找到对应的readwrite方法

.write= snd_soc_8_16_write, .read= snd_soc_8_16_read,

.i2c_read= snd_soc_8_16_read_i2c,


至于上面两个方法是如何实现的,因为在codecdriver 里面也会调用上面的方法,在那里会详细讲解,这里只需要最终调用的是codec里面的方法进行操作就可以了。到这里alsa的控制接口的讲解就已经讲解完了,从上层到底层,应该说比较详细的


  • 3
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
ALSA(Advanced Linux Sound Architecture)是Linux内核中的音频驱动框架,它提供了一套完整的音频处理流程,包括音频采集、音频处理和音频播放等功能。ALSA框架的设计目标是提供一个高效、灵活、可靠的音频处理框架,让开发人员能够方便地开发音频应用程序。 ALSA框架的核心包括以下几个组件: 1. 驱动程序:驱动程序是ALSA框架的核心组件,负责管理音频设备硬件,并提供音频数据输入输出的接口。ALSA驱动程序一般由硬件厂商或开源社区开发,可以通过内核模块的形式加载到Linux内核中。 2. 应用程序接口:ALSA框架提供了一套完整的应用程序接口,包括ALSA库和ALSA命令行工具。ALSA库提供了一组API,让开发人员能够方便地访问ALSA驱动程序提供的音频数据输入输出接口。ALSA命令行工具则提供了一组命令行工具,让用户能够方便地对音频设备进行配置和管理。 3. 中间件:ALSA框架还提供了一些中间件组件,如MIDI子系统、混音器子系统等,用于提供更高级的音频处理功能。 ALSA框架的音频处理流程如下: 1. 音频采集:当音频设备接收到音频信号时,ALSA驱动程序将音频信号采集到内存中,并通过DMA(直接内存访问)将音频数据写入音频缓冲区。 2. 音频处理:ALSA驱动程序将音频信号从音频缓冲区读取到内存中,然后对音频数据进行处理。音频处理包括音频格式转换、音频采样率转换、音频混音等处理。 3. 音频播放:ALSA驱动程序将处理后的音频数据从内存中读取到音频缓冲区,并通过DMA将音频数据传输到音频设备中进行播放。 总之,ALSA框架提供了一套完整的音频处理流程,让开发人员能够方便地开发音频应用程序,并提供了一组API和命令行工具,方便用户对音频设备进行配置和管理。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值