Linux ALSA聲卡驅動之三:PCM設備的創建_第1頁
Linux ALSA聲卡驅動之三:PCM設備的創建_第2頁
Linux ALSA聲卡驅動之三:PCM設備的創建_第3頁
Linux ALSA聲卡驅動之三:PCM設備的創建_第4頁
Linux ALSA聲卡驅動之三:PCM設備的創建_第5頁
已閱讀5頁,還剩13頁未讀, 繼續免費閱讀

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領

文檔簡介

LinuxALSA聲卡驅動之三:PCM設備的創建聲明:本博內容均由/droidphone原創,轉載請注明出處,謝謝!PCM是什么PCM是英文Pulse-codemodulation的縮寫,中文譯名是脈沖編碼調制。我們知道在現實生活中,人耳聽到的聲音是模擬信號,PCM就是要把聲音從模擬轉換成數字信號的一種技術,他的原理簡單地說就是利用一個固定的頻率對模擬信號進行采樣,采樣后的信號在波形上看就像一串連續的幅值不一的脈沖,把這些脈沖的幅值按一定的精度進行量化,這些量化后的數值被連續地輸出、傳輸、處理或記錄到存儲介質中,所有這些組成了數字音頻的產生過程。圖1.1模擬音頻的采樣、量化PCM信號的兩個重要指標是采樣頻率和量化精度,目前,CD音頻的采樣頻率通常為44100Hz,量化精度是16bit。通常,播放音樂時,應用程序從存儲介質中讀取音頻數據(MP3、WMA、AAC......),經過解碼后,最終送到音頻驅動程序中的就是PCM數據,反過來,在錄音時,音頻驅動不停地把采樣所得的PCM數據送回給應用程序,由應用程序完成壓縮、存儲等任務。所以,音頻驅動的兩大核心任務就是:playback如何把用戶空間的應用程序發過來的PCM數據,轉化為人耳可以辨別的模擬音頻capture把mic拾取到得模擬信號,經過采樣、量化,轉換為PCM信號送回給用戶空間的應用程序alsa-driver中的PCM中間層ALSA已經為我們實現了功能強勁的PCM中間層,自己的驅動中只要實現一些底層的需要訪問硬件的函數即可。要訪問PCM的中間層代碼,你首先要包含頭文件<sound/pcm.h>,另外,如果需要訪問一些與hw_param相關的函數,可能也要包含<sound/pcm_params.h>。每個聲卡最多可以包含4個pcm的實例,每個pcm實例對應一個pcm設備文件。pcm實例數量的這種限制源于linux設備號所占用的位大小,如果以后使用64位的設備號,我們將可以創建更多的pcm實例。不過大多數情況下,在嵌入式設備中,一個pcm實例已經足夠了。一個pcm實例由一個playbackstream和一個capturestream組成,這兩個stream又分別有一個或多個substreams組成。圖2.1聲卡中的pcm結構在嵌入式系統中,通常不會像圖2.1中這么復雜,大多數情況下是一^聲卡,一^個pcm實例,pcm下面有一^個playback和capturestream,playback和capture下面各自有一個substreamo下面一張圖列出了pcm中間層幾個重要的結構,他可以讓我們從uml的角度看一看這列結構的關系,理清他們之間的關系,對我們理解pcm中間層的實現方式。2.2pcm中間層的幾個重要的結構體的關系圖snd_pcm是掛在snd_card下面的一個snd_devicesnd_pcm中的字段:streams[2],該數組中的兩個元素指向兩個snd_pcm_str結構,分別代表playbackstream和capturestreamsnd_pcm_str中的substream字段,指向snd_pcm_substream結構snd_pcm_substream是pcm中間層的核心,絕大部分任務都是在substream中處理尤其是他的op(snd_pcm_ops)字段,許多user空間的應用程序通過alsa-lib對驅動程序的請求都是由該結構中的函數處理。它的runtime字段則指向snd_pcm_runtime結構,snd_pcm_runtime記錄這substream的一些重要的軟件和硬件運行環境和參數。新建一^個pcmalsa-driver的中間層已經為我們提供了新建pcm的api:intsnd_pcm_new(structsnd_card*card,constchar*id,intdevice,intplayback_count,intcapture_count,structsnd_pcm**rpcm);參數device表示目前創建的是該聲卡下的第幾個pcm,第一個pcm設備從0開始。參數playback_count表示該pcm將會有幾個playbacksubstreamo參數capture_count表示該pcm將會有幾個capturesubstreamo另一個用于設置pcm操作函數接口的api:voidsnd_pcm_set_ops(structsnd_pcm*pcm,intdirection,structsnd_pcm_ops*ops);新建一個pcm可以用下面一張新建pcm的調用的序列圖進行描述:圖3.1新建pcm的序列圖snd_card_createpcm是聲卡下的一個設備(部件),所以第一步是要創建一個聲卡snd_pcm_new調用該api創建一個pcm,才該api中會做以下事情如果有,建立playbackstream,相應的substream也同時建立如果有,建立capturestream,相應的substream也同時建立調用snd_device_new()把該pcm掛到聲卡中,參數ops中的dev_register字段指向了函數snd_pcm_dev_register,這個回調函數會在聲卡的注冊階段被調用。snd_pcm_set_ops設置操作該pcm的控制/操作接口函數,參數中的snd_pcm_ops結構中的函數通常就是我們驅動要實現的函數snd_card_register注冊聲卡,在這個階段會遍歷聲卡下的所有邏輯設備,并且調用各設備的注冊回調函數,對于pcm,就是第二步提到的snd_pcm_dev_register函數,該回調函數建立了和用戶空間應用程序(alsa-lib)通信所用的設備文件節點:/dev/snd/pcmCxxDxxp和/dev/snd/pcmCxxDxxc設備文件節點的建立(dev/snd/pcmCxxDxxp、pcmCxxDxxc)4.1structsnd_minor每個snd_minor結構體保存了聲卡下某個邏輯設備的上下文信息,他在邏輯設備建立階段被填充,在邏輯設備被使用時就可以從該結構體中得到相應的信息。pcm設備也不例外,也需要使用該結構體。該結構體在include/sound/core.h中定義。[c-sharp]viewplaincopy?structsnd_minor(inttype;/*SNDRV_DEVICE_TYPE_XXX*/intcard;/*cardnumber*/intdevice;/*devicenumber*/conststructfile_operations*f_ops;/*fileoperations*/void*private_data;/*privatedataforf_ops->open*/structdevice*dev;/*deviceforsysfs*/};在sound/sound.c中定義了一個snd_minor指針的全局數組:[c-sharp]viewplaincopy?staticstructsnd_minor*snd_minors[256];前面說過,在聲卡的注冊階段(snd_card_register),會調用pcm的回調函數snd_pcm_dev_register(),這個函數里會調用函數snd_register_device_for_dev():[c-sharp]viewplaincopy?staticintsnd_pcm_dev_register(structsnd_device*device)(/*registerpcm*/err=snd_register_device_for_dev(devtype,pcm->card,pcm->device,&snd_pcm_f_ops[cidx],pcm,str,dev);我們再進入snd_register_device_for_dev():[c-sharp]viewplaincopy?intsnd_register_device_for_dev(inttype,structsnd_card*card,intdev,conststructfile_operations*f_ops,void*private_data,constchar*name,structdevice*device)(intminor;structsnd_minor*preg;if(snd_BUG_ON(!name))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;mutex_lock(&sound_mutex);#ifdefCONFIG_SND_DYNAMIC_MINORSminor=snd_find_free_minor();#elseminor=snd_kernel_minor(type,card,dev);if(minor>=0&&snd_minors[minor])minor=-EBUSY;#endifif(minor<0)(mutex_unlock(&sound_mutex);kfree(preg);returnminor;}snd_minors[minor]=preg;preg->dev=device_create(sound_class,device,MKDEV(major,minor),private_data,"%s",name);if(IS_ERR(preg->dev)){snd_minors[minor]=NULL;mutex_unlock(&sound_mutex);minor=PTR_ERR(preg->dev);kfree(preg);returnminor;mutex_unlock(&sound_mutex);return0;}首先,分配并初始化一個snd_minor結構中的各字段type:SNDRV_DEVICE_TYPE_PCM_PLAYBACK/SNDRV_DEVICE_TYPE_PCM_CAPTUREcard:card的編號device:pcm實例的編號,大多數情況為0f_ops:snd_pcm_f_opsprivate_data:指向該pcm的實例根據type,card和pcm的編號,確定數組的索引值minor,minor也作為pcm設備的此設備號把該snd_minor結構的地址放入全局數組snd_minors[minor]中最后,調用device_create創建設備節點4.2設備文件的建立在4.1節的最后,設備文件已經建立,不過4.1節的重點在于snd_minors數組的賦值過程,在本節中,我們把重點放在設備文件中?;氐絧cm的回調函數snd_pcm_dev_register()中:[c-sharp]viewplaincopy?staticintsnd_pcm_dev_register(structsnd_device*device)(intcidx,err;charstr[16];structsnd_pcm*pcm;structdevice*dev;pcm=device->device_data;for(cidx=0;cidx<2;cidx++)(switch(cidx)(caseSNDRV_PCM_STREAM_PLAYBACK:sprintf(str,"pcmC%iD%ip",pcm->card->number,pcm->device);devtype=SNDRV_DEVICE_TYPE_PCM_PLAYBACK;break;caseSNDRV_PCM_STREAM_CAPTURE:sprintf(str,"pcmC%iD%ic",pcm->card->number,pcm->device);devtype=SNDRV_DEVICE_TYPE_PCM_CAPTURE;break;}/*devicepointertouse,pcm->devtakesprecedenceifitisassigned,otherwisefallbacktocard'sdeviceifpossible*/dev=pcm->dev;if(!dev)dev=snd_card_get_device_link(pcm->card);/*registerpcm*/err=snd_register_device_for_dev(devtype,pcm->card,pcm->device,&snd_pcm_f_ops[cidx],pcm,str,dev);}以上代碼我們可以看出,對于一個pcm設備,可以生成兩個設備文件,一個用于playback,一個用于capture,代碼中也確定了他們的命名規則:playback--pcmCxDxp,通常系統中只有一各聲卡和一個pcm,它就是pcmC0D0pcapture--pcmCxDxc,通常系統中只有一各聲卡和一個pcm,它就是pcmC0D0csnd_pcm_f_opssnd_pcm_f_ops是一個標準的文件系統file_operations結構數組,它的定義在sound/core/pcm_native.c中:[c-sharp]viewplaincopy?conststructfile_operationssnd_pcm_f_ops[2]=((.owner=THIS_MODULE,.write=snd_pcm_write,.aio_write=snd_pcm_aio_write,.open=snd_pcm_playback_open,.release=snd_pcm_release,.llseek=no_llseek,.poll=snd_pcm_playback_poll,.unlocked_ioctl=snd_pcm_playback_ioctl,.compat_ioctl=snd_pcm_ioctl_compat,.mmap=snd_pcm_mmap,.fasync=snd_pcm_fasync,.get_unmapped_area=snd_pcm_get_unmapped_area,},(

.owner=THIS_MODULE,.read=snd_pcm_read,.aio_read=snd_pcm_aio_read,.open=snd_pcm_capture_open,.release=snd_pcm_release,.llseek=no_llseek,.poll=snd_pcm_capture_poll,.unlocked_ioctl=snd_pcm_capture_ioctl,.compat_ioctl=snd_pcm_ioctl_compat,.mmap=snd_pcm_mmap,.fasync=snd_pcm_fasync,.get_unmapped_area=snd_pcm_get_unmapped_area,}};snd_pcm_f_ops作為snd_register_device_for_dev的參數被傳入,并被記錄在snd_minors[minor]中的字段f_ops中。最后,在snd_register_device_for_dev中創建設備節點:[c-sharp]viewplaincopy?snd_minors[minor]=preg;preg->dev=device_create(sound_class,device,MKDEV(major,minor),private_data,"%s",name);4.3層層深入,從應用程序到驅動層pcm4.3.1字符設備注冊在sound/core/sound.c中有alsa_sound_init()函數,定義如下:[c-sharp]viewplaincopy?staticint—initalsa_sound_init(void)(snd_major=major;snd_ecards_limit=cards_limit;if(register_chrdev(major,"alsa",&snd_fops))(snd_printk(KERN_ERR"unabletoregisternativemajordevicenumber%d/n",major);return-EIO;}if(snd_info_init()<0)(unregister_chrdev(major,"alsa");return-ENOMEM;}snd_info_minor_register();return0;}register_chrdev中的參數major與之前創建pcm設備是device_create時的major是同一^,這樣的結果是,當應用程序open設備文件/dev/snd/pcmCxDxp時,會進入snd_fops的open回調函數,我們將在下一節中講述open的過程。4.3.2打開pcm設備從上一節中我們得知,open一個pcm設備時,將會調用snd_fops的open回調函數我們先看看snd_fops的定義:[c-sharp]viewplaincopy?staticconststructfile_operationssnd_fops=(.owner=THIS_MODULE,.open=snd_open};跟入snd_open函數,它首先從inode中取出此設備號,然后以次設備號為索引,從snd_minors全局數組中取出當初注冊pcm設備時填充的snd_minor結構(參看4.1節的內容),然后從snd_minor結構中取出pcm設備的f_ops,并且把file->f_op替換為pcm設備的f_ops緊接著直接調用pcm設備的f_ops->open(),然后返回。因為file->f_op已經被替

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯系上傳者。文件的所有權益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經權益所有人同意不得將文件中的內容挪作商業或盈利用途。
  • 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
  • 6. 下載文件中如有侵權或不適當內容,請與我們聯系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論