WINCE Driver 心得总结
一. Windows CE的驅(qū)動程序的區(qū)分
1.從加載以及接口方式來區(qū)分
可以分為本機設(shè)備驅(qū)動(Built-In Driver)、可加載驅(qū)動(Loadable Driver)以及混合型驅(qū)動。
(1)本機設(shè)備驅(qū)動
本機設(shè)備驅(qū)動即Native Device Drivers。這些驅(qū)動程序在系統(tǒng)啟動時,在GWES的進程空間內(nèi)被加載,因此它們不是以獨立的DLL形式存在。這些驅(qū)動對應(yīng)的設(shè)備通常在系統(tǒng)啟動時就被要求加載,如果沒有串口,也沒有LCD的話,整個系統(tǒng)就不能和用戶信息交流。另外,流驅(qū)動程序也能作為本機設(shè)備驅(qū)動而存在。
(2)可加載驅(qū)動
也被稱為流驅(qū)動。
這些驅(qū)動可以在系統(tǒng)啟動時或者和啟動后的任何時候由設(shè)備管理器動態(tài)加載。通常它們以DLL動態(tài)鏈接庫的形式存在,系統(tǒng)加載它們后,這些驅(qū)動程序也只是以用戶態(tài)的角色運行。可加載驅(qū)動程序通過文件操作API來從設(shè)備管理器和應(yīng)用程序獲得命令。
在Windows CE中典型的可加載驅(qū)動有以下各類:
n???? PCMCIA driver(PCMCIA.dll)
n???? Serial driver(SERIAL.dll)
n???? ATAFLASH driver(ATA.dll)
n???? Ethernet driver(NE2000.dll,SMSC100FD.dll)
(3)混合型驅(qū)動
這類驅(qū)動綜合了前兩種驅(qū)動的特性。它同時使用了stream接口和custom-purpose接口。
混合型驅(qū)動主要是提供custom-purpose 接口,但是由于需要和系統(tǒng)中只允許使用stream接口的那些模塊進行交互,因此也必須提供stream接口。例如,PC card socket驅(qū)動同時擁有兩套接口。
2.從驅(qū)動層次上分
可以分為獨立驅(qū)動和層次型驅(qū)動,下圖是這兩種驅(qū)動在系統(tǒng)中的位置。
?
(1)獨立驅(qū)動程序
可以將驅(qū)動程序編寫成同時包含MDD和PDD層的獨立驅(qū)動。獨立驅(qū)動的代碼應(yīng)當(dāng)包括中斷服務(wù)例程和平臺相關(guān)處理函數(shù)。使用獨立驅(qū)動的好處在于可以省去MDD和PDD層驅(qū)動之間的信息傳遞,這一點在實時處理中非常重要。另外,如果設(shè)備的操作和MDD驅(qū)動層的接口描述相吻合,可以使用獨立驅(qū)動程序提高處理性能。
(2)層次型驅(qū)動
層次型驅(qū)動分為兩層,較上層的Model Device Driver(MDD)和比較下層的Platform Dependent Driver(PDD)。MDD實現(xiàn)的是和平臺無關(guān)的功能,它描述了一個通用的驅(qū)動程序框架。而PDD是和硬件以及平臺相關(guān)的代碼組成。MDD調(diào)用PDD中特定的接口來獲取硬件相關(guān)的信息。當(dāng)使用層次型驅(qū)動的時候,一般只需要基于相近的樣列驅(qū)動程序,針對特定的硬件修改PDD程序,MDD建立的框架可繼續(xù)使用。由于層次間接口的層層調(diào)用以及消息的傳遞,使得處理速度相對獨立驅(qū)動程序要慢,因此在時間要求苛刻的環(huán)境下,層次型驅(qū)動顯得不是很適合。
?
一般MDD將完成以下任務(wù)。
n????連接PDD層,并且定義它要使用到的Device Driver Service Provider Interface(DDSI)函數(shù)集;
n???? 向設(shè)備管理器提供Device Driver Interface(DDI)接口集;
n???? 處理復(fù)雜的事件,如中斷等等。
每一種MDD驅(qū)動都處理不同種類的設(shè)備。DDI是由MDD層驅(qū)動以及獨立型驅(qū)動提供給設(shè)備管理器的一組接口集。DDSI是由PDD向MDD層提供的接口集。公司的設(shè)備可以用同樣的DDI。
在開發(fā)過程中,MDD層驅(qū)動是不需要被修改的。微軟公司不保證被修改的MDD能在系統(tǒng)中正確運行的。和MDD層驅(qū)動不同的是,PDD層驅(qū)動必須被修改成和特定硬件相匹配的代碼。程序員可以自己開發(fā)一個PDD程序,多數(shù)情況下建議開發(fā)者在Platform Builder提供的樣例驅(qū)動程序上進行修改。例如,Platform Builder提供了Wavedev驅(qū)動程序,它的代碼位于%WINCEROOT%\public\common\oak\drivers\WAVEDEV下,這是一個容易理解的流接口層次型驅(qū)動程序。此樣例audio驅(qū)動程序僅提供了播放及錄音功能,只提供播放功能的結(jié)構(gòu)框架,播放功能和音頻設(shè)備的交互還需要 PDD層來解決。
?
二.????????? ?Audio Driver 架構(gòu):
在WINCE中 Audio Driver 架構(gòu)支持兩種驅(qū)動模式
即獨立型的unified audio model (UAM)驅(qū)動和分層式的MDD and PDD mode驅(qū)動,(不論是UAM或者MDD/PDD都是流接口驅(qū)動)。
其架構(gòu)還支持的audio compression manager (ACM)驅(qū)動,例如codecs, converters, and filters等器件
1)???????? UAM
UAM支持標(biāo)準(zhǔn)波形驅(qū)動接口(standard wave driver interfaces),過去的波形驅(qū)動和采樣驅(qū)動是由MDD和PDD模型組成。MDD模型執(zhí)行了驅(qū)動的獨立硬件部分以及輸出到驅(qū)動接口的中間設(shè)備。PDD模型提供了驅(qū)動依賴硬件的執(zhí)行部分
The following illustration shows the UAM stack.:
Using MDD and PDD, the previous model had the following limitations:
·??????????? No support for multiple streams
·??????????? No multiple devices on one driver
·??????????? No reliable support for looping
·??????????? Poor support for streaming
OEM商可以圍繞這些限制來移植自己的MDD或者寫入他們自己的完整驅(qū)動來輸出到合適的接口到中間設(shè)備
UAM實現(xiàn)了對WAV和Microsoft DirectSound®音頻API的高效支持。它還使得編寫一個能有效支持WAV和DirectSound的驅(qū)動程序成為可能。
?
在我們的WM8753 音頻Driver中即使用了UAM這種驅(qū)動模式,它也是一種流接口驅(qū)動,故只需編寫驅(qū)動中WAVE和MIXER這兩部分,然后使用流接口函數(shù)調(diào)用即可。
?
2).音頻MDD和PDD
?
編寫音頻驅(qū)動我們可以選擇UAM架構(gòu),或者直接執(zhí)行流接口(stream interface),我們使用由微軟提供的MDD庫-Wavemdd.lib。這個庫通過DDSI來執(zhí)行流接口功能。如果使用了Wavemdd.lib,則必須一個PDD庫來執(zhí)行音頻DDSI的功能。這個庫被稱為Wavepdd,lib, 這兩個庫編譯連接后就形成了我們的音頻驅(qū)動,通常被為Wavedev,dll。
在系統(tǒng)程序文件中可能缺少器件的功能導(dǎo)致音頻器件的很多功能無法被使用,為了解決這個問題DeviceIOControl
PDD和MDD都依靠調(diào)用DDSI函數(shù)來實現(xiàn)相互通信,所以若采用分層式來編寫驅(qū)動,只需找到微軟提供的MDD,然后根據(jù)其DDSI來編寫PDD層即可。
?
三.????????? 流接口驅(qū)動
不論是UAM或者PDD/MDD的架構(gòu),它們都是流接口驅(qū)動
流接口驅(qū)動有一套標(biāo)準(zhǔn)的接口,這和本機驅(qū)動是不一樣的。
n???? 對于I/O設(shè)備來說是非常適合的。????????????????????????????????????
n???? 操作接口和文件系統(tǒng)API十分類似,比如ReadFile,IOControl等。
n???? 應(yīng)用程序可以和流接口驅(qū)動進行交互,并且可以把流驅(qū)動當(dāng)成文件來操作。
流驅(qū)動與驅(qū)動接口、提供設(shè)備的種類無關(guān),因為這組接口有統(tǒng)一的接口規(guī)范。對于需要數(shù)據(jù)流的設(shè)備來說,這種驅(qū)動是十分適合的,如串口就是個典型的例子。可以把使用流驅(qū)動的設(shè)備近似地看作是文件,這樣可以通過文件系統(tǒng)API來操作設(shè)備,如ReadFile,IOControl。由于采用了文件系統(tǒng)的API,使得驅(qū)動程序能通過文件系統(tǒng)進行訪問,這點和獨立驅(qū)動程序是不同的。
下圖是流驅(qū)動程序在整個系統(tǒng)中的結(jié)構(gòu)示意圖。
?
上圖顯示了作為本機驅(qū)動而存在的流驅(qū)動程序,在啟動時被設(shè)備管理器所加載。一般本機驅(qū)動是指custom接口的驅(qū)動程序,但是流驅(qū)動也可以成為本機驅(qū)動,例如串口。
流驅(qū)動通過文件系統(tǒng)API來和應(yīng)用程序交互,同時又通過流接口接受設(shè)備管理器的管理。無論流驅(qū)動管理的是本機設(shè)備還是動態(tài)加載的設(shè)備,它們自身是在啟動時被加載還是啟動后由設(shè)備管理器動態(tài)加載,這和系統(tǒng)中其他模塊的交互模型是一樣的。
實現(xiàn)流驅(qū)動程序大致需要完成以下步驟。
(1)選擇代表設(shè)備的文件名前綴;
(2)實現(xiàn)驅(qū)動的各個入口點;
(3)建立.DEF文件;
(4)在注冊表中為驅(qū)動程序建立表項。
?
以下是創(chuàng)建流驅(qū)動的具體步驟。
(1)首先確定設(shè)備名的前綴。前綴非常重要,設(shè)備管理器在注冊表中通過前綴來識別設(shè)備。同時,在流接口命名時,也將這個前綴作為入口點函數(shù)的前綴,如果設(shè)備前綴為XXX,那么流接口對應(yīng)為XXX_Close,XXX_Init等。
(2)實現(xiàn)流接口的各個入口點。所謂入口點是指提供給設(shè)備管理器的標(biāo)準(zhǔn)文件I/O接口。
下表是對這些接口的介紹:
| 接? 口? 名 | 功能描述 |
| XXX_Close | 關(guān)閉hOpenContext參數(shù)指定的設(shè)備上下文 |
| XXX_Deinit | 通知設(shè)備管理器回收設(shè)備初始化時分配的資源 |
| XXX_Init | 通知設(shè)備管理器為設(shè)備初始化時分配資源 |
| XXX_Open | 打開設(shè)備,這個接口可以由應(yīng)用程序直接調(diào)用createfile,然后通過文件系統(tǒng)映射為XXX_Open |
| XXX_IOControl | I/O控制指令 |
| XXX_PowerUp | 設(shè)備加電時,此接口會被自動調(diào)用,可以在這里分配資源等 |
| XXX_PowerDown | 如果設(shè)備能由軟件控制斷電,則在設(shè)備斷電前,設(shè)備管理器會調(diào)用這個接口做些安全性檢查 |
| XX_Read | 從打開的設(shè)備文件中讀取數(shù)據(jù) |
| XXX_Seek | 文件定位,如果設(shè)備支持的話 |
| XXX_Write | 寫數(shù)據(jù)到設(shè)備文件 |
對于流驅(qū)動,×××_Open/×××_Close/×××_IoControl等就是ddi;如果不是流驅(qū)動,它的ddi不具有上述形式;
?
以我們的driver為例,在程序中,我們可以在C:\WINCE500\PLATFORM\C340\Src\Drivers\audio\IIS\wm8753\wavemain.cpp中找到以下對應(yīng)點
| Programming element | Description |
| WAV_IOControl | This function is the device I/O control routine for the WAV I/O device. |
| WAV_Init | This function initializes the WAV I/O device. |
| WAV_Deinit | This function deinitializes the WAV I/O device. |
| WAV_Open | This function opens the WAV I/O device. |
| WAV_Close | This function closes the WAV I/O device. |
| WAV_Read | This function is the read routine for the WAV I/O device driver. |
| WAV_Write | This function is the write routine for the WAV I/O device. |
| WAV_Seek | This function is the seek routine for the WAV I/O device. |
| WAV_PowerUp | This function notifies the WAV I/O device that the system is leaving the suspend state. |
| WAV_PowerDown | This function turns off the WAV I/O device |
?
在注冊表中還要建立驅(qū)動程序的入口點,這樣設(shè)備管理器才能識別和管理這個驅(qū)動
[HKEY_LOCAL_MACHINE\Drivers\BuiltIn\Audio]
?? "Prefix"="WAV"
?"Dll"="s3c2440a_iis_wm8753.dll"
?? "Index"=dword:1
?? "Order"=dword:0
此外,注冊表還能儲存額外的信息,這些信息可以在驅(qū)動運行之后被使用到,DLL項是設(shè)備管理器在加載驅(qū)動時需要的DLL名稱;Prefix代表了設(shè)備前綴;Order是驅(qū)動程序被加載的順序
?
以下詳細(xì)說明下流接口函數(shù)的各入口
Streams入口:Open和Close
(1)XXX_Open入口
用于讀/寫打開一個設(shè)備文件。當(dāng)應(yīng)用程序調(diào)用CreateFile的時候,文件系統(tǒng)會自動調(diào)用本接口,打開一個已經(jīng)存在的設(shè)備文件。當(dāng)這個接口被調(diào)用的時候,設(shè)備驅(qū)動可以向設(shè)備管理器申請分配資源,并且為讀/寫文件做好準(zhǔn)備。
下面是接口的原形:
DWORD XXX_Open( DWORD hDeviceContext, DWORD AccessCode, DWORD ShareMode );
參數(shù)解釋:
hDeviceContext
指向XXX_Init返回的設(shè)備句柄(上下文)。
AccessCode
打開設(shè)備的權(quán)限描述符,這些權(quán)限包括讀設(shè)備、寫設(shè)備、讀 /寫等。
所謂的設(shè)備上下文指的是代表圖形設(shè)備接口graphics device interface(GDI)的數(shù)據(jù)結(jié)構(gòu)。它包含了在特定區(qū)域內(nèi)設(shè)備顯示圖象的信息。設(shè)備上下文包含了圖形對象(例如pen、brush、字體等),不斷地修改和調(diào)用它們,從而達(dá)到顯示不同圖象的效果。
(2)XXX_Close入口
在關(guān)閉設(shè)備的時候被操作系統(tǒng)調(diào)用。對應(yīng)于CloseHandle接口。
BOOL XXX_Close( DWORD hOpenContext );
參數(shù)解釋:
hOpenContext
指向XXX_Open返回的已經(jīng)打開的設(shè)備句柄(上下文)。
應(yīng)用程序可以調(diào)用CloseHandle來關(guān)閉正在使用的流驅(qū)動程序,然后設(shè)備管理器會相應(yīng)地調(diào)用本接口來關(guān)閉設(shè)備,當(dāng)設(shè)備關(guān)閉后hOpenContext描述的設(shè)備句柄將不再有效。
Streams入口:Init and Deinit
(1)XXX_Init入口
XXX_Init要完成以下任務(wù)。
n???? 在驅(qū)動被系統(tǒng)加載時,本接口被調(diào)用;
n???? 初始化需要的資源在本接口處理中被分配;
n???? 創(chuàng)建內(nèi)存映射。
下面是接口的原型:
DWORD XXX_Init( DWORD dwContext );
參數(shù)解釋:
n???? dwContext
指向一個字符串,它描述了注冊表中的一個流設(shè)備接口。
當(dāng)調(diào)用設(shè)備ActivateDeviceEx函數(shù)后,設(shè)備管理器自動調(diào)用這個函數(shù)。當(dāng)用戶激活一個新的設(shè)備時,如插入USB設(shè)備后,當(dāng)總線自檢時,設(shè)備就會被激活,這個接口就會被調(diào)用,這個接口是不允許應(yīng)用程序直接調(diào)用的。
當(dāng)這個接口的處理結(jié)果返回時,設(shè)備管理器就在注冊表中尋找驅(qū)動的Ioctl子鍵。如果這個子鍵存在,設(shè)備管理器將調(diào)用XXX_IOControl接口將dwCode參數(shù)傳入驅(qū)動入口點。
(2)XXX_Deinit入口
在驅(qū)動被系統(tǒng)卸載的時候,本接口將被調(diào)用,它將釋放所有占用的阻援,并且停止IST。
下面是接口的原型:
BOOL XXX_Deinit( DWORD hDeviceContext );
參數(shù)解釋:
hDeviceContext
指向設(shè)備上下文的句柄。這個句柄應(yīng)該是由XXX_Init返回的。
當(dāng)程序調(diào)用DeactivateDevice時,設(shè)備管理器將自動調(diào)用本接口。流接口將釋放全部它申請的資源,并且停止設(shè)備的運行。
Streams入口:Read,Write,Seek
(1)XXX_Read入口
當(dāng)應(yīng)用程序直接調(diào)用ReadFile函數(shù)時,設(shè)備管理器將調(diào)用這個接口。
下面是接口的原型:
DWORD XXX_Read( DWORD hOpenContext, LPVOID pBuffer, DWORD Count );
參數(shù)解釋:
hOpenContext
指向XXX_Open接口返回的設(shè)備上下文。
pBuffer
指向緩沖區(qū),這個緩沖區(qū)將用來存放從設(shè)備中讀出的數(shù)據(jù),以字節(jié)為單位。
Count
指定要從設(shè)備讀取多少字節(jié)的數(shù)據(jù)存入pBuffer指向的緩沖區(qū)中。
這個從指定的設(shè)備中讀取指定數(shù)量的字節(jié)數(shù)據(jù),它對應(yīng)的應(yīng)用層API為ReadFile。ReadFile函數(shù)的參數(shù)hFile是指向設(shè)備的句柄,ReadFile函數(shù)的參數(shù)hFile將被填寫到count參數(shù)中,ReadFile中的pSizeRead將存放實際讀取數(shù)據(jù)的字節(jié)數(shù)。本接口的返回值是pSizeRead中填充的數(shù)值,若返回-1,代表發(fā)生了錯誤。
(2)XXX_Write入口
當(dāng)應(yīng)用程序調(diào)用WriteFile的時候,設(shè)備管理器將調(diào)用本接口。
下面是接口的原型:
DWORD XXX_Write( DWORD hOpenContext, LPCVOID pBuffer, DWORD Count );
參數(shù)解釋:
hOpenContext
指向XXX_Open接口返回的設(shè)備上下文。
pBuffer
指向緩沖區(qū),這個緩沖區(qū)將用來存放要向設(shè)備中寫入的數(shù)據(jù),以字節(jié)為單位。
Count
指定要從pBuffer指向的緩沖區(qū)向設(shè)備讀取寫入多少字節(jié)的數(shù)據(jù)。
(3)XXX_Seek入口
在定位I/O指針的時候被調(diào)用。
下面是接口的原型:
DWORD XXX_Seek( DWORD hOpenContext, long Amount, WORD Type );
參數(shù)解釋:
hOpenContext
指向XXX_Open接口返回的設(shè)備上下文。
Amount
指定指針要移動多少距離,以字節(jié)為單位。正值代表向文件尾端移動,負(fù)值則相反。
Type
描述了起始點的位置。當(dāng)應(yīng)用程序調(diào)用了SetFilePointer函數(shù)后,設(shè)備管理器就會調(diào)用本接口。
如果設(shè)備是可以重復(fù)打開的,本接口用到的指針只是針對hOpenContext的。
Streams入口:PowerUp和PowerDown
(1)XXX_PowerDown入口
這個接口是在停止對設(shè)備供應(yīng)電源時被調(diào)用。下面是接口的原型:
void XXX_PowerDown( DWORD hDeviceContext );
參數(shù)解釋:
hDeviceContext
指向由XXX_Init返回的設(shè)備上下文。
這個函數(shù)應(yīng)當(dāng)執(zhí)行停止設(shè)備供電的操作,此設(shè)備必須支持軟關(guān)電的功能。在I/O control接口中,如果I/O命令字為IOCTL_POWER_XXX,那么就應(yīng)該調(diào)用本接口。這個接口是對應(yīng)與應(yīng)用程序的,系統(tǒng)電源管理并不會用到這個接口。
設(shè)備管理器在將設(shè)備設(shè)置成節(jié)電模式之前將調(diào)用本接口,本接口將盡可能避免引起阻塞的操作,并盡快返回。
(2)XXX_PowerUp入口
恢復(fù)了設(shè)備的供電。
下面是接口的原型:
void XXX_PowerUp( DWORD hDeviceContext );
參數(shù)解釋:
hDeviceContext
指向XXX_Open接口建立并且返回的設(shè)備上下文。
這個接口在需要恢復(fù)設(shè)備電源供應(yīng)時被調(diào)用。在I/O control接口中,如果I/O命令字為IOCTL_POWER_XXX,那么就應(yīng)該調(diào)用本接口。這個接口是對應(yīng)與應(yīng)用程序的,系統(tǒng)電源管理并不會用到這個接口。本接口應(yīng)盡可能避免引起阻塞的操作,將盡快返回,并設(shè)置全局變量來表明電源已經(jīng)被恢復(fù),可以進行后續(xù)操作。
?
Streams入口:IOControl
XXX_IOControl入口
允許應(yīng)用程序進行非文件的操作。
I/O控制字可以用來識別命令類別,普通的讀寫操作通常是不能完全滿足程序要求的,I/O控制字是和設(shè)備相關(guān)的。
下面是接口的原型:
BOOL XXX_IOControl( DWORD hOpenContext, DWORD dwCode, PBYTE
pBufIn, DWORD dwLenIn, PBYTE pBufOut, DWORD dwLenOut, PDWORD
pdwActualOut );
參數(shù)解釋:
hOpenContext
指向XXX_Open接口建立并且返回的設(shè)備上下文。
dwCode
指定驅(qū)動程序要操作的I/O操作的標(biāo)識碼。這是由設(shè)備特定的,一般在頭文件中有I/O操作的標(biāo)識碼的定義。
pBufIn
指向存放要向設(shè)備傳輸?shù)臄?shù)據(jù)的緩沖區(qū)。
dwLenIn
指定要從pBufIn指向的緩沖區(qū)向設(shè)備讀取寫入多少字節(jié)的數(shù)據(jù)。
pBufOut
指向緩沖區(qū),這個緩沖區(qū)將用來存放從設(shè)備中讀出的數(shù)據(jù),以字節(jié)為單位。
dwLenOut
指定要從設(shè)備讀取多少字節(jié)的數(shù)據(jù)存入pBuffer指向的緩沖區(qū)中。
pdwActualOut
DWORD型指針,指向的內(nèi)容反映了從設(shè)備中讀取的實際字節(jié)數(shù)。
這個接口主要是傳遞了包括讀寫在內(nèi)的I/O控制命令給設(shè)備。和Windows桌面平臺類似,應(yīng)用程序可以通過直接調(diào)用DeviceIOControl函數(shù)使設(shè)備管理器激活本接口。dwCode參數(shù)指定了命令的類型,這些命令類型是由驅(qū)動程序指定的,并且通過頭文件的形式提供給應(yīng)用程序,在音頻驅(qū)動中就有IOCTL_WAV_MESSAGE, IOCTL_DSDVR_MESSAGE, and IOCTL_MIX_MESSAGE.等。
如果注冊表中有HKEY_LOCAL_MACHINE\Drivers\BuiltIn\YourDevice\Ioctl鍵,設(shè)備管理器在加載驅(qū)動的時候就會調(diào)用本接口,并且使用注冊表中相應(yīng)項的值作為dwCode的值,把NULL填寫入pBufIn和pBufOut中。驅(qū)動程序可以在這個時候加載其他模塊以及其他不適合在XXX_Init出現(xiàn)的功能,在設(shè)備交互的過程中,以上各個接口基本遵循如下的調(diào)用順序:XXX_Init,XXX_Open,XXX_IOControl,XXX_Close。XXX_Open接口是要獲得設(shè)備句柄所必須的操作,XXX_Close是釋放資源所必須的操作。
?
上層調(diào)用I/O CONTROL函數(shù)后,I/O CONTROL便會使用消息或者結(jié)構(gòu)體發(fā)送到各個子Driver上
?
下表中的結(jié)構(gòu)體包含了器件的擴展信息
| Programming element | Description |
| MMDRV_MESSAGE_PARAMS | Passed to the WAV_IOControl function. |
| WAVEOPENDESC | Contains information needed by waveform input and output drivers. |
| WAVEOUTEXTCAPS | Contains extended device caps information. |
下表中列出了各個輸入driver消息
| Programming element | Description |
| WIDM_ADDBUFFER | This message is used to request a waveform input driver to add an empty input buffer to its input buffer queue. |
| WIDM_CLOSE | This message is used to request a waveform input driver to close a specified device instance previously opened with WIDM_OPEN. |
| ? | |
| WIDM_GETDEVCAPS | This message is used to request a waveform input driver to return the capabilities of a specified device. |
| WIDM_GETNUMDEVS | This message is used to request a waveform input driver to return the number of devices that it supports. |
| WIDM_GETPOS | This message is used to request a stream input driver to return the current input position within a waveform. The input position is relative to the first recorded sample of the waveform. |
| WIDM_OPEN | This message is used to request a waveform input driver to open a stream of a specified device. |
| WIDM_PREPARE | This message is used to request a waveform input driver to prepare a system-exclusive data buffer for input. |
| WIDM_RESET | This message is used to request a waveform input driver to stop recording and return all buffers in the input queue to the caller. |
| WIDM_START | This message is used to request a waveform input driver to begin recording. |
| WIDM_STOP | This message is used to request a waveform input driver to stop recording. |
| WIDM_UNPREPARE | This message is used to request a waveform input driver to undo the buffer preparation that was performed in response to a WIDM_PREPARE message. |
下表中列出了各個輸出driver消息
| Programming element | Description |
| WODM_BREAKLOOP | This message is used to request a waveform output driver to break an output loop that was created with a WODM_WRITE message. |
| WODM_CLOSE | This message is used to request a waveform output driver to close a specified stream that was previously opened with a WODM_OPEN message. |
| WODM_GETDEVCAPS | This message is used to request a waveform output driver to return the capabilities of a specified device. |
| WODM_GETNUMDEVS | This message is used to request a waveform output driver to return the number of device instances that it supports. |
| WODM_GETPITCH | This message is used to request a waveform output driver to return the specified device's current pitch multiplier value. |
| WODM_GETPLAYBACKRATE | This message is to request a waveform output driver to return the current playback rate multiplier value for the specified device. |
| WODM_GETPOS | This message is used to return the current position within a stream. The position is relative to the beginning of the waveform. |
| WODM_GETVOLUME | This message is used to request a waveform output driver to return the current volume level setting for the specified device or stream. |
| WODM_OPEN | This message is used to request a waveform output driver to open a stream on the specified device. |
| WODM_PAUSE | This message is used to request a waveform output driver to pause playback of a waveform. |
| WODM_PREPARE | This message is used to request a waveform output driver to prepare a system-exclusive data buffer for output. |
| WODM_RESET | This message is used to request a waveform output driver to stop sending output data and return all output buffers to the list. |
| WODM_RESTART |
| This message is used to request a waveform output driver to continue playback of a waveform after playback has been paused with WODM_PAUSE. | |
| WODM_SETPITCH | This message is used to request a waveform output driver to set the specified device's pitch multiplier value. |
| WODM_SETPLAYBACKRATE | This message is used to request a waveform output driver to set the playback rate multiplier value for the specified device. |
| WODM_SETVOLUME | This message is used to request a waveform output driver to set the playback rate multiplier value for the specified device. |
| WODM_UNPREPARE | This message is used to request a waveform output driver to remove the buffer preparation performed in response to WODM_PREPARE. |
| WODM_WRITE | This message is used to request a waveform output driver to write a waveform data block to the specified device. |
?
以我們的WM8753音頻Driver 為例,整個驅(qū)動里包含了以下重要功能的子驅(qū)動:
| Devctxt.cpp | 器件關(guān)聯(lián)——包含了音頻流的創(chuàng)造,刪除,打開,關(guān)閉,格式等功能 |
| Hwctxt.cpp | 硬件關(guān)聯(lián)——包含了基本的硬件功能在各個狀態(tài)的全局配置 |
| I2citf.cpp | I2C傳輸配置 |
| I2S.cpp | I2S傳輸配置 |
| Input.cpp | 負(fù)責(zé)輸入音頻流 |
| Output.cpp | 負(fù)責(zé)輸出音頻流 |
| Midinote.cpp | 負(fù)責(zé)輸出MIDI |
| Midistrm.cpp | 負(fù)責(zé)MIDI的開關(guān)以及控制 |
| Mixerdrv.cpp | 系統(tǒng)軟件混音 |
| RTcodecComm.cpp | Wm8753的所有功能配置,以及初始化設(shè)置 |
| Strmctxt.cpp | 負(fù)責(zé)所有音頻流的增益,buffer請求等功能以及對Devctxt的控制 |
| Wavemain.cpp | 包含了所有的流接口函數(shù) |
?
?
按照UAM的定義, wm8753的driver主要由mixer和wave兩部分組成;
由于mixer只實現(xiàn)一些基本的混音功能,其主要由Mixerdrv.cpp承擔(dān),還包括Midistrm.cpp,Input.cpp,Output.cpp的部分功能。而其他基本都是wave功能
Wavemain.cpp基于整個驅(qū)動的最上層,其中,流接口函數(shù)做到了以下控制:
?
Wav_init ——> hwctxt??? RTcodecComm
?????????????????????? I2citf.,
I2S
?
Wav_deinit?????????????? devctxt???????????????????
Wav_Powerup??????????? hwctxt
Wav_powerdown????????? mixerdrv
??????????????????????? strmctxt
?
Wav_IOControl——> Wav_open/close——> 所有??
若要移植一個音頻驅(qū)動到另一個器件,一般只需更改Hwctxt.cpp,I2citf.cpp,I2S.cpp,RTcodecComm.cpp 即可
?
四.以下是WM8753的WAV_IOControl調(diào)用說明:
? extern "C" BOOL WAV_IOControl(DWORD? dwOpenData,
?????????????????? DWORD? dwCode,
?????????????????? PBYTE? pBufIn,
?????????????????? DWORD? dwLenIn,
?????????????????? PBYTE? pBufOut,
DWORD? dwLenOut,
?????????????????? PDWORD pdwActualOut)
{
?
?
??? _try
??? {
?? switch (dwCode)
?????? {
??????? case IOCTL_MIX_MESSAGE:
??????? return HandleMixerMessage((PMMDRV_MESSAGE_PARAMS)pBufIn, (DWORD *)pBufOut);//調(diào)用混音消息
?
??????? case IOCTL_WAV_MESSAGE:
???????? return HandleWaveMessage((PMMDRV_MESSAGE_PARAMS)pBufIn, (DWORD *)pBufOut);//調(diào)用音頻消息
???????????
??????? //以下為電源管理功能
??????? case IOCTL_POWER_CAPABILITIES:
??????? case IOCTL_POWER_SET:
??????? case IOCTL_POWER_GET:
??????????? return g_pHWContext->IOControl
??????????? (dwOpenData, dwCode, pBufIn, dwLenIn, pBufOut, dwLenOut, pdwActualOut);
???????????????????????????????
?????
??????? }
?
??? }
?
BOOL HandleWaveMessage(PMMDRV_MESSAGE_PARAMS pParams, DWORD *pdwResult) //管理音頻消息
{
??? //? set the error code to be no error first
??? SetLastError(MMSYSERR_NOERROR);
?
??? UINT uMsg = pParams->uMsg;
??? UINT uDeviceId = pParams->uDeviceId;
??? DWORD dwParam1 = pParams->dwParam1;
??? DWORD dwParam2 = pParams->dwParam2;
??? DWORD dwUser?? = pParams->dwUser;
??? StreamContext *pStreamContext = (StreamContext *)dwUser;
?
??? DWORD dwRet;
?
??? g_pHWContext->Lock();
?
????? // catch exceptions inside device lock, otherwise device will remain locked!
??? _try
??? {
??? ??
??? switch (uMsg)
??? {
??? case WODM_GETNUMDEVS: //This message is used to request a waveform output driver to return the capabilities of a specified device.
??????? {
??????????? dwRet = g_pHWContext->GetNumOutputDevices();
??????????? break;
??????? }
?
??? case WIDM_GETNUMDEVS:// This message is used to request a waveform input driver to return the number of devices that it supports.
??????? {
??????????? dwRet = g_pHWContext->GetNumInputDevices();
??????????? break;
??????? }
?
??? case WODM_GETDEVCAPS:// This message is used to request a waveform output driver to return the capabilities of a specified device.
??????? {
??????????? DeviceContext *pDeviceContext;
??????????? UINT NumDevs = g_pHWContext->GetNumOutputDevices();
?
??????????? if (pStreamContext)
??????????? {
??????????????? pDeviceContext=pStreamContext->GetDeviceContext();
??????????? }
??????????? else
??????????? {
??????????????? pDeviceContext = g_pHWContext->GetOutputDeviceContext(uDeviceId);
??????????? }
?
??????????? dwRet = pDeviceContext->GetDevCaps((PVOID)dwParam1,dwParam2);
??????????? break;
??????? }
?
?
??? case WIDM_GETDEVCAPS:// This message is used to request a waveform input driver to return the capabilities of a specified device.
??????? {
???????????
DeviceContext *pDeviceContext;
???????? ???UINT NumDevs = g_pHWContext->GetNumInputDevices();
?
??????????? if (pStreamContext)
??????????? {
??????????????? pDeviceContext=pStreamContext->GetDeviceContext();
??????????? }
??????????? else
??????????? {
??????????????? pDeviceContext = g_pHWContext->GetInputDeviceContext(uDeviceId);
??????????? }
?
??????????? dwRet = pDeviceContext->GetDevCaps((PVOID)dwParam1,dwParam2);
??????????? break;
??????? }
?
??? case WODM_GETEXTDEVCAPS:
??????? {
??????????? DeviceContext *pDeviceContext;
??????????? UINT NumDevs = g_pHWContext->GetNumOutputDevices();
?
??????????? if (pStreamContext)
??????????? {
??????????????? pDeviceContext=pStreamContext->GetDeviceContext();
??????????? }
??????????? else
??????????? {
??????????????? pDeviceContext = g_pHWContext->GetOutputDeviceContext(uDeviceId);
??????????? }
?
??????????? dwRet = pDeviceContext->GetExtDevCaps((PVOID)dwParam1,dwParam2);
??????????? break;
??????? }
?
??? case WODM_OPEN:// This message is used to request a waveform output driver to open a stream on the specified device.
??????? {
??????????? // DEBUGMSG(1, (TEXT("WODM_OPEN\r\n"));
??????????? DeviceContext *pDeviceContext = g_pHWContext->GetOutputDeviceContext(uDeviceId);
??????????? dwRet = pDeviceContext->OpenStream((LPWAVEOPENDESC)dwParam1, dwParam2, (StreamContext **)dwUser);
??????????? break;
??????? }
?
??? case WIDM_OPEN:// This message is used to request a waveform input driver to open a stream of a specified device.
??????? {
??????????? // DEBUGMSG(1, (TEXT("WIDM_OPEN\r\n"));
??????????? DeviceContext *pDeviceContext = g_pHWContext->GetInputDeviceContext(uDeviceId);
??????????? dwRet = pDeviceContext->OpenStream((LPWAVEOPENDESC)dwParam1, dwParam2, (StreamContext **)dwUser);
??????????? break;
??????? }
?
??? case WODM_CLOSE:
??? case WIDM_CLOSE:
??????? {
??????????? // DEBUGMSG(1, (TEXT("WIDM_CLOSE/WODM_CLOSE\r\n"));
??????????? dwRet = pStreamContext->Close();
?
??????????? // Release stream context here, rather than inside StreamContext::Close, so that if someone
??????????? // (like CMidiStream) has subclassed Close there's no chance that the object will get released
??????????? // out from under them.
??????????? if (dwRet==MMSYSERR_NOERROR)
??????????? {
??????????????? pStreamContext->Release();
???????????}
??????????? break;
??????? }
?
?? ?case WODM_RESTART:
??? case WIDM_START:
??????? {
??????????? dwRet = pStreamContext->Run();
??????????? break;
??????? }
?
??? case WODM_PAUSE:
??? case WIDM_STOP:
??????? {
??????????? dwRet = pStreamContext->Stop();
??????????? break;
??????? }
?
??? case WODM_GETPOS:
??? case WIDM_GETPOS:
??????? {
??????????? dwRet = pStreamContext->GetPos((PMMTIME)dwParam1);
??????????? break;
??????? }
?
??? case WODM_RESET:
??? case WIDM_RESET:
??????? {
??????????? dwRet = pStreamContext->Reset();
??????????? break;
??????? }
?
??? case WODM_WRITE:
??? case WIDM_ADDBUFFER:
??????? {
??????????? // DEBUGMSG(1, (TEXT("WODM_WRITE/WIDM_ADDBUFFER, Buffer=0x%x\r\n"),dwParam1);
??????????? dwRet = pStreamContext->QueueBuffer((LPWAVEHDR)dwParam1);
??????????? break;
??????? }
?
??? case WODM_GETVOLUME:// This message is used to request a waveform output driver to return the current volume level setting for the specified device or stream.
??????? {
??????????? PULONG pdwGain = (PULONG)dwParam1;
?
??????????? if (pStreamContext)
? ??????????{
??????????????? *pdwGain = pStreamContext->GetGain();
??????????? }
??????????? else
??????????? {
#ifdef USE_HW_GAIN_WODM_SETGETVOLUME
??????????????? // Handle device gain in hardware
??????????????? *pdwGain = g_pHWContext->GetOutputGain();
#else
??????????????? // Handle device gain in software
??????????????? DeviceContext *pDeviceContext = g_pHWContext->GetOutputDeviceContext(uDeviceId);
??????????????? *pdwGain = pDeviceContext->GetGain();
#endif
??????????? }
??????????? dwRet = MMSYSERR_NOERROR;
??????????? break;
??????? }
?
??? case WODM_SETVOLUME:// This message is used to request a waveform output driver to set the playback rate multiplier value for the specified device.
??????? {
??????????? LONG dwGain = dwParam1;
??????????? if (pStreamContext)
??????????? {
??????????????? dwRet = pStreamContext->SetGain(dwGain);
??????????? }
??????????? else
??????????? {
#ifdef USE_HW_GAIN_WODM_SETGETVOLUME
??????????????? // Handle device gain in hardware
??????????????? dwRet = g_pHWContext->SetOutputGain(dwGain);
#else
???????????????// Handle device gain in software
??????????????? DeviceContext *pDeviceContext = g_pHWContext->GetOutputDeviceContext(uDeviceId);
??????????????? dwRet = pDeviceContext->SetGain(dwGain);
#endif
??????????? }
?? ?????????break;
??????? }
?
??? case WODM_BREAKLOOP:// This message is used to request a waveform output driver to break an output loop that was created with a WODM_WRITE message.
??????? {
??????????? dwRet = pStreamContext->BreakLoop();
??????????? break;
??????? }
?
??? case WODM_SETPLAYBACKRATE:// This message is used to request a waveform output driver to set the playback rate multiplier value for the specified device. ??????? {
??????????? WaveStreamContext *pWaveStream = (WaveStreamContext *)dwUser;
?? ?????????dwRet = pWaveStream->SetRate(dwParam1);
??????????? break;
??????? }
?
??? case WODM_GETPLAYBACKRATE:// This message is to request a waveform output driver to return the current playback rate multiplier value for the specified device.
??????? {
?? ?????????WaveStreamContext *pWaveStream = (WaveStreamContext *)dwUser;
??????????? dwRet = pWaveStream->GetRate((DWORD *)dwParam1);
??????????? break;
??????? }
?
??? case MM_WOM_SETSECONDARYGAINCLASS:
??????? {
??????????? dwRet = pStreamContext->SetSecondaryGainClass(dwParam1);
??????????? break;
??????? }
?
??? case MM_WOM_SETSECONDARYGAINLIMIT:
??????? {
??????????? DeviceContext *pDeviceContext;
??????????? if (pStreamContext)
??????????? {
??????????????? pDeviceContext = pStreamContext->GetDeviceContext();
??????????? }
??????????? else
??????????? {
??????????????? pDeviceContext = g_pHWContext->GetOutputDeviceContext(uDeviceId);
??????????? }
??????????? dwRet = pDeviceContext->SetSecondaryGainLimit(dwParam1,dwParam2);
??????????? break;
??????? }
?
? ??case MM_MOM_MIDIMESSAGE:
??????? {
??????????? CMidiStream *pMidiStream = (CMidiStream *)dwUser;
??????????? dwRet = pMidiStream->MidiMessage(dwParam1);
??????????? break;
??????? }
?
??????? ?? // For Debug Register
??? case WPDM_PRIVATE_WRITE_CODEC:
??? case WPDM_PRIVATE_READ_CODEC:
??? ?? ? //{
?????????? //??? dwRet=g_pHWContext->Private_AudioMessage(pParams->uMsg, pParams->dwParam1, pParams->dwParam2);
?????????? //???? break;
?? ??????????? //}
?
// unsupported messages
??? case WODM_GETPITCH:
??? case WODM_SETPITCH:
??? case WODM_PREPARE:
??? case WODM_UNPREPARE:
??? case WIDM_PREPARE:
??? case WIDM_UNPREPARE:
??? default:
?????? dwRet? = MMSYSERR_NOTSUPPORTED;
??????? break;
??? }
???
??? }
??? _except (GetExceptionCode() == STATUS_ACCESS_VIOLATION ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
??? {
??????? ERRORMSG(1, (TEXT("Access violation in HandleWaveMessage!!!!\r\n")));
??????? SetLastError(E_FAIL);
??? }???
???
??? g_pHWContext->Unlock();
?
??? // Pass the return code back via pBufOut
??? if (pdwResult)
??? {
??????? *pdwResult = dwRet;
??? }
?
??? return(TRUE);
}??
?????????????????
?
總結(jié)
以上是生活随笔為你收集整理的WINCE Driver 心得总结的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 2017最新广发信用卡密码修改方法
- 下一篇: CTL_CODE说明