Linux ALSA声卡驱动之二:声卡的创建

声明:本博内容均由http://blog.csdn.net/droidphone

1. struct snd_card

1.1. snd_card是什么

snd_card可以说是整个ALSA音频驱动最顶层的一个结构,整个声卡的软件逻辑结构开始于该结构,几乎所有与声音相关的逻辑设备都是在snd_card的管理之下,声卡驱动的第一个动作通常就是创建一个snd_card结构体。正因为如此,本节中,我们也从 struct cnd_card开始吧。

1.2. snd_card的定义

snd_card的定义位于头文件中:include/sound/core.h

[c-sharp]view plaincopy

    /*mainstructureforsoundcard*/structsnd_card{intnumber;/*numberofsoundcard(indextosnd_cards)*/charid[16];/*idstringofthiscard*/chardriver[16];/*drivername*/charshortname[32];/*shortnameofthissoundcard*/charlongname[80];/*nameofthissoundcard*/charmixername[80];/*mixername*/charcomponents[128];/*cardcomponentsdelimitedwithspace*/structmodule*module;/*top-levelmodule*/void*private_data;/*privatedataforsoundcard*/void(*private_free)(structsnd_card*card);/*callbackforfreeingofprivatedata*/structlist_headdevices;/*devices*/unsignedintlast_numid;/*lastusednumericID*/structrw_semaphorecontrols_rwsem;/*controlslistlock*/rwlock_tctl_files_rwlock;/*ctl_fileslistlock*/intcontrols_count;/*countofallcontrols*/intuser_ctl_count;/*countofallusercontrols*/structlist_headcontrols;/*allcontrolsforthiscard*/structlist_headctl_files;/*activecontrolfiles*/structsnd_info_entry*proc_root;/*rootforsoundcardspecificfiles*/structsnd_info_entry*proc_id;/*thecardid*/structproc_dir_entry*proc_root_link;/*numberlinktorealid*/structlist_headfiles_list;/*allfilesassociatedtothiscard*/structsnd_shutdown_f_ops*s_f_ops;/*fileoperationsintheshutdownstate*/spinlock_tfiles_lock;/*lockthefilesforthiscard*/intshutdown;/*thiscardisgoingdown*/intfree_on_last_close;/*freeincontextoffile_release*/wait_queue_head_tshutdown_sleep;structdevice*dev;/*deviceassignedtothiscard*/#ifndefCONFIG_SYSFS_DEPRECATEDstructdevice*card_dev;/*cardXobjectforsysfs*/#endif#ifdefCONFIG_PMunsignedintpower_state;/*powerstate*/structmutexpower_lock;/*powerlock*/wait_queue_head_tpower_sleep;#endif#ifdefined(CONFIG_SND_MIXER_OSS)||defined(CONFIG_SND_MIXER_OSS_MODULE)structsnd_mixer_oss*mixer_oss;intmixer_oss_change_count;#endif};

struct list_head devices 记录该声卡下所有逻辑设备的链表struct list_head controls 记录该声卡下所有的控制单元的链表void *private_data 声卡的私有数据,可以在创建声卡时通过参数指定数据的大小2. 声卡的建立流程

2.1.1. 第一步,创建snd_card的一个实例

[c-sharp]view plaincopy

    structsnd_card*card;interr;….err=snd_card_create(index,id,THIS_MODULE,0,&card);

index 一个整数值,该声卡的编号id 字符串,声卡的标识符第四个参数 该参数决定在创建snd_card实例时,需要同时额外分配的私有数据的大小,该数据的指针最终会赋值给snd_card的private_data数据成员card 返回所创建的snd_card实例的指针

2.1.2. 第二步,创建声卡的芯片专用数据

声卡的专用数据主要用于存放该声卡的一些资源信息,例如中断资源、io资源、dma资源等。可以有两种创建方法:

通过上一步中snd_card_create()中的第四个参数,让snd_card_create自己创建[c-sharp]view plaincopy

    //structmychip用于保存专用数据err=snd_card_create(index,id,THIS_MODULE,sizeof(structmychip),&card);//从private_data中取出structmychip*chip=card->private_data;

自己创建:[c-sharp]view plaincopy

    structmychip{structsnd_card*card;….};structsnd_card*card;structmychip*chip;err=snd_card_create(index[dev],id[dev],THIS_MODULE,0,&card);//专用数据记录snd_card实例chip->card=card;…..chip=kzalloc(sizeof(*chip),GFP_KERNEL);

然后,把芯片的专有数据注册为声卡的一个低阶设备:

[c-sharp]view plaincopy

    staticintsnd_mychip_dev_free(structsnd_device*device){returnsnd_mychip_free(device->device_data);}staticstructsnd_device_opsops={.dev_free=snd_mychip_dev_free,};….snd_device_new(card,SNDRV_DEV_LOWLEVEL,chip,&ops);

2.1.3. 第三步,设置Driver的ID和名字[c-sharp]view plaincopy

    strcpy(card->driver,"MyChip");strcpy(card->shortname,"MyOwnChip123");sprintf(card->longname,"%sat0x%lxirq%i",card->shortname,chip->ioport,chip->irq);

snd_card的driver字段保存着芯片的ID字符串,user空间的alsa-lib会使用到该字符串,所以必须要保证该ID的唯一性。shortname字段更多地用于打印信息,longname字段则会出现在/proc/asound/cards中。

2.1.4. 第四步,创建声卡的功能部件(逻辑设备),例如PCM,Mixer,MIDI等

这时候可以创建声卡的各种功能部件了,还记得开头的snd_card结构体的devices字段吗?每一种部件的创建最终会调用snd_device_new()来生成一个snd_device实例,并把该实例链接到snd_card的devices链表中。

通常,alsa-driver的已经提供了一些常用的部件的创建函数,而不必直接调用snd_device_new(),比如:

PCM —- snd_pcm_new()

RAWMIDI — snd_rawmidi_new()

CONTROL — snd_ctl_create()

TIMER — snd_timer_new()

INFO — snd_card_proc_new()

JACK — snd_jack_new()

2.1.5. 第五步,注册声卡

[c-sharp]view plaincopy

    err=snd_card_register(card);if(err<0){snd_card_free(card);returnerr;}

2.2. 一个实际的例子

我把/sound/arm/pxa2xx-ac97.c的部分代码贴上来:

[cpp]view plaincopy

    staticint__devinitpxa2xx_ac97_probe(structplatform_device*dev){structsnd_card*card;structsnd_ac97_bus*ac97_bus;structsnd_ac97_templateac97_template;intret;pxa2xx_audio_ops_t*pdata=dev->dev.platform_data;if(dev->id>=0){dev_err(&dev->dev,"PXA2xxhasonlyoneAC97port./n");ret=-ENXIO;gotoerr_dev;}////(1)////ret=snd_card_create(SNDRV_DEFAULT_IDX1,SNDRV_DEFAULT_STR1,THIS_MODULE,0,&card);if(ret<0)gotoerr;card->dev=&dev->dev;////(3)////strncpy(card->driver,dev->dev.driver->name,sizeof(card->driver));////(4)////ret=pxa2xx_pcm_new(card,&pxa2xx_ac97_pcm_client,&pxa2xx_ac97_pcm);if(ret)gotoerr;////(2)////ret=pxa2xx_ac97_hw_probe(dev);if(ret)gotoerr;////(4)////ret=snd_ac97_bus(card,0,&pxa2xx_ac97_ops,NULL,&ac97_bus);if(ret)gotoerr_remove;memset(&ac97_template,0,sizeof(ac97_template));ret=snd_ac97_mixer(ac97_bus,&ac97_template,&pxa2xx_ac97_ac97);if(ret)gotoerr_remove;////(3)////snprintf(card->shortname,sizeof(card->shortname),"%s",snd_ac97_get_short_name(pxa2xx_ac97_ac97));snprintf(card->longname,sizeof(card->longname),"%s(%s)",dev->dev.driver->name,card->mixername);if(pdata&&pdata->codec_pdata[0])snd_ac97_dev_add_pdata(ac97_bus->codec[0],pdata->codec_pdata[0]);snd_card_set_dev(card,&dev->dev);////(5)////ret=snd_card_register(card);if(ret==0){platform_set_drvdata(dev,card);return0;}err_remove:pxa2xx_ac97_hw_remove(dev);err:if(card)snd_card_free(card);err_dev:returnret;}staticint__devexitpxa2xx_ac97_remove(structplatform_device*dev){structsnd_card*card=platform_get_drvdata(dev);if(card){snd_card_free(card);platform_set_drvdata(dev,NULL);pxa2xx_ac97_hw_remove(dev);}return0;}staticstructplatform_driverpxa2xx_ac97_driver={.probe=pxa2xx_ac97_probe,.remove=__devexit_p(pxa2xx_ac97_remove),.driver={.name="pxa2xx-ac97",.owner=THIS_MODULE,#ifdefCONFIG_PM.pm=&pxa2xx_ac97_pm_ops,#endif},};staticint__initpxa2xx_ac97_init(void){returnplatform_driver_register(&pxa2xx_ac97_driver);}staticvoid__exitpxa2xx_ac97_exit(void){platform_driver_unregister(&pxa2xx_ac97_driver);}module_init(pxa2xx_ac97_init);module_exit(pxa2xx_ac97_exit);MODULE_AUTHOR("NicolasPitre");MODULE_DESCRIPTION("AC97driverfortheIntelPXA2xxchip");

驱动程序通常由probe回调函数开始,对一下2.1中的步骤,是否有相似之处?

经过以上的创建步骤之后,声卡的逻辑结构如下图所示:

图 2.2.1 声卡的软件逻辑结构

下面的章节里我们分别讨论一下snd_card_create()和snd_card_register()这两个函数。

3. snd_card_create()

snd_card_create()在/sound/core/init.c中定义。

[cpp]view plaincopy

    /***snd_card_create-createandinitializeasoundcardstructure*@idx:cardindex(address)[0…(SNDRV_CARDS-1)]*@xid:cardidentification(ASCIIstring)*@module:toplevelmoduleforlocking*@extra_size:allocatethisextrasizeafterthemainsoundcardstructure*@card_ret:thepointertostorethecreatedcardinstance**Createsandinitializesasoundcardstructure.**Thefunctionallocatessnd_cardinstanceviakzallocwiththegiven*spaceforthedrivertousefreely.Theallocatedstructisstored*inthegivencard_retpointer.**Returnszeroifsuccessfuloranegativeerrorcode.*/intsnd_card_create(intidx,constchar*xid,structmodule*module,intextra_size,structsnd_card**card_ret)

首先,根据extra_size参数的大小分配内存,该内存区可以作为芯片的专有数据使用(见前面的介绍):

[c-sharp]view plaincopy

    card=kzalloc(sizeof(*card)+extra_size,GFP_KERNEL);if(!card)return-ENOMEM;

拷贝声卡的ID字符串:

[c-sharp]view plaincopy

    if(xid)strlcpy(card->id,xid,sizeof(card->id));

如果传入的声卡编号为-1,自动分配一个索引编号:

[c-sharp]view plaincopy

    if(idx<0){for(idx2=0;idx2<SNDRV_CARDS;idx2++)/*idx==-1==0xffffmeans:takeanyfreeslot*/if(~snd_cards_lock&idx&1<<idx2){if(module_slot_match(module,idx2)){idx=idx2;break;}}}if(idx<0){for(idx2=0;idx2<SNDRV_CARDS;idx2++)/*idx==-1==0xffffmeans:takeanyfreeslot*/if(~snd_cards_lock&idx&1<<idx2){if(!slots[idx2]||!*slots[idx2]){idx=idx2;break;}}}

初始化snd_card结构中必要的字段:

[c-sharp]view plaincopy

    card->number=idx;card->module=module;INIT_LIST_HEAD(&card->devices);init_rwsem(&card->controls_rwsem);rwlock_init(&card->ctl_files_rwlock);INIT_LIST_HEAD(&card->controls);INIT_LIST_HEAD(&card->ctl_files);spin_lock_init(&card->files_lock);INIT_LIST_HEAD(&card->files_list);init_waitqueue_head(&card->shutdown_sleep);#ifdefCONFIG_PMmutex_init(&card->power_lock);init_waitqueue_head(&card->power_sleep);#endif

建立逻辑设备:Control

[c-sharp]view plaincopy

    /*thecontrolinterfacecannotbeaccessedfromtheuserspaceuntil*//*snd_cards_bitmaskandsnd_cardsaresetwithsnd_card_register*/err=snd_ctl_create(card);

建立proc文件中的info节点:通常就是/proc/asound/card0

[c-sharp]view plaincopy

    err=snd_info_card_create(card);

把第一步分配的内存指针放入private_data字段中:

[c-sharp]view plaincopy

    if(extra_size>0)card->private_data=(char*)card+sizeof(structsnd_card);

4. snd_card_register()

snd_card_create()在/sound/core/init.c中定义。

[c-sharp]view plaincopy

    /***snd_card_register-registerthesoundcard*@card:soundcardstructure**Thisfunctionregistersallthedevicesassignedtothesoundcard.*Untilcallingthis,theALSAcontrolinterfaceisblockedfromthe*externalaccesses.Thus,youshouldcallthisfunctionattheend*oftheinitializationofthecard.**Returnszerootherwiseanegativeerrorcodeiftheregistrainfailed.*/intsnd_card_register(structsnd_card*card)

首先,创建sysfs下的设备:

[c-sharp]view plaincopy

    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;}

其中,sound_class是在/sound/sound_core.c中创建的:

[c-sharp]view plaincopy

    staticchar*sound_devnode(structdevice*dev,mode_t*mode){if(MAJOR(dev->devt)==SOUND_MAJOR)returnNULL;returnkasprintf(GFP_KERNEL,"snd/%s",dev_name(dev));}staticint__initinit_soundcore(void){intrc;rc=init_oss_soundcore();if(rc)returnrc;sound_class=class_create(THIS_MODULE,"sound");if(IS_ERR(sound_class)){cleanup_oss_soundcore();returnPTR_ERR(sound_class);}sound_class->devnode=sound_devnode;return0;}

由此可见,声卡的class将会出现在文件系统的/sys/class/sound/下面,并且,sound_devnode()也决定了相应的设备节点也将会出现在/dev/snd/下面。

接下来的步骤,通过snd_device_register_all()注册所有挂在该声卡下的逻辑设备,snd_device_register_all()实际上是通过snd_card的devices链表,遍历所有的snd_device,并且调用snd_device的ops->dev_register()来实现各自设备的注册的。

[c-sharp]view plaincopy

    if((err=snd_device_register_all(card))<0)returnerr;

最后就是建立一些相应的proc和sysfs下的文件或属性节点,代码就不贴了。

至此,整个声卡完成了建立过程。

与其临渊羡鱼,不如退而结网。

Linux ALSA声卡驱动之二:声卡的创建

相关文章:

你感兴趣的文章:

标签云: