Chinaunix首页 | 论坛 | 博客
  • 博客访问: 85366
  • 博文数量: 28
  • 博客积分: 1430
  • 博客等级: 上尉
  • 技术积分: 240
  • 用 户 组: 普通用户
  • 注册时间: 2006-06-30 23:34
文章分类

全部博文(28)

文章存档

2011年(2)

2006年(26)

我的朋友

分类: WINDOWS

2006-09-26 20:08:55

 
介绍


已经存在许多讨论像dialogs,MFC,ATL,thread,process,registry等程序设计的文章。但是却很难找到任何带有完整源代码的探讨驱动设计的文章。主要原因是大多数驱动是硬件相关的。如果没有领域知识,大家从来都不想接触他。我深信许多软件开发者都会为第一次被卷入内核模式模式程序开发而发愁,而且并没有多少能够把他们从对DDK学习的过程转变到实际应用中来的资源。因此,我决定跟大家分享我在Windows驱动开发中的一些经验。这是一个简单的集中于介绍WDM驱动架构和Windows中的DMABuffered两种I/O模式,以及如何跟深居内核空间中的驱动程序进行交互和与驱动程序进行数据读写交换的一篇文章。

在读文章时你不需要具有掌握任何的硬件背景,因为这是一个虚拟的驱动程序。它的安装并不需要计算机中任何实存的物理设备。程序中的成员函数你可以作为在你随后驱动开发中的模版函数,以节约你的开发时间。

背景

你可能是一个具有丰富经验的应用程序开发者,但是你想转行搞内核模式程序开发,比如
开发一个驱动程序。

 

创建你自己的WDM驱动程序: 一个假冒的驱动程序
开始之前,首先让我们声明所需要的函数和需要的数据结构。最重要的数据结构就是DEVICE_EXTENSION

typedef struct tagDEVICE_EXTENSION {

    PDEVICE_OBJECT DeviceObject;       // 由该程序创建的设备对象

    PDEVICE_OBJECT NextDeviceObject;   // 该设备栈中的下一个设备对象

    DEVICE_CAPABILITIES pdc;           // 设备能力

    IO_REMOVE_LOCK RemoveLock;         // removal control locking structure

    LONG handles;                      // # 打开句柄

    PVOID DataBuffer;                  //  Read/Write I/O内部句柄

    UNICODE_STRING Device_Description; // 设备描述

    SYSTEM_POWER_STATE SysPwrState;    // 当前系统状态

    DEVICE_POWER_STATE DevPwrState;    // 当前设备电源状态

    PIRP PowerIrp;                     // Current Handling Power-Related IRP

} DEVICE_EXTENSION, *PDEVICE_EXTENSION;
下面的代码片断示例创建一个有效WDM驱动的开始部分。

WDM驱动中有许多强制的和可选的成员:一个有效的WDM驱动程序应该具有DriverEntry函数,该函数最重要的任务就是把所有的成员函数注册到内核。
NTSTATUS

DriverEntry(

IN PDRIVER_OBJECT DriverObject,

IN PUNICODE_STRING RegistryPath

)

{

RtlInitUnicodeString(

&Global_sz_Drv_RegInfo,

RegistryPath->Buffer);

 

// Initialize function pointers

 

DriverObject->DriverUnload = DriverUnload;

DriverObject->DriverExtension->AddDevice = AddDevice;

 

DriverObject->MajorFunction[IRP_MJ_CREATE] = PsdoDispatchCreate;

DriverObject->MajorFunction[IRP_MJ_CLOSE] = PsdoDispatchClose;

DriverObject->MajorFunction[IRP_MJ_READ] = PsdoDispatchRead;

DriverObject->MajorFunction[IRP_MJ_WRITE] = PsdoDispatchWrite;

DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = PsdoDispatchDeviceControl;

DriverObject->MajorFunction[IRP_MJ_POWER] = PsdoDispatchPower;

DriverObject->MajorFunction[IRP_MJ_PNP] = PsdoDispatchPnP;

 

return STATUS_SUCCESS;

}

 
 
WDM驱动程序的正常操作流程
下面的代码段描述了AddDevice函数的工作流程:AddDevice函数的最重要的部分的任务就是创建设备对象并且把他和设备栈联系起来。
NTSTATUS

AddDevice(

IN PDRIVER_OBJECT DriverObject,

IN PDEVICE_OBJECT PhysicalDeviceObject

)

{

    ULONG DeviceExtensionSize;

    PDEVICE_EXTENSION p_DVCEXT;

    PDEVICE_OBJECT ptr_PDO;

    NTSTATUS status;

 

    RtlInitUnicodeString(

        &Global_sz_DeviceName, L"");

    //Get DEVICE_EXTENSION required memory space

    DeviceExtensionSize = sizeof(DEVICE_EXTENSION);

    //Create Device Object

    status = IoCreateDevice(

        DriverObject,

        DeviceExtensionSize,

        &Global_sz_DeviceName,

        FILE_DEVICE_UNKNOWN,

        FILE_DEVICE_SECURE_OPEN,

        FALSE,

        &ptr_PDO

        );

 

    if (NT_SUCCESS(status)) {

        ptr_PDO->Flags &= ~DO_DEVICE_INITIALIZING;

        ptr_PDO->Flags |= DO_BUFFERED_IO;  //For Buffered I/O

//ptr_PDO->Flags |= DO_DIRECT_IO;
//For Direct I/O

        p_DVCEXT = ptr_PDO->DeviceExtension;

        p_DVCEXT->DeviceObject = ptr_PDO;

        RtlInitUnicodeString(

 

        /*

        //Other initialization tasks go here

        */

 

        //Store next-layered device object

        //Attach device object to device stack

        p_DVCEXT->NextDeviceObject =

            IoAttachDeviceToDeviceStack(ptr_PDO, PhysicalDeviceObject);

    }

 

    return status;

}
下面的代码片断介绍如何支持IRP_MJ_CREATE当客户端应用程序开始与Pseudo驱动接头的时候就会发出该IRP。在继续进行前,让我们观图来快速了解接头过程。

通常我们使用Win32API CreateFile/fopen 与底层的驱动相联系。这也恰恰就是Win32 子系统发送IRP_MJ_CREATE并且请求驱动程序与目标设备联系的过程。
NTSTATUS

PsdoDispatchCreate(

IN PDEVICE_OBJECT DeviceObject,

IN PIRP Irp

)

{

    PIO_STACK_LOCATION p_IO_STK;

    PDEVICE_EXTENSION p_DVCEXT;

    NTSTATUS status;

 

    p_IO_STK = IoGetCurrentIrpStackLocation(Irp);

    p_DVCEXT = DeviceObject->DeviceExtension;

    status = IoAcquireRemoveLock(&p_DVCEXT->RemoveLock, p_IO_STK->FileObject);

    if (NT_SUCCESS(status)) {

        CompleteRequest(Irp, STATUS_SUCCESS, 0);

        return STATUS_SUCCESS;

    } else {

        IoReleaseRemoveLock(&p_DVCEXT->RemoveLock, p_IO_STK->FileObject);

        CompleteRequest(Irp, status, 0);

        return status;

    }

}
下面代码片断介绍了如何支持IRP_MJ_CLOSE,当客户程序关闭根底层驱动程序之间的连接时就会有该IRP发生。让我们观图来快速了解关闭连接过程。

通常你需要使用Win32API CloseHandle/fclose 来关闭与底层驱动程序的联系。这也就是Win32子系统发出该IRP并且请求驱动关闭与目标设备连接的时刻。
NTSTATUS
PsdoDispatchClose(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp)
{
    PIO_STACK_LOCATION p_IO_STK;
    PDEVICE_EXTENSION p_DVCEXT;
    p_IO_STK = IoGetCurrentIrpStackLocation(Irp);
    p_DVCEXT = DeviceObject->DeviceExtension;
    IoReleaseRemoveLock(&p_DVCEXT->RemoveLock,
            p_IO_STK->FileObject);

    CompleteRequest(Irp, STATUS_SUCCESS, 0);

    return STATUS_SUCCESS;

}
I/O
支持 : 缓冲区I/O
Windows内核有三种数据传输模式:缓冲区I/ODMA、两者都不是. 接下来让我们讨论一下缓冲区I/O, 这里我们将不探讨非两者I/O if processing under user-thread occupied memory space, it might be dangerous!! 在缓冲区I/O中如果应用程序读写驱动程序中的数据,底层驱动的缓冲区地址并不是上层应用程序的相应缓冲区的虚拟地址. 系统将会在内核中重新分配一块同样大小的数据缓冲区. 所有的数据在传输到目标地址以前必须被拷贝到这个内核数据缓冲区. 一般来说, 你会调用 ReadFile/WriteFile 或者 fread/fwrite 来请求数据读写.

下面代码片说明了在I/O数据处理中的读操作. 正如我们所看到的, 我们注册的读函数是 DriverEntry 中的PsdoDispatchRead, 这个函数将会从驱动程序中的变量 DataBuffer 中读出数据到应用程序中:
NTSTATUS
PsdoDispatchRead(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp)
{
    PVOID Buf; //Buffer provided by user program
    ULONG BufLen; //Buffer length for user provided buffer
    LONGLONG Offset;//Buffer Offset
    PVOID DataBuf; //Buffer provided by Driver
    ULONG DataLen; //Buffer length for Driver Data Buffer
    ULONG ByteTransferred;
    PIO_STACK_LOCATION p_IO_STK;
    PDEVICE_EXTENSION p_DVCEXT;

    DbgPrint("IRP_MJ_READ : Begin\r\n");
    //Get I/o Stack Location & Device Extension

    p_IO_STK = IoGetCurrentIrpStackLocation(Irp);
    p_DVCEXT = DeviceObject->DeviceExtension;

    //Get User Output Buffer & Length
    BufLen = p_IO_STK->Parameters.Read.Length;
    Offset = p_IO_STK->Parameters.Read.ByteOffset.QuadPart;
    Buf = (PUCHAR)(Irp->AssociatedIrp.SystemBuffer) + Offset;

    //Get Driver Data Buffer & Length
    DataBuf = p_DVCEXT->DataBuffer;

    if (DataBuf == NULL)
        DataLen = 0;
    else
        DataLen = 1024;

    IoAcquireRemoveLock(&p_DVCEXT->RemoveLock, Irp);

    DbgPrint("Output Buffer Length : %d\r\n", BufLen);
    DbgPrint("Driver Data Length : %d\r\n", DataLen);

    //
    if (BufLen <= DataLen) {
        ByteTransferred = BufLen;
    } else {
        ByteTransferred = DataLen;
    }

    RtlCopyMemory(
        Buf, DataBuf,
        ByteTransferred);

    IoReleaseRemoveLock(&p_DVCEXT->RemoveLock, Irp);
    CompleteRequest(Irp, STATUS_SUCCESS, ByteTransferred);

    DbgPrint("IRP_MJ_READ : End\r\n");
    return STATUS_SUCCESS;
}
相反下边的代码片断说明了工作流程图中的支持正常从应用程序到驱动的写请求的可能任务项目。
NTSTATUS
PsdoDispatchWrite(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp)
{
    PVOID Buf; //Buffer provided by user program
    ULONG BufLen; //Buffer length for user provided buffer
    LONGLONG Offset;//Buffer Offset
    PVOID DataBuf; //Buffer provided by Driver
    ULONG DataLen; //Buffer length for Driver Data Buffer
    ULONG ByteTransferred;
    PIO_STACK_LOCATION p_IO_STK;
    PDEVICE_EXTENSION p_DVCEXT;
    NTSTATUS status;

    DbgPrint("IRP_MJ_WRITE : Begin\r\n");
    //Get I/o Stack Location & Device Extension
    p_IO_STK = IoGetCurrentIrpStackLocation(Irp);
    p_DVCEXT = DeviceObject->DeviceExtension;

    //Get User Input Buffer & Length
    BufLen = p_IO_STK->Parameters.Write.Length;
    Offset = p_IO_STK->Parameters.Read.ByteOffset.QuadPart;
    Buf = (PUCHAR)(Irp->AssociatedIrp.SystemBuffer) + Offset;

    //Get Driver Data Buffer & Length
    DataBuf = p_DVCEXT->DataBuffer;
    DataLen = 1024;
    IoAcquireRemoveLock(&p_DVCEXT->RemoveLock, Irp);
    DbgPrint("Input Buffer Length : %d\r\n", BufLen);
    DbgPrint("Driver Data Length : %d\r\n", DataLen);

    if (BufLen <= DataLen) {
        ByteTransferred = BufLen;
    } else {
        ByteTransferred = DataLen;
    }
    ByteTransferred = BufLen;

RtlZeroMemory(
    p_DVCEXT->DataBuffer,
    1024);

RtlCopyMemory(
    DataBuf,
    Buf,
    ByteTransferred);
  IoReleaseRemoveLock(&p_DVCEXT->RemoveLock, Irp);
  CompleteRequest(Irp, STATUS_SUCCESS, ByteTransferred);
 
DbgPrint("IRP_MJ_WRITE : End\r\n");
  return STATUS_SUCCESS;
}
I/O
支持 : DMA
下面代码片断揭示了应用程序和驱动在DMA模式下是如何交互的. DMA模式中, 内存管理将创建MDL(内存描述表) 来引用用户区的缓冲区地址, 内核通过MDL可以引用所有的用户缓冲区数据。

DDK, 一些MMXxx 函数支持你得到映射到用户物理地址区的缓冲区的MDL.

下面代码片断陈述了在DMA模式下用户从内核缓冲区读数据过程. 这是通过一系列的 Mmxxx 函数来实现的, 请仔细的读, 并且你也可以在压缩包里边找到该代码. 你所需要的最重要的 MmXxx 函数是 - MmGetSystemAddressForMdlSafe, 它可以返回引用用户缓冲区物理地址的MDL.
NTSTATUS
PsdoDispatchRead(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp)
{
    PVOID Buf; //Buffer provided by user program
    ULONG BufLen; //Buffer length for user provided buffer
    ULONG Offset;//Buffer Offset
    PVOID DataBuf; //Buffer provided by Driver
    ULONG DataLen; //Buffer length for Driver Data Buffer
    ULONG ByteTransferred;
    PIO_STACK_LOCATION p_IO_STK;
    PDEVICE_EXTENSION p_DVCEXT;
    DbgPrint("IRP_MJ_READ : Begin\r\n");
    //Get I/o Stack Location & Device Extension
    p_IO_STK = IoGetCurrentIrpStackLocation(Irp);

    p_DVCEXT = DeviceObject->DeviceExtension;
    //Get User Output Buffer & Length
    Buf = MmGetSystemAddressForMdlSafe(
        Irp->MdlAddress, HighPagePriority);

    if (Buf == NULL) {
        DbgPrint("Can't get Virtual Address from MDL\r\n");
        return STATUS_INSUFFICIENT_RESOURCES;
    }
    BufLen = MmGetMdlByteCount(Irp->MdlAddress);
    Offset = MmGetMdlByteOffset(Irp->MdlAddress);
    //Get Driver Data Buffer & Length
    DataBuf = p_DVCEXT->DataBuffer;
    if (DataBuf == NULL)
        DataLen = 0;
    else
        DataLen = 1024;

    IoAcquireRemoveLock(&p_DVCEXT->RemoveLock, Irp);
    DbgPrint("Output Buffer Length : %d\r\n", BufLen);
    DbgPrint("Offset for Buffer in the Memory Page: %d\r\n", Offset);
    DbgPrint("Driver Data Length : %d\r\n", DataLen);
    //
    if (BufLen <= DataLen) {
        ByteTransferred = BufLen;
    } else {
        ByteTransferred = DataLen;
    }

    RtlCopyMemory(Buf,  DataBuf, ByteTransferred);
    IoReleaseRemoveLock(&p_DVCEXT->RemoveLock, Irp);
    CompleteRequest(Irp, STATUS_SUCCESS, ByteTransferred);

DbgPrint("IRP_MJ_READ : End\r\n");
    return STATUS_SUCCESS;
}
下面代码展示了相反的操作:
NTSTATUS
PsdoDispatchWrite(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp)
{
    PVOID Buf; //Buffer provided by user program
    ULONG BufLen; //Buffer length for user provided buffer
    ULONG Offset;//Buffer Offset
    PVOID DataBuf; //Buffer provided by Driver
    ULONG DataLen; //Buffer length for Driver Data Buffer
    ULONG ByteTransferred;
    PIO_STACK_LOCATION p_IO_STK;
    PDEVICE_EXTENSION p_DVCEXT;
    NTSTATUS status;

DbgPrint("IRP_MJ_WRITE : Begin\r\n");
    //Get I/o Stack Location & Device Extension
    p_IO_STK = IoGetCurrentIrpStackLocation(Irp);
    p_DVCEXT = DeviceObject->DeviceExtension;
    //Get User Input Buffer & Length
    Buf = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, HighPagePriority);

    if (Buf == NULL) {
        DbgPrint("Can't get Virtual Address from MDL\r\n");
        return STATUS_INSUFFICIENT_RESOURCES;
    }
    BufLen = MmGetMdlByteCount(Irp->MdlAddress);
    Offset = MmGetMdlByteOffset(Irp->MdlAddress);

   //Get Driver Data Buffer & Length
   DataBuf = p_DVCEXT->DataBuffer;
   DataLen = 1024;
   IoAcquireRemoveLock(&p_DVCEXT->RemoveLock, Irp);
    DbgPrint("Input Buffer Length : %d\r\n", BufLen);
    DbgPrint("Offset for Buffer in the Memory Page: %d\r\n", Offset);
    DbgPrint("Driver Data Length : %d\r\n", DataLen);
    if (BufLen <= DataLen) {
        ByteTransferred = BufLen;
    } else {
        ByteTransferred = DataLen;
    }
    ByteTransferred = BufLen;
    RtlZeroMemory(p_DVCEXT->DataBuffer, 1024);
    RtlCopyMemory(DataBuf, Buf, ByteTransferred);
    IoReleaseRemoveLock(&p_DVCEXT->RemoveLock, Irp);
    CompleteRequest(Irp, STATUS_SUCCESS, ByteTransferred);
    DbgPrint("IRP_MJ_WRITE : End\r\n");
    return STATUS_SUCCESS;
}

阅读(1719) | 评论(2) | 转发(0) |
给主人留下些什么吧!~~

chinaunix网友2008-03-28 13:24:50

就是翻译了一下,这个谁不会啊,而且翻译得不好! 原文链接http://www.codeproject.com/KB/system/WDM_Driver_development.aspx

chinaunix网友2008-02-19 18:03:44

这个是codeproject上的文章,copy别人的文章应该表明一下出处吧