日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

Linux ALSA声卡驱动之四:Control设备的创建

發布時間:2023/11/29 linux 39 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Linux ALSA声卡驱动之四:Control设备的创建 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

聲明:本博內容均由http://blog.csdn.net/droidphone原創,轉載請注明出處,謝謝!

Control接口


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

?

ALSA已經為AC97定義了完整的控制接口模型,如果你的Codec芯片只支持AC97接口,你可以不用關心本節的內容。

?

<sound/control.h>定義了所有的Control API。如果你要為你的codec實現自己的controls,請在代碼中包含該頭文件。

Controls的定義?


要自定義一個Control,我們首先要定義3各回調函數:info,get和put。然后,定義一個snd_kcontrol_new結構:

?

[c-sharp]?view plaincopy
  • static?struct?snd_kcontrol_new?my_control?__devinitdata?=?{??
  • ????.iface?=?SNDRV_CTL_ELEM_IFACE_MIXER,??
  • ????.name?=?"PCM?Playback?Switch",??
  • ????.index?=?0,??
  • ????.access?=?SNDRV_CTL_ELEM_ACCESS_READWRITE,??
  • ????.private_value?=?0xffff,??
  • ????.info?=?my_control_info,??
  • ????.get?=?my_control_get,??
  • ????.put?=?my_control_put??
  • };??
  • ?

    ?

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

    ?

    name字段是該control的名字,從ALSA 0.9.x開始,control的名字是變得比較重要,因為control的作用是按名字來歸類的。ALSA已經預定義了一些control的名字,我們再Control Name一節詳細討論。

    ?

    index字段用于保存該control的在該卡中的編號。如果聲卡中有不止一個codec,每個codec中有相同名字的control,這時我們可以通過index來區分這些controls。當index為0時,則可以忽略這種區分策略。

    ?

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

    ?

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

    ?

    tlv字段為該control提供元數據。

    Control的名字


    control的名字需要遵循一些標準,通常可以分成3部分來定義control的名字:源--方向--功能。

    ?

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

    ?也有一些命名上的特例:

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

    訪問標志(ACCESS Flags)


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

    ?

    如果是一個只讀control,access應該設置為:SNDDRV_CTL_ELEM_ACCESS_READ,這時,我們不必定義put回調函數。類似地,如果是只寫control,access應該設置為:SNDDRV_CTL_ELEM_ACCESS_WRITE,這時,我們不必定義get回調函數。

    ?

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

    ?

    回調函數


    ?info回調函數

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

    ?

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

    ?

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

    ?

    ENUMERATED類型有些特殊。它的value需要設定一個字符串和字符串的索引,請看以下例子:

    ?

    [c-sharp]?view plaincopy
  • static?int?snd_myctl_enum_info(struct?snd_kcontrol?*kcontrol,??
  • struct?snd_ctl_elem_info?*uinfo)??
  • {??
  • ????static?char?*texts[4]?=?{??
  • ????????"First",?"Second",?"Third",?"Fourth"??
  • ????};??
  • ????uinfo->type?=?SNDRV_CTL_ELEM_TYPE_ENUMERATED;??
  • ????uinfo->count?=?1;??
  • ????uinfo->value.enumerated.items?=?4;??
  • ????if?(uinfo->value.enumerated.item?>?3)??
  • ????????uinfo->value.enumerated.item?=?3;??
  • ????strcpy(uinfo->value.enumerated.name,??
  • ????????texts[uinfo->value.enumerated.item]);??
  • ????return?0;??
  • }??
  • ?

    ?

    alsa已經為我們實現了一些通用的info回調函數,例如:snd_ctl_boolean_mono_info(),snd_ctl_boolean_stereo_info()等等。

    get回調函數

    該回調函數用于讀取control的當前值,并返回給用戶空間的應用程序。

    ?

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

    ?

    value字段的賦值依賴于control的類型(如同info回調)。很多聲卡的驅動利用它存儲硬件寄存器的地址、bit-shift和bit-mask,這時,private_value字段可以按以下例子進行設置:

    ?

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

    ?

    然后,get回調函數可以這樣實現:

    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;
    ??? ....

    ??? //根據以上的值讀取相應寄存器的值并填入value中
    }

    ?

    如果control的count字段大于1,表示control有多個元素單元,get回調函數也應該為value填充多個數值。

    ?

    put回調函數


    put回調函數用于把應用程序的控制值設置到control中

    ?

    [c-sharp]?view plaincopy
  • static?int?snd_myctl_put(struct?snd_kcontrol?*kcontrol,??
  • ????struct?snd_ctl_elem_value?*ucontrol)??
  • {??
  • ????struct?mychip?*chip?=?snd_kcontrol_chip(kcontrol);??
  • ????int?changed?=?0;??
  • ????if?(chip->current_value?!=??
  • ????????ucontrol->value.integer.value[0])?{??
  • ????????change_current_value(chip,??
  • ????????ucontrol->value.integer.value[0]);??
  • ????????changed?=?1;??
  • ????}??
  • ????return?changed;??
  • }??
  • ?

    ?

    如上述例子所示,當control的值被改變時,put回調必須要返回1,如果值沒有被改變,則返回0。如果發生了錯誤,則返回一個負數的錯誤號。

    ?

    和get回調一樣,當control的count大于1時,put回調也要處理多個control中的元素值。

    創建Controls


    當把以上討論的內容都準備好了以后,我們就可以創建我們自己的control了。alsa-driver為我們提供了兩個用于創建control的API:

    • snd_ctl_new1()
    • snd_ctl_add()

    我們可以用以下最簡單的方式創建control:

    ?

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

    ?

    在這里,my_control是一個之前定義好的snd_kcontrol_new對象,chip對象將會被賦值在kcontrol->private_data字段,該字段可以在回調函數中訪問。

    ?

    snd_ctl_new1()會分配一個新的snd_kcontrol實例,并把my_control中相應的值復制到該實例中,所以,在定義my_control時,通常我們可以加上__devinitdata前綴。snd_ctl_add則把該control綁定到聲卡對象card當中。

    元數據(Metadata)


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

    ?

    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,它所代表的值按一個固定的dB值的步長變化。該宏的第一個參數是要定義變量的名字,第二個參數是最小值,以0.01dB為單位。第三個參數是變化的步長,也是以0.01dB為單位。如果該control處于最小值時會做出mute時,需要把第四個參數設為1。

    ?

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

    ?

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

    Control設備的建立


    Control設備和PCM設備一樣,都屬于聲卡下的邏輯設備。用戶空間的應用程序通過alsa-lib訪問該Control設備,讀取或控制control的控制狀態,從而達到控制音頻Codec進行各種Mixer等控制操作。

    ?

    Control設備的創建過程大體上和PCM設備的創建過程相同。詳細的創建過程可以參考本博的另一篇文章:Linux音頻驅動之三:PCM設備的創建。下面我們只討論有區別的地方。

    ?

    我們需要在我們的驅動程序初始化時主動調用snd_pcm_new()函數創建pcm設備,而control設備則在snd_card_create()內被創建,snd_card_create()通過調用snd_ctl_create()函數創建control設備節點。所以我們無需顯式地創建control設備,只要建立聲卡,control設備被自動地創建。

    ?

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

    ?

    [c-sharp]?view plaincopy
  • /*?
  • ?*?registration?of?the?control?device?
  • ?*/??
  • static?int?snd_ctl_dev_register(struct?snd_device?*device)??
  • {??
  • ????struct?snd_card?*card?=?device->device_data;??
  • ????int?err,?cardnum;??
  • ????char?name[16];??
  • ??
  • ????if?(snd_BUG_ON(!card))??
  • ????????return?-ENXIO;??
  • ????cardnum?=?card->number;??
  • ????if?(snd_BUG_ON(cardnum?<?0?||?cardnum?>=?SNDRV_CARDS))??
  • ????????return?-ENXIO;??
  • ????????/*?control設備的名字?*/??
  • ????sprintf(name,?"controlC%i",?cardnum);??
  • ????if?((err?=?snd_register_device(SNDRV_DEVICE_TYPE_CONTROL,?card,?-1,??
  • ???????????????????????&snd_ctl_f_ops,?card,?name))?<?0)??
  • ????????return?err;??
  • ????return?0;??
  • }??
  • ?

    ?

    snd_ctl_dev_register()函數會在snd_card_register()中,即聲卡的注冊階段被調用。注冊完成后,control設備的相關信息被保存在snd_minors[]數組中,用control設備的此設備號作索引,即可在snd_minors[]數組中找出相關的信息。注冊完成后的數據結構關系可以用下圖進行表述:

    ??????????????????????????????????????????????????? control設備的操作函數入口

    ?

    用戶程序需要打開control設備時,驅動程序通過snd_minors[]全局數組和此設備號,可以獲得snd_ctl_f_ops結構中的各個回調函數,然后通過這些回調函數訪問control中的信息和數據(最終會調用control的幾個回調函數get,put,info)。詳細的代碼我就不貼了,大家可以讀一下代碼:/sound/core/control.c。

    轉載于:https://www.cnblogs.com/Ph-one/p/6292597.html

    總結

    以上是生活随笔為你收集整理的Linux ALSA声卡驱动之四:Control设备的创建的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。