WINCE Driver 心得总结

一. 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)独立驱动程序

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

2)层次型驱动

层次型驱动分为两层,较上层的Model Device DriverMDD)和比较下层的Platform Dependent DriverPDD)。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是由PDDMDD层提供的接口集。公司的设备可以用同样的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),过去的波形驱动和采样驱动是由MDDPDD模型组成。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实现了对WAVMicrosoft DirectSound®音频API的高效支持。它还使得编写一个能有效支持WAVDirectSound的驱动程序成为可能。

 

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

 

2).音频MDDPDD

 

编写音频驱动我们可以选择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来操作设备,如ReadFileIOControl。由于采用了文件系统的API,使得驱动程序能通过文件系统进行访问,这点和独立驱动程序是不同的。

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

 

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

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

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

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

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

3)建立.DEF文件;

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

 

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

1)首先确定设备名的前缀。前缀非常重要,设备管理器在注册表中通过前缀来识别设备。同时,在流接口命名时,也将这个前缀作为入口点函数的前缀,如果设备前缀为XXX,那么流接口对应为XXX_CloseXXX_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入口:OpenClose

1XXX_Open入口

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

下面是接口的原形:

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

参数解释:

hDeviceContext

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

AccessCode

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

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

2XXX_Close入口

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

BOOL XXX_Close( DWORD hOpenContext );

参数解释:

hOpenContext

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

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

Streams入口:Init and Deinit

1XXX_Init入口

XXX_Init要完成以下任务。

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

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

n     创建内存映射。

下面是接口的原型:

DWORD XXX_Init( DWORD dwContext );

参数解释:

n     dwContext

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

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

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

2XXX_Deinit入口

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

下面是接口的原型:

BOOL XXX_Deinit( DWORD hDeviceContext );

参数解释:

hDeviceContext

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

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

Streams入口:ReadWriteSeek

1XXX_Read入口

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

下面是接口的原型:

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

参数解释:

hOpenContext

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

pBuffer

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

Count

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

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

2XXX_Write入口

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

下面是接口的原型:

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

参数解释:

hOpenContext

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

pBuffer

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

Count

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

3XXX_Seek入口

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

下面是接口的原型:

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

参数解释:

hOpenContext

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

Amount

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

Type

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

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

Streams入口:PowerUpPowerDown

1XXX_PowerDown入口

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

void XXX_PowerDown( DWORD hDeviceContext );

参数解释:

hDeviceContext

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

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

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

2XXX_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填写入pBufInpBufOut中。驱动程序可以在这个时候加载其他模块以及其他不适合在XXX_Init出现的功能,在设备交互的过程中,以上各个接口基本遵循如下的调用顺序:XXX_InitXXX_Open,XXX_IOControlXXX_CloseXXX_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的定义, wm8753driver主要由mixerwave两部分组成;

由于mixer只实现一些基本的混音功能,其主要由Mixerdrv.cpp承担,还包括Midistrm.cppInput.cppOutput.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.cppI2citf.cppI2S.cppRTcodecComm.cpp 即可

 

四.以下是WM8753WAV_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);

}  

                 





 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/435573.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【转】进阶 JavaScript 必知的 33 个点【进阶必备】

转自:进阶 JavaScript 必知的 33 个点【进阶必备】 进阶 JavaScript 必知的 33 个点【进阶必备】 Original 前端小菜鸡之菜鸡互啄 前端开发爱好者 2022-04-11 08:32 收录于话题#javaScript进阶1个 点击下方“前端开发爱好者”,选择“设为星标” 第一…

Struts配置

Struts应用采用两个基于XML的配置文件来配置,分别是web.xml和struts-cofig.xml文件。web.xml文件是配置所有web应用的而struts-config.xml文件是struts专用的配置文件,事实上也是可以根据需要给这个配置文件起其他名称的. Web应用的发布描述文件:web应用发布描述文件…

【转】开发者需要了解的领域特定语言(DSL)

转自:开发者需要了解的领域特定语言(DSL) - 知乎 领域特定语言是在特定领域下用于特定上下文的语言。作为开发者,很有必要了解领域特定语言的含义,以及为什么要使用特定领域语言。 领域特定语言domain-specific lang…

POJ 2240题(Floyd)

//使用Floyd的变形实现//这就是个套汇的问题&#xff0c;可以用Floyd求最大环&#xff0c;然后判断是不是大于1。#include <cstdio>#include <string>#include <map>using namespace std;map<string,int> MAP;double value[31][31];double rate;double…

【转】自动化构建、自动化部署发布一览

转自&#xff1a;自动化构建、自动化部署发布一览 - 知乎 在软件系统开发的过程中&#xff0c;一个项目工程通常会包含很多的代码文件、配置文件、第三方文件、图片、样式文件等等&#xff0c;是如何将这些文件有效的组装起来最终形成一个可以流畅使用的应用程序的呢&#xff…

承博士:让云计算落地生根的中国云计算平台

2010-01-22 09:48:41 [0评论 ] 金蝶中间件秉承自主创新&#xff0c;努力打造自主知识产权的云计算平台;金蝶Apusic云计算解决方案是为大型企业和组织提供私有云的解决方案&#xff0c;通过云计算中间件平台&#xff0c;为云计算提供落地的务实价值。金蝶中间件与国际厂商合作&…

【转】g++以及gcc的区别

转自&#xff1a;g以及gcc的区别 - 知乎 GCC &#xff0c;gcc 和g&#xff1a; 一直没搞清这几个东西的概念&#xff0c;搜了半天看到了一个不错的解释&#xff0c;所以大致记录一下&#xff0c;以免以后再忘记&#xff0c;链接。&#xff08;原谅没找到原文出处&#xff09;…

Microsoft Windows CE .NET 中的中断体系结构

概述 通过 Microsoft Windows CE .NET&#xff0c;Microsoft 已经升级了 Windows CE 的中断体系结构。该操作系统 (OS) 所具有的处理共享中断的能力极大地扩展了 Windows CE .NET 支持许多中断体系结构的能力。本文从原始设备制造商 (OEM) 和应用程序开发人员的角度探讨了处理…

微软企业库5.0学习笔记(三十三)数据访问模块

前言 鉴于企业库5.0已经发布正式版&#xff0c;同时有广大读者的要求&#xff08;臭屁一下&#xff0c;o(∩_∩)o...&#xff09;&#xff0c;后面文章的内容和代码将基于Enterprise Library5.0和Unity2.0来写&#xff0c;感谢大家的一贯支持。 正文 数据库访问模块都能实现哪些…

ARM中断分析之一:中断控制器和CPU、外设的关系

“中断控制器”也是CPU众多外设中的一个&#xff0c;不同的是&#xff0c;它一方面接收其它外设中断引脚的输入&#xff0c;另一方面&#xff0c;它会发出中断信号给CPU。下图是一张中断控制器外设的框图&#xff0c;s3c2410的框图。 为了把中断控制器、CPU、外设联系起来&…

【转】Jenkins项目常用三种构建类型风格详解

转自&#xff1a;Jenkins项目常用三种构建类型风格详解_ぃ小小宇宙的博客-CSDN博客_jenkins项目类型 Jenkins构建的项目类型介绍 jenkins 的安装配置请参考&#xff1a;《jenkins war包安装部署&#xff0c;tomcatJDKmaven》 Jenkins中自动构建项目的类型有很多&#xff0c;…

ARM中断分析之二:裸机下面的中断处理

EINT4中断的裸机处理 这是基于S3C2410的EINT4中断的裸机处理&#xff0c;当中断发生时就把LED灯取反显示。下面是电路图。 上面是外部KEY连接到CPU的EINT4引脚上面&#xff0c;即&#xff1a;按下键就会产生一个中断。 按照先前介绍的&#xff0c;中断处理流程来介绍&#xff0…

单节点hadoop部署成功

经过前面的配置&#xff1a; linux-ot1w:/home/macula/download/hadoop-0.20.2 # bin/start-all.sh starting namenode, logging to /home/macula/download/hadoop-0.20.2/bin/../logs/hadoop-macula-namenode-linux-ot1w.out Password: localhost: starting datanode, logging…

【转】Jenkins 构建触发器操作详解

转自&#xff1a;Jenkins 构建触发器操作详解 - 习久性成 - 博客园 前言 触发远程构建 【https://www.cnblogs.com/Rocky_/p/8297260.html】 例如&#xff0c;使用脚本&#xff1a;通过一个网址的访问来触发构建&#xff0c;这样就不需要登录jenkins系统也能触发构建了。 示…

ARM中断分析之三:WinCE驱动的中断分析

现在有许多高端的ARM芯片&#xff0c;像苹果、三星、华为都采用ARM芯片做为智能手机芯片。 这篇文章介绍基于ARM的WinCE操作系统的驱动的中断分析。WinCE驱动分为两类&#xff0c;这里介绍流驱动&#xff0c;流驱动比较简单。 关于流驱动&#xff0c;下面是一张框图&#xff0c…

【转】密码破解全能工具:Hashcat密码破解攻略

转自&#xff1a;密码破解全能工具&#xff1a;Hashcat密码破解攻略 - FreeBuf网络安全行业门户 Hashcat密码破解 hashcat号称世界上最快的密码破解&#xff0c;世界上第一个和唯一的基于GPGPU规则引擎&#xff0c;免费多GPU&#xff08;高达128个GPU&#xff09;&#xff0c…

基于Visual Studio2010讲解LINQ读出数据库数据生成XML

LINQ to XML 是一种启用了 LINQ 的内存 XML 编程接口&#xff0c;使用它&#xff0c;可以在 .NET Framework 编程语言中处理 XML。 LINQ to XML 最重要的优势是它与 Language-Integrated Query (LINQ) 的集成。 由于实现了这一集成&#xff0c;因此&#xff0c;可以对内存 XML…

ARM中断分析之四:WinCE的OAL层的中断分析

从前面的介绍&#xff0c;我们知道了裸机中断处理的流程、WINCE驱动的中断处理&#xff0c;但是&#xff0c;WINCE底层是怎么处理中断的呢&#xff1f;这里就是介绍WinCE系统的OAL层的中断处理。它和裸机的处理总体一样&#xff0c;只是实现细节方面有点区别&#xff0c;具体流…

【转】TinyXML2 入门教程

转自&#xff1a;TinyXML2 入门教程_恋喵大鲤鱼的博客-CSDN博客_tinyxml2中文指南 代码编译运行环境&#xff1a;Linux 64bits Debug g -m64&#xff08;-m 表示生成 64bits 的程序&#xff09; 文章目录 1.TinyXML2 概述2. TinyXML1 与 TinyXML2 对比3. TinyXML2 用法用例…

解决“A problem has been encountered while loading the setup components. Canceling setup.”的问题...

近来因为需要开发C的程序&#xff0c;所以要在Visual Studio 2008中添加C的开发组件&#xff0c;但是在添加组件的时候&#xff0c;弹出“A problem has been encountered while loading the setup components. Canceling setup.”的提示。无奈之下&#xff0c;只好卸载&#x…