Session 2: Testing Windows CE .NET 嵌入式软件开发导论 7. 设备驱动程序开发 同济大学软件学院 2006 Alan Page, Microsoft
主要内容 概述 流驱动接口 设备驱动程序架构 USB mouse 驱动开发示例 Windows CE 系统服务
什么是设备驱动程序 设备驱动程序是与硬件设备进行通信的系统程序。一个设备可以是物理设备,也可以是一个逻辑实体。通常,这些实体需要操作系统对其进行控制,资源管理。设备驱动程序就是管理这些物理设备或者虚拟设备,协议 或者系统服务的软件模块。 对于每一个基于Windows CE的设备,设备驱动程序都是必不可少。 --译自 MSDN
“一般”驱动程序开发的理解 必须使用汇编语言对端口进行读写 设备驱动程序是操作系统的一部分,难于开发与调试。 设备驱动程序控制硬件 中断比较难于处理
必须使用汇编语言读写端口 CEDDK.dll 提供 APIs与硬件进行通信 对于逻辑设备驱动程序,采用Win32 APIs 从硬件获取数据 HalGetBusData READ_PORT_UCHAR WRITE_REGISTER_ULONG 对于逻辑设备驱动程序,采用Win32 APIs 从硬件获取数据
设备驱动程序是操作系统的一部分,难于开发与调试 对于UNIX或者是 Windows 9x,设备驱动程序是与操作系统映像连接的,并运行于系统内核态。 对于windows CE,大多数的设备驱动程序运行于用户态,简单的来说就是一个DLL文件。
设备驱动程序控制硬件 大多数的设备驱动程序用来控制硬件。 对于某些硬件,是没有驱动的。 对于虚拟设备驱动程序,没有物理设备。 CPU 内存 文件系统驱动程序 RAM disk
中断有时难于处理 Windows CE提供逻辑中断 (SYSINTRs) 中断由一个用户态的线程进行处理 (IST)
为什么应当写驱动程序? 当我们设计硬件时,就需要提供这一硬件的驱动软件。 硬件OEM厂商不提供基于Windows CE的硬件驱动,但是提供硬件的规格说明。 对于一个现有硬件驱动的功能进行扩展。
驱动分类—整体 Windows CE 整体上将设备驱动分为三种: 内置驱动 总线 流接口
内置驱动 硬件所必须的驱动程序,这些驱动程序通常由设计OEM厂商进行设计。 如:键盘,触摸屏,音频设备。 可能不支持一般的驱动接口,也有可能带有扩展的接口或者完全提供一个用户自定义的接口。 内置驱动通常需要针对具体的操作系统进行小幅改动。
总线驱动 管理系统总线如PCI总线等。PCMCIA与 CompactFlash也在其列。 负责处理总线上硬件的询问,以及资源的分配。 同时也请求设备管理器为总线上的硬件安装适当驱动程序。
驱动分类 – 详细 参见文档: Driver Development -> Driver Categories Audio Drivers Battery Drivers Block Drivers Bluetooth HCI Transport Driver Direct3D Device Driver Interface DirectDraw Display Drivers Display Drivers DVD-Video Renderer IEEE 1394 Drivers Keyboard Drivers Notification LED Drivers Parallel Port Drivers PC Card Drivers Printer Drivers Serial Port Drivers Smart Card Drivers Stream Interface Drivers Touch Screen Drivers USB Drivers 参见文档: Driver Development -> Driver Categories
驱动的加载过程 大多数的设备驱动由设备管理器(Device.exe)进程在系统启动时进行加载。 另一方面,其中一部分内置驱动程序由GWES.exe进行加载。这些驱动包括显示器驱动 (DDI.dll)、键盘驱动、鼠标驱动、触摸屏驱动等。
驱动的加载过程 当Device.exe对设备驱动程序进行安装时将在注册表的[HKEY_LOCAL_ MACHINE]\Drivers项下检查一个字符串,其值为 RootKey,传统地,其名字为 BuiltIn 。 然后设备管理器在进行初始化时使用注册信息检查器为它需要安装的各个驱动读取值为RootKey的键。
驱动的加载过程
驱动的加载过程 安装DLL,给每一个驱动创建一个Active 键,然后调用ActivateDevice 或者 ActivateDeviceEx将DLL注册为系统中的一个设备驱动程序。 ActivateDevice在 [HKEY_LOCAL_MACHINE\Drivers\Active下创建一个新的键值。
驱动的加载过程
驱动加载API 设备驱动程序也能通过应用程序进行手动的安装,这是ActivateDeviceEx的一个首选的功能。 安装驱动一个更老的方法是使用RegisterDevice 与 DeregisterDevice
主要内容 概述 流驱动接口 设备驱动程序架构 USB mouse 驱动开发示例 Windows CE 系统服务
什么是流接口 流接口驱动包括所有提供流接口函数的驱动程序,不管由这个驱动控制的设备是什么类型。 典型的流接口驱动程序有: 文件系统驱动 (iostream, fstream) COM, LPT
使用流接口 直接使用Win32文件系统API。 hSer = CreateFile(TEXT(“COM1:”), GENERIC_READ, 0, NULL, OPEN_EXSITING, 0, NULL); rc = ReadFile(hSer, &ch, 1, &cBytes, NULL); TransmitCommChar(hSer, ‘a’); CloseHandle(hSer); 直接使用Win32文件系统API。
创建一个流驱动程序 写一个导出具体流接口的DLL 编译设备驱动程序 对注册表进行配置
流接口函数 函数名 说明 XXX_Close 关闭以hOpenContext标识的设备上下文。 XXX_Deinit 由设备管理器调用来删除对某一设备的初始化信息。 XXX_Init 由设备管理器调用来对某一设备进行初始化。 XXX_IOControl 向设备发送命令 XXX_Open 打开一个设备以进行读、写或者既读又写。当应用调用CreateFile来打开某一个设备文件名时将间接地调用这一个函数。 XXX_PowerDown 停止向设备供电。只有在软件控制下关机的设备上才可用。 XXX_PowerUp 恢复向设备供电 XXX_Read 从设备读取数据 XXX_Seek 在设备中移动数据指针 XXX_Write 向设备写数据
构建设备驱动程序 构建设备驱动程序与构建一个DLL一样简单。 可以使用Platform Builder与EVC++。只需创建一个Windows CE DLL项目, 输出适当 的入口点,然后编写代码。 常犯的一个错误是没有将入口点声明为 extern C,因此 C++编译器就不能处理输出的函数名。
注册表设置 在 Project.reg中: [HKEY_LOCAL_MACHINE\Drivers\BuiltIn\Sample] "Dll" = "mydriver.Dll" "Prefix" = "DEM" "Index" = dword:1 "Order" = dword:0 "FriendlyName" = "Demo Driver" "Ioctl" = dword:0
注册表设置 Order 给所有的驱动设置相对安装顺序。Order 为0的驱动先安装,依次安装Order为1,2,…的驱动。Oder可以让开发者保证有相到依赖关系的多个驱动以适当的顺序进行安装。 Index 指定在文件系统中的驱动名的数字部分。在默认情况下,带有前缀COM的驱动在文件系统中分配名字为 COM1,下一个即为COM2。为了保证驱动总是安装成COM2,必须提供Index = 2。
演示 : 使用emulator编写一个流接口驱动程序并使用它
驱动是如何工作的 When CE启动时, Device.exe根据注册表中的设置安装所有的驱动程序。 当安装mydriver.dll时, device.exe 调用 DEM1_Init。 Application程序调用CreateFile(“DEM1”…)
驱动是如何工作的(2) Coredll.dll 处理API调用并切换到 device.exe Device.exe 调用mydriver.dll 的DEM1_Open。 CreateFile返回DEM1_Open的retval
设备功能栈 应用程序 设备管理器(device.exe) 设备驱动程序 Windows CE DDK API 硬件
主要内容 概述 流驱动接口 设备驱动程序架构 USB mouse 驱动开发示例 Windows CE 系统服务
Session 2: Testing Windows CE .NET 驱动架构 Alan Page, Microsoft
Session 2: Testing Windows CE .NET 驱动的类型 Alan Page, Microsoft
Session 2: Testing Windows CE .NET 内置与流安装机制 通常内置驱动程序根据注册键值安装在 GWES进程空间 总线驱动由Device.exe根据键值进行安装 可安装的流驱动或其它驱动由Device.exe根据总线驱动或注册检查器进行安装。 Alan Page, Microsoft
Session 2: Testing Windows CE .NET 设备管理器 用户级进程继续运行 与内核、注册表交互的单独应用程序,以及流接口驱动DLL。 提供ActivateDevice 与 DeactivateDevice API。 Alan Page, Microsoft
Session 2: Testing Windows CE .NET 设备管理器 包括IO资源管理器 安装注册表检查器(RegEnum.dll) ,并由这一个注册表检查器根据注册表进行驱动的安装 提供电源通知回调函数 跟踪所有已安装的驱动程序,给设备接口发出拉入或删除的通知 Alan Page, Microsoft
Session 2: Testing Windows CE .NET 设备管理器 设备驱动程序的安装过程 DEVICE.EXE DEVICE.EXE Kernel loads loads I/O Resource Manager (part of Device.exe) I/O Resource Manager (part of Device.exe) REGENUM.DLL REGENUM.DLL loads PCIBUS.DLL PCIBUS.DLL Alan Page, Microsoft
Session 2: Testing Windows CE .NET 注册表枚举器 注册枚举器是RegEnum.dll Device.exe根据HKEY_LOCAL_MACHINE\Drivers\RootKey安装注册枚举器 注册枚举器给每一个要安装的驱动程序扫描表项 注册枚举器是可重入的,并支持分级使用 当注册枚举器卸载时,同时也御载它安装的所有信息 注册枚举器根据Order,检查紧挨着传给它的键的下一级键值。 注册枚举器对每一个找到的子键调用ActivateDeviceEx Alan Page, Microsoft
Session 2: Testing Windows CE .NET ActivateDeviceEx 由Device.exe提供 总线驱动程序当安装设备驱动时调用ActivateDeviceEx ActivateDeviceEx也在RAM工作区锁定流接口驱动程序 这可以防止代码页被丢弃 注册检查器对找到的每一个内置子键调用ActivateDeviceEx ActivateDeviceEx安装驱动并更新注册表 Alan Page, Microsoft
Session 2: Testing Windows CE .NET 接口类 驱动程序的接口有以下特征 每一个Iclass有一个GUID,一个名字 GUID描述类设备接口 Name 描述接口的实例 COM1:, DSK1: 等等 提供一个驱动接口 在注册表中的Iclass子键 驱动程序发布接口 – AdvertiseInterface 应用程序访问接口 – RequestDeviceNotifications 每个已有的接口的预定义GUID A32942B7-920C-486b-B0E6-92A702A99B35 Alan Page, Microsoft
Session 2: Testing Windows CE .NET I/O 资源管理(IORM) IORM是设备管理器必备的部分 跟踪可用I/O资源 (IRQ与I/O基地址) OEM为内置设备预分配的资源 当在总线上安装一个客户驱动时,总线驱动请求资源 ResourceRelease ResourceRequest ResourceCreateList 当存在冲突时IORM调用 ResourceRequest失败 Alan Page, Microsoft
Session 2: Testing Windows CE .NET 注册表帮助例程 从注册表中读取资源初始化信息 OpenDeviceKey DDKReg_GetIsrInfo DDKReg_GetWindowInfo DDKReg_GetPciInfo 在COREDLL的API 在<devload.h> and <ddkreg.h> 的函数原型 在public\common\oak\drivers中的例子 wavedev\pdd\es1371\wavepdd.cpp -> GetRegistryConfig Alan Page, Microsoft
Session 2: Testing Windows CE .NET 电源管理 电源管理器 灵活的系统级与外设级电源管理 使设备智能化地管理它们的电源 在设备与系统或应用之间充当协调者 使OEM修改调用PowerOffSystem()的相关代码 Alan Page, Microsoft
Session 2: Testing Windows CE .NET 电源管理 系统级电源状态 设备(外设)级电源状态 打开 系统空闲 用户空闲 挂起 D0 全供电 D3 睡眠 D1 降级模式 D4 关闭 D2 备用模式 Alan Page, Microsoft
Session 2: Testing Windows CE .NET 电源管理器架构 应用API 应用程序 PM APIs 通知消息队列 电源管理器 (pm.dll) 驱动 驱动 APIs Alan Page, Microsoft
Session 2: Testing Windows CE .NET 物理内存 内核虚拟地址 物理内存 Reserved C0000000 Dbg Serial Port 512 MB Uncached 82000000 32 MB Flash 32 MB Flash 64 MB RAM 80000000 A0000000 04000000 64 MB RAM 512 MB Cached 32 MB Flash 64 MB RAM 80000000 2 GB User Alan Page, Microsoft
Session 2: Testing Windows CE .NET 虚拟地址空间 4 GB Not Used Accessable via MmMapiIoSpace 3 GB 512M Non-Cached Above 2G-3G Mapped to physical memory 0xA0000000 512M Cached Virtual address space 2 GB 0x80000000 Memory mapped files Slot 32 Slot 32 64 MB Slot 1 32 MB Slot 0 64 KB NULL pointers Alan Page, Microsoft
Session 2: Testing Windows CE .NET 内存管理函数 设备驱动程序是用户态模块 必须将物理内存映射成虚拟内存 VirtualAlloc, VirtualFree: 预留,释放虚拟内存 MEM_RESERVE VirtualCopy: 将一个物理内存区域映射到一个虚拟内存区域 PAGE_NOCACHE PAGE_PHYSICAL Alan Page, Microsoft
Session 2: Testing Windows CE .NET 驱动的内存访问与映射 MapPtrToProcess 将一个地址空间的指针映射成另外一个指针 GetCurrentProcess / SetProcPermissions 重新找回进程ID用于MapPtrToProcess函数 MmMapIoSpace 将一个物理地址空间映射成一个不分页的,由进程决定的地址空间 Alan Page, Microsoft
Session 2: Testing Windows CE .NET CEDDK CE v2.1x 以及后来的版本支持独立于平台的I/O配置 让OEM以及驱动开发人员使用它们 CEDDK.DLL 总线管理 内存管理 I/O管理 从NTDDK拿来的CEDDK API 直到V2.12以后的版本才有文档 但在大多数V2.00版中都有 Alan Page, Microsoft
Session 2: Testing Windows CE .NET CEDDK 简介 设备管理器 设备驱动使用CEDDK 正常开发的驱动是跨平台二进制兼容的 跨CPU的源代码兼容 设备驱动 CEDDK.DLL 硬件 Alan Page, Microsoft
Session 2: Testing Windows CE .NET CEDDK 内容 部分 函示例 总线管理 HalGetBusDataByOffset() HalGetBusData() HalSetBusDataByOffset() HalSetBusData() HalTranslateBusAddress() HalTranslateSystemAddress() 内存管理 MmMapIOSpace – MmUnmapIOSpace HalAllocateCommonBuffer I/O 存取 XXXX => UCHAR/USHORT/ULONG READ_REGISTER_XXXX WRITE_REGISTER_XXXX READ_PORT_XXXX WRITE_PORT_XXXX Alan Page, Microsoft
Session 2: Testing Windows CE .NET 中断架构 中断只是异常处理的一部分 在内核代码中被获取 在恢复到用户态之间进行处理 但是硬件是独立于操作系统的 CE使用二阶段中断服务模式 少量的代码进行中断的记录 调度中断服务程序代码进行处理 操作系统提供API来使能IRQ信号,设置中断的优先级,等等。 与硬件有关的操作在OAL,以及设备驱动代码中进行。 Alan Page, Microsoft
Session 2: Testing Windows CE .NET 关于ISR与IST的一些 解释 IST是老的用户模式代码 有完整的CE .NET API与CRT库 CE .NET调度程序根据线程的优先级控制IST的执行 运行态绑定到SYSINTR 从异常处理函数调用ISR 没有操作系统服务 堆栈空间大小有限 可设置为可重入的 (在OAL进行设置) MIPS, SHx汇编语言 Alan Page, Microsoft
Session 2: Testing Windows CE .NET ISR IRQ 优先级的设定依赖于不同CPU的系统架构 在x86上由PIC硬件进行设置 在MIPS上用OAL的IntrPriority, IntrMask 进行设置 通过让IST进程推断ISR服务,OAL能够增加实时性。 在CE 3.0中,所有的ISR代码都是OAL的一部分 没有特殊的扩展 IRQ绑定到OEMInit的事件ID 没有支持PNP, 共享IRQ (PCI总线) CE .NET 通过IRS(IISR)链解决了这些限制。 Alan Page, Microsoft
Session 2: Testing Windows CE .NET CE .NET 可安装ISR CE 3.0 ISR 架构的首选扩展 DLL 代码,在运行时进行绑定 源代码可以是独立于CPU的 一般用C编写 有一些限制 不能调用CRT, Windows API, 或其它的DLL 不可推断的ISR,或 IST发布 支持硬件共IRQ共享 从同一个IRQ关联的不同SYSINTR ID Alan Page, Microsoft
Session 2: Testing Windows CE .NET 驱动 IST SYSINTR_CHAIN IISR2 IISR IISR1 NKCallIntChain(IRQ) IISRn SYSINTR_xx SYSINTR_NOP 内核 无中断 异常处函数 ISR 更高优先级的中断 PSR 设置事件 OAL SYSINTR_ID 硬件 被屏蔽的IRQ Alan Page, Microsoft
Session 2: Testing Windows CE .NET 中断服务线程 是内置设备的驱动程序的作户态线程 进行实际的中断处理 调用CreateEvent 创建一个与逻辑中断相关联的事件对象 IST在大多数时间里都是处于空闲状态,以内核用信号通知事件对象时被唤醒 IST通常运行的优先级高于普通优先级,其优先级由 CeSetThreadPriority函数设定 Alan Page, Microsoft
Session 2: Testing Windows CE .NET 中断服务线程 InterruptInitialize 调用InterruptInitialize将事件与ISR的中断标识符关联起来 WaitForSingleObject 用来等待一个用信号通知的事件 这一调用通常放在一个循环中,以使和中断处理时,IST可以回到这一个调用等待下一个要处理的中断 InterruptDone 中断数据处理完以后,IST必须调用 InterruptDone函数使内核使能与这一个线程相关的中断 Alan Page, Microsoft
Session 2: Testing Windows CE .NET 典型的IST启动 struct ISTData // Declare the Strucure to pass to the IST { HANDLE hThread; // IST Handle DWORD sysIntr; // Logical ID HANDLE hEvent; // handle to the event to wait for interrupt volatile BOOL abort; // flag to test to exit the IST }; ISTData g_KeypadISTData; // Create event to link to IST g_KeypadISTData.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL); // Translate IRQ to an logical ID (x86 CEPC) g_KeypadISTData.sysIntr =Mapirq2Sysintr(5); // start the thread g_KeypadISTData.hThread = CreateThread(NULL,0,&KeypadIST, &g_KeypadISTData, 0, NULL); Alan Page, Microsoft
Session 2: Testing Windows CE .NET 典型的IST启动 //Change the threads priority CeSetThreadPriority(g_KeypadISTData.hThread,0); //disconnect any previous event from logical ID InterruptDisable(g_KeypadISTData.sysIntr); // Connect Logical ID with Event InterruptInitialize(g_KeypadISTData.sysIntr, g_KeypadISTData. hEvent,NULL,0); 设置IST的优先级 与以前的事件断开 与ISR关联 Alan Page, Microsoft
Session 2: Testing Windows CE .NET 典型的IST启动 DWORD KeypadIST(void *dat) { ISTData* pData= (ISTData*)dat; // loop until told to stop While(!pData->abort) { // wait for the interrupt event... WaitForSingleObject(pData->hEvent, INFINITE) if(pData->abort) break; // Handle the interrupt... // Let OS know the interrupt processing is done InterruptDone(pData->sysIntr); } Return 0; Alan Page, Microsoft
Session 2: Testing Windows CE .NET 典型的IST结束 // set abort flag to true to let thread know // that it should exit g_KeypadISTData.abort =TRUE; //disconnect event from logical ID //this internally sets g_KeypadISTData.sysIntr which in turn //sets g_KeypadISTData.hEvent through the kernel InterruptDisable(g_KeypadISTData.sysIntr); //wait for thread to exit WaitForSingleObject(g_KeypadISTData.hEvent,INFINITE); CloseHandle(g_KeypadISTData.hEvent); CloseHandle(g_KeypadISTData.hThread); 设置一个会导致IST退出的标志 Alan Page, Microsoft
Session 2: Testing Windows CE .NET 总线驱动概览 PCMCIA I82365 (ISA) TI-1250 (PCI) USB Host 多主控制器 OHCI 与 UHCI (PCI) Alan Page, Microsoft
Session 2: Testing Windows CE .NET PCMCIA Host PCMCIA总线驱动有三个主要的线程 状态改变线程 (卡的插入与移除) 中断线程(处理卡的功能性中断请求) 回调线程 (通知事件相关的设备卡) 总线驱动支持Card Serv消息的一个子集,从回调线程中用回调函数发送 中断以回调的形式发送到客户端驱动 对新插入的卡进行扫描以确定是否PnP元组,并与注册表中的相关项进行比较 如果没有发现相匹配的信息,则候选的驱动程序就运行与卡相关的“detect”例程对卡进行识别 可能显示出一个对话框询问DLL的名字 Alan Page, Microsoft
Session 2: Testing Windows CE .NET USB Host 兼容USB 1.1版 完全支持OHCI与UHCI 支持所有的传输类型 Isoch, bulk, interrupt, control USBD支持多个HC的复用 总线设备驱动典型地采用LoadDriver进行安装,使用USBD操作与HC进行通信 总线设备驱动能可选地调用ActivateDeviceEx()以提供一个流接口给应用程序 检测并识别新装设备 按照注册表中的描述安装驱动 可能显示一个对话框询问DLL的名字 Alan Page, Microsoft
Session 2: Testing Windows CE .NET Alan Page, Microsoft
Session 2: Testing Windows CE .NET 编程模型 USB 总线设备驱动 GWE 子系统 设备管理器 USBD DDI 单块驱动 (显示) MDD MDD MDD DDSI DDSI HCD PDD PDD PDD OEM硬件 Alan Page, Microsoft
主要内容 概述 流驱动接口 设备驱动程序架构 USB mouse 驱动开发示例 Windows CE 系统服务
USB 驱动传输类型 控制传输 同步传输 中断传输 批量传输 USB 打印机, USB数码相机 USB 网络电话 鼠标,键盘,游戏控制器
必须的入口点 USBDeviceAttach USBInstallDriver USBUnInstallDriver 当一个不明设备挂接到USB上时,USB驱动模块调用这个函数 USBUnInstallDriver 总线设备驱动能调用这个函数从USB驱动注消
USB鼠标模型 usbd.dll USB鼠标驱动 硬件 应用
演示: USB鼠标驱动代码
主要内容 概述 流驱动接口 设备驱动程序架构 USB mouse 驱动开发示例 Windows CE 系统服务
系统服务的概念与历史 在Windows CE 4.0以前,没有系统服务 所谓的 “设备驱动”被写成一个工作区
系统服务注册
系统服务体系架构 CE的系统服务是一个DLL,这个DLL的构建几乎与流设备驱动一模一样 与流设备驱动一样,Windows CE系统服务提供同样的10个入点口,从xxx_Init到 xxx_PowerDown 系统服务有三个前缀字符
系统服务IOCTL命令 IOCTL_SERVICE_START IOCTL_SERVICE_STOP IOCTL_SERVICE_REFRESH IOCTL_SERVICE_INSTALL IOCTL_SERVICE_STATUS IOCTL_SERVICE_SUPPORTED_OPTIONS
系统服务的应用控制 ActivateService RegisterService GetServiceHandle ServiceIoControl DeregisterService
作业 读Service.exe的在线帮助,自已实现关于 Windows CE 的系统服务。 应用程序发送I/O控制命令,然后服务例程将当前时间写到日志文件