触摸屏驱动是从触摸屏硬件上获取用户的输入信息,然后将触摸事件传递给GWES子系统。驱动会矫正坐标值,比如将歪斜的线条修正为直线。
在用户的笔或者手指触摸屏幕的时候,驱动必须随时返回坐标值,当笔或者手指离开屏幕后,必须产生事件来标记“抬笔”事件。
下面是触摸屏驱动的注册表设置。
[HKEY_LOCAL_MACHINE/ControlPanel]
InputConfig"=dword:3 ; 3 => 键盘和触摸屏
[HKEY_LOCAL_MACHINE/HARDWARE/DEVICEMAP/TOUCH]
"DriverName"="touch.dll"
"MaxCalError"=dword:10
本数据结构绘出返回到系统的坐标值的属性。
本数据结构由TouchDriverCalibrationPointGet.例程代码组成,并运行在系统启动时,用户可以看见屏幕上会有一个X,从中间再到四周指引用户矫正坐标值。TouchDriverCalibrationPointGet例程通常利用屏幕的高和宽来确定中心和四周的坐标值。
struct TPDC_CALIBRATION_POINT {
INT PointNumber;
INT cDisplayWidth;
INT cDisplayHeight;
INT CalibrationX;
INT CalibrationY;
};
本数据结构由DdsiTouchPanelGetDeviceCaps例程代码组成。
struct TPDC_SAMPLE_RATE {
INT SamplesPerSecondLow;
INT SamplesPerSecondHigh;
INT CurrentSampleRateSetting;
};
数据结构中成员说明
SamplesPerSecondLow
低采样率值。
SamplesPerSecondHigh
高采样率值。
CurrentSampleRateSetting
当前采用的采样率值,1代表高采样率,0代表低采样率。
本例程使触摸屏具有采样功能。可以被程序直接调用。
本例程在MDD的DLL中被加载,DLL入口获得DLL_PROCESS_ATTACH事件时被执行。在多数系统中,一般只有一个进程加载触摸屏驱动,然而在某些系统中也不排除多个进程都加载触摸屏驱动DLL的可能性。在本例程中可以为它们复制全局变量等信息,这样可使每个加载驱动的进程都从本例程获得一个全局信息的副本。但是在本例程中不需要操作硬件或者中断。硬件和中断的操作应当在DdsiTouchPanelEnable和DdsiTouchPanelDisable中进行。
本例程原型:
LONG DdsiTouchPanelAttach(void);
返回值一般为0。
本例程在MDD的DLL被卸载,DLL入口获得DLL_PROCESS_DETACH事件时被执行。在多数系统中,一般只有一个进程加载触摸屏驱动,然而在某些系统中也不排除多个进程都加载触摸屏驱动DLL的可能性。在本例程中可以为它们复制全局变量等信息,这样可使每个加载驱动的进程都获得一个全局信息的副本。但是在本例程中不需要操作硬件或者中断。硬件和中断的操作应当在DdsiTouchPanelEnable和DdsiTouchPanelDisable中进行。
本例程原型:
LONG DdsiTouchPanelDetach(void);
本例程关闭触摸屏设备,删除任何没有被提交给GWES的坐标,关闭电源。
本例程原型:
VOID DdsiTouchPanelDisable(void);
无参数,无须返回值。
本例程原型:
BOOL DdsiTouchPanelEnable(void);
返回值:
TRUE代表成功,FALSE代表失败。
本例程为触摸屏加载电源,并且进行初始化。调用AllocTouchPanelRegs为寄存器分配内存。这些虚拟内存分配时,参数中使用MEM_RESERVED标志,这样被分配的内存不与物理内存绑定,不能被malloc等函数访问,最后使用virtual_copy将虚拟地址和寄存器的物理地址绑定(不是物理内存)。
在为寄存器分配完空间后,建立中断,即“落笔”中断,但是建立后就关闭中断,直到InterruptEnable()被执行后再建立中断。
本例程查询触摸屏设备的精度。
本例程原型:
BOOL DdsiTouchPanelGetDeviceCaps(
ULONG iIndex,
LPVOID lpOutput
);
其参数为:
iIndex,代表查询的项目。可以有以下类型:
TPDC_SAMPLE_RATE_ID 查询采样频率。
TPDC_CALIBRATION_POINT_ID 返回指定精度的坐标值,精度由lpOutput参数的PointNumber域指定。
TPDC_CALIBRATION_POINT_COUNT_ID 查询校准坐标所需要的坐标样本数。
lpOutput:指向存放返回信息的内存区域,类型和iIndex对应。
当iIndex为TPDC_SAMPLE_RATE_ID时,指向TPDC_SAMPLE_RATE结构。
当iIndex为TPDC_CALIBRATION_POINT_ID时,指向TPDC_CALIBRATION_POINT结构。
lpOutput不能为空指针。
返回值:
TRUE代表成功,FALSE代表失败。
本例程返回最近的一次数据采样坐标值。在“落笔”之后,就会触发定时器的一系列中断。定时器每秒钟约产生150次中断,每次中断都要返回坐标值。
在pxa255中,本例程分为三部分:
n 处理“落笔”中断
关闭“落笔”中断
n 处理定时器中断
递增定时器中断计数。
以上两个中断处理,都会调用SampleTouchScreen,从采集到的3个点中,选出最优化的点,返回给系统。
n 处理“抬笔”中断
最后,打开“落笔”中断,将UCB1400设置为可中断模式。
本例程通知驱动程序,系统正从暂停模式恢复到正常模式,或者进行相反的操作。本例程会被内核态程序调用,因此不要在其中使用系统调用。
基于微软公司的DirectDraw显示驱动,必须在DDI内实现DDHAL用于支持DirectDraw。
DirectDraw提供了独立于设备的显示驱动功能。它将图形显示数据直接加载到硬件设备上,而不需要图形设备接口来对数据进行转换,从而使图形显示效果更加顺畅,避免了屏幕的闪烁。实现这样的接口,需要扩展能够直接访问硬件的驱动接口,这些能够直接访问硬件的驱动接口组成了DirectDraw硬件抽象层(DirectDraw Hardware Abstraction Layer,DDHAL)
图10-1 DirectDraw和GDI
|
Windows CE中的DirectDraw是Windows桌面环境Direct-
Draw的子集。DirectDraw的核心可执行代码常驻在系统进程gwes.exe中,而应用程序则和较小的客户端代理Ddraw.dll建立连接,这个代理负责维护系统和用户进程之间的远程DirectDraw COM接口连接。Windows CE在默认的情况下不提供进程外部的COM服务器。
Windows CE还提供了一个名为DirectDraw图形原始引擎(DirectDraw Graphics Primitive Engine,DDGPE)的抽象层,GDI和DirectDraw组件在系统进程中使用这个抽象层将GDI/DDI和DDRAW/DDHAL的功能捆绑到一个由C++类和函数组成的框架上。DDGPE提供的C++类实现了GPE类的扩展。
可以复制Platform Builder提供的样例显示驱动程序作为新创建的驱动程序的基础。
1.修改二进制文件导出的接口,二进制文件应当导出HALInit接口。在Platform Builder提供的样例显示驱动程序有一个.def后缀名的文件,在这个文件中添加HALInit。HALInit是Ddgpe.lib库的一部分,应当总是被链接到程序中。
2.在程序中包含路径drive:/wince400/public/directX/oak/inc,这应当是DDHFuncs.h所在路径。这是一个所有显示驱动程序公共的头文件,不应当被修改。
将下列文件从样例显示驱动程序中复制到自己的文件夹下:
n HalCaps.cpp
n HalDD.cpp
n HalSurf.cpp
n HalPalette.cpp
在选择样例显示驱动程序时,应当选择和自己设备最接近的驱动程序。最后,编译自己创建的驱动程序。
DDGPE类是DirectDraw对象继承的基类。
表10-1列出了DDGPE类中的成员函数:
表10-1 DDGPE类中的成员函数
成 员 |
功 能 描 述 |
DDGPE::AllocSurface |
为一个surface分配内存 |
DDGPE::AllocVideoSurface |
在内存中创建DDGPESurf对象 |
DDGPE::BltExpanded |
建立并且执行一个GPE blit |
DDGPE::BltPrepare |
GPE BltPrepare 函数的DDGPE版本 |
DDGPE::DetectMode |
根据提供的参数确定显示模式 |
DDGPE::DetectPixelFormat |
将DDPIXELFORMAT转换成EGPEFormat或者EDDGPEPixelFormat. |
DDGPE::GetDriverData |
返回指向驱动数据的指针 |
DDGPE::GetDriverGUID |
获取驱动的GUID |
DDGPE::GetModeInfoEx |
返回扩展模式信息 |
DDGPE::GetPhysicalModeId |
获取当前实际的显示模式 |
DDGPE::InDisplay |
判断给定的扫描线是否正在显示 |
DDGPE::PerformBlt |
执行一次blit |
DDGPE::SetDriverData |
将驱动特定的数据存储到DDGPE对象中 |
DDGPE::SetDriverGUID |
将驱动的GUID赋予特定的对象 |
DDGPE::SetMode |
更改当前的显示模式 |
DDGPE::SetVisibleSurface |
使指定的surface被推到前台,成为可视 |
DDGPE::WrapSurface |
在既存的内存块之上创建一个DDGPESurf对象 |
DDGPESurf类扩展了GPESurf类,并且代表了由GDI分配的一个surface,如表10-2描述。
表10-2 DDGPSurf类中的成员函数
成 员 |
功 能 描 述 |
DDGPESurf::AlignedWidth |
获取一个surface内存的宽度 |
DDGPESurf::Bpp |
决定在当前surface中,每个象素占用的位数 |
DDGPESurf::ColorKey |
获取当前surface的color key |
DDGPESurf::ColorKeyMask |
获取当前surface的color key掩码 |
DDGPESurf::DeleteSurface |
删除一个surface,然后释放内存 |
DDGPESurf::Init |
初始化当前surface的像素格式 |
DDGPESurf::GetDDGPESurf |
获取和一个DirectDraw surface相关的DDGPESurf对象 |
DDGPESurf::GetDirectDrawSurface |
获取和一个DDGPESurf对象相关的DirectDraw surface |
DDGPESurf::GetDriverData |
获取指向驱动程序数据的指针,这个驱动程序和某个DDGPESurf对象相关联 |
续表
成 员 |
功 能 描 述 |
DDGPESurf::GetDriverGUID |
获取当前对象的驱动GUID |
DDGPESurf::PixelFormat |
获取当前surface的DDGPE风格的像素格式 |
DDGPESurf::SetColorKey |
为当前surface设置color key |
DDGPESurf::SetColorKeyMask |
为当前surface设置color key掩码 |
DDGPESurf::SetDirectDrawSurface |
将一个DirectDraw surface和当前的DDGPESurf 对象绑定 |
DDGPESurf::SetDDGPESurf |
将一个DDGPESurf对象和一个DirectDraw surface绑定 |
DDGPESurf::SetDriverData |
将驱动的数据存储到DDGPESurf对象中 |
DDGPESurf::SetDriverGUID |
将驱动的GUID存储到DDGPESurf对象中 |
Windows CE支持总线的枚举、电源管理、传输类型、类驱动、集成的和外部的集线器及对未知USB设备的有限支持。
Windows CE提供了对总线供电和自供电设备的支持。对于这两种类型的设备,USB驱动模块都会从设备描述符中读出电源需求,如果设备对电源的需求超出了系统的极限,那么这个设备将被停用。
在进入驱动程序开发前,先了解USB设备总线的拓扑结构
主机是USB设备树的根结点并且包含了一个固有的集线器,称为根集线器。集线器是一种功能设备,它能将USB传输通道给多个端口共享,这样,各个端口上就可以连接多个USB外设了。各个叶结点也就是USB外设。如图10-2所显示:
图10-2 USB总线的拓扑结构
和Windows 2000 DDK一样,Windows CE也支持四种基本的传输类型。
USB规范中定义了四种数据传输方式,如表10-3所示。它们的不同之处有,单个事务能携带的数据量、能否保证特定的周期或延迟、能否自动校正错误。每种传输方式对应特定类型的端点。实际上,给定类型的端点(控制、批量、中断、等时)总是使用对应类型的传输。
表10-3 USB规范中的四种数据传输方式
传输类型 |
描 述 |
纠 错 |
包容量(字节) |
控制 |
控制传输是双向传输,被USB系统软件主要用来进行查询、配置和给USB设备发送通用的命令。 |
是 |
少于或等于 |
批量 |
适合于需要大批量地传输和接收数据的设备,同时在没有带宽和间隔时间要求的情况下,要求保证传输。打印机和扫描仪属于这种类型。这种类型的设备适合于传输非常慢和大量被延误的传输,可以知道所有数据的传输完成之后再接收数据 |
是 |
少于或等于 8,16,32,64 |
中断驱动传输 |
中断驱动传输主要用于定时查询是否有中断数据需要传输。设备的端点模式器的结构决定了它的查询频率,频率值可以从1到255毫秒。这种传输典型地用于少量分散的、不可预测数据的传输。键盘、操纵杆和鼠标设备属于这一类型 |
是 |
少于或等于64 |
等时传输 |
用于发送或接收有周期保证的大块无结构数据 |
否 |
少于或等于1023 |
如果把本文的设备前缀定为MDV的话,则导出如下函数。
n MDV_Init 由设备管理器调用来初始化设备;
n MDV_Deinit 上述例程的逆过程;
n MDV_Open 打开设备,由应用程序调用CreateFile;
n MDV_Close 关闭设备上下文;
n MDV_IOControl 向设备发送命令;
n MDV_Write 把数据写入设备;
n MDV_Read 读设备,由应用程序调用readfile;
n MDV_Seek 数据指针的移动;
n MDV_PowerUp 电源管理;
n MDV_PowerDown 解除电源,只有当设备可以在软件控制下可以关机;
n USBDeviceAttach 激活设备;
n USBInstallDriver 安装驱动程序;
n USBUnInstallDriver 卸载驱动程序。
上一节只是产生了一个虚拟的驱动程序,没有针对任何硬件设备。但是,如果要对特定设备进行开发,必然需要对硬件状态进行设置。以下是对软件狗模块的设置以及所需要的结构体:
1, USB_CONFIGRATION
这个结构用来描述USB设备的配置。
typedef struct {
UCHAR bLength;//结构体大小,单位是字节
UCHAR bDescriptorType;// GWFIGuRATION
USHORT wTotalLength;//该配制返回的数据总长度
UCHAR bNumInterfaces;//该配制支持的接口数
UCHAR bConfigurationValue;
UCHAR iConfiguration;
UCHAR bmAttributes;
……
} USB_CONFIGURATION;
2, USB_DEVICE
这个结构存放了USB设备的信息。
typedef struct {
UCHAR bLength;
UCHAR bDescriptorType;// DEVICE常另值
UCHAR bDeviceClass;
UCHAR bDeviceSubClass;
UCHAR bDeviceProtocol;
UCHAR bMaxPacketSize0;
USHORT idVendor;//厂商标识符。USB工作组分配厂商标识符
……
} USB_DEVICE;
3, USB_DRIVER_SETTINGS
这个结构包含了USB驱动的成员,用来修改注册表。
typedef struct {
DWORD dwCount;
DWORD dwVendorId;
DWORD dwProductId;
DWORD dwDeviceClass;
DWORD dwDeviceSubClass;
DWORD dwDeviceProtocol;
DWORD dwInterfaceClass;
……
} USB_DRIVER_SETTINGS;
4, USB_ENDPOINT
这个结构包含了USB设备端口相关的信息。
typedef struct {
UCHAR bLength;
UCHAR bDescriptorType;//常另值
UCHAR bEndpointAddress;
/*--------------------------------------------------------*/
bEndpointAddress,定义了USB设备的端点地址,低4位为端点号,第四位到第六位作为保留位默认为0,第七位是数据流向为0的时候端点为输出端点,为1的时候端点为输入端点。
/*--------------------------------------------------------*/
UCHAR bmAttributes;
/*--------------------------------------------------------*/
bmAttributes,用于描述端点的类型,0x00位控制,0x01位同步,0x02位块传送,0x03位中断传送
/*--------------------------------------------------------*/
USHORT wMaxPacketSize;
UCHAR bInterval;
} USB_ENDPOINT
USB_INTERFACE
typedef struct {
UCHAR bLength;
UCHAR bDescriptorType;// Constant value INTERFACE
UCHAR bInterfaceNumber;
UCHAR bNumEndpoints;
UCHAR bInterfaceClass;
UCHAR bInterfaceSubClass;
UCHAR bInterfaceProtocol;
UCHAR iInterface;
} USB_INTERFACE;
图10-3是驱动程序中获取设备描述符、设置端口的流程图:
如前所述,Windows CE支持5种传输类型的函数接口,因此在读写设备时将调用相应的API如图10-4所示。具体为。
(1)IssueControlTransfer
在特定端口上初始化控制传输。
(2)IssueBulkTransfer
在特定端口上初始化块传输。
(3)IssueInterruptTransfer
在特定端口上初始化中断传输。
(4)IssueIsochTransfer
在特定端口上初始化同步传输。
图10-3 驱动程序中获取设备描述符、设置端口的流程图
在本文的程序中,暂时不用到此传输。
(5)IssueVendorTransfer
在特定端口上初始化厂商自定义传输。
驱动程序的测试可以通过注册表来查看
注册表:
n 对于注册表的查看可以使用Platform
选择“Builder”->“tool”->“remote register reader”进行查看。
n 选择设备Default Device,接着系统就会自动地去连接设备,
并且查询注册表
n 打开[HKEY_LOCAL_MACHINE/Drivers/USB/LoadClients]
查看其下是否有以下子键:
[Default/Default/ Default/Philip_USB_Driver]
查看该子键是否有以下属性值:
"DLL"="USBphlip.dll"
"Prefix"=dword:1
图10-4 USB驱动程序和系统、设备交互的结构
n 打开[HKEY_LOCAL_MACHINE/ Drivers/USB/
ClientDrivers/MDV]
查看其下是否有以下属性值:
"DLL"="USBphlip.dll"
"Prefix"=dword:1
n 查看当前被激活的设备,可以使用Platform
选择“Builder”->“tool”->“Remote System Information”
Platform Builder输出的调试信息:
Kernel debugger is waiting to connect with target.
0 PID:0 TID:0 RTC - Status Reg B - 0x02
0 PID:0 TID:0 X86Init done, OEMAddressTable = 8026a638.
Welcome to the Windows® CE Shell. Type ? for help.
2410 PID:22fc81d2 TID:22fce802 MyDriver - DLL_PROCESS_ATTACH
2410 PID:22fc81d2 TID:22fce802 MyDriver - MDV_Init -
2410 PID:22fc81d2 TID:22fce802 Drivers/Active/02
68640 PID:22fc81d2 TID:62e3995a MyDriver - MDV_Open
68640 PID:22fc81d2 TID:62e3995a hDeviceContext -
68640 PID:22fc81d2 TID:62e3995a 4660
68640 PID:22fc81d2 TID:62e3995a MyDriver - ~ MDV_Open
75270 PID:22fc81d2 TID:62e3995a MyDriver - MDV_Read
75270 PID:22fc81d2 TID:62e3995a hOpenContext -
75270 PID:22fc81d2 TID:62e3995a 22136
75280 PID:22fc81d2 TID:62e3995a MyDriver - ~ MDV_Read
77900 PID:22fc81d2 TID:62e3995a MyDriver - MDV_Write
77900 PID:22fc81d2 TID:62e3995a hOpenContext -
77900 PID:22fc81d2 TID:62e3995a 22136
77900 PID:22fc81d2 TID:62e3995a MyDriver – MDV_Write
同样的,也可以使用Platform Builder自带的远程调试工具来查看驱动程序的加载情况。首先,可以利用Remote Register来查看目标平台被设备跟踪的设备的注册表情况,通过这个工具,可以直接找到驱动程序加载点位置:[HKEY_LOCAL_MACHINE/.Drivers/USB],并查看被跟踪的设备。如图10-5所示。
图10-5 查看驱动程序的加载情况
也可以使用Remote File Viewer工具查看USB驱动MyCdriver.d11是否被下载到目标机,一般驱动程序Dll会被下载到目标机的/Windows目录下,如图10-6所示。
图10-6 用remoteFile Viewer查看dll是否被下载
也可以使用Remote System Information工具查看自定义的MDV1:设备的情况,如图10-7所示。
图10-7 用remote System Info查看目标平台活动的设备
Koala开发板中,提供了一个典型的Windows CE流式驱动,即处理电源按键事件的Power Button驱动PWRBUTTON。
PWRBUTTON是一个完全符合Windows CE要求的标准流式驱动程序,代码简单,结构严谨,为初学者提供了一个易学易用的驱动范例。PWRBUTTON以PWR作为驱动程序的设备头,提供了以下接口(其中大部分接口函数为空):
n PWR_Init
n PWR_DllEntry
n PWR_PowerHandler
n PWR_Open
n PWR_Close
n PWR_Read
n PWR_Write
n PWR_Seek
n PWR_PowerUp
n PWR_PowerDown
n PWR_Deinit
n PWR_IOControl
由于PWRBUTTON仅对应按键开关的关机信号,并不提供更为复杂的I/O操作或是数据读写服务,因此只实现了PWR_PowerHandler、PWR_Init以及PWR_DllEntry三个必要的函数,其他接口函数都是空函数,以满足流式驱动的架构要求。
根据Windows CE流式驱动要求,PWR_DLLEntry最先调用,完成驱动程序加载或卸载过程中必要的操作;PWR_Init则初始化按键响应所需要的一些变量,加载一些中断服务历程;PWR_PowerHandler则为可能要用到的状态切换提供一个操作接口。下面将详细介绍这三个函数。
作为标准的流驱动入口,
BOOL WINAPI PWR_DllEntry(
HANDLE hInstDll,
DWORD dwReason,
LPVOID lpvReserved
)
提供三个入口参数,hInstDll句柄、dwReason DLL和调用阶段说明和保留的参数lpvReserved。由于PWRBUTTON作为一个built-in驱动一直工作,并不会被系统卸载,因此在PWR_DllEntry函数中,也不需要根据加载和卸载的不同状态,而实现相应的操作。PWR_DllEntry函数仅在Debug版本中打印相应入口信息,代码如下:
switch ( dwReason ) {
case DLL_PROCESS_ATTACH:
DEBUGREGISTER((HINSTANCE) hInstDll);
DEBUGMSG(ZONE_INIT, (TEXT("PwrButton : DLL_PROCESS_ATTACH/r/n")));
break;
case DLL_PROCESS_DETACH:
// 信号线程
DEBUGMSG(ZONE_INIT, (TEXT("PwrButton : DLL_PROCESS_DETACH/r/n")));
break;
}
实际上,PWR_DllEntry完全可以作为一个空函数,只返回一个True状态。
PWR_Init函数是PWRBUTTON驱动的两个重点之一,它完成全局变量的初始化并且安装中断服务程序。
(1)PWR_Init初始化全局事情gPwrButtonIntrEvent
gPwrButtonIntrEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
(2)与按键事件关联在一起
InterruptInitialize(SYSINTR_POWER, gPwrButtonIntrEvent, NULL, 0)
(3)PWR_Init加载键按中断服务程序PwrButtonIntrThread
CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) PwrButtonIntrThread, NULL, 0, NULL)
这样,PWR_Init函数便完成了所有的初始化工作,而将按键处理过程交给了PwrButtonIn-
trThread,而PwrButtonIntrThread则是PWRBUTTON驱动的另一个重点。
PwrButtonIntrThread是一个死循环函数,无限期等待按键事件gPwrButtonIntrEvent发生。
WaitForSingleObject(gPwrButtonIntrEvent, INFINITE);
一旦电源按键按下,硬件产生中断,Windows CE的中断服务线程IST立即响应,并产生SYSINTR_POWER事件,从而触发了中断服务线程PwrButtonIntrThread,来完成关机操作。
PWR_PowerHandler是Windows CE下的电源管理接口。
void WINAPI PWR_PowerHandler(BOOL bOff)
参数bOff为True时,系统正在关机,bOff为False的时候,系统正在开机,一般流式驱动根据硬件特点,决定是否需要单独设置设备的工作状态。在Power Button的实现中,并不需要单独设置状态,因此该函数基本上也是一个空函数
void WINAPI PWR_PowerHandler(BOOL bOff)
{
if (bOff) {
}
else {
}
}
电池驱动为操作系统其他模块提供了主电池和备用电池的电力信息。由设备管理器加载的电池驱动提供流式接口。
Battery驱动例程被转换为驱动IOCTL控制字传递给驱动程序,然后驱动(MDD)调用相应的Battery PDD接口。
数据结构描述
_SYSTEM_POWER_STATUS_EX2
本结构体描述了系统电源状态如表10-4。
表10-4 SYSTEM_POWER_STATUS_EX2结构体描述
成 员 |
类 型 |
功 能 |
ACLineStatus |
BYTE |
AC电源的状态,可以有以下值: |
BatteryFlag |
BYTE |
电源电力状态,可有以下值: |
续表
成 员 |
类 型 |
功 能 |
BatteryLifePercent |
BYTE |
剩余电池电量百分比,值可以从0~100,255代表未知状态 |
Reserved1 |
BYTE |
保留,应当设置为0 |
BatteryLifeTime |
DWORD |
电池剩余的供电时间(单位:秒),如果未知则值为0xFFFFFFFF |
Reserved2 |
BYTE |
保留,应当设置为0 |
BackupBatteryFlag |
BYTE |
备用电池电量状态。可以为以下值: |
BackupBatteryLifePercent |
BYTE |
备用电池剩余供电电量百分比,可以为0~100,或者为BATTERY_PERCENTAGE_UNKNOWN |
Reserved3 |
BYTE |
保留,应当设置为0 |
BackupBatteryLifeTime |
DWORD |
备用电池剩余供电时间 |
BatteryVoltage |
DWORD |
电池电压(单位:mV),值可以从0~65535 |
BatteryCurrent |
DWORD |
瞬间的负载(单位mA),充电时可以取值0~32767;放电时可以取值0~32768 |
BatteryAverageCurrent |
DWORD |
平均的设备负载(单位:mA),充电时可以取值0~32767;放电时可以取值0~32768 |
BatteryAverageInterval |
DWORD |
用于计算BatteryAverageCurrent的计时常数(单位:ms) |
BatteryTemperature |
DWORD |
电池温度 |
BackupBatteryVoltage |
DWORD |
后备电池电压 |
BatteryChemistry |
BYTE |
电池化学材料,可以有以下选择: |
下列代码是pxa255的电池驱动所需要更改的注册表设置:
IF BSP_NOBATTERY !
; HIVE BOOT SECTION
[HKEY_LOCAL_MACHINE/System/Events]
"SYSTEM/BatteryAPIsReady"="Battery Interface APIs"
; 这段注册表负责加载电池驱动,Iclass值必须跟Battery.h内定义的
; BATTERY_DRIVER_CLASS一样,这样系统才知道那个设备是电池驱动。这里我们使用
; DEVFLAGS_NAKEDENTRIES,这样就告诉设备管理器,加载驱动的时候忽略prefix键也就
; 是说,当驱动进行加载的时候设备管理器找到的是Init函数而不是BAT_Init这样就允许
; 在注册表内更改Prefix值而不需要再重新更改驱动了。
; for DLL entry points without the prefix. For example, it will look for Init
if
; desired) without editing the driver code.
[HKEY_LOCAL_MACHINE/Drivers/BuiltIn/Battery]
"Prefix"="BAT"
"Dll"="battdrvr.dll"
"Flags"=dword:8 ; DEVFLAGS_NAKEDENTRIES
"Order"=dword:0
"IClass"="{DD176277-CD34-4980-91EE-67DBEF3D8913}"
ENDIF BSP_NOBATTERY !
以下例程可以在应用程序中调用。并非所有例程都需要被支持。
通过本例程可以查询驱动程序对系统电力状态的监控功能,具体指GetSystemPowerStatusEx2结构中BatteryFlag域里支持的电源状态数量。如果BatteryFlag里只能支持BATTERY_FLAG_HIGH和BATTERY_FLAG_LOW,那么就支持2个level。返回值为双字,高字代表备用电池,低字代表主电池。
其代码如下:
LONG BatteryDrvrGetLevels( void)
{
return MAKELONG (1 /*main battery levels*/,1/*backup battery levels*/);
}
通过本例程可以查询驱动程序是否能检测出电池是否该被更换。
返回值为TRUE,则代表驱动支持通知机制,否则代表不支持。返回值为FALSE情况下,当调用BatteryPDDGetStatus时,参数pfBatteriesChanged应当设置为FALSE。
BOOL BatteryDrvrSupportsChangeNotification(void )
{
return FALSE;
}
通过本例程可以查询用户更换电池以后的时间:
void BatteryGetLifeTimeInfo(
LPSYSTEMTIME pstLastChange,
DWORD* pcmsCpuUsage,
DWORD* pcmsPreviousCpuUsage
);
其参数为:
pstLastChange
填写指向用户更换电池以来的时间数据。
pcmsCpuUsage
填写指向当前电池被激活后的时间数据。
pcmsPreviousCpuUsage
填写前一个电池被使用的时间数据。
本调用完成硬件初始化工作。函数原型如下:
BOOL WINAPI BatteryPDDInitialize(
LPCTSTR pszRegistryContext
);
参数pszRegistryContext指向驱动的注册表键。如果操作成功则返回TRUE。如果返回错误,则驱动将被设备管理器卸载。
在PXA255的驱动中,只是设置了模块名称,初始化上下文,然后打印调试信息,并且返回TRUE。
BOOL WINAPI BatteryPDDInitialize(LPCTSTR pszRegistryContext)
{
BOOL fOk = TRUE;
SETFNAME(_T("BatteryPDDInitialize"));
UNREFERENCED_PARAMETER(pszRegistryContext);
DEBUGMSG(ZONE_PDD, (_T("%s: returning %d/r/n"), pszFname, fOk));
return fOk;
}
完成硬件特定的删除卸载任务。在PXA255的驱动中,仅仅打印调试信息。
void WINAPI BatteryPDDDeinitialize(void)
{
SETFNAME(_T("BatteryPDDDeinitialize"));
DEBUGMSG(ZONE_PDD, (_T("%s: invoked/r/n"), pszFname));
}
在系统从睡眠中恢复后,执行硬件电池处理操作。在PXA255的驱动中,仅仅打印调试信息。
void WINAPI BatteryPDDResume(void)
{
SETFNAME(_T("BatteryPDDResume"));
DEBUGMSG(ZONE_PDD, (_T("%s: invoked/r/n"), pszFname));
}
获取当前的电源状态。下面pxa255驱动中的代码,重新填写了PSYSTEM_POWER_STA-
TUS_EX2结构的各个域。
BOOL WINAPI BatteryPDDGetStatus(
PSYSTEM_POWER_STATUS_EX2 pstatus,
PBOOL pfBatteriesChangedSinceLastCall
)
{
pstatus->ACLineStatus = AC_LINE_ONLINE;
pstatus->BatteryFlag = BATTERY_FLAG_NO_BATTERY;
pstatus->BatteryLifePercent = 0;
pstatus->BatteryLifeTime = BATTERY_LIFE_UNKNOWN;
pstatus->BatteryFullLifeTime = BATTERY_LIFE_UNKNOWN;
pstatus->BackupBatteryFlag = BATTERY_FLAG_HIGH;
pstatus->BackupBatteryLifePercent = 0;
pstatus->BackupBatteryLifeTime = BATTERY_LIFE_UNKNOWN;
pstatus->BackupBatteryFullLifeTime = BATTERY_LIFE_UNKNOWN;
pstatus->BatteryChemistry = BATTERY_CHEMISTRY_UNKNOWN;
pstatus->BatteryVoltage = 0;
pstatus->BatteryCurrent = 0;
pstatus->BatteryAverageCurrent = 0;
pstatus->BatteryAverageInterval = 0;
pstatus->BatterymAHourConsumed = 0;
pstatus->BatteryTemperature = 0;
pstatus->BackupBatteryVoltage = 0;
*pfBatteriesChangedSinceLastCall = FALSE;
return (TRUE);
}
在一般的产品中,应当探测AC电源和电池电源的剩余电力然后再进行域填充。对电力的探测可以使用IsACOnline、Battery_sampleADC等操作,具体可以参考PLATFORM BUILDER所自带的驱动demo。
查询驱动程序对系统电力状态的监控功能,具体指GetSystemPowerStatusEx2结构中BatteryFlag域里支持的电源状态数量。如果BatteryFlag里只能支持BATTERY_FLAG_HIGH和BATTERY_FLAG_LOW,那么就支持2个level。返回值为双字,高字代表备用电池,低字代表主电池。
功能,返回值基本和BatteryDrvrGetLevels相同。
LONG BatteryPDDGetLevels( void)
{
LONG lLevels = MAKELONG (3 /* main battery levels */,
3 /* backup battery levels */);
SETFNAME(_T("BatteryPDDPowerHandler"));
DEBUGMSG(ZONE_PDD, (_T("%s: returning %u (%d main levels, %d backup levels)/r/n"),pszFname, LOWORD(lLevels), HIWORD(lLevels)));
return lLevels;
}
功能和返回值与BatteryDrvrSupportsChangeNotification相同。在pxa255中将返回值设置为FALSE。
BOOL BatteryPDDSupportsChangeNotification(void)
{
BOOL fSupportsChange = FALSE;
SETFNAME(_T("BatteryPDDPowerHandler"));
DEBUGMSG(ZONE_PDD, (_T("%s: returning %d/r/n"), pszFname, fSupportsChange));
return fSupportsChange;
}
Windows CE中的音频驱动接口分为三个种类。
1.UAM驱动:Unified Audio Model统一音频模型。这是一种新的音频驱动模型,它将Wave audio和Microsoft® DirectSound® audio整合到了一起;
2.Waveform驱动:作为UAM驱动的一种替代方案。沿用传统的MDD和PDD架构;
3.ACM音频压缩管理驱动:Audio Compression Manager Drivers,是一种流接口驱动程序,其中包含了不同的音频压缩管理驱动类型并且提供开发ACM驱动的必要信息。
在本书中将针对kaola开发板中使用的普通音频MDD和PDD驱动进行分析。
作为UAM驱动或者流驱动的替代方案,开发者可以使用由微软公司提供的MDD库Wavemdd.lib。在这个库中,通过音频设备驱动服务提供接口(DDSI)实现了所有的流接口。如果要使用Wavemdd.lib库,则必须自己实现平台相关的PDD库,DDSI函数将使用到PDD库中的接口PDD库通常被命名为Wavepdd.lib。然后,这两个库可以被链接到音频驱动程序中,这种类型的音频驱动通常被命名为Wavedev.dll。
音频硬件通常支持比文件操作更多的接口。例如文件不需要音量控制而声卡需要。文件操作中的DeviceIOControl操作,在音频操作中同样可以由WAV_IOControl函数来实现,程序可以通过WAV_IOControl向声卡发送不同的命令。
图10-8显示了应用程序如何通过驱动和音频硬件进行数据交互。
图10-8 驱动和音频
|
流接口驱动程序需要实现下列接口:
n WAV_Close
n WAV_Deinit
n WAV_Init
n WAV_IOControl
n WAV_Open
n WAV_PowerDown
n WAV_PowerUp
n WAV_Read
n WAV_Seek
n WAV_Write
DDSI的接口罗列如下:
n PDD_AudioDeinitialize
n PDD_AudioGetInterruptType
n PDD_AudioInitialize
n PDD_AudioMessage
n PDD_AudioPowerHandler
n PDD_WaveProc
主要数据结构如表10-5所示。
表10-5 主要用到的数据结构
结 构 体 |
描 述 |
MMDRV_MESSAGE_PARAMS |
这个结构体代表了要传输给WAV_IOControl的数据 |
WAVEOPENDESC |
包含了waveform输入输出必要的信息 |
WAVEOUTEXTCAPS |
包含额外的设备功能信息 |
表10-6是wave驱动程序的流接口。
表10-6 Wave驱动程序的流接口
接 口 |
描 述 |
WAV_IOControl |
I/O控制例程 |
WAV_Init |
初始化WAV I/O设备 |
WAV_Deinit |
初始化WAV I/O设备的反操作 |
WAV_Open |
打开WAV I/O设备 |
WAV_Close |
关闭WAV I/O设备 |
WAV_Read |
WAV I/O设备的读操作 |
WAV_Write |
WAV I/O设备的写操作 |
WAV_Seek |
WAV I/O设备的寻址操作 |
WAV_PowerUp |
通知WAV I/O设备,系统已经离开suspend状态 |
WAV_PowerDown |
关闭(停止供电)WAV I/O |
wave驱动程序的输入消息如表10-7。
表10-7 Wave驱动程序的输入消息
消 息 |
描 述 |
WIDM_ADDBUFFER |
请求waveform输入驱动在输入队列中添加一个buffer |
WIDM_CLOSE |
请求waveform输入驱动关闭一个指定的设备实例,这个设备实例由先前的WIDM_OPEN创建并且打开 |
WIDM_GETDEVCAPS |
请求waveform输入驱动返回指定设备的功能 |
WIDM_GETNUMDEVS |
请求waveform输入驱动返回其支持的设备数量 |
WIDM_GETPOS |
请求输入流接口输入,驱动返回当前输入位置,位置值是相对于第一个采样的waveform样本 |
续表
消 息 |
描 述 |
WIDM_OPEN |
请求waveform输入驱动打开一个设备对应的流接口 |
WIDM_PREPARE |
请求waveform输入驱动为输入准备一个系统中唯一的数据缓冲 |
WIDM_RESET |
请求waveform输入驱动停止录音并且将所有的缓冲数据返回给调用者 |
WIDM_START |
请求waveform输入驱动开始录音 |
WIDM_STOP |
请求waveform输入驱动停止录音 |
WIDM_UNPREPARE |
WIDM_PREPARE的逆操作 |
wave驱动程序的输出消息如表10-8。
表10-8 Wave驱动程序的输出消息
消 息 |
描 述 |
WODM_BREAKLOOP |
请求waveform输出驱动停止由WODM_WRIT建立的输出循环 |
WODM_CLOSE |
请求waveform输出驱动关闭由WODM_OPEN建立的流接口 |
WODM_GETDEVCAPS |
请求waveform输出驱动返回特定设备的功能 |
WODM_GETNUMDEVS |
请求waveform输出驱动返回其支持的设备实例数量 |
WODM_GETPLAYBACKRATE |
请求waveform输出驱动返回特定设备当前的播放率放大值 |
WODM_GETPOS |
请求返回当前数据流中相对于waveform起始的相对位置 |
WODM_GETVOLUME |
请求waveform输出驱动返回指定设备的音量水平 |
WODM_OPEN |
请求waveform输出驱动打开指定设备上的流接口 |
WODM_PAUSE |
请求waveform输出驱动暂停waveform的播放 |
WODM_RESET |
请求waveform输出驱动停止发送输出数据并且返回所有的输出缓冲到列表 |
WODM_RESTART |
请求waveform输出驱动继续播放被暂停的waveform |
WODM_SETPLAYBACKRATE |
请求waveform输出驱动设置特定设备当前的播放率放大值 |
WODM_SETVOLUME |
请求waveform输出驱动设置指定设备的音量水平 |
PDD函数如表10-9所示。
表10-9 PDD函数描述
函 数 |
描 述 |
PDD_AudioDeinitialize |
关闭并且断开和声卡的连接 |
PDD_AudioGetInterruptType |
判断音频中断的原因,并且返回当前设备的状态 |
PDD_AudioInitialize |
初始化音频设备 |
续表
函 数 |
描 述 |
PDD_AudioMessage |
将应用程序的消息发送到PDD |
PDD_AudioPowerHandler |
负责在POWER_UP和POWER_DOWN时管理硬件 |
PDD_WaveProc |
将消息发送到驱动的PDD层 |
WPDM消息如表10-10所示。
表10-10 WPDM消息描述
消 息 |
描 述 |
WPDM_CLOSE |
这个消息被传递到PDD_WaveProc函数来通知音频驱动PDD,驱动程序的waveXXXClose函数已经被调用 |
WPDM_CONTINUE |
这个消息被传递到PDD_WaveProc函数来通知音频驱动PDD,从wave header指向的数据开始继续处理 |
WPDM_ENDOFDATA |
这个消息被传递到PDD_WaveProc函数来通知音频驱动PDD,已经没有输出数据需要处理 |
WPDM_GETDEVCAPS |
这个消息被传递到PDD_WaveProc函数来通知音频驱动PDD查询硬件的功能 |
WPDM_GETVOLUME |
这个消息被传递到PDD_WaveProc函数来通知音频驱动PDD查询当前音频设备的音量水平 |
WPDM_OPEN |
这个消息被传递到PDD_WaveProc函数来通知音频驱动PDD,将有特定格式的数据被发送到waveform设备 |
WPDM_PAUSE |
这个消息被传递到PDD_WaveProc函数来通知音频驱动PDD,暂停播放 |
WPDM_RESTART |
这个消息被传递到PDD_WaveProc函数来通知音频驱动继续播放被暂停的数据 |
WPDM_SETVOLUME |
这个消息被传递到PDD_WaveProc函数来通知音频驱动调整音频设备的音量 |
WPDM_STANDBY |
这个消息被传递到PDD_WaveProc函数来通知音频驱动将设备设置为standby状态 |
WPDM_START |
这个消息被传递到PDD_WaveProc函数来通知音频驱动,从wave header指向的数据开始处理 |
WPDM_STOP |
这个消息被传递到PDD_WaveProc函数来通知音频驱动,停止录音 |