加入星計(jì)劃,您可以享受以下權(quán)益:

  • 創(chuàng)作內(nèi)容快速變現(xiàn)
  • 行業(yè)影響力擴(kuò)散
  • 作品版權(quán)保護(hù)
  • 300W+ 專業(yè)用戶
  • 1.5W+ 優(yōu)質(zhì)創(chuàng)作者
  • 5000+ 長(zhǎng)期合作伙伴
立即加入
  • 正文
    • 1.ALSA概述
    • 2. ALSA組件
    • 3. ?ALSA 設(shè)備文件
    • 4. Linux ALSA 源碼目錄結(jié)構(gòu)
    • 5. ?ALSA核心數(shù)據(jù)結(jié)構(gòu)
    • 6. alsa數(shù)據(jù)結(jié)構(gòu)之間關(guān)系
    • 7. 內(nèi)核中alsa幾個(gè)主要函數(shù)
  • 推薦器件
  • 相關(guān)推薦
  • 電子產(chǎn)業(yè)圖譜
申請(qǐng)入駐 產(chǎn)業(yè)圖譜

瑞芯微-I2S | ALSA基礎(chǔ)-3

04/21 08:55
2872
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點(diǎn)資訊討論

一口君后面會(huì)陸續(xù)更新基于瑞芯微rk3568的I2S系列文章。

預(yù)計(jì)10篇左右。有對(duì)語音感興趣的朋友,可以收藏該專題。

《瑞芯微 | I2S-音頻基礎(chǔ) -1》

《瑞芯微-I2S | 音頻驅(qū)動(dòng)調(diào)試基本命令和工具-基于rk3568-2》

針對(duì)音頻設(shè)備,linux內(nèi)核中包含了兩類音頻設(shè)備驅(qū)動(dòng)框架;

OSS:開放聲音系統(tǒng),包含dsp和mixer字符設(shè)備接口,應(yīng)用訪問底層硬件是直接通過sound設(shè)備節(jié)點(diǎn)實(shí)現(xiàn)的;

ALSA:先進(jìn)linux聲音架構(gòu)(Advanced Linux Sound Archiecture),以card和組件(PCM、mixer等)為組件,應(yīng)用是通過ALSA提供的alsa-lib庫(kù)訪問底層硬件的操作,不再訪問sound設(shè)備節(jié)點(diǎn)了

1.ALSA概述

ALSA由一系列的內(nèi)核驅(qū)動(dòng)、應(yīng)用程序編程接口(API)以及支持linux下聲音的應(yīng)用程序組成、

ALSA項(xiàng)目發(fā)起的原因是linux下的聲卡驅(qū)動(dòng)(OSS)沒有獲得積極的維護(hù),而且落后于新的聲卡技術(shù)。

Jaroslav Kysela早先寫了一個(gè)聲卡驅(qū)動(dòng),并由此開始了ALSA項(xiàng)目,隨后,更多的開發(fā)者加入到開發(fā)隊(duì)伍中,更多的聲卡獲得支持,API的結(jié)構(gòu)也獲得了重組。目前已經(jīng)成為了linux的主流音頻體系結(jié)構(gòu)。

ALSA的官網(wǎng):https://www.alsa-project.org/wiki/Main_Page

2. ALSA組件

ALSA系統(tǒng)包括:

1、alsa-driver:alsa系統(tǒng)驅(qū)動(dòng)。

2、alsa-lib:alsa庫(kù),用戶空間調(diào)用,和內(nèi)核空間交互。

3、alsa-utils:命令行工具。

4、alsa-plugin:alsa插件。

5、alsa-tools:alsa工具。

應(yīng)用層,ALSA 為我們提供了 alsa-lib,在 Linux 內(nèi)核設(shè)備驅(qū)動(dòng)層,ALSA 提供了 alsa-driver。

Linux 應(yīng)用程序只需要調(diào)用 alsa-lib 提供的 API,即可完成對(duì)底層音頻硬件的控制。

linux內(nèi)核中alsa的軟件結(jié)構(gòu)如下:

用戶空間的 alsa-lib 對(duì)應(yīng)用程序提供統(tǒng)一的 API 接口,隱藏了驅(qū)動(dòng)層的實(shí)際細(xì)節(jié),簡(jiǎn)化了應(yīng)用程序的實(shí)現(xiàn)難度

但是由于 alsa-lib 也由于過大,因此在 android 等下也經(jīng)常使用 tiny-alsa

如上圖所示,在 Linux 內(nèi)核中,有對(duì) alsa-driver 進(jìn)一步的封裝,即 alsa-soc

ALSA框架從上到下依次為應(yīng)用程序、ALSA Library API、ALSA CORE、ASoC CORE、硬件驅(qū)動(dòng)程序、硬件設(shè)備;

應(yīng)用程序:

tinyplay/tinycap/tinymix,這些用戶程序直接調(diào)用alsa用戶庫(kù)接口來實(shí)現(xiàn)放音、錄音、控制;

ALSA Library API:

alsa用戶庫(kù)接口,對(duì)應(yīng)用程序提供統(tǒng)一的API接口,這樣可以隱藏了驅(qū)動(dòng)層的實(shí)現(xiàn)細(xì)節(jié),簡(jiǎn)化了應(yīng)用程序的實(shí)現(xiàn)難度;常見有tinyalsa、alsa-lib;

ALSA CORE:

alsa核心層,向上提供邏輯設(shè)備(PCM/CTL/MIDI/TIMER/…)系統(tǒng)調(diào)用,向下驅(qū)動(dòng)硬件設(shè)備(Machine/I2S/DMA/CODEC);

ASoC CORE:

建立在標(biāo)準(zhǔn)ALSA CORE基礎(chǔ)上,為了更好支持嵌入式系統(tǒng)和應(yīng)用于移動(dòng)設(shè)備的音頻Codec的一套軟件體系,它將音頻硬件設(shè)備驅(qū)動(dòng)劃分為Codec、Platform 和 Machine;

Hardware Driver:

音頻硬件設(shè)備驅(qū)動(dòng),由三大部分組成,分別是 Machine、Platform、Codec;

3. ?ALSA 設(shè)備文件

Linux 系統(tǒng)下看到的設(shè)備文件結(jié)構(gòu)如下:

rk3568_r:/?#?cd?/dev/snd/
rk3568_r:/dev/snd?#?ls?-l
total?0
crw-rw----?1?system?audio?116,???4?2024-02-18?15:28?controlC0
crw-rw----?1?system?audio?116,???6?2024-02-18?15:28?controlC1
crw-rw----?1?system?audio?116,???3?2024-02-18?15:28?pcmC0D0c
crw-rw----?1?system?audio?116,???2?2024-02-18?15:28?pcmC0D0p
crw-rw----?1?system?audio?116,???5?2024-02-18?15:28?pcmC1D0p
crw-rw----?1?system?audio?116,??33?2024-02-18?15:28?timer

從上面能看到有如下設(shè)備文件:

controlC0?-->??????????????用于聲卡的控制,例如通道選擇,混音,麥克控制,音量加減,開關(guān)等
controlC1
pcmC0D0c?-->???????????????用于錄音的pcm設(shè)備
pcmC0D0p?-->???????????????用于播放的pcm設(shè)備
pcmC1D0p
timer????-->???????????????定時(shí)器

有的平臺(tái)還有以下設(shè)備節(jié)點(diǎn):
midiC0D0??-->??????????????用于播放midi音頻
seq??????-->???????????????音序器

其中,C0、D0代表的是聲卡0中的設(shè)備0

pcmC0D0c最后一個(gè)c代表capture

pcmC0D0p最后一個(gè)p代表playback

這些都是alsa-driver中的命名規(guī)則,從上面的列表可以看出,聲卡下掛了6個(gè)設(shè)備

根據(jù)聲卡的實(shí)際能力,驅(qū)動(dòng)實(shí)際上可以掛載更多種類的設(shè)備,在include/sound/core.h 中,定義了以下設(shè)備類型,通常更關(guān)心的是 pcmcontrol 這兩種設(shè)備,Default 一個(gè)聲卡對(duì)應(yīng)一個(gè) Control 設(shè)備。

#define????SNDRV_DEV_TOPLEVEL????((__force?snd_device_type_t)?0)
#define????SNDRV_DEV_CONTROL????((__force?snd_device_type_t)?1)
#define????SNDRV_DEV_LOWLEVEL_PRE????((__force?snd_device_type_t)?2)
#define????SNDRV_DEV_LOWLEVEL_NORMAL?((__force?snd_device_type_t)?0x1000)
#define????SNDRV_DEV_PCM????????((__force?snd_device_type_t)?0x1001)
#define????SNDRV_DEV_RAWMIDI????((__force?snd_device_type_t)?0x1002)
#define????SNDRV_DEV_TIMER????????((__force?snd_device_type_t)?0x1003)
#define????SNDRV_DEV_SEQUENCER????((__force?snd_device_type_t)?0x1004)
#define????SNDRV_DEV_HWDEP????????((__force?snd_device_type_t)?0x1005)
#define????SNDRV_DEV_INFO????????((__force?snd_device_type_t)?0x1006)
#define????SNDRV_DEV_BUS????????((__force?snd_device_type_t)?0x1007)
#define????SNDRV_DEV_CODEC????????((__force?snd_device_type_t)?0x1008)
#define????SNDRV_DEV_JACK??????????((__force?snd_device_type_t)?0x1009)
#define????SNDRV_DEV_COMPRESS????((__force?snd_device_type_t)?0x100A)
#define????SNDRV_DEV_LOWLEVEL????((__force?snd_device_type_t)?0x2000)

4. Linux ALSA 源碼目錄結(jié)構(gòu)

在 Linux 源碼中 ALSA 架構(gòu)的代碼在 /sound 下,Linux 5.0 的目錄如下:

其中各主要子目錄的作用如下:

core???????該目錄包含了ALSA?驅(qū)動(dòng)的中間層,它是整個(gè)?ALSA?驅(qū)動(dòng)的核心部分
core/oss???包含模擬舊的?OSS?架構(gòu)的?PCM?和?Mixer?模塊
core/seq???有關(guān)音序器相關(guān)的代碼
drivers????放置一些與CPU、BUS架構(gòu)無關(guān)的公用代碼
i2c????????ALSA自己的I2C控制代碼
pci????????pci聲卡的頂層目錄,子目錄包含各種pci聲卡的代碼
isa????????isa聲卡的頂層目錄,子目錄包含各種isa聲卡的代碼
soc????????針對(duì)?system-on-chip?體系的中間層代碼,即ASOC代碼
soc/rockchip?瑞芯微平臺(tái)i2s控制器驅(qū)動(dòng)代碼
soc/codecs???針對(duì)soc?體系的各種?codec?的代碼,與平臺(tái)無關(guān)?
include????ALSA驅(qū)動(dòng)的公共頭文件目錄,其路徑為?/include/sound,該目錄的頭文件需要導(dǎo)出給用戶空間的應(yīng)用程序使用,通常,驅(qū)動(dòng)模塊私有的頭文件不應(yīng)放置在這里

更多目錄結(jié)構(gòu)信息可以參考:?https://www.kernel.org/doc/html/latest/sound/kernel-api/writing-an-alsa-driver.html。

5. ?ALSA核心數(shù)據(jù)結(jié)構(gòu)

由于ASoC是建立在標(biāo)準(zhǔn)ALSA CORE上的一套軟件體系,因此本篇博客重點(diǎn)介紹的都是ALSA CORE中的數(shù)據(jù)結(jié)構(gòu),并不涉及到ASoC CORE中的數(shù)據(jù)結(jié)構(gòu)。

ALSA CORE中的數(shù)據(jù)結(jié)構(gòu)大部分定義在include/sound/core.h、以及sound/core/目錄下的文件中。

1) struct snd_card

linux內(nèi)核中使用struct snd_card表示ALSA音頻驅(qū)動(dòng)中的聲卡設(shè)備

struct snd_card可以說是整個(gè)ALSA音頻驅(qū)動(dòng)最頂層的一個(gè)結(jié)構(gòu),整個(gè)聲卡的軟件邏輯結(jié)構(gòu)開始于該結(jié)構(gòu),

幾乎所有與聲音相關(guān)的邏輯設(shè)備都是在snd_card的管理之下

聲卡驅(qū)動(dòng)的第一個(gè)動(dòng)作通常就是創(chuàng)建一個(gè)snd_card結(jié)構(gòu)體。

@include/sound/core.h
????
/*?main?structure?for?soundcard?*/

struct?snd_card?{
?int?number;???/*?number?of?soundcard?(index?to
????????snd_cards)?*/

?char?id[16];???/*?id?string?of?this?card?*/
?char?driver[16];??/*?driver?name?*/
?char?shortname[32];??/*?short?name?of?this?soundcard?*/
?char?longname[80];??/*?name?of?this?soundcard?*/
?char?irq_descr[32];??/*?Interrupt?description?*/
?char?mixername[80];??/*?mixer?name?*/
?char?components[128];??/*?card?components?delimited?with
????????space?*/
?struct?module?*module;??/*?top-level?module?*/

?void?*private_data;??/*?private?data?for?soundcard?*/
?void?(*private_free)?(struct?snd_card?*card);?/*?callback?for?freeing?of
????????private?data?*/
?struct?list_head?devices;?/*?devices?*/

?struct?device?ctl_dev;??/*?control?device?*/
?unsigned?int?last_numid;?/*?last?used?numeric?ID?*/
?struct?rw_semaphore?controls_rwsem;?/*?controls?list?lock?*/
?rwlock_t?ctl_files_rwlock;?/*?ctl_files?list?lock?*/
?int?controls_count;??/*?count?of?all?controls?*/
?int?user_ctl_count;??/*?count?of?all?user?controls?*/
?struct?list_head?controls;?/*?all?controls?for?this?card?*/
?struct?list_head?ctl_files;?/*?active?control?files?*/

?struct?snd_info_entry?*proc_root;?/*?root?for?soundcard?specific?files?*/
?struct?snd_info_entry?*proc_id;?/*?the?card?id?*/
?struct?proc_dir_entry?*proc_root_link;?/*?number?link?to?real?id?*/

?struct?list_head?files_list;?/*?all?files?associated?to?this?card?*/
?struct?snd_shutdown_f_ops?*s_f_ops;?/*?file?operations?in?the?shutdown
????????state?*/
?spinlock_t?files_lock;??/*?lock?the?files?for?this?card?*/
?int?shutdown;???/*?this?card?is?going?down?*/
?struct?completion?*release_completion;
?struct?device?*dev;??/*?device?assigned?to?this?card?*/
?struct?device?card_dev;??/*?cardX?object?for?sysfs?*/
?const?struct?attribute_group?*dev_groups[4];?/*?assigned?sysfs?attr?*/
?bool?registered;??/*?card_dev?is?registered??*/
?wait_queue_head_t?remove_sleep;
?int?offline;???/*?if?this?sound?card?is?offline?*/
?unsigned?long?offline_change;
?wait_queue_head_t?offline_poll_wait;

#ifdef?CONFIG_PM
?unsigned?int?power_state;?/*?power?state?*/
?wait_queue_head_t?power_sleep;
#endif

#if?IS_ENABLED(CONFIG_SND_MIXER_OSS)
?struct?snd_mixer_oss?*mixer_oss;
?int?mixer_oss_change_count;
#endif
};

@include/sound/core.h

struct?snd_card?{
????number:?聲卡設(shè)備編號(hào),通常從0開始,通過編號(hào)可以在snd_cards指針數(shù)組中找到對(duì)應(yīng)的聲卡設(shè)備;
????id[16]:聲卡設(shè)備的標(biāo)識(shí)符;
??? driver:驅(qū)動(dòng)名稱;
??? shortname:設(shè)備簡(jiǎn)稱,更多地用于打印信息;
??? longname:設(shè)備名稱,會(huì)在具體驅(qū)動(dòng)中設(shè)置,主要反映在/proc/asound/cards中;
??? irq_descr:中斷描述信息;
??? mixername:混音器名稱;
??? components:聲卡組件名稱,由空格分隔;
????module:頂層模塊;
??? private_data:聲卡的私有數(shù)據(jù);
??? private_free:釋放私有數(shù)據(jù)的回調(diào)函數(shù);
??? devices:保存該聲卡下所有邏輯設(shè)備的鏈表;鏈表中存放的數(shù)據(jù)類型為struct?snd_device;
????ctl_dev:聲卡Control設(shè)備內(nèi)核設(shè)備結(jié)構(gòu)體,其parent為card_dev,class為sound_class,主設(shè)備號(hào)為116;
????last_numid:存儲(chǔ)注冊(cè)snd_control時(shí)為其分配的編號(hào);
????controls_rwsem:讀寫信號(hào)量,用于并發(fā)操作controls鏈表;
????ctl_files_rwlock:讀寫自旋鎖,用于并發(fā)操作ctl_files鏈表;
????controls_count:controls鏈表的長(zhǎng)度;
????user_ctl_count:用戶控制設(shè)備的數(shù)量;
????controls:保存該聲卡下所有控件(controls)的鏈表;該鏈表中存放的數(shù)據(jù)類型為struct?snd_kcontrol;
????ctl_files:用于管理該card下的active的control設(shè)備;鏈表中存放的數(shù)據(jù)類型為struct?snd_ctl_file;
????proc_root:聲卡設(shè)備在proc文件系統(tǒng)的根目錄;即/proc/asound/card%d目錄;
????proc_root_link:指向/proc/asound/card%d的鏈接文件,文件名為id;
????files_list:保存此聲卡相關(guān)的所有文件的鏈表;鏈表中存放的數(shù)據(jù)類型為struct?snd_monitor_file;
????s_f_ops:關(guān)機(jī)狀態(tài)下的文件操作;
????files_lock:自旋鎖;
????shutdown:此聲卡正在關(guān)閉;
????release_completion:釋放完成;
????dev:分配給此聲卡的設(shè)備,一般為平臺(tái)設(shè)備的device;
????card_dev:聲卡設(shè)備的內(nèi)核設(shè)備結(jié)構(gòu)體,card用于在sys中顯示,用于代表該card;把snd_card看做是device的子類,其parent為dev,class為sound_class,未設(shè)置設(shè)備號(hào)devt(默認(rèn)就是0);
????dev_groups:分配的sysfs屬性組;
????registered:聲卡設(shè)備card_dev是否已注冊(cè);
????remove_sleep:等待隊(duì)列頭;
????power_state:電源狀態(tài);
????power_sleep:電源等待隊(duì)列頭;
};

每一個(gè)聲卡設(shè)備的創(chuàng)建都是通過snd_card_new函數(shù)實(shí)現(xiàn)的,聲卡設(shè)備被注冊(cè)后都會(huì)被添加到全局snd_cards指針數(shù)組中;

@include/sound/core.h

int?snd_card_new(struct?device?*parent,?int?idx,?const?char?*xid,
???struct?module?*module,?int?extra_size,
???struct?snd_card?**card_ret);

@sound/core/init.c???
static?struct?snd_card?*snd_cards[SNDRV_CARDS];

2) struct snd_device

聲卡設(shè)備一般包含許多功能模塊,比如PCM(錄音和播放)、Control(聲卡控制),因此ALSA將聲卡的功能模塊又抽象為一個(gè)邏輯設(shè)備,與之對(duì)應(yīng)的數(shù)據(jù)結(jié)構(gòu)就是struct snd_device;

@sound/core/device.c

struct?snd_device?{
????????struct?list_head?list;??????????/*?list?of?registered?devices?*/
????????struct?snd_card?*card;??????????/*?card?which?holds?this?device?*/
????????enum?snd_device_state?state;????/*?state?of?the?device?*/
????????enum?snd_device_type?type;??????/*?device?type?*/
????????void?*device_data;??????????????/*?device?structure?*/
????????struct?snd_device_ops?*ops;?????/*?operations?*/
};
????list:用于構(gòu)建雙向鏈表節(jié)點(diǎn),該節(jié)點(diǎn)會(huì)添加到聲卡設(shè)備snd_card的devices鏈表中;
??? snd_card:表示當(dāng)前聲卡邏輯設(shè)備所屬的聲卡設(shè)備;
??? state:表示當(dāng)前聲卡邏輯設(shè)備的狀態(tài);
??? type:表示當(dāng)前聲卡邏輯設(shè)備的類型,比如pcm、control設(shè)備;
??? device_data:一般用于存放具體的功能模塊邏輯設(shè)備的結(jié)構(gòu),比如對(duì)于pcm邏輯設(shè)備存放的就是snd_pcm實(shí)例;
??? ops:聲卡邏輯設(shè)備的操作集;

每一個(gè)聲卡邏輯設(shè)備的創(chuàng)建最終會(huì)調(diào)用snd_device_new來生成一個(gè)snd_device實(shí)例,并把該實(shí)例鏈接到snd_card的devices鏈表中。

通常,linux內(nèi)核已經(jīng)提供了一些常用的功能模塊邏輯設(shè)備的創(chuàng)建函數(shù),而不必直接調(diào)用snd_device_new,比如:snd_pcm_new、snd_ctl_create。

需要注意的是:聲卡邏輯設(shè)備注冊(cè)后會(huì)在**/dev/snd**目錄下生成對(duì)應(yīng)的字符設(shè)備文件。

3) struct snd_device_ops

linux中使用snd_device_ops來表示聲卡邏輯設(shè)備的操作集;

@include/sound/core.h

struct?snd_device_ops?{
?int?(*dev_free)(struct?snd_device?*dev);
?int?(*dev_register)(struct?snd_device?*dev);
?int?(*dev_disconnect)(struct?snd_device?*dev);
};

其中:

    dev_free:聲卡邏輯設(shè)備釋放函數(shù),在卸載聲卡設(shè)備時(shí)被調(diào)用;dev_register:聲卡邏輯設(shè)備注冊(cè)函數(shù),在注冊(cè)聲卡設(shè)備被調(diào)用;dev_disconnect:聲卡邏輯設(shè)備斷開連接函數(shù),在關(guān)閉聲卡設(shè)備時(shí)被調(diào)用。

4) enum snd_device_state

linux內(nèi)核使用snd_device_state表示聲卡邏輯設(shè)備的狀態(tài)

@include/sound/core.h

enum?snd_device_state?{
????????SNDRV_DEV_BUILD,?????????//?構(gòu)建中
????????SNDRV_DEV_REGISTERED,????//?已經(jīng)準(zhǔn)備并準(zhǔn)備就緒
????????SNDRV_DEV_DISCONNECTED,??//?已斷開連接
};

5) enum snd_device_type

linux內(nèi)核使用snd_device_state表示聲卡邏輯設(shè)備的類型

@include/sound/core.h

enum?snd_device_type?{
?SNDRV_DEV_LOWLEVEL,
?SNDRV_DEV_INFO,
?SNDRV_DEV_BUS,
?SNDRV_DEV_CODEC,
?SNDRV_DEV_PCM,
?SNDRV_DEV_COMPRESS,
?SNDRV_DEV_RAWMIDI,
?SNDRV_DEV_TIMER,
?SNDRV_DEV_SEQUENCER,
?SNDRV_DEV_HWDEP,
?SNDRV_DEV_JACK,
?SNDRV_DEV_CONTROL,?/*?NOTE:?this?must?be?the?last?one?*/
};

其中:

??? SNDRV_DEV_LOWLEVEL:低級(jí)別硬件訪問接口;
??? SNDRV_DEV_INFO:信息查詢接口;
??? SNDRV_DEV_BUS:總線接口,如USB、PCI等;
??? SNDRV_DEV_CODEC:編解碼器設(shè)備;
??? SNDRV_DEV_PCM:PCM 設(shè)備,包括輸入輸出設(shè)備以及混音器等;
??? SNDRV_DEV_COMPRESS:壓縮和解壓縮設(shè)備;
??? SNDRV_DEV_RAWMIDI:原始MIDI設(shè)備;
??? SNDRV_DEV_TIMER:定時(shí)器設(shè)備;
??? SNDRV_DEV_SEQUENCER:序列器設(shè)備;
??? SNDRV_DEV_HWDEP:硬件依賴設(shè)備;
??? SNDRV_DEV_JACK:JACK音頻連接設(shè)備;
??? SNDRV_DEV_CONTROL:control設(shè)備,此項(xiàng)必須放在最后;

6) struct snd_minor

linux內(nèi)核使用snd_minor表示聲卡邏輯設(shè)備上下文信息,它在調(diào)用snd_register_device函數(shù)注冊(cè)聲卡邏輯設(shè)備時(shí)被初始化,在聲卡邏輯設(shè)備被使用時(shí)就可以從該結(jié)構(gòu)體中得到相應(yīng)的信息。

struct?snd_minor?{
????????int?type;???????????????????????/*?SNDRV_DEVICE_TYPE_XXX?*/
????????int?card;???????????????????????/*?card?number?*/
????????int?device;?????????????????????/*?device?number?*/
????????const?struct?file_operations?*f_ops;????/*?file?operations?*/
????????void?*private_data;?????????????/*?private?data?for?f_ops->open?*/
????????struct?device?*dev;?????????????/*?device?for?sysfs?*/
????????struct?snd_card?*card_ptr;??????/*?assigned?card?instance?*/
};

其中:

    type:設(shè)備類型,取值為 SNDRV_DEVICE_TYPE_XXX;card:聲卡邏輯設(shè)備所屬的聲卡設(shè)備的編號(hào);device:設(shè)備索引;f_ops:文件操作集。private_data:用戶提供給 f_ops->open函數(shù)的私有數(shù)據(jù)指針;dev:聲卡邏輯設(shè)備對(duì)應(yīng)的 struct device 結(jié)構(gòu)體指針;card_ptr:指向所屬聲卡設(shè)備;

每一個(gè)snd_minor的創(chuàng)建都是通過snd_register_device函數(shù)實(shí)現(xiàn)的,并被添加到全局snd_minors指針數(shù)組中;

static?struct?snd_minor?*snd_minors[SNDRV_OS_MINORS];

6. alsa數(shù)據(jù)結(jié)構(gòu)之間關(guān)系

為了更加形象的表示struct snd_card、struct snd_device、struct snd_minor 之間的關(guān)系,我們繪制了如下關(guān)系框圖:

7. 內(nèi)核中alsa幾個(gè)主要函數(shù)

1) snd_register_device()

Linux 內(nèi)核 ALSA 音頻框架的其它部分需要?jiǎng)?chuàng)建音頻設(shè)備文件時(shí),調(diào)用 snd_register_device() 函數(shù)為聲卡注冊(cè) ALSA 設(shè)備文件,該函數(shù)定義 (位于 sound/core/sound.c)

/**
?*?snd_register_device?-?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?for?f_ops->open()
?*?@device:?the?device?to?register
?*
?*?Registers?an?ALSA?device?file?for?the?given?card.
?*?The?operators?have?to?be?set?in?reg?parameter.
?*
?*?Return:?Zero?if?successful,?or?a?negative?error?code?on?failure.
?*/
int?snd_register_device(int?type,?struct?snd_card?*card,?int?dev,
???const?struct?file_operations?*f_ops,
???void?*private_data,?struct?device?*device)
?int?minor;
?int?err?=?0;
?struct?snd_minor?*preg;

?if?(snd_BUG_ON(!device))
??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;
?preg->card_ptr?=?card;
?mutex_lock(&sound_mutex);
?minor?=?snd_find_free_minor(type,?card,?dev);
?if?(minor?<?0)?{
??err?=?minor;
??goto?error;
?}

?preg->dev?=?device;
?device->devt?=?MKDEV(major,?minor);
?err?=?device_add(device);
?if?(err?<?0)
??goto?error;

?snd_minors[minor]?=?preg;
?error:
?mutex_unlock(&sound_mutex);
?if?(err?<?0)
??kfree(preg);
?return?err;
}
EXPORT_SYMBOL(snd_register_device);

參數(shù)說明如下

type:??設(shè)備類型
card:??聲卡實(shí)例
dev:???設(shè)備索引
f_ops:?文件操作。用戶空間程序?qū)υO(shè)備文件的各種操作,都將由這里傳入的文件操作執(zhí)行。
private_data:f_ops->open()?要用到的用戶指針
device:要注冊(cè)的設(shè)備

snd_register_device() 函數(shù)的執(zhí)行過程如下:

    分配 struct snd_minor 對(duì)象,并初始化它;為要注冊(cè)的設(shè)備尋找可用的從設(shè)備號(hào)。這有兩種策略,一種是動(dòng)態(tài)從設(shè)備號(hào),另一種是靜態(tài)從設(shè)備號(hào)。無論是哪種策略,SEQUENCER 和 TIMER 類型的設(shè)備的從設(shè)備號(hào)都是固定的 1 和 33。其它類型的設(shè)備,在動(dòng)態(tài)從設(shè)備號(hào)策略中,順序查找可用的從設(shè)備號(hào);在靜態(tài)從設(shè)備號(hào)策略中,根據(jù)聲卡索引、設(shè)備類型和設(shè)備索引構(gòu)造從設(shè)備號(hào);構(gòu)造包含主設(shè)備號(hào)和從設(shè)備號(hào)的設(shè)備號(hào);向設(shè)備層次體系結(jié)構(gòu)添加設(shè)備;將 struct snd_minor 對(duì)象指針保存在 struct snd_minor 對(duì)象指針數(shù)組中。

2) device_add()

snd_register_device() 函數(shù)調(diào)用 device_add() 函數(shù)向設(shè)備層次體系結(jié)構(gòu)添加設(shè)備,這個(gè)函數(shù)定義 (位于 drivers/base/core.c) 如下:

int?device_add(struct?device?*dev)
{
?struct?device?*parent;
?struct?kobject?*kobj;
?struct?class_interface?*class_intf;
?int?error?=?-EINVAL;
?struct?kobject?*glue_dir?=?NULL;

?dev?=?get_device(dev);
?if?(!dev)
??goto?done;

?if?(!dev->p)?{
??error?=?device_private_init(dev);
??if?(error)
???goto?done;
?}

?/*
??*?for?statically?allocated?devices,?which?should?all?be?converted
??*?some?day,?we?need?to?initialize?the?name.?We?prevent?reading?back
??*?the?name,?and?force?the?use?of?dev_name()
??*/
?if?(dev->init_name)?{
??dev_set_name(dev,?"%s",?dev->init_name);
??dev->init_name?=?NULL;
?}

?/*?subsystems?can?specify?simple?device?enumeration?*/
?if?(!dev_name(dev)?&&?dev->bus?&&?dev->bus->dev_name)
??dev_set_name(dev,?"%s%u",?dev->bus->dev_name,?dev->id);

?if?(!dev_name(dev))?{
??error?=?-EINVAL;
??goto?name_error;
?}

?pr_debug("device:?'%s':?%sn",?dev_name(dev),?__func__);

?parent?=?get_device(dev->parent);
?kobj?=?get_device_parent(dev,?parent);
?if?(IS_ERR(kobj))?{
??error?=?PTR_ERR(kobj);
??goto?parent_error;
?}
?if?(kobj)
??dev->kobj.parent?=?kobj;

?/*?use?parent?numa_node?*/
?if?(parent?&&?(dev_to_node(dev)?==?NUMA_NO_NODE))
??set_dev_node(dev,?dev_to_node(parent));

?/*?first,?register?with?generic?layer.?*/
?/*?we?require?the?name?to?be?set?before,?and?pass?NULL?*/
?error?=?kobject_add(&dev->kobj,?dev->kobj.parent,?NULL);
?if?(error)?{
??glue_dir?=?get_glue_dir(dev);
??goto?Error;
?}

?/*?notify?platform?of?device?entry?*/
?error?=?device_platform_notify(dev,?KOBJ_ADD);
?if?(error)
??goto?platform_error;

?error?=?device_create_file(dev,?&dev_attr_uevent);
?if?(error)
??goto?attrError;

?error?=?device_add_class_symlinks(dev);
?if?(error)
??goto?SymlinkError;
?error?=?device_add_attrs(dev);
?if?(error)
??goto?AttrsError;
?error?=?bus_add_device(dev);
?if?(error)
??goto?BusError;
?error?=?dpm_sysfs_add(dev);
?if?(error)
??goto?DPMError;
?device_pm_add(dev);

?if?(MAJOR(dev->devt))?{
??error?=?device_create_file(dev,?&dev_attr_dev);
??if?(error)
???goto?DevAttrError;

??error?=?device_create_sys_dev_entry(dev);
??if?(error)
???goto?SysEntryError;

??devtmpfs_create_node(dev);
?}

?/*?Notify?clients?of?device?addition.??This?call?must?come
??*?after?dpm_sysfs_add()?and?before?kobject_uevent().
??*/
?if?(dev->bus)
??blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
??????????BUS_NOTIFY_ADD_DEVICE,?dev);

?kobject_uevent(&dev->kobj,?KOBJ_ADD);

?/*
??*?Check?if?any?of?the?other?devices?(consumers)?have?been?waiting?for
??*?this?device?(supplier)?to?be?added?so?that?they?can?create?a?device
??*?link?to?it.
??*
??*?This?needs?to?happen?after?device_pm_add()?because?device_link_add()
??*?requires?the?supplier?be?registered?before?it's?called.
??*
??*?But?this?also?needs?to?happen?before?bus_probe_device()?to?make?sure
??*?waiting?consumers?can?link?to?it?before?the?driver?is?bound?to?the
??*?device?and?the?driver?sync_state?callback?is?called?for?this?device.
??*/
?if?(dev->fwnode?&&?!dev->fwnode->dev)?{
??dev->fwnode->dev?=?dev;
??fw_devlink_link_device(dev);
?}

?bus_probe_device(dev);
?if?(parent)
??klist_add_tail(&dev->p->knode_parent,
??????????&parent->p->klist_children);

?if?(dev->class)?{
??mutex_lock(&dev->class->p->mutex);
??/*?tie?the?class?to?the?device?*/
??klist_add_tail(&dev->p->knode_class,
??????????&dev->class->p->klist_devices);

??/*?notify?any?interfaces?that?the?device?is?here?*/
??list_for_each_entry(class_intf,
????????&dev->class->p->interfaces,?node)
???if?(class_intf->add_dev)
????class_intf->add_dev(dev,?class_intf);
??mutex_unlock(&dev->class->p->mutex);
?}
done:
?put_device(dev);
?return?error;
?SysEntryError:
?if?(MAJOR(dev->devt))
??device_remove_file(dev,?&dev_attr_dev);
?DevAttrError:
?device_pm_remove(dev);
?dpm_sysfs_remove(dev);
?DPMError:
?bus_remove_device(dev);
?BusError:
?device_remove_attrs(dev);
?AttrsError:
?device_remove_class_symlinks(dev);
?SymlinkError:
?device_remove_file(dev,?&dev_attr_uevent);
?attrError:
?device_platform_notify(dev,?KOBJ_REMOVE);
platform_error:
?kobject_uevent(&dev->kobj,?KOBJ_REMOVE);
?glue_dir?=?get_glue_dir(dev);
?kobject_del(&dev->kobj);
?Error:
?cleanup_glue_dir(dev,?glue_dir);
parent_error:
?put_device(parent);
name_error:
?kfree(dev->p);
?dev->p?=?NULL;
?goto?done;
}
EXPORT_SYMBOL_GPL(device_add);

device_add() 函數(shù)調(diào)用 device_create_file() 和 devtmpfs_create_node() 等函數(shù)在 sysfs 和 devtmpfs 文件系統(tǒng)中創(chuàng)建文件。

內(nèi)核各模塊通過 devtmpfs_create_node() 函數(shù)創(chuàng)建 devtmpfs 文件,這個(gè)函數(shù)定義 (位于 drivers/base/devtmpfs.c) 如下:

static?int?devtmpfs_submit_req(struct?req?*req,?const?char?*tmp)
{
?init_completion(&req->done);

?spin_lock(&req_lock);
?req->next?=?requests;
?requests?=?req;
?spin_unlock(&req_lock);

?wake_up_process(thread);
?wait_for_completion(&req->done);

?kfree(tmp);

?return?req->err;
}

int?devtmpfs_create_node(struct?device?*dev)
{
?const?char?*tmp?=?NULL;
?struct?req?req;

?if?(!thread)
??return?0;

?req.mode?=?0;
?req.uid?=?GLOBAL_ROOT_UID;
?req.gid?=?GLOBAL_ROOT_GID;
?req.name?=?device_get_devnode(dev,?&req.mode,?&req.uid,?&req.gid,?&tmp);
?if?(!req.name)
??return?-ENOMEM;

?if?(req.mode?==?0)
??req.mode?=?0600;
?if?(is_blockdev(dev))
??req.mode?|=?S_IFBLK;
?else
??req.mode?|=?S_IFCHR;

?req.dev?=?dev;

?return?devtmpfs_submit_req(&req,?tmp);
}

這個(gè)函數(shù)通過 device_get_devnode() 函數(shù)獲得 devtmpfs 設(shè)備文件的文件名,創(chuàng)建一個(gè) devtmpfs 設(shè)備文件創(chuàng)建請(qǐng)求,并提交。在 devtmpfs_submit_req() 函數(shù)中,可以看到所有的請(qǐng)求由單鏈表維護(hù),新的請(qǐng)求被放在單鏈表的頭部。

device_get_devnode() 函數(shù)定義 (位于 drivers/base/core.c) 如下:

const?char?*device_get_devnode(struct?device?*dev,
??????????umode_t?*mode,?kuid_t?*uid,?kgid_t?*gid,
??????????const?char?**tmp)
{
?char?*s;

?*tmp?=?NULL;

?/*?the?device?type?may?provide?a?specific?name?*/
?if?(dev->type?&&?dev->type->devnode)
??*tmp?=?dev->type->devnode(dev,?mode,?uid,?gid);
?if?(*tmp)
??return?*tmp;

?/*?the?class?may?provide?a?specific?name?*/
?if?(dev->class?&&?dev->class->devnode)
??*tmp?=?dev->class->devnode(dev,?mode);
?if?(*tmp)
??return?*tmp;

?/*?return?name?without?allocation,?tmp?==?NULL?*/
?if?(strchr(dev_name(dev),?'!')?==?NULL)
??return?dev_name(dev);

?/*?replace?'!'?in?the?name?with?'/'?*/
?s?=?kstrdup(dev_name(dev),?GFP_KERNEL);
?if?(!s)
??return?NULL;
?strreplace(s,?'!',?'/');
?return?*tmp?=?s;
}

device_get_devnode() 函數(shù)按照一定的優(yōu)先級(jí),嘗試從幾個(gè)地方獲得設(shè)備文件名:

    設(shè)備的設(shè)備類型 struct device_type 的 devnode 操作;設(shè)備的總線 struct class 的 devnode 操作;設(shè)備名字。對(duì)于音頻設(shè)備,我們?cè)?sound/sound_core.c 文件中看到,其總線 struct class 的 devnode 操作定義如下:
static?char?*sound_devnode(struct?device?*dev,?umode_t?*mode)
{
?if?(MAJOR(dev->devt)?==?SOUND_MAJOR)
??return?NULL;
?return?kasprintf(GFP_KERNEL,?"snd/%s",?dev_name(dev));
}

3) snd_unregister_device()

注銷 ALSA 設(shè)備文件
當(dāng)不再需要某個(gè) ALSA 設(shè)備文件時(shí),可以注銷它,這通過 snd_unregister_device() 函數(shù)完成。snd_unregister_device() 函數(shù)定義 (位于 sound/core/sound.c)

int?snd_unregister_device(struct?device?*dev)
{
?int?minor;
?struct?snd_minor?*preg;

?mutex_lock(&sound_mutex);
?for?(minor?=?0;?minor?<?ARRAY_SIZE(snd_minors);?++minor)?{
??preg?=?snd_minors[minor];
??if?(preg?&&?preg->dev?==?dev)?{
???snd_minors[minor]?=?NULL;
???device_del(dev);
???kfree(preg);
???break;
??}
?}
?mutex_unlock(&sound_mutex);
?if?(minor?>=?ARRAY_SIZE(snd_minors))
??return?-ENOENT;
?return?0;
}
EXPORT_SYMBOL(snd_unregister_device);

這個(gè)函數(shù)根據(jù)傳入的設(shè)備,查找對(duì)應(yīng)的 struct snd_minor 對(duì)象,找到時(shí),則從系統(tǒng)中刪除設(shè)備,這包括刪除 devtmpfs 文件系統(tǒng)中的設(shè)備文件等,并釋放 struct snd_minor 對(duì)象。

在 snd_register_device() 函數(shù)中可以看到,是給設(shè)備計(jì)算了設(shè)備號(hào)的,這里不能獲取設(shè)備號(hào),并根據(jù)設(shè)備號(hào)在 struct snd_minor 對(duì)象指針數(shù)組中快速查找么?

音頻設(shè)備文件的文件操作
注冊(cè)音頻字符設(shè)備時(shí),綁定的文件操作是 snd_fops,這個(gè)文件操作只定義了 open 和 llseek 兩個(gè)操作,其中 llseek 操作 noop_llseek 的定義 (位于 fs/read_write.c) 如下:

loff_t?noop_llseek(struct?file?*file,?loff_t?offset,?int?whence)
{
?return?file->f_pos;
}
EXPORT_SYMBOL(noop_llseek);

這個(gè)操作基本上什么也沒做。

open 操作 snd_open 的定義 (位于 sound/core/sound.c) 如下:

static?int?snd_open(struct?inode?*inode,?struct?file?*file)
{
?unsigned?int?minor?=?iminor(inode);
?struct?snd_minor?*mptr?=?NULL;
?const?struct?file_operations?*new_fops;
?int?err?=?0;

?if?(minor?>=?ARRAY_SIZE(snd_minors))
??return?-ENODEV;
?mutex_lock(&sound_mutex);
?mptr?=?snd_minors[minor];
?if?(mptr?==?NULL)?{
??mptr?=?autoload_device(minor);
??if?(!mptr)?{
???mutex_unlock(&sound_mutex);
???return?-ENODEV;
??}
?}
?new_fops?=?fops_get(mptr->f_ops);
?mutex_unlock(&sound_mutex);
?if?(!new_fops)
??return?-ENODEV;
?replace_fops(file,?new_fops);

?if?(file->f_op->open)
??err?=?file->f_op->open(inode,?file);
?return?err;
}

這個(gè)函數(shù):

    從 struct inode 中獲得音頻設(shè)備文件的從設(shè)備號(hào);根據(jù)從設(shè)備號(hào),在 struct snd_minor 對(duì)象指針數(shù)組中,找到對(duì)應(yīng)的 struct snd_minor 對(duì)象;從 struct snd_minor 對(duì)象獲得它的文件操作,即注冊(cè) ALSA 設(shè)備文件時(shí)傳入的文件操作;將 struct file 的文件操作替換為獲得的文件操作;執(zhí)行新的文件操作的 open 操作。

具體詳細(xì)的函數(shù),后面驅(qū)動(dòng)分析章節(jié)會(huì)繼續(xù)分析討論。

推薦器件

更多器件
器件型號(hào) 數(shù)量 器件廠商 器件描述 數(shù)據(jù)手冊(cè) ECAD模型 風(fēng)險(xiǎn)等級(jí) 參考價(jià)格 更多信息
SN74LVC2G07DCKR 1 Texas Instruments 2-ch, 1.65-V to 5.5-V buffers with open-drain outputs 6-SC70 -40 to 125

ECAD模型

下載ECAD模型
$0.57 查看
PVT422SPBF 1 International Rectifier Transistor Output SSR, 2-Channel, 4000V Isolation, LEAD FREE, PLASTIC, SURFCAE MOUNT, DIP-8
$8.57 查看
CY7C1061G30-10ZSXIT 1 Cypress Semiconductor Standard SRAM, 1MX16, 10ns, CMOS, PDSO54, TSOP2-54
$51.08 查看
瑞芯微電子

瑞芯微電子

瑞芯微專注于移動(dòng)互聯(lián)網(wǎng)、數(shù)字多媒體芯片設(shè)計(jì),是專業(yè)的個(gè)人移動(dòng)信息終端SOC解決方案供應(yīng)商。瑞芯微在移動(dòng)互聯(lián)網(wǎng)領(lǐng)域有多個(gè)較完整的自主創(chuàng)新的知識(shí)產(chǎn)權(quán)群,為中國(guó)電子業(yè)發(fā)展做出積極努力。目前產(chǎn)品涵蓋Android平板電腦、Android電視機(jī)頂盒(智能電視)、電子書、WIFI/藍(lán)牙音頻解決方案等。

瑞芯微專注于移動(dòng)互聯(lián)網(wǎng)、數(shù)字多媒體芯片設(shè)計(jì),是專業(yè)的個(gè)人移動(dòng)信息終端SOC解決方案供應(yīng)商。瑞芯微在移動(dòng)互聯(lián)網(wǎng)領(lǐng)域有多個(gè)較完整的自主創(chuàng)新的知識(shí)產(chǎn)權(quán)群,為中國(guó)電子業(yè)發(fā)展做出積極努力。目前產(chǎn)品涵蓋Android平板電腦、Android電視機(jī)頂盒(智能電視)、電子書、WIFI/藍(lán)牙音頻解決方案等。收起

查看更多

相關(guān)推薦

電子產(chǎn)業(yè)圖譜

公眾號(hào)『一口Linux』號(hào)主彭老師,擁有15年嵌入式開發(fā)經(jīng)驗(yàn)和培訓(xùn)經(jīng)驗(yàn)。曾任職ZTE,某研究所,華清遠(yuǎn)見教學(xué)總監(jiān)。擁有多篇網(wǎng)絡(luò)協(xié)議相關(guān)專利和軟件著作。精通計(jì)算機(jī)網(wǎng)絡(luò)、Linux系統(tǒng)編程、ARM、Linux驅(qū)動(dòng)、龍芯、物聯(lián)網(wǎng)。原創(chuàng)內(nèi)容基本從實(shí)際項(xiàng)目出發(fā),保持原理+實(shí)踐風(fēng)格,適合Linux驅(qū)動(dòng)新手入門和技術(shù)進(jìn)階。