windows WDF 驱动开发总结(6)----PCI驱动开发

来源:互联网 发布:希特勒我的奋斗 知乎 编辑:程序博客网 时间:2024/06/04 21:02

(2.19)MmAllocateContiguousMemory

函数功能:TheMmAllocateContiguousMemory routine allocates a range of physically contiguous, nonpaged memory and maps it to the system address space. (分配连续的物理内存,并映射成连续的虚拟内存).

PVOID MmAllocateContiguousMemory(

  __in  SIZE_T NumberOfBytes,

  __in  PHYSICAL_ADDRESS HighestAcceptableAddress

);

Parameters

NumberOfBytes [in]

Specifies the size, in bytes, of the block of contiguous memory to allocate.

HighestAcceptableAddress [in]

Specifies the highest valid physical address the driver can use. For example, for a device that can access only the lower 16 megabytes of physical memory, set this value to 0x00000000FFFFFF.

Return Value

returns the base virtual address for the allocated memory. If the request cannot be satisfied, NULL is returned

 

2.20MmGetPhysicalAddress

函数功能:TheMmGetPhysicalAddress routine returns the physical address corresponding to a valid nonpaged virtual address.

PHYSICAL_ADDRESS MmGetPhysicalAddress(

  __in  PVOID BaseAddress

);

Parameters

BaseAddress [in]

Pointer to the virtual address for which to return the physical address.

Return Value

MmGetPhysicalAddress returns the physical address that corresponds to the given virtual address.

Do not use this routine to obtain physical addresses for use with DMA operations. For information about the proper techniques for performing DMA operations, see Adapter Objects and DMA.

Remarks

Callers of MmGetPhysicalAddress can be running at any IRQL, provided that the BaseAddress value is valid.

 

(2.21) KeInitializeEvent

函数功能:内核函数KeInitializeEvent负责对事件对象初始化,其申明如下:

VOID KeInitializeEvent(

  __out  PRKEVENT Event,

  __in   EVENT_TYPE Type,

  __in   BOOLEAN State

);

Parameters

Event [out]

Pointer to an event object, for which the caller provides the storage.

初始化事件对象的指针

Type [in]

Specifies the event type, either NotificationEvent or SynchronizationEvent.

这个参数是事件的类型。事件的类型分为2类,一类是通知事件,对应参数是NotificationEvent.另一类是“同步事件”,对应参数是SynchronizationEvent

State [in]

Specifies the initial state of the event. TRUE indicates a signaled state.

如果为真,事件对象的初始化状态为激发状态,否则为未激发状态。

 

如果是通知事件,当事件变为激发态时,程序员需要手动将它变为未激发态,如果是同步事件,当事件处于激发态时,遇到KeWaitForXX等内核函数,事件对象则自动变为未激发态。

 

(2.22)KeSetEvent

函数功能:将事件设为有信号状态

LONG KeSetEvent(

  __inout  PRKEVENT Event,

  __in     KPRIORITY Increment,

  __in     BOOLEAN Wait

);

参数:

Event [in, out]

A pointer to an initialized event object for which the caller provides the storage.

Increment [in]

Specifies the priority increment to be applied if setting the event causes a wait to be satisfied.

Wait [in]

Specifies whether the call toKeSetEvent is to be followed immediately by a call to one of theKeWaitXxx routines. If TRUE, theKeSetEvent tcall must be followed by a call to KeWaitForMultipleObjects, KeWaitForMutexObject, or KeWaitForSingleObject. For more information, see the following Remarks section.

 

(2.23)ObReferenceObjectByHandle

函数功能:TheObReferenceObjectByHandle routine provides access validation on the object handle, and, if access can be granted, returns the corresponding pointer to the object's body.

(将句柄转化为指针)

NTSTATUS ObReferenceObjectByHandle(
  __in       HANDLE Handle,
  __in       ACCESS_MASK DesiredAccess,
  __in_opt   POBJECT_TYPE ObjectType,
  __in       KPROCESSOR_MODE AccessMode,
  __out      PVOID *Object,
  __out_opt  POBJECT_HANDLE_INFORMATION HandleInformation
);

Parameters

Handle [in]

Specifies an open handle for an object.

DesiredAccess [in]

Specifies the requested types of access to the object. The interpretation of this field is dependent on the object type. Do not use any generic access rights.

ObjectType [in, optional]

Pointer to the object type.ObjectType can be*ExEventObjectType,*ExSemaphoreObjectType,*IoFileObjectType, *PsProcessType, *PsThreadType,*SeTokenObjectType,*TmEnlistmentObjectType, *TmResourceManagerObjectType, *TmTransactionManagerObjectType, or *TmTransactionObjectType.

Note   TheSeTokenObjectType object type is supported in Windows XP and later versions of Windows.

IfObjectType is not NULL, the operating system verifies that the supplied object type matches the object type of the object thatHandle specifies.

AccessMode [in]

Specifies the access mode to use for the access check. It must be either UserMode or KernelMode. Lower-level drivers should specify KernelMode.

Object [out]

Pointer to a variable that receives a pointer to the object's body. The following table contains the pointer types.

 

(2.24)IoCreateNotificationEvent(通知事件)IoCreateSynchronizationEvent(同步事件)

函数功能:creates or opens a named notification event used to notify one or more threads of execution that an event has occurred.(创建通知事件对象)

PKEVENT IoCreateNotificationEvent(

  __in   PUNICODE_STRING EventName,

  __out  PHANDLE EventHandle

);

Parameters

EventName [in]

Pointer to a buffer containing a null-terminated Unicode string that names the event.

EventHandle [out]

Pointer to a location in which to return a handle for the event object. In Windows Server 2003 and later versions of Windows, the returned handle is a kernel handle.

Return Value

t returns a pointer to the created or opened event object or NULL if the event object could not be created or opened.

 

 

(2.25)内核模式下的信号灯(KeInitializeSemaphore)

函数功能:

初始化信号量,和事件一样,信号量在用户模式和内核模式是统一的,只不过操作方式不同。在用户模式下,信号量用句柄代表,在内核模式下,信号量对象用KSEMAPHORE数据结构表示。

VOID KeInitializeSemaphore(

  __out  PRKSEMAPHORE Semaphore,

  __in   LONG Count,

  __in   LONG Limit

);

 Parameters

Semaphore [out]

Pointer to a dispatcher object of type semaphore, for which the caller provides the storage.

这个参数获得内核信号量对象的指针。

Count [in]

Specifies the initial count value to be assigned to the semaphore. This value must be positive. A nonzero value sets the initial state of the semaphore to signaled.

初始化信号量的个数

Limit [in]

Specifies the maximum count value that the semaphore can attain. This value must be positive. It determines how many waiting threads become eligible for execution when the semaphore is set to the signaled state and can therefore access the resource that the semaphore protects.

指明信号量计数的上限值。

      释放信号量会增加信号量的计数,它对应的内核函数是KeReleaseSemaphore。程序员可以用这个函数指定增量值。获得信号量可以使用KeWaitXX系列函数,如果能获得,就将计数减1,否则陷入等待。

 

(2.26)PsCreateSystemThread

函数功能:创建一个系统线程,返回线程句柄

NTSTATUS PsCreateSystemThread(

  __out      PHANDLE ThreadHandle,

  __in       ULONG DesiredAccess,

  __in_opt   POBJECT_ATTRIBUTES ObjectAttributes,

  __in_opt   HANDLE ProcessHandle,

  __out_opt  PCLIENT_ID ClientId,

  __in       PKSTART_ROUTINE StartRoutine,

  __in_opt   PVOID StartContext

);

Parameters

ThreadHandle [out]

Points to a variable that will receive the handle. The driver must close the handle withZwClose once the handle is no longer in use. This handle is a kernel handle for Windows Vista and later versions of Windows. In earlier versions of Windows, the handle might not be a kernel handle.

DesiredAccess [in]

Specifies theACCESS_MASK value that represents the requested types of access to the created thread.

ObjectAttributes [in, optional]

Points to a structure that specifies the object's attributes. OBJ_PERMANENT, OBJ_EXCLUSIVE, and OBJ_OPENIF are not valid attributes for a thread object. On Windows XP and later versions of Windows, if the caller is not running in the system process context, it must set the OBJ_KERNEL_HANDLE attribute forObjectAttributes. Drivers for Microsoft Windows 2000 and Windows 98/Me must only call PsCreateSystemThread from the system process context. For Windows Vista and later versions of Windows, the handle will be a kernel handle.

ProcessHandle [in, optional]

Specifies an open handle for the process in whose address space the thread is to be run. The caller's thread must have PROCESS_CREATE_THREAD access to this process. If this parameter is not supplied, the thread will be created in the initial system process. This value should be NULL for a driver-created thread. Use theNtCurrentProcess macro, defined inNtddk.h, to specify the current process.

ClientId [out, optional]

Points to a structure that receives the client identifier of the new thread. This value should be NULL for a driver-created thread.

StartRoutine [in]

Is the entry point for a driver thread.

StartContext [in, optional]

Supplies a single argument that is passed to the thread when it begins execution.

 

 

(2.27)KeWaitForSingleObject

函数功能:等待一个同步事件对象,比如KEventKsemaphore KMutex(puts the current thread into a wait state until the given dispatcher object is set to a signaled state or (optionally) until the wait times out.)

NTSTATUS KeWaitForSingleObject(

  __in      PVOID Object,

  __in      KWAIT_REASON WaitReason,

  __in      KPROCESSOR_MODE WaitMode,

  __in      BOOLEAN Alertable,

  __in_opt  PLARGE_INTEGER Timeout

);

 

Parameters

Object [in]

Pointer to an initialized dispatcher object (event, mutex, semaphore, thread, or timer) for which the caller supplies the storage.

WaitReason [in]

Specifies the reason for the wait. A driver should set this value to Executive, unless it is doing work on behalf of a user and is running in the context of a user thread, in which case it should set this value to UserRequest.

WaitMode [in]

Specifies whether the caller waits in KernelMode or UserMode. Lowest-level and intermediate drivers should specify KernelMode. If the givenObject is a mutex, the caller must specify KernelMode.

Alertable [in]

Specifies a Boolean value that is TRUE if the wait is alertable and FALSE otherwise.

Timeout [in, optional]

Pointer to a time-out value that specifies the absolute or relative time, in 100-nanosecond units, at which the wait is to be completed.

A positive value specifies an absolute time, relative to January 1, 1601. A negative value specifies an interval relative to the current time. Absolute expiration times track any changes in the system time; relative expiration times are not affected by system time changes.

If *Timeout = 0, the routine returns without waiting. If the caller supplies a NULL pointer, the routine waits indefinitely until the dispatcher object is set to the signaled state. For more information, see the following Remarks section.

 

Remarks

The current state of the specifiedObject is examined to determine whether the wait can be satisfied immediately. If so, the necessary side effects are performed on the object. Otherwise, the current thread is put in a waiting state and a new thread is selected for execution on the current processor.

 

(2.24)内核模式下的互斥体(KeInitializeMutex)

函数功能:互斥体在内核中的数据结构式KMUTEX,使用前需要初始化互斥体对象。可以采用KeInitializeMutex内核函数初始化互斥体对象。

VOID KeInitializeMutex(

  __out  PRKMUTEX Mutex,

  __in   ULONG Level

);

 

Parameters

Mutex [out]

Pointer to a mutex object, for which the caller provides the storage.

获取内核互斥体对象的指针

Level [in]

Reserved. Drivers set this to zero.

初始化后的互斥体对象,就可以使线程之间互斥了。获得互斥体对象用KeWaitXX系列函数,释放互斥体用KeReleaseMutex内核函数。

 

 

(2.25)KewaitForMultipleObjects

函数功能:等待对个内核对象(puts the current thread into an alertable or nonalertable wait state until any or all of a number of dispatcher objects are set to a signaled state or (optionally) until the wait times out.)

NTSTATUS  KeWaitForMultipleObjects(

  __in       ULONG Count,

  __in       PVOID Object[],

  __in       WAIT_TYPE WaitType,

  __in       KWAIT_REASON WaitReason,

  __in       KPROCESSOR_MODE WaitMode,

  __in       BOOLEAN Alertable,

  __in_opt   PLARGE_INTEGER Timeout,

  __out_opt  PKWAIT_BLOCK WaitBlockArray

);

 

Parameters

Count [in]

Specifies the number of objects to be waited on.

Object [in]

Pointer to an array of pointers to dispatcher objects (events, mutexes, semaphores, threads, and timers) for which the caller supplies the storage.

WaitType [in]

Specifies either WaitAll, indicating that all of the specified objects must attain a signaled state before the wait is satisfied; or WaitAny, indicating that any one of the objects must attain a signaled state before the wait is satisfied.

WaitReason [in]

Specifies the reason for the wait. Drivers should set this value to Executive or, if the driver is doing work on behalf of a user and is running in the context of a user thread, to UserRequest.

WaitMode [in]

Specifies whether the caller waits in KernelMode or UserMode. Intermediate and lowest-level drivers should specify KernelMode. If the set of objects waited on includes a mutex, the caller must specify KernelMode.

Alertable [in]

Specifies a Boolean value that indicates whether the thread can be alerted while it is in the waiting state.

Timeout [in, optional]

Pointer to a time-out value that specifies the absolute or relative time, in 100-nanosecond units, at which the wait is to be completed.

A positive value specifies an absolute time, relative to January 1, 1601. A negative value specifies an interval relative to the current time. Absolute expiration times track any changes in the system time; relative expiration times are not affected by system time changes.

If *Timeout = 0, the routine returns without waiting. If the caller supplies a NULL pointer,

the routine waits indefinitely until any or all of the dispatcher objects are set to the signaled state. For more information, see the following Remarks section.

WaitBlockArray [out, optional]

If Count <= THREAD_WAIT_OBJECTS, thenWaitBlockArray can be NULL. Otherwise this parameter must point to a memory buffer of sizeof(KWAIT_BLOCK) * Count bytes. The routine uses this buffer for record-keeping while performing the wait operation.

 

 

(2.26)快速互斥体(Fast Mutex)

快速互斥体(Fast Mutex)DDK提供的另外一种内核同步对象,它的特性类似前面介绍的普通互斥体对象,快速互斥体和普通互斥体的作用完全一样,之所以被称为快速互斥体,是因为执行的速度比普通互斥体速度快(这里指获取和释放的速度)。然后快速互斥体比普通互斥体多了一个缺点,就是不能递归地获取互斥体对象。递归获取指的是,已经获得互斥体的线程,可以再次获得这个互斥体。换句话说,互斥体只互斥其它线程,而不互斥自己所在的线程。但是快速互斥体则不容许出现递归的情况。

快速互斥体的内核是用FAST_MUTEX数据结构描述的,初始化快速互斥体的内核函数是ExInitializeFastMutex,获取快速互斥体的内核函数是ExAcquireFastMutex,释放快速互斥体的内核函数是ExReleaseFastMutex

 

(2.27)ExInitializeFastMutex

TheEExInitializeFastMutex I routine initializes a fast mutex variable, used to synchronize mutually exclusive access by a set of threads to a shared resource.

VOID ExInitializeFastMutex(

  __out  PFAST_MUTEX FastMutex

);

Parameters

FastMutex [out]

A pointer to a caller-allocatedFAST_MUTEX structure, which represents the fast mutex, in the nonpaged memory pool.

Remarks

ExInitializeFastMutex must be called before any calls to other ExXxxFastMutex routines occur.

 

2.28)自旋锁

如果程序得到了自旋锁,其它程序希望获取自旋锁时,则不停地进入自旋状态。获得自旋锁的内核函数是KeAcquireSpinLock.直到自旋锁被释放后,另外的程序才能获得自旋锁。释放自旋锁的内核函数是KeReleaseSpinLock。如果希望同步某段代码区域,需要在这段代码区域前获得自旋锁,在代码区域后释放自旋锁。在单CPU的系统中,获取自旋锁是通过提升IRQL实现的。

无法获得自旋锁的线程会不断的自旋,这会浪费很多CPU的时间,因此需要同步的代码区域不能过长,换句话说就是占用自旋锁的时间不能过长。

 

(2.29) KeAcquireSpinLock

函数功能:TheKeAcquireSpinLock routine acquires a spin lock so the caller can synchronize access to shared data in a multiprocessor-safe way by raising IRQL.

VOID KeAcquireSpinLock(

  __in   PKSPIN_LOCK SpinLock,

  __out  PKIRQL OldIrql

);

Parameters

SpinLock [in]

Pointer to an initialized spin lock for which the caller provides the storage.

OldIrql [out]

Pointer to a variable that is set to the current IRQL when this call occurs.

Remarks

KeAcquireSpinLock first resets the IRQL to DISPATCH_LEVEL and then acquires the lock. The previous IRQL is written toOldIrqlafter the lock is acquired.

 

(2.29) KeReleaseSpinLock

函数功能:释放锁

VOID KeReleaseSpinLock(

  __inout  PKSPIN_LOCK SpinLock,

  __in     KIRQL NewIrql

);

Parameters

SpinLock [in, out]

Pointer to a spin lock for which the caller provides the storage.

NewIrql [in]

Specifies the IRQL value saved from the preceding call toKeAcquireSpinLock.

 

 

(2.30)StartIo

StartIo例程能够保证各个并行的Irp顺序执行,即串行化

当一个新的Irp请求来临时,首先检查设备是否处于忙状态,设备初始化为空闲状态。当设备处在空闲状态时,可以处理一个Irp请求,并改变当前的状态为忙状态。如果设备处于忙状态,则将新来的IRP插入队列,并立即返回,Irp留在以后处理。当设备状态由忙转入空闲时,则从队列中取出一个Irp进行处理,并重新将状态变为忙。这样,周而复始的将Irp串行处理了。

StartIo例程

typedef _KDEVICE_QUEUE

  {

CSHORT type;

CSHORT size;

LIST_ENTRY DeviceListHead;

KSPIN_LOCK Lock;

BOOLEAN Busy;

}KDEVICE_QUEUE

这个队列的DeviceListHead保存在设备对象的DeviceObject->DeviceQueue中,每一个设备对象用这个来管理自己收到的IRP包。在使用这个队列的时候,需要向系统提供一个叫做StartIo的例程,并将这个例程的函数名传送给系统。StartIO例程运行在DISPATCH_LEVEL级别,因此这个例程是不会被线程所打断的,StartIo例程的参数和派遣函数类似,只是没有返回值。注意StartIo在执行DisPATCH_level级别上,因此在申明时要加上#pragma LOCKEDCODE修饰符

 

(2.31)IoStartPacket

函数功能: calls the driver's StartIo routine with the given IRP or inserts the IRP into the device queue associated with the given device object if the device is already busy.

VOID IoStartPacket(

  __in      PDEVICE_OBJECT DeviceObject,

  __in      PIRP Irp,

  __in_opt  PULONG Key,

  __in_opt  PDRIVER_CANCEL CancelFunction

);

Parameters

DeviceObject [in]

Pointer to the target device object for the IRP.

Irp [in]

Pointer to the IRP to be processed.

Key [in, optional]

Pointer to a value that determines where to insert the packet into the device queue. If this is zero, the packet is inserted at the tail of the device queue.

CancelFunction [in, optional]

Specifies the entry point for a driver-suppliedCancel routine.

 

Remarks

If the driver is already busy processing a request for the target device object, then the packet is queued in the device queue. Otherwise, this routine calls the driver's StartIo routine with the specified IRP.

 

(2.32)IoStartNextPacket

函数功能:TheIoStartNextPacket routine dequeues the next IRP, if any, from the given device object's associated device queue and calls the driver'sStartIo routine.

VOID IoStartNextPacket(

  __in  PDEVICE_OBJECT DeviceObject,

  __in  BOOLEAN Cancelable

);

 

Parameters

DeviceObject [in]

Pointer to the device object for which the IRP is to be dequeued.

Cancelable [in]

Specifies whether IRPs in the device queue can be canceled.

 

系统的StartIo例程,将所有的IRP都存放在一个队列中,不管是读的还是写的,这样显得有些混乱。如果你想把读操作和写操作分开来的话,你可以自己创建并维护一个队列,只不过这种情况下你需要自己维护这个队列,自己的写出IoStartPacket等一些函数,这些貌似在WDF中已经解决,WDF把读写队列分开。

 

 

(2.34)中断服务例程

当硬件设备的中断信号发生后, IRQL会提升至相应的DIRQL级别,操作系统会调用相应的中断服务例程.如何在驱动程序中写中断处理程序呢?当硬件启动的时候,会有一个IRP_MN_START_DEVICE包发送给驱动程序,在这个包中包含中断信息,驱动程序调用IoConnectInterrupt()函数将该中断注册,这样windows内核就可以收到这个中断,并调用驱动中的对应的中断处理函数.

每一个硬件中断都具有一个DIRQL,所有的IRQL都比软件的IRQL.自旋锁的dispatch_level也要在DIRQL这下,所以在有硬件中断参与的情况下,自旋锁已经起不到同步的作用了.StartIO,派遣函数随时都可能被中断处理程序打断.为了防止这种事情发生, windows提供了KeSynchronizeExecution()函数来提升程序的IRQL级别。

 

BOOLEAN KeSynchronizeExecution(

  __inout   PKINTERRUPT Interrupt,

  __in      PKSYNCHRONIZE_ROUTINE SynchronizeRoutine,

  __in_opt  PVOID SynchronizeContext

);

Interrupt:这个参数是中断对象指针,ISR和这个对象关联

SynchronizeRoutine:将当前的IRQL提升到中断对象相应的DIRQL,并执行SynchronizeRoutine函数。

SynchronizeContext:为SynchronizeRoutine提供参数。

当运行到KeSynchronizeExecution的时候,ISR不会打断KeSynchronizeExecution提供的同步函数,这需要将同步代码放入KeSynchronizeExecution

 

(2.34)DPC例程

 为什么需要DPC?因为DIRQL的中断优先级特别高,一旦它执行起来了,其他的软件级别的代码根本得不到机会运行,一些级别低的硬件中断也得不到机会运行,比如鼠标和键盘可能都得不到响应.这种状态不好.所以我们WINDOWS提供给我们一种方法来解决这个问题,即将非常重要的代码放在ISR里面执行,不是特别重要的代码转到DPC里面去执行, DPC是运行在DISPATCH_LEVEL级别的,这样的话,至少键盘和鼠标可以有机会响应了.

 要使用DPC例程,首先要初始化DPC对象, KeInitializeDPC()负责这个初始化工作.DriverEntry()AddDevice()进行这个初始化操作。

最后在中断处理程序里面调用DPC例程:

 BOOL Interrupt(interrupt , context) 

   {

 

                                IoRequestDPC(device, device->currentIRP, NULL);

                                ...

    }