開發wince下的usb音頻設備驅動總結

在做usb audio設備驅動開發前我還不知道有usb audio device class,以爲這是個HID類型的驅動,開發起來應該容易實現,後來才發現原來自己進入了一個未知領域。幸虧之前有開發過mass storage固件程序,又瞭解過OV511的usb camera驅動,所以儘管多花了點時間,中間也出現了波折但還是完成了開發。完成開發後回頭一看發現自己對USB協議、音頻處理有了進一步的瞭解,這也算是收穫一吧;因爲之前沒做過,在開發過程中我有40%的時間是用在思考如何進行、10%的時間用於將思路文檔化、50%的時間編碼,發現這樣效率還不錯,人也沒那麼累,也算是收穫二吧。

一、在總結開發經驗的時候,先溫習一些基礎知識點做鋪墊。
1、usb協議
(1)USB設備通過描述符來描述其功能,標準的USB設備有5種USB描述符:設備描述符,配置描述符,字符串描述符,接口描述符,端點描述符。
(2)USB設備可以看作提供了多個串口的設備,依據USB的規範,我們將每個串口稱作端點(Endpoint),要和這個端點通信,我們就要打開到這個端點的連接,這個連接就是管道(Pipe)。
(3)由於一個設備可能要適應多種情況,端點的設置會有多套,以備使用。端點設置稱爲接口(Interface)。USB設備展現給我們能夠找到的東西就是這些Interface,我們選擇要用的Interface,就可以找到Endpoint,再打開Endpoint,就可以傳輸數據了。所以,在驅動程序開始的時候,需要記錄下這些Interface。
(4)打開端點之後,就可以像串口一樣進行數據傳輸了。USB有4種不同類型的傳輸方式:控制傳輸(Control Transfer),批量傳輸(Bulk Transfer),中斷傳輸(Interrupt Transfer)和實時傳輸(IsochTransfer)。象audio和viedo這種對實時性要求高的就要用實時傳輸。

2、usb設備驅動
(1)HID、usb audio class device、usb video class device、mass storage這四種設備可以開發通用的設備驅動,只需要修改VID和PID即可。
(2)對於一個USB設備的流驅動,除開實現流驅動接口外,還需要實現USBInstallDriver、USBDeviceAttach、USBUnInstallDriver這三個函數以及設備拔出的回調函數。

3、音頻處理
(1)PCM編碼方式:脈衝編碼調製PCM(Pulse Code Modulation),其實就是將聲音數字化進行傳輸。也是最常見的方式 。具體原理可以從網上查詢。
(2)PCM文件:模擬音頻信號經模數轉換(A/D變換)直接形成的二進制序列,該文件沒有附加的文件頭和文件結束標誌。
(3)傅立葉變換:數字信號處理領域一種很重要的算法。傅立葉原理表明:任何連續測量的時序或信號,都可以表示爲不同頻率的正弦波信號的無限疊加。而根據該原理創立的傅立葉變換算法利用直接測量到的原始信號,以累加方式來計算該信號中不同正弦波信號的頻率、振幅和相位。和傅立葉變換算法對應的是反傅立葉變換算法。該反變換從本質上說也是一種累加處理,這樣就可以將單獨改變的正弦波信號轉換成一個信號。因此,可以說,傅立葉變換將原來難以處理的時域信號轉換成了易於分析的頻域信號(信號的頻譜),可以利用一些工具對這些頻域信號進行處理、加工。最後還可以利用傅立葉反變換將這些頻域信號轉換成時域信號。
在數學可以認爲:任何波形都可以用多項式來表示,就是說可以進行數學運算。
(4)人耳能聽到的聲波範圍是200hz-20Khz,聲音信號中的噪音一般是在低頻段(比如500hz以下),通過傅立葉變換後將此部分消除再逆轉換回去,就可以達到去噪的目的
(5)WINDOWS下對音頻的處理,大致可分爲兩部分,即音頻的輸入、輸出,和ACM壓縮處理。

4、音頻驅動
(1)音頻驅動通過一個wave api manager的模塊與上層應用打交道。具體結構圖可參考MSDN
(2)音頻驅動處理的就是PCM數據流。因爲設備的I/O採樣率已經是預先設置好一個值了,因此對於上層應用要求的不同採樣率要進行轉換,此時用線性插值算法來保證轉換後聲音不失真。
(3)AC97音頻驅動結構功能分析。

二、設備分析
通過將此設備接到PC上,用工具軟件bushound和usbview分析出此設備的信息,它有四個接口(端點配置)如下:
1、接口0:音頻控制,使用端點0,用於調節音量等操作,class爲1(Audio),subclass爲1(Audio Control)
音量調節過程:缺省是接口0,發送SET CUR指令及音量大小值。因爲改變音量後會響一下,發送SET INTERFACE從接口0切換到接口2,然後往接口2發送音頻數據(PCM格式的WAVE文件),最後發送SET INTERFACE從接口2切換回接口1。
2、接口1、2:音頻流輸出(SPEAK),使用端點1,class爲1(Audio),subclass爲2(Audio Streaming),前者bAlternateSetting和bNumEndpoints均爲0(zero bandwidth),後者均爲1
3、接口3、4:音頻輸入(MIC),使用端點2,class爲1(Audio),subclass爲2(Audio Streaming),前者bAlternateSetting和bNumEndpoints均爲0(zero bandwidth),後者均爲1
4、接口5:HID,使用端點3。未使用,class爲3(HID),subclass爲0
5、Data Format Type
輸入是單聲道、8Khz採樣率、16位的PCM,端點最大傳輸值是16字節;輸出是雙聲道、48Khz採樣率、16位的PCM,端點最大傳輸值是192字節
6、接口1、2和3、4有Alternative接口,是爲了啓動/停止MIC或SPK而準備,usb audio device class規範要求實現zero bandwidth的接口。因此要實現此設備的錄音和播放只需要操作這幾個接口即可。

三、實現步驟及思路
第一步:參照usb print實現驅動,在驅動中提供自定義的IoControl控制碼供應用程序調用來實現操作設備的目的,因爲它跳過了waveapi所以不具有通用性,這主要是爲了測試設備使用,同時加深對USB設備開發的理解並積累經驗。
第二步:在第一步的基礎上參照wavedev驅動,這樣做出的驅動應用程序只需要調用waveapi系列函數就能實現操作設備的目的,具有通用性。 
第三步:實現平臺多音頻設備的切換。

四、開發中的重點問題
第一步
(1)採樣到的PCM文件在PC上播放有加快現象 
原因:播放加快是因爲數據丟失。因爲驅動的讀控制碼這個地方只是簡單地投遞一個實時傳輸請求,應用程序循環調用自然就有緩衝區溢出的問題。改成驅動中用線程結合事件和信號量,最多可投遞5個實時傳輸請求進入內核的異步等待隊列,引入雙端循環隊列來管理緩衝區就基本上不會有數據丟失的問題。需要注意的是dwLen數組賦值不能超過端點的最大傳輸,這點在MSDN也是有說明的。
(2)採樣到的PCM文件在PC上播放有噪音現象 
原因一:此設備一次傳送960字節(即10ms),因爲輸入端點的包大小是100,所以我的Frame數組是100*10,但是數據到來是並不是由Frame[0]開始填,而是每個Frame都填96,這樣在生成PCM文件的時候就會每隔96字節多出4個0,將Frame數組改成96*10後,噪音要小多了。
原因二:此音頻設備是USB的5V供電,開發板的5V電源還接了其它使用,受硬件原因的影響有噪音存在。換到產品設備上試驗基本上無噪音。
(3)多拔插幾次就會出異常
在設備拔出的回調函數中,對於之前分配的內存、事件、信號量及打開的管道沒有做回收或關閉操作。

第二步
對於wavedev結構的驅動的修改,我們只需要關注與硬件相關的幾個部分,那就是hwctxt、wavemain部分,其它通用部分中只需要修改devctxt中的設備名即可。
(1)幾個重要函數的說明
SetRate:設置進行線性插值算法的初始值
Render2:線性插值算法,分別對8位、單聲道、立體聲進行採樣率轉換。
TransferBuffers:對於輸入將數據從DMA緩衝經採樣率轉換後搬移到應用緩衝,對於輸出將應用緩衝經採樣率轉換後搬移到DMA緩衝
(2)WAV_INIT參數的修改
對於缺省的音頻設備在系統啓動的時候就會加載。根據WAV_INIT參數可以得知設備管理器是通過RegisterDevice來加載的,但對於USB音頻設備因爲是通過USBDeviceAttach動態加載的,它跳過了設備管理器,加載驅動用的ActivateDevice會傳遞不同的參數,因此需要修改WAV_INIT參數(或者改成用RegisterDevice,不過MSDN推薦用前者)

注:此處修改一下,對於WAV_INIT參數的理解是錯誤的,設備管理器實際上通過ActivateDeviceEx來加載,傳遞給流驅動接口XXX_INIT其實都是一個Active的註冊表項路徑。
(3)DMA操作的修改
不管是AC97還是IIS音頻驅動,其過程都是通過有限狀態機來進行狀態切換。數據傳輸有一個重要機制就是使用雙緩衝,作用是一個由DMA負責傳送,一個由CPU做數據搬移。第一次啓動的時候兩個緩衝區都填滿,但只有一個交給DMA,在IST中進行接下來的數據轉換和搬移,這樣就可以保證傳送的連續性,避免數據搬移的時候DMA空置停機。因爲DMA傳輸是不佔用CPU的,所以在CPU進行數據轉換和搬移的時候DMA也在進行,這樣codec一直有數據播放就不會有播放停頓現象。
在Usb音頻驅動中是沒有DMA而是實時傳輸,如果想完全照搬DMA的工作原理,通過創建一個傳輸線程來模擬DMA,不管傳輸緩衝區開多大,在傳送回調函數激活IST進行數據轉換和搬移時,codec已經播放完數據,IST完成數據轉換和搬移並通知傳送線程投遞新請求這整個過程都會佔用CPU其中是有延時的,因此會造成播放停頓現象。因此只能完全拋開DMA的工作機制,採用第一步中實現機制,這樣就不會有問題了。對於起停DMA只需要保留其接口,內部實現改爲切換USB的接口就能實現起/停音頻設備。

第三步
可以用waveInMessage、waveOutMessage來實現音頻設備的切換,另外如果確定設備已連接可以在waveInOpen、waveOutOpen中直接指定設備id。關於這個可以參照WINCE自帶的Bluetooth中關於音頻設備切換的代碼部分。

五、存在問題

1、想在插入的時候在WAV_INIT切換爲默認,拔出的時候在WAV_DEINIT切換回去,但是沒有起作用。估計此時驅動還沒加載完,還是要放到應用程序中,通過其它方法來動態切換了。

2、在USB音頻設備拔出的消息通知函數中把默認設備切換回去,再重新插入從調試信息看驅動加載,WODM_OPEN也調用了,但是播放無聲音。如果多拔插幾次在Active下生成的項不會被刪除,只是把項下面的值刪除了,我看了PRIVATE目錄下DeactivateDevice源碼註釋中說明:如果項不能刪除,就會刪除項下面的鍵值以確保此項意外地被重複利用,但是沒說明爲什麼不能刪除項。而且多拔插幾次後device.exe和coredll.dll就會出現熟悉的大堆DataAbort錯誤提示。是不是設備管理器或者USBD沒有完全釋放完與設備有關的內核資源?

關於此問題的說明及解決方案:

(1)剛開始一直以爲是內存泄露問題,甚至認爲OHCI驅動有BUG。從出錯的地方定位看來,主要是出現在usbd、ohci的驅動如PIPE、USB設備鏈表或device.exe的設備鏈表的訪問出錯,分析可能是拔出的時候內存沒有回收就插入申請新內存,而我這個USB設備因爲有好幾個管道要打開內存資源可能不夠用,我加大PDD層中OHCI的DMA內存問題依舊。
(2)接下來我發現在插上USB音頻設備加載驅動後會接着調用WAV_OPEN,但是我不知道這是誰在打開設備(是WAVEAPI還是PM?),在拔出的時候卻沒有調用WAV_CLOSE,我覺得可能是因爲缺省的音頻設備一直在運行,所以CreateFile後不需要CloseHandle。這樣的話USB音頻驅動通過WAV_OPEN打開的句柄一直得不到釋放,是不是因爲這個原因才造成多次拔插後出出異常?
(3)通過分析WAVEAPI,終於明白之前的猜測是對的:爲什麼會有WAV_OPEN了?第一次是PM,它取設備的電源屬性,然後WAV_CLOSE。第二次是WAVEAPI,它需要設備句柄,因爲WAVEAPI和音頻驅動是通過DeviceIoControl通訊的,所以打開後是不會關閉的。
我從一個國外的網站看到有人說的,原文如下:Note that in general CE hasn't been well tested for hot-unplug of audio devices。 原因可能就是因爲WAVEPAI打開音頻設備後是不會釋放句柄的。
(4)通過借鑑mass storage的方法,我把USB音頻驅動按功能拆分成兩個DLL:WAV.DLL和USB.DLL,前者是音頻處理實現,系統啓動時加載;後者是USB處理部分,提供USB設備操作函數接口給WAV.DLL(延遲加載)在設備插入的時候由USB.DLL通過fastcall方式將USB設備上下文傳遞給WAV.DLL,拔出的時候將WAV.DLL中的USB設備上下文賦爲0。
這樣的話在WAV部分是一直存在並與waveapi交互,當涉及到USB設備操作的時候先判斷USB設備上下文是否不爲空,否則不做處理直接返回TRUE,設備無關部分因爲符合wavedev結構也不會有問題。即使USB設備拔下,在應用程序切換兩個音頻設備並播放的時候不會有問題,錄音的話可能要做一點改動。
這樣做效率可能會降低,但是目前也想不到其它好辦法。另外我考慮在WAV.DLL中增加一個線程來判斷USB的拔出和插入,這樣可能實現自動切換而不需要應用程序來切換。

3、錄音的音量播放出來偏小,如果每次都要到控制面板去改很麻煩。考慮用API或者USB的命令來實現

關於此問題的說明及解決方案:

因應用程序需要的是8Khz+8bit+mono的數據,而設備是16位所以在進行轉換後變成unsigned的PCM,其值是110到140之間,振幅只有30;通過增益處理:現值=(原值-100)*4,振幅達到120達到音量放大效果。進行增益的時候要注意不要溢出

4、此設備爲USB組合設備,功能是音頻(usb audio class)和HID,音頻功能包括一個接口實現錄音,一個接口實現播放;HID包括一個接口實現鍵盤,一個接口實現鼠標。HID這一部分通過調試發現並沒有被系統識別,現象是USBHID.dll、KBDHID.dll沒有加載,將這個設備放到PC則一切工作正常,另外一個相同功能的複合設備HID工作正常。有可能wince不支持組合設備嗎?

六、引用文章
1、PCM文件格式簡介
http://blog.csdn.net/c0ffee1982/archive/2007/11/19/1892319.aspx
2、傅立葉變換的物理意義
http://blog.csdn.net/wjw2586121/archive/2009/05/02/4142352.aspx
3、WINDOWS下對音頻的處理過程
http://blog.csdn.net/vavale/articles/306481.aspx
4、wince音頻驅動
http://blog.sina.com.cn/s/blog_537bca2a0100ct4h.html~type=v5_one&label=rela_prevarticle
5、AC97+WM9715音頻驅動開發
http://i.cn.yahoo.com/ROCKFAN_1986/blog/p_35/
6、音頻驅動中採樣率轉換的線性插值算法說明
http://topic.csdn.net/u/20090629/14/99352117-12b9-41ce-ba2b-b3d937bacb8e.html
7、WINCE聲音驅動模型概述
http://blog.csdn.net/cnhighway/archive/2009/03/18/4001945.aspx
8、解決雙音頻設備切換問題
http://blog.csdn.net/embest_mhq/archive/2009/04/01/4041860.aspx
9、如何得到系統音頻設備列表
http://topic.csdn.net/u/20081006/17/609326BB-A166-4733-A3FE-E68AC9A56811.html
10、WinCE USB Audio/Video Driver
http://blog.naver.com/PostView.nhn?blogId=mkson69&logNo=30031777873&widgetTypeCall=true
11、How To Stream Isochronous Data Over Universal Serial Bus
http://support.microsoft.com/kb/317434/en-us


原作者鏈接:http://blog.csdn.net/alien75/article/details/4729398

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章