聲明:本博內容轉載自 http://blog.csdn.net/droidphone
一. 概述
ALSA是Advanced Linux Sound Architecture 的縮寫,目前已經成為了linux的主流音頻體繫結構,想瞭解更多的關於ALSA的這一開源項目的資訊和知識,請查看以下網址:http://www.alsa-project.org/。
在核心裝置驅動層,ALSA提供了alsa-driver,同時在應用程式層,ALSA為我們提供了alsa-lib,應用程式只要調用alsa-lib提供的API,即可以完成對底層音頻硬體的控制。
圖 1.1 alsa的軟體體繫結構
由圖1.1可以看出,使用者空間的alsa-lib對應用程式提供統一的API介面,這樣可以隱藏了驅動層的實現細節,簡化了應用程式的實現難度。核心空間中,alsa-soc其實是對alsa-driver的進一步封裝,他針對嵌入式裝置提供了一些列增強功能。本系列博文僅對嵌入式系統中的alsa-driver和alsa-soc進行討論。 二. ALSA裝置檔案結構
我們從alsa在linux中的裝置檔案結構開始我們的alsa之旅. 看看我的電腦中的alsa驅動的裝置檔案結構:
$ cd /dev/snd
$ ls -l
crw-rw----+ 1 root audio 116, 8 2011-02-23 21:38 controlC0
crw-rw----+ 1 root audio 116, 4 2011-02-23 21:38 midiC0D0
crw-rw----+ 1 root audio 116, 7 2011-02-23 21:39 pcmC0D0c
crw-rw----+ 1 root audio 116, 6 2011-02-23 21:56 pcmC0D0p
crw-rw----+ 1 root audio 116, 5 2011-02-23 21:38 pcmC0D1p
crw-rw----+ 1 root audio 116, 3 2011-02-23 21:38 seq
crw-rw----+ 1 root audio 116, 2 2011-02-23 21:38 timer
$
我們可以看到以下裝置檔案: controlC0 --> 用於音效卡的控制,例如通道選擇,混音,麥克風的控制等 midiC0D0 --> 用於播放midi音頻 pcmC0D0c --〉 用於錄音的pcm裝置 pcmC0D0p --〉 用於播放的pcm裝置 seq --〉 音序器 timer --〉 定時器
其中,C0D0代表的是音效卡0中的裝置0,pcmC0D0c最後一個c代表capture,pcmC0D0p最後一個p代表playback,這些都是alsa-driver中的命名規則。從上面的列表可以看出,我的音效卡下掛了6個裝置,根據音效卡的實際能力,驅動實際上可以掛上更多種類的裝置,在include/sound/core.h中,定義了以下裝置類型:
[c-sharp] view plain copy print ? #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_LOWLEVEL ((__force snd_device_type_t) 0x2000)
通常,我們更關心的是pcm和control這兩種裝置。
三. 驅動的代碼檔案結構
在Linux2.6代碼樹中,Alsa的代碼檔案結構如下:
sound
/core
/oss
/seq
/ioctl32
/include
/drivers
/i2c
/synth
/emux
/pci
/(cards)
/isa
/(cards)
/arm
/ppc
/sparc
/usb
/pcmcia /(cards)
/oss
/soc
/codecs core 該目錄包含了ALSA驅動的中介層,它是整個ALSA驅動的核心部分 core/oss 包含類比舊的OSS架構的PCM和Mixer模組 core/seq 有關音序器相關的代碼 include ALSA驅動的公用標頭檔目錄,該目錄的標頭檔需要匯出給使用者空間的應用程式使用,通常,驅動模組私人的標頭檔不應放置在這裡 drivers 放置一些與CPU、BUS架構無關的公用代碼 i2c ALSA自己的I2C控制碼 pci pci音效卡的頂層目錄,子目錄包含各種pci音效卡的代碼 isa isa音效卡的頂層目錄,子目錄包含各種isa音效卡的代碼 soc 針對system-on-chip體系的中介層代碼 soc/codecs 針對soc體系的各種codec的代碼,與平台無關 Linux ALSA音效卡驅動之二:音效卡的建立
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 plain copy print ? /* 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 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 */ 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 */ int free_on_last_close; /* free in context of file_release */ wait_queue_head_t shutdown_sleep; struct device *dev; /* device assigned to this card */ #ifndef CONFIG_SYSFS_DEPRECATED struct device *card_dev; /* cardX object for sysfs */ #endif #ifdef CONFIG_PM unsigned int power_state; /* power state */ struct mutex power_lock; /* power lock */ wait_queue_head_t power_sleep; #endif #if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE) struct snd_mixer_oss *mixer_oss; int mixer_oss_change_count; #endif };
struct list_head devices 記錄該音效卡下所有邏輯裝置的鏈表 struct list_head controls 記錄該音效卡下所有的控制單元的鏈表 void *private_data 音效卡的私人資料,可以在建立音效卡時通過參數指定資料的大小 2. 音效卡的建立流程
2.1.1. 第一步,建立snd_card的一個執行個體
[c-sharp] struct snd_card *card; int err; .... 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 plain copy print ? // struct mychip 用於儲存專用資料 err = snd_card_create(index, id, THIS_MODULE, sizeof(struct mychip), &card); // 從private_data中取出 struct mychip *chip = card->private_data;
自己建立:
[c-sharp] view plain copy print ? struct mychip { struct snd_card *card; .... }; struct snd_card *card; struct mychip *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 plain copy print ? static int snd_mychip_dev_free(struct snd_device *device) { return snd_mychip_free(device->device_data); } static struct snd_device_ops ops = { .dev_free = snd_mychip_dev_free, }; .... snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
2.1.3. 第三步,設定Driver的ID和名字