一. Windows CE的驱动程序的区分

1.从加载以及接口方式来区分

可以分为本机设备驱动(Built-In Driver)、可加载驱动(Loadable Driver)以及混合型驱动。

(1)本机设备驱动

本机设备驱动即Native Device Drivers。这些驱动程序在系统启动时,在GWES的进程空间内被加载,因此它们不是以独立的DLL形式存在。这些驱动对应的设备通常在系统启动时就被要求加载,如果没有串口,也没有LCD的话,整个系统就不能和用户信息交流。另外,流驱动程序也能作为本机设备驱动而存在。

(2)可加载驱动

也被称为流驱动。

这些驱动可以在系统启动时或者和启动后的任何时候由设备管理器动态加载。通常它们以DLL动态链接库的形式存在,系统加载它们后,这些驱动程序也只是以用户态的角色运行。可加载驱动程序通过文件操作API来从设备管理器和应用程序获得命令。

在Windows CE中典型的可加载驱动有以下各类:

n     PCMCIA driver(PCMCIA.dll)

n     Serial driver(SERIAL.dll)

n     ATAFLASH driver(ATA.dll)

n     Ethernet driver(NE2000.dll,SMSC100FD.dll)

(3)混合型驱动

这类驱动综合了前两种驱动的特性。它同时使用了stream接口和custom-purpose接口。

混合型驱动主要是提供custom-purpose 接口,但是由于需要和系统中只允许使用stream接口的那些模块进行交互,因此也必须提供stream接口。例如,PC card socket驱动同时拥有两套接口。

2.从驱动层次上分

可以分为独立驱动和层次型驱动,下图是这两种驱动在系统中的位置。

(1)独立驱动程序

可以将驱动程序编写成同时包含MDD和PDD层的独立驱动。独立驱动的代码应当包括中断服务例程和平台相关处理函数。使用独立驱动的好处在于可以省去MDD和PDD层驱动之间的信息传递,这一点在实时处理中非常重要。另外,如果设备的操作和MDD驱动层的接口描述相吻合,可以使用独立驱动程序提高处理性能。

(2)层次型驱动

层次型驱动分为两层,较上层的Model Device Driver(MDD)和比较下层的Platform Dependent Driver(PDD)。MDD实现的是和平台无关的功能,它描述了一个通用的驱动程序框架。而PDD是和硬件以及平台相关的代码组成。MDD调用PDD中特定的接口来获取硬件相关的信息。当使用层次型驱动的时候,一般只需要基于相近的样列驱动程序,针对特定的硬件修改PDD程序,MDD建立的框架可继续使用。由于层次间接口的层层调用以及消息的传递,使得处理速度相对独立驱动程序要慢,因此在时间要求苛刻的环境下,层次型驱动显得不是很适合。

一般MDD将完成以下任务。

n    连接PDD层,并且定义它要使用到的Device Driver Service Provider Interface(DDSI)函数集;

n     向设备管理器提供Device Driver Interface(DDI)接口集;

n     处理复杂的事件,如中断等等。

每一种MDD驱动都处理不同种类的设备。DDI是由MDD层驱动以及独立型驱动提供给设备管理器的一组接口集。DDSI是由PDD向MDD层提供的接口集。公司的设备可以用同样的DDI。

在开发过程中,MDD层驱动是不需要被修改的。微软公司不保证被修改的MDD能在系统中正确运行的。和MDD层驱动不同的是,PDD层驱动必须被修改成和特定硬件相匹配的代码。程序员可以自己开发一个PDD程序,多数情况下建议开发者在Platform Builder提供的样例驱动程序上进行修改。例如,Platform Builder提供了Wavedev驱动程序,它的代码位于%WINCEROOT%\public\common\oak\drivers\WAVEDEV下,这是一个容易理解的流接口层次型驱动程序。此样例audio驱动程序仅提供了播放及录音功能,只提供播放功能的结构框架,播放功能和音频设备的交互还需要 PDD层来解决。

二.           Audio Driver 架构:

在WINCE中 Audio Driver 架构支持两种驱动模式

即独立型的unified audio model (UAM)驱动和分层式的MDD and PDD mode驱动,(不论是UAM或者MDD/PDD都是流接口驱动)。

其架构还支持的audio compression manager (ACM)驱动,例如codecs, converters, and filters等器件

1)         UAM

UAM支持标准波形驱动接口(standard wave driver interfaces),过去的波形驱动和采样驱动是由MDD和PDD模型组成。MDD模型执行了驱动的独立硬件部分以及输出到驱动接口的中间设备。PDD模型提供了驱动依赖硬件的执行部分

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或者写入他们自己的完整驱动来输出到合适的接口到中间设备

UAM实现了对WAV和Microsoft DirectSound®音频API的高效支持。它还使得编写一个能有效支持WAV和DirectSound的驱动程序成为可能。

在我们的WM8753 音频Driver中即使用了UAM这种驱动模式,它也是一种流接口驱动,故只需编写驱动中WAVE和MIXER这两部分,然后使用流接口函数调用即可。

2).音频MDD和PDD

编写音频驱动我们可以选择UAM架构,或者直接执行流接口(stream interface),我们使用由微软提供的MDD库-Wavemdd.lib。这个库通过DDSI来执行流接口功能。如果使用了Wavemdd.lib,则必须一个PDD库来执行音频DDSI的功能。这个库被称为Wavepdd,lib, 这两个库编译连接后就形成了我们的音频驱动,通常被为Wavedev,dll。

在系统程序文件中可能缺少器件的功能导致音频器件的很多功能无法被使用,为了解决这个问题DeviceIOControl

PDD和MDD都依靠调用DDSI函数来实现相互通信,所以若采用分层式来编写驱动,只需找到微软提供的MDD,然后根据其DDSI来编写PDD层即可。

三.          流接口驱动

不论是UAM或者PDD/MDD的架构,它们都是流接口驱动

流接口驱动有一套标准的接口,这和本机驱动是不一样的。

n     对于I/O设备来说是非常适合的。

n     操作接口和文件系统API十分类似,比如ReadFile,IOControl等。

n     应用程序可以和流接口驱动进行交互,并且可以把流驱动当成文件来操作。

流驱动与驱动接口、提供设备的种类无关,因为这组接口有统一的接口规范。对于需要数据流的设备来说,这种驱动是十分适合的,如串口就是个典型的例子。可以把使用流驱动的设备近似地看作是文件,这样可以通过文件系统API来操作设备,如ReadFile,IOControl。由于采用了文件系统的API,使得驱动程序能通过文件系统进行访问,这点和独立驱动程序是不同的。

下图是流驱动程序在整个系统中的结构示意图。

上图显示了作为本机驱动而存在的流驱动程序,在启动时被设备管理器所加载。一般本机驱动是指custom接口的驱动程序,但是流驱动也可以成为本机驱动,例如串口。

流驱动通过文件系统API来和应用程序交互,同时又通过流接口接受设备管理器的管理。无论流驱动管理的是本机设备还是动态加载的设备,它们自身是在启动时被加载还是启动后由设备管理器动态加载,这和系统中其他模块的交互模型是一样的。

实现流驱动程序大致需要完成以下步骤。

(1)选择代表设备的文件名前缀;

(2)实现驱动的各个入口点;

(3)建立.DEF文件;

(4)在注册表中为驱动程序建立表项。

以下是创建流驱动的具体步骤。

(1)首先确定设备名的前缀。前缀非常重要,设备管理器在注册表中通过前缀来识别设备。同时,在流接口命名时,也将这个前缀作为入口点函数的前缀,如果设备前缀为XXX,那么流接口对应为XXX_Close,XXX_Init等。

(2)实现流接口的各个入口点。所谓入口点是指提供给设备管理器的标准文件I/O接口。

下表是对这些接口的介绍:

接  口  名

功能描述

XXX_Close

关闭hOpenContext参数指定的设备上下文

XXX_Deinit

通知设备管理器回收设备初始化时分配的资源

XXX_Init

通知设备管理器为设备初始化时分配资源

XXX_Open

打开设备,这个接口可以由应用程序直接调用createfile,然后通过文件系统映射为XXX_Open

XXX_IOControl

I/O控制指令

XXX_PowerUp

设备加电时,此接口会被自动调用,可以在这里分配资源等

XXX_PowerDown

如果设备能由软件控制断电,则在设备断电前,设备管理器会调用这个接口做些安全性检查

XX_Read

从打开的设备文件中读取数据

XXX_Seek

文件定位,如果设备支持的话

XXX_Write

写数据到设备文件

对于流驱动,×××_Open/×××_Close/×××_IoControl等就是ddi;如果不是流驱动,它的ddi不具有上述形式;

以我们的driver为例,在程序中,我们可以在C:\WINCE500\PLATFORM\C340\Src\Drivers\audio\IIS\wm8753\wavemain.cpp中找到以下对应点

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

在注册表中还要建立驱动程序的入口点,这样设备管理器才能识别和管理这个驱动

[HKEY_LOCAL_MACHINE\Drivers\BuiltIn\Audio]

"Prefix"="WAV"

"Dll"="s3c2440a_iis_wm8753.dll"

"Index"=dword:1

"Order"=dword:0

此外,注册表还能储存额外的信息,这些信息可以在驱动运行之后被使用到,DLL项是设备管理器在加载驱动时需要的DLL名称;Prefix代表了设备前缀;Order是驱动程序被加载的顺序

以下详细说明下流接口函数的各入口

Streams入口:Open和Close

(1)XXX_Open入口

用于读/写打开一个设备文件。当应用程序调用CreateFile的时候,文件系统会自动调用本接口,打开一个已经存在的设备文件。当这个接口被调用的时候,设备驱动可以向设备管理器申请分配资源,并且为读/写文件做好准备。

下面是接口的原形:

DWORD XXX_Open( DWORD hDeviceContext, DWORD AccessCode, DWORD ShareMode );

参数解释:

hDeviceContext

指向XXX_Init返回的设备句柄(上下文)。

AccessCode

打开设备的权限描述符,这些权限包括读设备、写设备、读 /写等。

所谓的设备上下文指的是代表图形设备接口graphics device interface(GDI)的数据结构。它包含了在特定区域内设备显示图象的信息。设备上下文包含了图形对象(例如pen、brush、字体等),不断地修改和调用它们,从而达到显示不同图象的效果。

(2)XXX_Close入口

在关闭设备的时候被操作系统调用。对应于CloseHandle接口。

BOOL XXX_Close( DWORD hOpenContext );

参数解释:

hOpenContext

指向XXX_Open返回的已经打开的设备句柄(上下文)。

应用程序可以调用CloseHandle来关闭正在使用的流驱动程序,然后设备管理器会相应地调用本接口来关闭设备,当设备关闭后hOpenContext描述的设备句柄将不再有效。

Streams入口:Init and Deinit

(1)XXX_Init入口

XXX_Init要完成以下任务。

n     在驱动被系统加载时,本接口被调用;

n     初始化需要的资源在本接口处理中被分配;

n     创建内存映射。

下面是接口的原型:

DWORD XXX_Init( DWORD dwContext );

参数解释:

n     dwContext

指向一个字符串,它描述了注册表中的一个流设备接口。

当调用设备ActivateDeviceEx函数后,设备管理器自动调用这个函数。当用户激活一个新的设备时,如插入USB设备后,当总线自检时,设备就会被激活,这个接口就会被调用,这个接口是不允许应用程序直接调用的。

当这个接口的处理结果返回时,设备管理器就在注册表中寻找驱动的Ioctl子键。如果这个子键存在,设备管理器将调用XXX_IOControl接口将dwCode参数传入驱动入口点。

(2)XXX_Deinit入口

在驱动被系统卸载的时候,本接口将被调用,它将释放所有占用的阻援,并且停止IST。

下面是接口的原型:

BOOL XXX_Deinit( DWORD hDeviceContext );

参数解释:

hDeviceContext

指向设备上下文的句柄。这个句柄应该是由XXX_Init返回的。

当程序调用DeactivateDevice时,设备管理器将自动调用本接口。流接口将释放全部它申请的资源,并且停止设备的运行。

Streams入口:Read,Write,Seek

(1)XXX_Read入口

当应用程序直接调用ReadFile函数时,设备管理器将调用这个接口。

下面是接口的原型:

DWORD XXX_Read( DWORD hOpenContext, LPVOID pBuffer, DWORD Count );

参数解释:

hOpenContext

指向XXX_Open接口返回的设备上下文。

pBuffer

指向缓冲区,这个缓冲区将用来存放从设备中读出的数据,以字节为单位。

Count

指定要从设备读取多少字节的数据存入pBuffer指向的缓冲区中。

这个从指定的设备中读取指定数量的字节数据,它对应的应用层API为ReadFile。ReadFile函数的参数hFile是指向设备的句柄,ReadFile函数的参数hFile将被填写到count参数中,ReadFile中的pSizeRead将存放实际读取数据的字节数。本接口的返回值是pSizeRead中填充的数值,若返回-1,代表发生了错误。

(2)XXX_Write入口

当应用程序调用WriteFile的时候,设备管理器将调用本接口。

下面是接口的原型:

DWORD XXX_Write( DWORD hOpenContext, LPCVOID pBuffer, DWORD Count );

参数解释:

hOpenContext

指向XXX_Open接口返回的设备上下文。

pBuffer

指向缓冲区,这个缓冲区将用来存放要向设备中写入的数据,以字节为单位。

Count

指定要从pBuffer指向的缓冲区向设备读取写入多少字节的数据。

(3)XXX_Seek入口

在定位I/O指针的时候被调用。

下面是接口的原型:

DWORD XXX_Seek( DWORD hOpenContext, long Amount, WORD Type );

参数解释:

hOpenContext

指向XXX_Open接口返回的设备上下文。

Amount

指定指针要移动多少距离,以字节为单位。正值代表向文件尾端移动,负值则相反。

Type

描述了起始点的位置。当应用程序调用了SetFilePointer函数后,设备管理器就会调用本接口。

如果设备是可以重复打开的,本接口用到的指针只是针对hOpenContext的。

Streams入口:PowerUp和PowerDown

(1)XXX_PowerDown入口

这个接口是在停止对设备供应电源时被调用。下面是接口的原型:

void XXX_PowerDown( DWORD hDeviceContext );

参数解释:

hDeviceContext

指向由XXX_Init返回的设备上下文。

这个函数应当执行停止设备供电的操作,此设备必须支持软关电的功能。在I/O control接口中,如果I/O命令字为IOCTL_POWER_XXX,那么就应该调用本接口。这个接口是对应与应用程序的,系统电源管理并不会用到这个接口。

设备管理器在将设备设置成节电模式之前将调用本接口,本接口将尽可能避免引起阻塞的操作,并尽快返回。

(2)XXX_PowerUp入口

恢复了设备的供电。

下面是接口的原型:

void XXX_PowerUp( DWORD hDeviceContext );

参数解释:

hDeviceContext

指向XXX_Open接口建立并且返回的设备上下文。

这个接口在需要恢复设备电源供应时被调用。在I/O control接口中,如果I/O命令字为IOCTL_POWER_XXX,那么就应该调用本接口。这个接口是对应与应用程序的,系统电源管理并不会用到这个接口。本接口应尽可能避免引起阻塞的操作,将尽快返回,并设置全局变量来表明电源已经被恢复,可以进行后续操作。

Streams入口:IOControl

XXX_IOControl入口

允许应用程序进行非文件的操作。

I/O控制字可以用来识别命令类别,普通的读写操作通常是不能完全满足程序要求的,I/O控制字是和设备相关的。

下面是接口的原型:

BOOL XXX_IOControl( DWORD hOpenContext, DWORD dwCode, PBYTE

pBufIn, DWORD dwLenIn, PBYTE pBufOut, DWORD dwLenOut, PDWORD

pdwActualOut );

参数解释:

hOpenContext

指向XXX_Open接口建立并且返回的设备上下文。

dwCode

指定驱动程序要操作的I/O操作的标识码。这是由设备特定的,一般在头文件中有I/O操作的标识码的定义。

pBufIn

指向存放要向设备传输的数据的缓冲区。

dwLenIn

指定要从pBufIn指向的缓冲区向设备读取写入多少字节的数据。

pBufOut

指向缓冲区,这个缓冲区将用来存放从设备中读出的数据,以字节为单位。

dwLenOut

指定要从设备读取多少字节的数据存入pBuffer指向的缓冲区中。

pdwActualOut

DWORD型指针,指向的内容反映了从设备中读取的实际字节数。

这个接口主要是传递了包括读写在内的I/O控制命令给设备。和Windows桌面平台类似,应用程序可以通过直接调用DeviceIOControl函数使设备管理器激活本接口。dwCode参数指定了命令的类型,这些命令类型是由驱动程序指定的,并且通过头文件的形式提供给应用程序,在音频驱动中就有IOCTL_WAV_MESSAGE, IOCTL_DSDVR_MESSAGE, and IOCTL_MIX_MESSAGE.等。

如果注册表中有HKEY_LOCAL_MACHINE\Drivers\BuiltIn\YourDevice\Ioctl键,设备管理器在加载驱动的时候就会调用本接口,并且使用注册表中相应项的值作为dwCode的值,把NULL填写入pBufIn和pBufOut中。驱动程序可以在这个时候加载其他模块以及其他不适合在XXX_Init出现的功能,在设备交互的过程中,以上各个接口基本遵循如下的调用顺序:XXX_Init,XXX_Open,XXX_IOControl,XXX_Close。XXX_Open接口是要获得设备句柄所必须的操作,XXX_Close是释放资源所必须的操作。

上层调用I/O CONTROL函数后,I/O CONTROL便会使用消息或者结构体发送到各个子Driver上

下表中的结构体包含了器件的扩展信息

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 为例,整个驱动里包含了以下重要功能的子驱动:

Devctxt.cpp

器件关联——包含了音频流的创造,删除,打开,关闭,格式等功能

Hwctxt.cpp

硬件关联——包含了基本的硬件功能在各个状态的全局配置

I2citf.cpp

I2C传输配置

I2S.cpp

I2S传输配置

Input.cpp

负责输入音频流

Output.cpp

负责输出音频流

Midinote.cpp

负责输出MIDI

Midistrm.cpp

负责MIDI的开关以及控制

Mixerdrv.cpp

系统软件混音

RTcodecComm.cpp

Wm8753的所有功能配置,以及初始化设置

Strmctxt.cpp

负责所有音频流的增益,buffer请求等功能以及对Devctxt的控制

Wavemain.cpp

包含了所有的流接口函数

按照UAM的定义, wm8753的driver主要由mixer和wave两部分组成;

由于mixer只实现一些基本的混音功能,其主要由Mixerdrv.cpp承担,还包括Midistrm.cpp,Input.cpp,Output.cpp的部分功能。而其他基本都是wave功能

Wavemain.cpp基于整个驱动的最上层,其中,流接口函数做到了以下控制:

Wav_init ——> hwctxt    RTcodecComm

I2citf.,

I2S

Wav_deinit               devctxt

Wav_Powerup            hwctxt

Wav_powerdown          mixerdrv

strmctxt

Wav_IOControl——> Wav_open/close——> 所有

若要移植一个音频驱动到另一个器件,一般只需更改Hwctxt.cpp,I2citf.cpp,I2S.cpp,RTcodecComm.cpp 即可

四.以下是WM8753的WAV_IOControl调用说明:

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);//调用混音消息

case IOCTL_WAV_MESSAGE:

return HandleWaveMessage((PMMDRV_MESSAGE_PARAMS)pBufIn, (DWORD *)pBufOut);//调用音频消息

//以下为电源管理功能

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);

}

WINCE Driver 心得总结相关推荐

  1. winCE开发心得之一:不让使用MethodInvoker

    在C#开发开发中经常会用到MethodInvoker,但在winCE中没有MethodInvoker方法. 解决方法如下 private delegate void MethodInvoker();

  2. 【嵌入式】嵌入式硬软件开发介绍(个人看法)

    系列文章目录 提示:这里可以添加系列文章的所有文章的目录,目录需要自己手动添加 TODO:写完再整理 文章目录 系列文章目录 前言 一.嵌入式的定义 二.嵌入式硬件开发 1.要求掌握的入门的能力 (1 ...

  3. WinCE EBOOT中的Boot Args与Driver Globals (转)

    在EBOOT中包含的一个重要的缓冲区叫Driver Globals,它用于在设备驱动和WinCE OS之间共享数据.而在EBOOT中会用到的启动参数结构被称为Boot Args,是指用于EBOOT和W ...

  4. Wince Battery driver

    最近调了wince battery driver, 对电池驱动有了一点点了解,如下: 1.Windows CE电池驱动属于分层驱动,由MDD层和PDD层组成.微软给我们的代码在D:/WINCE600/ ...

  5. 关于IT8951 Tinydrm Driver开源项目学习的心得

    关于IT8951 Tinydrm Driver开源项目学习的心得 项目名称: julbouln/tinydrm_it8951 项目链接:https://github.com/julbouln/tiny ...

  6. PHP7 - MongoDB Driver 使用心得

    php7 只能使用Mongodb driver来驱动mongodb. 使用Mongodb Driver连接数据库 刚开始使用Mongodb Driver的时候我是拒绝的.查看官方文档只看到一排的类和不 ...

  7. 在WinXP上通过Virtual PC安装WinCE

    开发WinCE程序的调试,要么用Emulator,要么用触摸屏等等硬件,模拟器不真实,硬件又难找还不易随身带.       象我这样穷得买不了带CE的PDA,懒得不想下巨型的PB.VS,要随时调试还真 ...

  8. Java EE学习心得

    –Java EE学习心得   1.    称为编程专家的秘诀是: 思考-----编程--------思考------编程--.. 编程不能一步到位,不能一上来就编,必须先思考如何写,怎样写?然后再编程 ...

  9. 基于WinCE的I2C驱动程序设计

    http://www.mcu123.com/news/Article/rtos/WinCE/200607/88.html 引言 随着以计算机技术.通信技术和软件技术为核心的信息技术的迅速发展,嵌入式系 ...

最新文章

  1. 使用行为树(Behavior Tree)实现网游奖励掉落系统
  2. Spring-AOP 动态切面
  3. 文件上传与下载问题记录
  4. linux内核设备树及编译--完整清晰
  5. 凹入表形式打印树形结构_体育场径向环形大悬挑钢结构综合施工技术研究
  6. 人工智能(9)---人工智能的发展趋势是什么?人工智能的行业应用
  7. python画雷达图-Python 详解雷达图/蛛网图
  8. css中如何设置字体
  9. Excel导入txt数据乱码
  10. FPGA学习小例子:38译码器设计与仿真
  11. java公司薪酬管理系统计算机毕业设计MyBatis+系统+LW文档+源码+调试部署
  12. GPIO模拟MDC/MDIO协议
  13. MATLAB的矩阵输入
  14. seo之html优化,SEO优化技巧之HTML优化
  15. 【050】SylixOS全面支持C-SKY系列处理器
  16. Parellel TSP
  17. 使用jquery,按回车键实现tab键的功能
  18. Photoshop调出清晰的阴雨天气山水风景照
  19. linux设置网络 命令,Linux网络配置相关命令
  20. 3DMax入门级—制作简易沙发

热门文章

  1. vbs运算符号和函数
  2. mysql myisam/innodb高并发优化经验_MySQL MyISAM / PHP 高并发优化经验
  3. 搭建spring MVC项目
  4. 移植uboot之修改代码支持NorFlash记录续集二
  5. [转帖]什么是α射线、β射线、γ射线
  6. java自学 day8
  7. 获取select被选中的option的值
  8. JavaScript 函数(作用域以及闭包)
  9. 十二:内存简单介绍和OC的内存管理
  10. 解密多媒体封装解封装框架