版权信息:

    原创作品版权都归smilestone322所有,保留所有权利,仅用来学习,请勿用于商业用途,欢迎转载,转载请注明出处。谢谢!

2 驱动基础知识

   68013带有自己的驱动的程序的,但是在这里为了讲解usb驱动程序的开发方法,我们还是自己的驱动程序吧,我自己的驱动程序比它自带的驱动程序效果更好,呵呵,传输速度更快,网上有网友说,Ezusb 驱动程序的缺点一大堆,下面讨论驱动程序的编写方法,在本文中,widows环境下我主要想讲解不同的工具的开发方法和技巧。

 

2.1 windows 驱动开发基础

    windowsPc开发驱动的方法主要有,windriverdriverstudio 3.2+ddkddkWDK4种,windriver开发驱动最简单,但它开发的驱动不符合微软的wdm模型的思想,在本文中,不对windriver进行讲解,主要讲解driverstudio 3.2(dw 3.2)+ddk,及ddk,和wdk进行驱动开发。

       Driverstudio 简单来说,就像apiMFC,它是对ddk的进一步的封装,采用driverstudiodriverworks能够很方便的生成程序框架。而且生成的程序满足wdm模型,因此它以它的易用性影响了一代人。下面首先讲解采用vc6.0+driverstudio 3.2 +ddk搭建驱动开发环境。

1.装系统XP->安装VC 6.0—>安装DDK->driver studio 3.2

2.如果安装的是DDK2600,则需要安装补丁,ntstrsafecsq,到网上下载文件ntstrsafe.lib+csq.lib.rar,把解压出来的两个库文件拷贝到WinXP_DDK的安装目录下的库目录中(我的是E:/WINDDK/2600/lib/wxp/i386)。

3.对driver studio,VC6.0ddk进行简单的设置;

  1)在VC6.0 菜单的DriverStudio菜单下的DDK Build Settings,在弹出的对话框中选择已经安装的DDK目录(我的是E:/WINDDK/2600);

   (2) VC6.0-->Tools-->Options,点击"Directories"选项卡:)“Show directories for:"下选择Include files,然后检查有没有包含ddk的头文件目录(我的是E:/WINDDK/2600/inc/wxp),如果没有则加上;Show directories for:"下选择Library files,然后检查有没有包含ddk的库文件目录(我的是E:/WINDDK/2600/lib/wxp/i386),如果没有则加上;

   (3)启动driverstudio,在develop下的DDK Building Settings中确保“DDK Root Directory”下方的内容是ddk的安装目录(我的是E:/WINDDK/2600),然后点击下方的"Luanch Program"正式启动vc6的开发环境。

   (4) VC6.0进入菜单File-->Open Workspace(打开位于DriverStudio3.2安装目录的/DriverWorks/Source/vdwlibs.dsw)-->进入菜单Build-->batch Build,点击“Select x86"按钮只选中全部的32位库(对于32位的电脑一定不要选中64位的库,否则后面编译会出错)-->点击按钮"Rebuild AlL”开始编译。

(5)编译一个DriverStudio自带的实例,启动VC6.0,点击菜单File-->Open Workspace,打开项目文件:

C:/Program Files/Compuware/DriverStudio/DriverWorks/Examples/wdm/

hellowdm/HelloWdm.dsw,然后编译,如果没有报错,那说明安装和配置成功。

  (6)应用driverstudioDriver wizard生成驱动程序框架:此后系统会一步一步引导你完成设置,最后自动生产的驱动程序框架。设置好后将生成驱动文件,然后用VC6.0进行编译:进行Build菜单,Rebuild AlL将生成.sys文件,说明驱动模块编译成功!

 

    讲解完dw3.2的环境搭建后,接着谈谈wdm的驱动到底有哪些内容,本文主要讲解的驱动都是功能驱动,过滤驱动本文不在进行讲解。无论是dw3.2还是ddkwdk开发驱动都包括以下内容:

1. pnp   2.电源管理,3.内存管理等。

    对于dw3.2+ddk或单独采用ddk开发驱动来说,还有派遣函数等内容,而对于wdk来说,就没有派遣函数了,都用队列来处理。

    本文主要针对usb驱动进行讲解,在dw3.2中,与usb驱动相关的几个类如下:

        1.KUsbLowerDevice :主要用于逻辑设备的编程;

     2.KusbInterface:主要用于接口设备的编程;

     3.KusbPipe:主要用于管道的编程;

 

       对于dw3.2+ddk怎么开发usb驱动程序,首先采用driverworksDriverWizard生成usb驱动框架,生成的流程在这里不进行讲解了,网上有10分钟开发一个usb驱动程序等资料。

      

对于DDKWDK的最大的不同是DDK采用wdm模型,而WDK采用的是WDF模型,WDM中的IrpWDFWDFREQUEST对象表示,WDFREQUEST是对Irp的封装,而wdm中的i/o对象在wdk中不再是一个设备对象,wdki/o target表示,i/o target 对象比设备对象(device_object)表示的范围更大,可以表示驱动,和队列对象等。

其次DDKWDK的不同就是在ddk中,用队列用的比较少,而在wdk中无处不见队列,而wdm的对象都是杂乱的并行队列,而wdf中队列分成了3种,串行队列,并行队列,手动队列。

DDKWDK的另外一个不同就是电源管理和Pnp了,WDK几乎不要用户在写电源管理和Pnp代码了。

呵呵,总结一句,WDKDDK的代码更加简洁,少了很多代码。这些不同,我会在后面的举例中,都可以看到。其中ddkusb讲解,我就采用ddk下面的例子,bulkusb进行讲解,而对于wdk usb讲解,我采用的是wdk下的例子,usbsamp

Window驱动的开发基础先介绍到这里了,对于最基本的DriverEntryAdddevice等,我还会在后面的例子中进行讲解。另外WDF又分成2种,第一种是KMDF,第二种是UMDFKMDF是内核的驱动,UMDF是用户层的驱动。

 

2.2 应用程序与驱动之间的通信

当应用程序通过USB管道发送或接收数据时,它首先调用Win32 API(ReadFileCreateFileWriteFileDeviceIoControl)调用使得功能驱动程序收到一个IRP。而驱动程序的工作是把客户的请求(应用软件)引导到正确端点的管道上。它把请求提交到总线驱动程序,总线驱动程序再把请求分解成多个事物(transaction),然后这些事务被送到总线。

应用程序可以调用ReadFileCreateFileWriteFileDeviceIoControlAPI通过IRP来和驱动程序进行通信。Windows中,应用程序实现与WDM通信的过程是:应用程序先用CreateFile函数打开设备,然后用DeviceIoControlWDM通信,包括从WDM中读数据和写数据给WDM。也可以使用ReadFileWDM中读数据或用WriteFile写数据给WDM,当应用程序退出时,用CloseHandle关闭设备。对应的IRP如下所示:

 

1 Win32 API 对应的IRP

Win32函数

IRP主功能代码(IRP_MJ_xxx)

KDevice类成员函数

CreateFile

CREATE

Create

ReadFile

READ

Read

WriteFile

WRITE

Write

DeviceIoControl

DEVICE_CONTROL

DeviceControl

CloseHandle

CLOSE CLENUP

Close CleanUp

 

23 驱动程序与底层硬件之间的通信

驱动程序(FDO)和硬件之间的通信主要是在功能驱动层,首先构造USB请求包,然后将USB请求包发送到底层驱动总线上,具体的流程如下:

(1)     当设备插入到PC时,驱动程序调用DriverEntry()例程,主要用于负责驱动程序的初始化,用于初始化驱动程序范围的数据结构和资源。DriverEntry()例程包括主要有以下3个功能。设置AddDeviceUnloadDispath和其它例程的入口指针;

(2)PC给硬件发送控制命令,即厂商请求。

对于dw3.2来说,发送厂商请求的函数为:BuildVendorRequest,然后构建URB将厂商请求发给硬件;发送Urb的命令为:SubmitUrb函数;

对于ddk来说,发送厂商请求的函数如下:

IoBuildDeviceIoControlRequest,它创建一个I/O控制码的Irp,然后将URB作为Irp的参数,用IoCallDriver发送到底层总线驱动,然后转发到硬件,对硬件设备进行控制;

对于wdk,发送厂商请求分为同步和异步2种方式:

 

(3)对设备进行读写;

dw3.2对设备进行读的函数如下:

要对usb设备进行进行读(read),首先必须发送控制请求,然后在将该请求以urb发给底层usb总线,然后获取硬件返回的数据。

发送请求有以下几种方式:

1)      对于KUsbLowerDevice类来说,发送厂商请求,函数如下:BuildVendorRequest

PURB BuildVendorRequest(

   PUCHAR TransferBuffer,               //为驱动程序存放传输数据的内存

   ULONG TransferBufferLength,   //传输的字节数

   UCHAR RequestTypeReservedBits,  //为类别请求字节的保留位

   UCHAR Request,                          //具体的请求数值

   USHORT Value,                            //为数值

   BOOLEAN bIn=FALSE,                //False表示主机到设备

   BOOLEAN bShortOk=FALSE,       //传输字节数是否可以少于指定的字节数

   PURB Link=NULL                        //指为连接下一个传输的URB

   UCHAR Index=0,                          //为索引值

   USHORT Function=URB_FUNCTION_VENDOR_DEVICE,  //类别请求

   PURB pUrb=NULL                     //NULL表示分配一个新的URB

);

 

然后将URB发送给底层USB总线,提交的函数原型如下:

NTSTATUS SubmitUrb(
   PURB pUrb,
              //创建的URB

   PIO_COMPLETION_ROUTINE CompletionRoutine=NULL,
 //完成例程
   PVOID CompletionContext=NULL,
           //传替给完成例程的参数
   ULONG mSecTimeOut=0
                        //同步调用的超时参数
);

 

dw 3.2下有个usbtherm的例子,里面读取数据的函数,如下:

NTSTATUS UsbThermometer::ReadRamAsynch( UCHAR offset, PIO_COMPLETION_ROUTINE pCompRoutine)

{

     t << "Entering UsbThermometer::ReadRamAsynch/n";

 

     KIrp I = KIrp::Allocate( m_Usb.StackRequirement() );

     if ( I.IsNull() )

         return STATUS_INSUFFICIENT_RESOURCES;

     // allocate a new context structure

     THERMO_READ_COMPLETION_INFO* pCompInfo = new (NonPagedPool) THERMO_READ_COMPLETION_INFO;

     // make sure it succeeded

     if ( pCompInfo == NULL )

     {

         KIrp::Deallocate(I);

         return STATUS_INSUFFICIENT_RESOURCES;

     }

     RtlZeroMemory(pCompInfo,sizeof(THERMO_READ_COMPLETION_INFO));

     // initialize the context structure

     pCompInfo->m_pClass = this;

     pCompInfo->m_OffsetRead = offset;

     // allocate and initialize an URB, and store the pointer in the context structure

     pCompInfo->m_pUrb =

              m_Usb.BuildVendorRequest(

                   pCompInfo->m_buffer,             // transfer buffer

                   8,                                   // transfer buffer size

                   0,                                   // request reserved bits

                  2,                                   // request

                   offset,                              // Value

                   TRUE,                                // In

                   FALSE,                               // Short Ok

                   NULL,                                // link urb

                   0,                                   // index

                   URB_FUNCTION_VENDOR_ENDPOINT     // function

                   );

     if ( pCompInfo->m_pUrb == NULL )

     {

         delete pCompInfo;

         KIrp::Deallocate(I);

         return STATUS_INSUFFICIENT_RESOURCES;

     }

     // submit the URB to USBD

     return m_Usb.SubmitUrb(I, pCompInfo->m_pUrb, pCompRoutine, pCompInfo);

}

 

 

对于KusbPipe类来说,构建urb又多了几个函数,其实都差不多,BuildControlTransferBuildInterruptTransferBuildBulkTransferBuildIsochronousTransfer,分别对应的控制传输,中断传输,块传输,同步传输。这些都在usb驱动开发的dw 3.2驱动开发中以例子的形式进行讲解,同时对于usb写,其实是一样的,都是这些函数,只是传输方向变了,改变一个参数就行。

 

ddk对设备进行读时的函数如下:UsbBuildInterruptOrBulkTransferRequest,然后调用IoCallDriverURB请求转发给底层总线驱动,在转发给硬件设备。

 

wdk对设备进行读时的函数如下(分为同步和异步2种方式)

 

1)对于usb设备:

同步控制命令(控制传输):

WdfUsbTargetDeviceSendControlTransferSynchronously

函数功能:builds a USB control transfer request and sends it synchronously to an I/O target.

NTSTATUS WdfUsbTargetDeviceSendControlTransferSynchronously(

  [in]           WDFUSBDEVICE UsbDevice,

  [in, optional]   WDFREQUEST Request,

  [in, optional]   PWDF_REQUEST_SEND_OPTIONS RequestOptions,

  [in]           PWDF_USB_CONTROL_SETUP_PACKET SetupPacket,

  [in, optional]   PWDF_MEMORY_DESCRIPTOR MemoryDescriptor,

  [out, optional]  PULONG BytesTransferred

);

 

 

举例如下:

 

WDF_USB_CONTROL_SETUP_PACKET  controlSetupPacket;

WDF_USB_CONTROL_SETUP_PACKET_INIT_VENDOR(

                                         &controlSetupPacket,

                                         BmRequestHostToDevice,

                                         BmRequestToDevice,

                                         USBFX2LK_REENUMERATE,

                                         0,

                                         0

                                         );

 

status = WdfUsbTargetDeviceSendControlTransferSynchronously(

                                         UsbDevice,

                                         WDF_NO_HANDLE,

                                         NULL,

                                         &controlSetupPacket,

                                         NULL,

                                         NULL

                                         );

 

异步操作(控制传输):

WdfUsbTargetDeviceFormatRequestForControlTransfer

NTSTATUS
  
  WdfUsbTargetDeviceFormatRequestForControlTransfer(
    IN WDFUSBDEVICE  UsbDevice,
    IN WDFREQUEST  
Request,
    IN PWDF_USB_CONTROL_SETUP_PACKET  
SetupPacket,
    IN OPTIONAL WDFMEMORY  
TransferMemory,
    IN OPTIONAL PWDFMEMORY_OFFSET  
TransferOffset
    );

该函数和同步控制传输函数的区别是只构建控制传输请求,但是不发送控制传输请求,而同步操作WdfUsbTargetDeviceSendControlTransferSynchronously构建完控制传输请求的同时,也发送给I/O target

 

2)对于usb管道:

同步写:

WdfUsbTargetPipeWriteSynchronously函数,函数原型如下:

The WdfUsbTargetPipeWriteSynchronously method builds a write request and sends it synchronously to a specified USB output pipe.

NTSTATUS
  WdfUsbTargetPipeWriteSynchronously(
    IN WDFUSBPIPE  
Pipe,
    IN OPTIONAL WDFREQUEST  
Request,
    IN OPTIONAL PWDF_REQUEST_SEND_OPTIONS  
RequestOptions,
    IN OPTIONAL PWDF_MEMORY_DESCRIPTOR  
MemoryDescriptor,
    OUT OPTIONAL PULONG  
BytesWritten
    );

异步写:

usb 写数据发送请求的函数为WdfUsbTargetPipeFormatRequestForWrite,然后将WdfRequestSend将请求发送出去。

NTSTATUS WdfUsbTargetPipeFormatRequestForWrite(
    IN WDFUSBPIPE  Pipe,
    IN WDFREQUEST  
Request,
    IN OPTIONAL WDFMEMORY  
WriteMemory,
    IN OPTIONAL PWDFMEMORY_OFFSET  
WriteOffset
    );

 

该函数和同步写函数WdfUsbTargetPipeWriteSynchronously的区别是异步写函数需要调用WdfRequestSend发送请求。WdfRequestSend函数原型如下:

BOOLEAN
  WdfRequestSend(
    IN WDFREQUEST  
Request,
    IN WDFIOTARGET  
Target,
    IN OPTIONAL PWDF_REQUEST_SEND_OPTIONS  
RequestOptions
    );

 

同步读:

WdfUsbTargetPipeReadSynchronously函数,函数原型如下:

NTSTATUS WdfUsbTargetPipeReadSynchronously(
    IN WDFUSBPIPE  Pipe,
    IN OPTIONAL WDFREQUEST  
Request,
    IN OPTIONAL PWDF_REQUEST_SEND_OPTIONS  
RequestOptions,
    IN OPTIONAL PWDF_MEMORY_DESCRIPTOR  
MemoryDescriptor,
    OUT OPTIONAL PULONG  
BytesRead
    );

 

异步读:

WdfUsbTargetPipeFormatRequestForRead函数原型如下:

NTSTATUS
  WdfUsbTargetPipeFormatRequestForRead(
    IN WDFUSBPIPE  
Pipe,
    IN WDFREQUEST  
Request,
    IN OPTIONAL WDFMEMORY  
ReadMemory,
    IN OPTIONAL PWDFMEMORY_OFFSET  
ReadOffset
    );

 

同步读和异步读的差别和同步写与异步写的区别类似。

  

 

 

Logo

更多推荐