前文所提及的WDM驱动程序开发方法,笔者都曾尝试过。个人认为用DriverStudio开发工具包来开发USB驱动程序行之有效。其中的Driver Wizard是创建WDM驱动程序框架的一个很好的工具,后文将介绍用它来创建USB设备驱动程序的基本框架。 1、搭建开发平台 由于利用 DriverStudio 开发WDM驱动程序在搭建开发平台的过程中对软件的安装顺序要求颇高,在开发过程中我也曾因为安装顺序的颠倒而失败。在实践中总结了以下的安装步骤,有必要在此作以介绍。 ①在已装了Windows 2000 操作系统的机子上安装 Microsoft Visual C++6.0。 ②安装 Win2000 DDK 。③安装 NuMega DriverStudio 2.0 ( or 2.6 ) 驱动程序开发工具包。它包含DriverWorks(用于开发内核模式WDM驱动程序 )、SoftICE( 用于调试WDM驱动程序 )等开发工具。④由于DriverWorks 所用的类库是对 DDK 函数的封装,必须在 VC中编译,创建自己的库文件。⑤设置 DDK 路径。 2、利用DriverStudio 的DriverWorks生成USB设备驱动程序框架 驱动程序开发平台搭建成功后,我们可利用驱动程序生成向导Driver Wizard,根据硬件设置较为容易的生成USB设备驱动程序的大体框架。本人的设置如下:①选择WDM的驱动程序类型和Windows 2000运行平台。②选择USB总线类型,系统选择的USB芯片是Philip公司的ISP1581,填写它的VID(供应商ID)和PID(设备 ID),这些信息由芯片的供应商提供。③增加端点1和端点2,它们分别具有IN和OUT属性。④根据需要选择对设备的操作有:Read、Write、 Device Control和CleanUp。⑤选择给端点2产生BULK Read和Write的代码, 向导会自动产生一套对端点2进行读、写的代码。⑥设置驱动程序的属性,采用WDM接口;在选取读写方式时应遵循一条原则:需要快速传送大量数据时,用 Direct I/O ,反之用 Buffer I/O ,这里选择BufferI/O;由于无特殊的电源需求,故选用系统默认的Manage Power For This Device。⑧增加IOCTL接口,在其生成的代码框架中加入自己的操作,以实现一个完整的USB设备驱动程序。最后就生成了一个WDM型的USB设备驱动程序框架和一个测试该驱动程序的测试程序大体框架。然后在其中添加需要的功能代码。 3、USB设备驱动程序中的关键例程代码实现 下面以我们的驱动程序为例,介绍USB驱动程序开发中的几个关键例程的实现。本驱动程序的主要功能是控制USB设备上LED灯通断并且对设备进行读写。 1) 初始化例程 DriverEntry() 设备驱动程序与应用程序不同,它没有main()或WinMain()函数,而是有一个名为DriverEntry()的入口函数,它通常完成一些初始化工作。当设备驱动程序被加载时,操作系统调用这个入口。在使用DriverWizard 创建的驱动程序基本框架中,DriverEntry()函数已经写好了,无需添写代码。在该例程中,驱动程序要向操作系统登记并注册一些消息处理器,通过 RegistryPath来找到位于注册表中的驱动程序参数,当驱动程序正确安装后,在注册表KEY_LOCAL_MACHINE / SYSTEM / CurrentControlSet / Service 下可以找到MyUSB 项。而用DDK编写该入口函数还需初始化Dispatch(分派)例程入口。 2) 创建设备例程 AddDevice() 大多数的PDO 都是在 PnP 管理器调用该程序入口点时被创建的。插入新设备后,系统启动时,总线枚举器会发现总线上的所有设备,会自动寻找并安装设备的驱动程序,并由驱动程序中的处理 PnP 功能模块自动处理 AddDevice() 例程及其他PnP消息。此例程使用IoCreateDevice() 函数创建设备对象,再使用 IoRegisterDeviceInterface() 函数将设备组成为一个特定的设备接口,然后使用IoAttachDeviceToDeviceStack() 函数关联设备栈。 NTSTATUS MyUSBDevice::AddDevice( PDEVICE_OBJECT Pdo ) { // 产生一个DDK中KDevice类新的设备对象 MyUSBDevice *pDevice = new ( static cast<PCWSTR>( KUnitizedName(L“MyUSBDevice”,m_Unit) ),// 设备名 FILE_DEVICE_UNKNOWN, // 设备类型 NULL, // 指针链接名 0, // 设备特征标志位 DO_BUFFERED_IO| DO_POWER_PAGABLE); // I/O传输方式 MyUSBDevice(Pdo, m_Unit); if ( pDevice == NULL ) { return STATUS_INSUFFICIENT_RESOURCES; } NTSTATUS status = devices -> ConstructorStatus(); if ( !NT_SUCCESS(status) ) // 不成功,返回错误状态并删除指针 { delete pDevice; } else // 如果成功,向系统报考设备的电源状态变化为PowerDeviceD0 { m_Unit++; pDevice -> ReportNewDevicePowerState( PowerDeviceD0 ); } return status; } 3) LED控制处理例程 MyUSB_IOCTL_LED_Handler() 该例程是实现本驱动程序功能的关键例程,它是用来控制设备上的LED灯通断,主要利用USB Vendor Request来向设备传送。其中,request=1的时候表示让LED亮,request=0的时候让LED灭。它是通过DeviceControl 由上层应用程序传下来。实现代码如下: NTSTATUS MyUSBDevice::MyUSB_IOCTL_LED_Handler(KIrp I) { NTSTATUS status = STATUS_INVALID_PARAMETER; //检查输入参数是否正确,如果不正确,返回STATUS_INVALID_PARAMETER if(I.IoctlOutputBufferSize() || !I.IoctlBuffer() ||(I.IoctlInputBufferSize() != sizeof(UCHAR))) return status; //处理MyUSB_IOCTL_LED_ON请求 PURB pUrb = m_Lower.BuildVendorRequest(NULL, // 传输缓冲区 0, // 传输缓冲区大小 0, // 请求保留位 (UCHAR)(*(PUCHAR)I.IoctlBuffer()), // 请求1=LED_ON ,0=LED_OFF 0 ); // 值 //向下传送URB status = m_Lower.SubmitUrb(pUrb, NULL, NULL, 5000L); //若请求在此处理,设置I.Information指示多少数据拷贝回用户 I.Information()=0; I.Status()=status; return status; } 4) 访问硬件例程 DeviceControl() 上层应用软件程序就是通过此例程来将IRP传到下层。 NTSTATUS MyUSBDevice::DeviceControl(KIrp I) { NTSTATUS status; switch (I.IoctlCode()) { case MyUSB_IOCTL_LED: status = MyUSB_IOCTL_LED_Handler(I); break; default: // 未被声明的I/O 控制请求 status = STATUS_INVALID_PARAMETER; break; } } 限于篇幅,这里仅介绍本驱动程序中的部分例程实现代码。编写完驱动程序后,首先在Visual C++ 中编译通过,然后连接硬件,用DriverStudio 工具包中的SoftICE调试器调试该驱动程序,并且修改编译DriverStudio产生的该驱动程序的测试程序,就通过命令行来测试我们的驱动程序。最后对于LED的控制,我们可以直观的在设备上看到。 结束语 USB技术的不断发展和完善,已经使其逐渐成为先进总线接口技术的标志和方向,如今USB OTG标准已经发布,那么USB的应用领域也将越发的广泛。开发一些特定功能的USB接口并设计其设备驱动程序也将成为应用USB技术的关键。通过对 USB的学习和Windows 2000下的WDM驱动程序的研究,本文已经给出了编写WDM型USB设备驱动程序的一般方法,读者可以在实际应用中逐步提高对USB和驱动程序的认识,取得事半功倍的效果。
|