九色国产,午夜在线视频,新黄色网址,九九色综合,天天做夜夜做久久做狠狠,天天躁夜夜躁狠狠躁2021a,久久不卡一区二区三区

打開APP
userphoto
未登錄

開通VIP,暢享免費(fèi)電子書等14項(xiàng)超值服

開通VIP
Linux ALSA聲卡驅(qū)動(dòng)之四:Control設(shè)備的創(chuàng)建

聲明:本博內(nèi)容均由http://blog.csdn.net/droidphone原創(chuàng),轉(zhuǎn)載請(qǐng)注明出處,謝謝!

Control接口

Control接口主要讓用戶空間的應(yīng)用程序(alsa-lib)可以訪問和控制音頻codec芯片中的多路開關(guān),滑動(dòng)控件等。對(duì)于Mixer(混音)來說,Control接口顯得尤為重要,從ALSA 0.9.x版本開始,所有的mixer工作都是通過control接口的API來實(shí)現(xiàn)的。

 

ALSA已經(jīng)為AC97定義了完整的控制接口模型,如果你的Codec芯片只支持AC97接口,你可以不用關(guān)心本節(jié)的內(nèi)容。

 

<sound/control.h>定義了所有的Control API。如果你要為你的codec實(shí)現(xiàn)自己的controls,請(qǐng)?jiān)诖a中包含該頭文件。

Controls的定義 

要自定義一個(gè)Control,我們首先要定義3各回調(diào)函數(shù):info,get和put。然后,定義一個(gè)snd_kcontrol_new結(jié)構(gòu):

[c-sharp] view plaincopy?
  1. static struct snd_kcontrol_new my_control __devinitdata = {  
  2.     .iface = SNDRV_CTL_ELEM_IFACE_MIXER,  
  3.     .name = "PCM Playback Switch",  
  4.     .index = 0,  
  5.     .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,  
  6.     .private_value = 0xffff,  
  7.     .info = my_control_info,  
  8.     .get = my_control_get,  
  9.     .put = my_control_put  
  10. };  

 

iface字段指出了control的類型,alsa定義了幾種類型(SNDDRV_CTL_ELEM_IFACE_XXX),常用的類型是MIXER,當(dāng)然也可以定義屬于全局的CARD類型,也可以定義屬于某類設(shè)備的類型,例如HWDEP,PCMRAWMIDI,TIMER等,這時(shí)需要在device和subdevice字段中指出卡的設(shè)備邏輯編號(hào)。

 

name字段是該control的名字,從ALSA 0.9.x開始,control的名字是變得比較重要,因?yàn)閏ontrol的作用是按名字來歸類的。ALSA已經(jīng)預(yù)定義了一些control的名字,我們?cè)貱ontrol Name一節(jié)詳細(xì)討論。

 

index字段用于保存該control的在該卡中的編號(hào)。如果聲卡中有不止一個(gè)codec,每個(gè)codec中有相同名字的control,這時(shí)我們可以通過index來區(qū)分這些controls。當(dāng)index為0時(shí),則可以忽略這種區(qū)分策略。

 

access字段包含了該control的訪問類型。每一個(gè)bit代表一種訪問類型,這些訪問類型可以多個(gè)“或”運(yùn)算組合在一起。

 

private_value字段包含了一個(gè)任意的長整數(shù)類型值。該值可以通過info,get,put這幾個(gè)回調(diào)函數(shù)訪問。你可以自己決定如何使用該字段,例如可以把它拆分成多個(gè)位域,又或者是一個(gè)指針,指向某一個(gè)數(shù)據(jù)結(jié)構(gòu)。

 

tlv字段為該control提供元數(shù)據(jù)。

Control的名字

control的名字需要遵循一些標(biāo)準(zhǔn),通??梢苑殖?部分來定義control的名字:源--方向--功能。

 

  • 源,可以理解為該control的輸入端,alsa已經(jīng)預(yù)定義了一些常用的源,例如:Master,PCM,CD,Line等等。 
  • 方向,代表該control的數(shù)據(jù)流向,例如:Playback,Capture,Bypass,Bypass Capture等等,也可以不定義方向,這時(shí)表示該Control是雙向的(playback和capture)。 
  • 功能,根據(jù)control的功能,可以是以下字符串:Switch,Volume,Route等等。

 也有一些命名上的特例:

  • 全局的capture和playback    "Capture Source","Capture Volume","Capture Switch",它們用于全局的capture source,switch和volume。同理,"Playback Volume","Playback Switch",它們用于全局的輸出switch和volume。
  • Tone-controles    音調(diào)控制的開關(guān)和音量命名為:Tone Control - XXX,例如,"Tone Control - Switch","Tone Control - Bass","Tone Control - Center"。
  • 3D controls    3D控件的命名規(guī)則:,"3D Control - Switch","3D Control - Center","3D Control - Space"。
  • Mic boost    麥克風(fēng)音量加強(qiáng)控件命名為:"Mic Boost"或"Mic Boost(6dB)"。

訪問標(biāo)志(ACCESS Flags)

Access字段是一個(gè)bitmask,它保存了改control的訪問類型。默認(rèn)的訪問類型是:SNDDRV_CTL_ELEM_ACCESS_READWRITE,表明該control支持讀和寫操作。如果access字段沒有定義(.access==0),此時(shí)也認(rèn)為是READWRITE類型。

 

如果是一個(gè)只讀control,access應(yīng)該設(shè)置為:SNDDRV_CTL_ELEM_ACCESS_READ,這時(shí),我們不必定義put回調(diào)函數(shù)。類似地,如果是只寫control,access應(yīng)該設(shè)置為:SNDDRV_CTL_ELEM_ACCESS_WRITE,這時(shí),我們不必定義get回調(diào)函數(shù)。

 

如果control的值會(huì)頻繁地改變(例如:電平表),我們可以使用VOLATILE類型,這意味著該control會(huì)在沒有通知的情況下改變,應(yīng)用程序應(yīng)該定時(shí)地查詢?cè)揷ontrol的值。

 

回調(diào)函數(shù)

 info回調(diào)函數(shù)

info回調(diào)函數(shù)用于獲取control的詳細(xì)信息。它的主要工作就是填充通過參數(shù)傳入的snd_ctl_elem_info對(duì)象,以下例子是一個(gè)具有單個(gè)元素的boolean型control的info回調(diào):

[c-sharp] view plaincopy?
  1. static int snd_myctl_mono_info(struct snd_kcontrol *kcontrol,  
  2.     struct snd_ctl_elem_info *uinfo)  
  3. {  
  4.     uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;  
  5.     uinfo->count = 1;  
  6.     uinfo->value.integer.min = 0;  
  7.     uinfo->value.integer.max = 1;  
  8.     return 0;  
  9. }  

 

type字段指出該control的值類型,值類型可以是BOOLEAN, INTEGER, ENUMERATED, BYTES,IEC958和INTEGER64之一。count字段指出了改control中包含有多少個(gè)元素單元,比如,立體聲的音量control左右兩個(gè)聲道的音量值,它的count字段等于2。value字段是一個(gè)聯(lián)合體(union),value的內(nèi)容和control的類型有關(guān)。其中,boolean和integer類型是相同的。

 

ENUMERATED類型有些特殊。它的value需要設(shè)定一個(gè)字符串和字符串的索引,請(qǐng)看以下例子:

[c-sharp] view plaincopy?
  1. static int snd_myctl_enum_info(struct snd_kcontrol *kcontrol,  
  2. struct snd_ctl_elem_info *uinfo)  
  3. {  
  4.     static char *texts[4] = {  
  5.         "First""Second""Third""Fourth"  
  6.     };  
  7.     uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;  
  8.     uinfo->count = 1;  
  9.     uinfo->value.enumerated.items = 4;  
  10.     if (uinfo->value.enumerated.item > 3)  
  11.         uinfo->value.enumerated.item = 3;  
  12.     strcpy(uinfo->value.enumerated.name,  
  13.         texts[uinfo->value.enumerated.item]);  
  14.     return 0;  
  15. }  

 

alsa已經(jīng)為我們實(shí)現(xiàn)了一些通用的info回調(diào)函數(shù),例如:snd_ctl_boolean_mono_info(),snd_ctl_boolean_stereo_info()等等。

get回調(diào)函數(shù)

該回調(diào)函數(shù)用于讀取control的當(dāng)前值,并返回給用戶空間的應(yīng)用程序。

[c-sharp] view plaincopy?
  1. static int snd_myctl_get(struct snd_kcontrol *kcontrol,  
  2.     struct snd_ctl_elem_value *ucontrol)  
  3. {  
  4.     struct mychip *chip = snd_kcontrol_chip(kcontrol);  
  5.     ucontrol->value.integer.value[0] = get_some_value(chip);  
  6.     return 0;  
  7. }  

 

value字段的賦值依賴于control的類型(如同info回調(diào))。很多聲卡的驅(qū)動(dòng)利用它存儲(chǔ)硬件寄存器的地址、bit-shift和bit-mask,這時(shí),private_value字段可以按以下例子進(jìn)行設(shè)置:

 

.private_value = reg | (shift << 16) | (mask << 24);

 

然后,get回調(diào)函數(shù)可以這樣實(shí)現(xiàn):

static int snd_sbmixer_get_single(struct snd_kcontrol *kcontrol,
    struct snd_ctl_elem_value *ucontrol)

{
    int reg = kcontrol->private_value & 0xff;
    int shift = (kcontrol->private_value >> 16) & 0xff;
    int mask = (kcontrol->private_value >> 24) & 0xff;
    ....

    //根據(jù)以上的值讀取相應(yīng)寄存器的值并填入value中
}

 

如果control的count字段大于1,表示control有多個(gè)元素單元,get回調(diào)函數(shù)也應(yīng)該為value填充多個(gè)數(shù)值。

 

put回調(diào)函數(shù)


put回調(diào)函數(shù)用于把應(yīng)用程序的控制值設(shè)置到control中。

[c-sharp] view plaincopy?
  1. static int snd_myctl_put(struct snd_kcontrol *kcontrol,  
  2.     struct snd_ctl_elem_value *ucontrol)  
  3. {  
  4.     struct mychip *chip = snd_kcontrol_chip(kcontrol);  
  5.     int changed = 0;  
  6.     if (chip->current_value !=  
  7.         ucontrol->value.integer.value[0]) {  
  8.         change_current_value(chip,  
  9.         ucontrol->value.integer.value[0]);  
  10.         changed = 1;  
  11.     }  
  12.     return changed;  
  13. }  

 

如上述例子所示,當(dāng)control的值被改變時(shí),put回調(diào)必須要返回1,如果值沒有被改變,則返回0。如果發(fā)生了錯(cuò)誤,則返回一個(gè)負(fù)數(shù)的錯(cuò)誤號(hào)。

 

和get回調(diào)一樣,當(dāng)control的count大于1時(shí),put回調(diào)也要處理多個(gè)control中的元素值。

創(chuàng)建Controls

當(dāng)把以上討論的內(nèi)容都準(zhǔn)備好了以后,我們就可以創(chuàng)建我們自己的control了。alsa-driver為我們提供了兩個(gè)用于創(chuàng)建control的API:

  • snd_ctl_new1()
  • snd_ctl_add()

我們可以用以下最簡單的方式創(chuàng)建control:

[c-sharp] view plaincopy?
  1. err = snd_ctl_add(card, snd_ctl_new1(&my_control, chip));  
  2. if (err < 0)  
  3.     return err;  

 

在這里,my_control是一個(gè)之前定義好的snd_kcontrol_new對(duì)象,chip對(duì)象將會(huì)被賦值在kcontrol->private_data字段,該字段可以在回調(diào)函數(shù)中訪問。

 

snd_ctl_new1()會(huì)分配一個(gè)新的snd_kcontrol實(shí)例,并把my_control中相應(yīng)的值復(fù)制到該實(shí)例中,所以,在定義my_control時(shí),通常我們可以加上__devinitdata前綴。snd_ctl_add則把該control綁定到聲卡對(duì)象card當(dāng)中。

元數(shù)據(jù)(Metadata)

很多mixer control需要提供以dB為單位的信息,我們可以使用DECLARE_TLV_xxx宏來定義一些包含這種信息的變量,然后把control的tlv.p字段指向這些變量,最后,在access字段中加上SNDRV_CTL_ELEM_ACCESS_TLV_READ標(biāo)志,就像這樣:

 

static DECLARE_TLV_DB_SCALE(db_scale_my_control, -4050, 150, 0);


static struct snd_kcontrol_new my_control __devinitdata = {
    ...
    .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
            SNDRV_CTL_ELEM_ACCESS_TLV_READ,
    ...
    .tlv.p = db_scale_my_control,
};

 

DECLARE_TLV_DB_SCALE宏定義的mixer control,它所代表的值按一個(gè)固定的dB值的步長變化。該宏的第一個(gè)參數(shù)是要定義變量的名字,第二個(gè)參數(shù)是最小值,以0.01dB為單位。第三個(gè)參數(shù)是變化的步長,也是以0.01dB為單位。如果該control處于最小值時(shí)會(huì)做出mute時(shí),需要把第四個(gè)參數(shù)設(shè)為1。

 

DECLARE_TLV_DB_LINEAR宏定義的mixer control,它的輸出隨值的變化而線性變化。 該宏的第一個(gè)參數(shù)是要定義變量的名字,第二個(gè)參數(shù)是最小值,以0.01dB為單位。第二個(gè)參數(shù)是最大值,以0.01dB為單位。如果該control處于最小值時(shí)會(huì)做出mute時(shí),需要把第二個(gè)參數(shù)設(shè)為TLV_DB_GAIN_MUTE。

 

這兩個(gè)宏實(shí)際上就是定義一個(gè)整形數(shù)組,所謂tlv,就是Type-Lenght-Value的意思,數(shù)組的第0各元素代表數(shù)據(jù)的類型,第1個(gè)元素代表數(shù)據(jù)的長度,第三個(gè)元素和之后的元素保存該變量的數(shù)據(jù)。

Control設(shè)備的建立

Control設(shè)備和PCM設(shè)備一樣,都屬于聲卡下的邏輯設(shè)備。用戶空間的應(yīng)用程序通過alsa-lib訪問該Control設(shè)備,讀取或控制control的控制狀態(tài),從而達(dá)到控制音頻Codec進(jìn)行各種Mixer等控制操作。

 

Control設(shè)備的創(chuàng)建過程大體上和PCM設(shè)備的創(chuàng)建過程相同。詳細(xì)的創(chuàng)建過程可以參考本博的另一篇文章:Linux音頻驅(qū)動(dòng)之三:PCM設(shè)備的創(chuàng)建。下面我們只討論有區(qū)別的地方。

 

我們需要在我們的驅(qū)動(dòng)程序初始化時(shí)主動(dòng)調(diào)用snd_pcm_new()函數(shù)創(chuàng)建pcm設(shè)備,而control設(shè)備則在snd_card_create()內(nèi)被創(chuàng)建,snd_card_create()通過調(diào)用snd_ctl_create()函數(shù)創(chuàng)建control設(shè)備節(jié)點(diǎn)。所以我們無需顯式地創(chuàng)建control設(shè)備,只要建立聲卡,control設(shè)備被自動(dòng)地創(chuàng)建。

 

和pcm設(shè)備一樣,control設(shè)備的名字遵循一定的規(guī)則:controlCxx,這里的xx代表聲卡的編號(hào)。我們也可以通過代碼正是這一點(diǎn),下面的是snd_ctl_dev_register()函數(shù)的代碼:

[c-sharp] view plaincopy?
  1. /* 
  2.  * registration of the control device 
  3.  */  
  4. static int snd_ctl_dev_register(struct snd_device *device)  
  5. {  
  6.     struct snd_card *card = device->device_data;  
  7.     int err, cardnum;  
  8.     char name[16];  
  9.   
  10.     if (snd_BUG_ON(!card))  
  11.         return -ENXIO;  
  12.     cardnum = card->number;  
  13.     if (snd_BUG_ON(cardnum < 0 || cardnum >= SNDRV_CARDS))  
  14.         return -ENXIO;  
  15.         /* control設(shè)備的名字 */  
  16.     sprintf(name, "controlC%i", cardnum);  
  17.     if ((err = snd_register_device(SNDRV_DEVICE_TYPE_CONTROL, card, -1,  
  18.                        &snd_ctl_f_ops, card, name)) < 0)  
  19.         return err;  
  20.     return 0;  
  21. }  

 

snd_ctl_dev_register()函數(shù)會(huì)在snd_card_register()中,即聲卡的注冊(cè)階段被調(diào)用。注冊(cè)完成后,control設(shè)備的相關(guān)信息被保存在snd_minors[]數(shù)組中,用control設(shè)備的此設(shè)備號(hào)作索引,即可在snd_minors[]數(shù)組中找出相關(guān)的信息。注冊(cè)完成后的數(shù)據(jù)結(jié)構(gòu)關(guān)系可以用下圖進(jìn)行表述:

                                                    control設(shè)備的操作函數(shù)入口

 

用戶程序需要打開control設(shè)備時(shí),驅(qū)動(dòng)程序通過snd_minors[]全局?jǐn)?shù)組和此設(shè)備號(hào),可以獲得snd_ctl_f_ops結(jié)構(gòu)中的各個(gè)回調(diào)函數(shù),然后通過這些回調(diào)函數(shù)訪問control中的信息和數(shù)據(jù)(最終會(huì)調(diào)用control的幾個(gè)回調(diào)函數(shù)get,put,info)。詳細(xì)的代碼我就不貼了,大家可以讀一下代碼:/sound/core/control.c。

本站僅提供存儲(chǔ)服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊舉報(bào)。
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
snd_kcontrol的分析
ALSA SOC在Linux3.1上的一些改進(jìn)
Use ALSA lib apis to set the Mic Capture Volu...
Linux音頻驅(qū)動(dòng)之二:聲卡的創(chuàng)建
Linux 音頻設(shè)備驅(qū)動(dòng)
AM335x(TQ335x)學(xué)習(xí)筆記
更多類似文章 >>
生活服務(wù)
熱點(diǎn)新聞
分享 收藏 導(dǎo)長圖 關(guān)注 下載文章
綁定賬號(hào)成功
后續(xù)可登錄賬號(hào)暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服