【文章标题】: 关于驱动设计中的几个常的例程:
【文章作者】: vbcs
【作者主页】: http://hi.baidu.com/vbcs003
【下载地址】: http://hi.baidu.com/vbcs003
【作者声明】: 只是感兴趣,没有其他目的。失误之处敬请诸位大侠赐教!
--------------------------------------------------------------------------------
【详细过程】
  关于驱动设计中的几个常的例程:
  DriverEntry例程
  一个DriverEntry例程初始化一个驱动程序,所有的驱动程序必须有一个DriverEntry例程.当装载驱动程序的时候,PNP管理器为每个驱动程序调用一次DriverEntry.在驱动程序初始化之后,PNP管理器能够调用驱动程序的AddDevice例程来初始化由该驱动程序控制的设备.
  DriverEntry例程示例如下:
  NTSTATUS
  DriverEntry(IN PDRIVER_OBJET DriverObject,IN PUNICODE_STRING RegistryPath)
  只要驱动程序标识了链接程序的初始化例程名,可以给初始化例程命名DriverEntry之外的某个名字.链接程序必须有初始化例程名字来链接驱动程序的传输地址到OS装载程序里.一个名为DriverEntry的驱动程序初始化例程会自动生成该链接.
  DriverEntry例程在一系统环境IRQL PASSIVE_LEVEL上被调用.
  一个DriverEntry例程能使用注册表来得到它所需要的初始化驱动程序的一些信息,DriverEntry例程必须在注册表里为另外的驱动程序和/或要用的被保护的子系统设置信息.
  一个DriverEntry例程是可分页的,且应该在INIT节以便它被丢弃。
  一个DriverEntry例程必须采取一下得步骤:
  1)  初始化驱动程序得调度表。
  一个驱动必须为驱动程序AddDevice例程指定入口点,调度例程,StartIo例程,Unload例程和其他得任何驱动程序得入口点。例如:一个驱动程序应该用像下面描述得样子,来为AddDevice,DispatchPnp和DispathPower例程设置入口:
  DriverObject->DriverExtension->AddDevice=xxxAddDevice;
  DriverObject->MajorFunction[IRP_MJ_PNP]=xxxDispatchPnp;
  2)  初始化任何其他的全局变量和数据结构。
  由于一个DriverEntry全程运行在一个系统线程环境的IRQL PASSIVE_LEVEL上,只要驱动程序没有控制存有系统分页文件的设备,任何由ExAllocatePool分配的且专在初始化过程上使用的内存能够从分页的池中得到。这样的内存分配必须在ExFreePool在DriverEntry返回控制之前释放。
  3)(可选择的)在注册表里使用Zwxx和Rtlxxx例程来读或设置独立于设备的值。
  4)保存输入的DriverEntry里的RegistryPath参数。
  这个参数指向一个被枚举的Unicode信息串,此Uncode信息串指定了驱动程序注册表键的一个路径:\Registry\Machine\System\CurentControlSet\Services\Drivername。该例程应该保存一个此信息的拷贝,而不是指针本身。在DriverEntry例程返回控制以后,指针不再有效
  5)返回状态
      如果DriverEntry例程返回得信息不是STATUS_SUCCESS,则驱动程序不须保留装载状态。然而,在初始化失效和返回控制之前,一个DriverEntry例程必须做下面得事情:
  1>  释放有他设置得任何系统资源。
  2>  记录错误。
  AddDevice例程
  在驱动程序的AddDevice例程里,驱动程序创建一个设备作为它的I/O设备的目标,并附着设备对象到设备堆栈,设备堆栈包括每一个相关设备驱动程序的一个设备对象。
  PNP管理器调用由驱动程序控制的每一个设备的一个驱动程序AddDevice例程。在设备被首次枚举时,AddDevice例程在系统初始化时被调用。当系统运行时,任何时候一个新设备被枚举,AddDevice例程被调用。
  一个驱动程序的AddDevice例程应该被命名为XxxAddDevice,这里X  xx是一个标识特定的驱动程序的前缀。在DricerEvtry过程中,一个驱动程序在 DriverObject'DriverExtension'AddDevice里存储了它的AssDevice例程地址。
  在一个系统线程环境的IRQL PASSIVE_LEVEL上调用一个  AddDevice例程。
  AddDevice例程由PnP管理器定义如下:
  NTSTATUS
  (*PDRIVER_ADD_DEVICE)(
  IN  PDRIVER_OBJECT  Driverobjict.
  IN  PDEVICE_OBJECT  PhysicalDeviceobject
  );
  DriverObject指向代表驱动程序对象,PhysicalDeviceObject指向被添加PnP设备的PDO。
  在功能或过滤器驱动程序里,一个例程应该有以下步骤:
  1)调用IoCreateDevice产生一个被添加的设备的设备对象(一个FDO)。
  不要指定FDO的一个设备名,命名一个FDO绕过了PnP管理器的安全管理。如果一个用户模式组件需要一个设备和符号链接,须注册一个设备接口(看下一步)。如果一个内核模式组件需要一个早期的设备名,驱动程序必须命名FDO,但并不推荐命名。
  在设备特性参数里包括,这个特性指示管理器来执行安全检查防止设备对象的所有打开请求,包括相对打开和尾部文件名打开。
  2)(可选择和)生成一个或多个与设备的符号链接。
  调用IoRegisterDeviceInterface来注册设备功能并生成一个符号链接,通过该链接应用程序式系统组件能够打开设备。当驱动程序处理IRP_MN_STRAT_DEVICE请求时,驱动程序应该激活接口.
  3)在设备扩展里存储设备PDO指针。
  PnP管理器给提供一个PDO指针来作为AddDevice的PhysicalOeviceObject参数,驱动程序使用FDO指针调用诸如IoGetDeviceProperty的例程。
  4)在设备扩展里定义标志来跟踪设备一定的PnP状态,比如暂停、删除和突然的删除。
  例如,定义一个标记说明当设备处于一暂停状态时,到来的IRP应该被保留。如果驱动程序还没有一个列队IRP的机制,将产生一个队列来保留IRP。要了解更多信息,请看本篇第二部分。
  也在设备扩展里分配一个IO_REMOVE_LOCK结构并调用IoInitializeRemoveLock来初始化该结构。
  5)如果需要,设置电源管理的DO_POWER_INRUSH或DO_POWER_PAGABLE标记,可分页的驱动程序必须设置DO_POWER_PAGABLE标记.当设备对象产生设备PDO时,设备对象标记典型得有总线驱动程序设置 。然后,当高层得驱动程序产生PDO时,高层得驱动程序有时需要改变它们AddDevice例程里的标记值。
  6)产生和/或初始化任何其他得软件资源,驱动程序使用这些软件资源来管理这个设备。如:事件,自旋锁或者其他得对象(硬件资源,比如:I/O端口,在稍后配置来响应一个IRP_MN_START_DEVICE请求)。
  由于一个AddDevice例程在一个系统线程环境得IRQL PASSIVE_LEVEL上运行,所以只要驱动程序没有控制保留有系统分页文件的设备,任何由ExAllocatePool分配,且专门在初始化过程中使用的内存从分页池得到。这样的内存分配必须由ExFreePoolAddDevice在返回控制之前释放。
  7)附着设备对象给设备堆栈。(IoAttachDeviceToDeviceStack)。
  在TargetDevice参数里给设备的PDO指定一个指针。
  存储由IoAttachDeviceToDeviceStack返回的指针,当向下传递到设备堆栈时,这个指针是IoCallDriver和PoCallDriver的一个必需的参数,该指针指向紧邻的低层的支持驱动程序的设备对象。
  8)用一个如下的状态,在FDO或者过虑程序DO里消除DO_DEVICE_INITIALIZING标记:
  FunctionalDeviceObject->Flags &=~DO_DEVICE_INITIALIZING;
  9)准备处理设备(比如:IRP_MN_QUERY_RESOURCE_REQUIREMENTS和IRP_MN_START_DEVICE)的PnP IRP.
  直到驱动程序接收到一个IRP_MN_START_DEVICE时,它保留由PnP管理器分配给设备的硬件资源列表,才可以控制设备。
  一个PnP总路线驱动程序有一个AddDevice例程,但是,当总线驱动程序用做它的控制器和适配器的功能驱动程序时,该PnP总线驱动程序才被调用。
  编写AddDevice例程的准则
  当编写一个AddDevice例程时,考虑下面的设计准则:
  1>  如果一个过滤器驱动程序决定它的AddDevice例程调用一个它无需服务得设备时,过滤器驱动程序必须返回STATUS_SUCCESS来允许剩余得设备堆栈为设备而被装入。过滤器驱动程序没有生产一个设备对象,也不附带它到设备堆栈;过滤驱动程序只是返回成功并允许其余得驱动程序添加到堆栈。
  2>  通常在一个设备对象得设备扩展里,对于它所使用得任何内核定义得对象和执行程序得自旋锁,一个驱动程序也必须提供特定对象得指针存储,这个对象从I/O管理器或其他的系统组件得到。如果你决定分配另外得存储空间内存来满足驱动程序得需要。如果是这样得话,一个AddDevice例程能够调用下面的例程:
  (1)  分页或无分页系统空间内存得ExAllocatePool
  (2)  ExInitializePagedLooksideList或ExInitializeNPagedLooksideList来初始化一个分页或无分页得辅助列表。
  (3)  如果驱动程序有一个专为设备服务得线程或在任何内核定义得调度程序对象上等待是得AddDevice例程必须初始化调度程序对象。该调度程序对象得线程或驱动程序调度适当得KeInitializeXXX支持例程,该支持例程有一个时间指针,信号量,互斥体和/或定时器等驱动程序提供存储得对象。由于它在一系统线程得环境里执行,一个AddDevice例程本身能够在一个调度程序对象上等待一个非零间隔,只要调度程序对象在等待开始之前已经初始化。
  3>  如果驱动程序使用任何执行程序得自旋锁或为一个中断自旋锁提供存储,则AddDevice例程必须在传递他的任何别的支持例程之前,用每个这样得自旋锁调用KeInitializeSpinLock.
  
  DispatchPnp例程
  PnP管理器使用IRP来指导驱动程序启动,停止和删除设备并查询驱动程序得设备,所有得PnP IRP得主要功能代码IRP_MJ_PNP。
  每个PnP驱动程序对处理特定得IRP是必要得。并能够有选择得处理其余得IRP。来获得关于每种驱动程序那个IRP是必要得,那个IRP是可选得。
  驱动程序应该在一个XXXDispatchPnp例程里处理PnP IRP 这里XXX是一个用来标识驱动程序得前缀。在驱动程序初始化它得DriverEntry例程过程中,驱动程序子DriverObject->MajorFunction[IRP_MJ_PNP]里设置他的DispatchPnp例程得地址。通过I/O管理器,PNP管理器调用一个驱动程序得DispatchPnp例程。
  支持一个设备得所有驱动程序必须有机会处理设备得Pnp IRP,除在极个别情况下。
  Unload例程
  一个PnP驱动程序必须有一个Unload例程来删除任何驱动程序专用得资源,如:内存。这些有DriverEntry例程产生,如果驱动程序没有专用资源要被删除。驱动程序必须仍有一个Unload例程,但是只是简单得返回。
  在所有得设备被删除之后,可在任何时候调用驱动程序的Unload例程,PnP管理器在系统线程环境IRQL PASSIVE_LEVEL上调用一个驱动程序得Unload例程。
  
--------------------------------------------------------------------------------
【经验总结】
  我也上一个驱动得新手。如有不对指出还请大家指出。
  欢迎大家一起交流,把自己得学习经验拿出来大家共享,学习!
  
--------------------------------------------------------------------------------
【版权声明】: 本文原创于看雪技术论坛, 转载请注明作者并保持文章的完整, 谢谢!

                                                       2008年01月07日 21:56:29