新聞中心

        EEPW首頁 > 嵌入式系統 > 設計應用 > WindowsNT4.0下設備驅動程序的開發與應用

        WindowsNT4.0下設備驅動程序的開發與應用

        作者: 時間:2006-05-07 來源:網絡 收藏

        摘要: 介紹了Windows NT4.0內核模式設備驅動程序開發中的一般性過程。通過提供一個最小化驅動程序的核心代碼,解釋各組成部分的結構功能和使用方法。在實踐中,結合自身的開發需要,可編寫出具有實用價值的驅動程序。

        本文引用地址:http://www.104case.com/article/241584.htm

        關鍵詞:Win32子系統 設備驅動 系統注冊表 I/O請求包

        Windows NT 以其安全、穩定及界面友好等特性逐漸成為工業控制領域的前臺操作系統。面對工業控制中大量采用的串/并行通信及總線控制等技術,要求用戶不斷開發出滿足自身需要的硬件設備,同時又要求用戶應用程序與這些硬件設備進行通信,發送控制命令,讀取狀態信息等等。Windows NT出于安全性、穩定性等考慮,不允許用戶應用程序對物理硬件進行直接訪問,這就需要使用設備驅動程序跨越操作系統邊界對物理硬件進行操作,并向上提供客戶應用程序控制接口以供調用。

        1 分層結構與設備驅動程序

        Windows NT分層結構(如圖1所示)包括運行于用戶模式及內核模式的各種部件,設備驅動程序在圖1的左下角,處于內核模式下I/O管理器之中。

        2 驅動程序工作方式

        內核模式驅動程序與應用程序之間的最大差別之一是驅動程序的控制結構。內核模式驅動程序沒有main或WinMain,而是由I/O管理器根據需要調用一個驅動程序例程:

        · 驅動程序被裝入時;

        · 驅動程序被卸出或系統關閉時;

        · 用戶程序發出I/O系統服務調用時;

        · 共享硬件資源對驅動程序可用時;

        · 設備操作過程中的任何時候。

        3 初始化過程

        3.1 系統注冊表中有關設備驅動程序的項目是系統加載設備驅動程序的入口點

        系統注冊表中用于系統加載設備驅動程序的項目如下:

        [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\DriverName]

        ″Type″ = dword00000001

        ″Start″ = dword00000002

        ″Group″ = ″Extended Base″

        ″ErrorControl″ = dword∶00000001

        其中Start含義如下:

        SERVICE_BOOT_START (0×0) 操作系統裝入時

        SERVICE_SYSTEM_START (0×01) 操作系統初始化時

        SERVICE_AUTO_START (0×02) 服務控制管理器啟動時

        SERVICE_DEMAND_START (0×03) 服務控制管理器手工啟動

        SERVICE_DISABLED (0×04) 不啟動

        Type含義如下:

        SERVICE_KERNEL_DRIVER (0×1)

        SERVICE_FILE_SYSTEM_DRIVER (0×2)

        SERVICE_ADAPTER (0×4)

        系統注冊表中用于設備驅動程序加載后讀取的項目如下:

        [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\DriverName\Parameters]

        ″Parameter1″ = dword∶00000001

        ″Parameter2″ = dword∶00000004

        3.2 加載驅動程序的裝入例程

        I/O管理器調用驅動程序的DriverEntry例程,執行初始化。該例程完成:

        · 初始化其它例程的入口;

        · 創建命名設備對象;

        · 讀取系統注冊表中相關項目并聲明必要的資源;

        · 設置內核驅動程序名與Win32子系統名的聯接;

        · 創建或初始化任意驅動程序使用的對象、類型和資源;

        · 返回狀態值。

        I/O管理器建立與設備關聯的Driver對象,并將其傳遞給DriverEntry例程。實際上Driver對象基本上是一個目錄,含有指向各個驅動程序服務例程函數的指針,其結構如表1所示。

        表1 Driver對象

        說明
        PDRIVER_STARTIO DriverStartIo 驅動程序的Start I/O例程的地址
        PDRIVER_UNLOAD DriverUnload 驅動程序的Unload例程地址
        PDRIVER_DISPATCH Majorfunction[ ] 驅動程序的Dispatch例程的表,由I/O操作代碼索引
        PDEVICE_OBJECT DeviceObject 驅動程序創建的Device對象鏈表

        I/O管理器能夠找到DriverEntry例程,是因為它有一個公認的名字,而其他的例程則通過下列兩種方法查找:

        ·在Driver對象中有明確槽的函數如DirverObject->DriverUnload;

        ·在Driver對象的MajorFunction數組中——Driver對象的MajorFunction支持兩種類型的功能代碼。一種為標準的功能代碼,如IRP_MJ_CREATE。另一種是用戶自定義的功能代碼,如IRP_MJ_DEVICE_CONTROL。

        所有驅動程序必須支持IRP_MJ_CREATE功能代碼,這是因為Win32子系統下的用戶程序調用CreateFile函數創建設備時,產生該功能代碼。如果不處理這個功能代碼,Win32程序就不能得到設備句柄。

        用戶自定義的功能代碼IRP_MJ_DEVICE_CONTROL只有在用戶模式下的客戶程序執行自定義的功能時可用。

        NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject,IN PUNICODE_STRING RegistryPath)



        //聲明設備對象

        PDEVICE_OBJECT DeviceObject,

        //生成函數接口指針

        DriverObject->MajorFunction[IRP_MJ_CREATE]=XxSelfDispatch;

        DriverObject->MajorFunction[IRP_MJ_CLOSE]=
        XxSelfDispatch;

        DriverObject->MajorFunction[IRP_MJ_READ]=
        XxReadDispatch;

        DriverObject->MajorFunction[IRP_MJ_WRITE]=
        XxWriteDispatch

        DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL]=XxSelfDispatch

        DriverObject->DriverUnload=XxUnload

        //生成Windows NT Executive知道的設備名

        RtlInitUnicodeString(&NtDeviceName, SelfDeviceName);

        //生成自己的設備

        Status=IoCreateDevice(

        DriverObject, // Driver對象

        sizeof(SELF_DEVICE_INFO), // Device對象

        Extension結構大小

        &NtDeviceName,

        DeviceType,

        0,

        FALSE, // 不執行

        &DeviceObject //Device對象指針

        );
         //生成Win32子系統下的用戶程序可識別的設備名

        RtlInitUnicodeString(&Win32DeviceName, SelfWin32Name);

        //聯接內部設備名與Win32子系統下的設備名

        Status = IoCreateSymbolicLink( &Win32DeviceName, &NtDeviceName);

        //利用RtlQueryRegistryValues函數讀出注冊表中Parameters下的參數值,初始化自己的硬件

        ...



        4 驅動程序服務例程

        驅動程序初始化之后,始終等待發自用戶的命令或由其它事件源引起的事件。一旦命令或事件發生,I/O管理器就調用相應的服務例程提供服務。而幾乎所有的I/O都是通過I/O請求包IRP驅動的。所謂IRP驅動,就是I/O管理器負責在非分頁的系統內存中分配一定空間,當接受用戶發出的命令或由事件引發后,將工作指令按一定的數據結構置于其中,傳遞到驅動程序服務例程。換言之,IRP包含了驅動程序服務例程所需要的信息指令。表2、表3為IRP的一些數據結構。

        同時,I/O管理器和驅動程序都需要在所有時候知道一個I/O設備所進行的情況。系統提供Device對象以滿足此要求。該對象在DriverEntry例程中生成設備時由系統創建后,分配給驅動程序,并在整個驅動程序生存期內有效。當I/O管理器調用驅動程序服務例程時,傳遞該對象。表4為Device對象的外部可見域。

        表2 IRP標頭中外部可見的域

          內 容 含 義
        標頭 I/O_STATUS_BLOCK IoStatus Stutus I/O請求的狀態
        Information
        AssociatedIrp.Systembuffer   執行緩沖I/O時,系統空間緩沖區指針
        PMDL MdlAddress   執行直接I/O時,用戶空間緩沖區的內存描述符列表的指針
        PVOID User Buffer   I/O緩沖區的用戶空間地址
        BOOLEAN Cancel   指示IRP已被取消

        表3 IRP堆棧單元的一些內容

          IO_STACK_LOCATION,*PIO_STACK_LOCATION
        I/O堆棧單元 UCHAR MajorFunction   指定操作的 IRP_MJ_XXX 函數
        UCHAR MinorFunction   有文件系統和 SCSI 驅動程序
        union Parameters   Majorfunction 代碼的聯合類型
        struct Read ULONG Length IRP_MJ_READ 的參數
        ULONG Key
        LARGE_INTEGER ByteOffset
        struct Write ULONG Length IRP_MJ_WRITE 的參數據
        ULONG Key
        LARGE_INTEGER ByteOffset
        struct DeviceIoControl ULONG OutputBufferLength IRP_MJ_DEVICE_CONTROL, IRP_MJ_INTERNAL_DEVICE _CONTROL 的參數
        OutputBuffer Length
        ULONG Iocontrolcode

        表4 Device 對象的外部可見域

        含 義
        PVOID DeviceExtension 指向 Device Extension 結構的指針
        PDRIVER_OBJECT DriverObject 指向這個設備 Driver 對象
        ULONG Flags 指定這個設備的緩沖策略 DO_BUFFER_IO DO_DIRECT_IO
        PDEVICE_OBJECT NextDevice 指向屬于這個驅動程序的下一個設備
        CCHAR StackSize 發送到這個設備的IRP需要的I/O堆棧單元的最小數目
        ULONG AlignmentRequirement 緩沖區要求的內存對齊

        其中,DeviceExtension域是一個重要的數據結構。它是由I/O管理器創建并自動掛接到Device對象的非分頁池,是保存驅動程序任意全局變量的最好辦法。因為DeviceExtension是驅動程序特定的,要自定義它的數據結構。

        下面是一個驅動程序服務例程利用Device對象和IRP的片段:

        NTSTATUS XxSelfDispatch(IN PDEVICE_OBJECT pDO IN PIRP pIrp);



        PLOCAL_DEVICE_INFO pLDI;

        PIO_STACK_LOCATION pIrpStack;

        PULONG pIOBuffer;

        //得到全局信息

        pLDI = (PSELF_DEVICE_INFO)pDO->DeviceExtension;

        pIrpStack = IoGetCurrentIrpStackLocation(pIrp);
        
        //得到由用戶應用程序發來的用戶數據,并在需要時,將結果通過此變量返回給用戶

        pIOBuffer=PULONGpIrp->AssociatedIrp.System

        Buffer;

        // 由IRP攜帶的信息決定驅動程序的執行

        switch (pIrpStack->MajorFunction)



        case IRP_MJ_CREATE:

        case IRP_MJ_CLOSE:

        Status = STATUS_SUCCESS;

        break;

        case IRP_MJ_DEVICE_CONTROL:

        //由Parameters進一步解釋控制代碼含義

        switch (pIrpStack->Parameters.DeviceIoControl.IoControlCode)



        case IOCTL_Function1:

        //執行功能代碼

        Field1 = pLDI->SelfField1;

        ...

        break;

        case IOCTL_Function2:

        //執行功能代碼

        ...

        break;

        }

        break



        // 返回I/O操作的狀態

        pIrp->IoStatus.Status = Status;

        IoCompleteRequest(pIrp IO_NO_INCREMENT); 

        return Status;



        5 驅動程序終止例程

        Unload例程負責取消由DriverEntry例程所做的任何事情,包括解除屬于該驅動程序的任何硬件資源的分配,以及刪除屬于驅動程序的任何內核對象。通常這僅在系統關閉時需要。

        VOID XxUnload(PDRIVER_OBJECT DriverObject)



        PLOCAL_DEVICE_INFO pLDI;

        UNICODE_STRING Win32DeviceName;

        // 得到全局數據,根據全局數據進行清理工作

        pLDI=PLOCAL_DEVICE_INFODriverObject->Device

        Object->DeviceExtension

        if (pLDI->Field2 == TRUE)


        ...

        // 刪除分配的設備名及設備

        RtlInitUnicodeString(&Win32DeviceName, SelfWin32 Name);

        IoDeleteSymbolicLink(&Win32DeviceName);

        IoDeleteDevice(pLDI->DeviceObject);



        6 用戶層應用程序與驅動程序間的接口

        驅動程序完成后,將在系統重新引導時裝入并初始化(由DriverEntry例程完成)。此時,驅動程序處于可用狀態,等待用戶層應用程序使用。用戶層應用程序可以:

        ·打開該設備文件(由IRP_MJ_CREATE功能代碼完成)

        ·讀出數據(由IRP_MJ_READ功能代碼完成)

        ·寫入數據(由IRP_MJ_WRITE功能代碼完成)

        ·執行用戶自定義的功能代碼(由IRP_MJ_DEVICE_CONTROL功能代碼完成)

        ·關閉該設備文件(由IRP_MJ_CLOSE功能代碼完成)

        以下是部分實現代碼:

        void main()



        HANDLE hndFile; // 由CreateFile得到

        union {

        ULONG LongData;

        USHORT ShortData;

        UCHAR CharData;

         }DataBuffer; //從設備驅動程序中得到的數據

        LONG IoctlCode; //功能代碼

        ULONG DataLength;

        LONG Parameter1;

        //調用IRP中的IRP _MJ_CREATE功能

        hndFile = CreateFile(

        ″\\\\.\\SelfWin32Name″, // 打開設備文件″ SelfWin32Name″

        GENERIC_READ | GENERIC_WRITE,

        FILE_SHARE_READ | FILE_SHARE_WRITE,

        NULL,

        OPEN_EXISTING,

        0,

        NULL

        if (hndFile == INVALID_HANDLE_VALUE)



        printf(″Unable to open the device.\n″);

        exit(1);

        }

        IoctlCode = IOCTL_Function1; //自定義功能代碼

        Parameter1 = 1;

        DataLength = sizeof(DataBuffer.CharData);

        IoctlResult = DeviceIoControl(

        hndFile //設備文件句柄

        IoctlCode//功能代碼,對應IRP中的Parameter.

        //DeviceIoControl.IoControlCode域

        &Parameter1,//傳遞到驅動程序的參數緩沖區,對應

        //IRP中的AssociatedIrp.SystemBuffer

        sizeof(Parameter1) //參數緩沖區長度

        &DataBuffer, //從驅動程序傳出的數據緩沖區

        DataLength, //緩沖區長度

        &ReturnedLength, //返回的實際緩沖區長度

        NULL //等待,直到操作完成

        );

        if(!CloseHandlehndFile)) //關閉設備



        printf(″Failed to close device.\n″);
        }


        以上介紹了Windows NT4.0設備驅動程序開發中的一般性過程。用戶可利用NT SDK 及DDK開發工具包,并根據自身需要,對以上核心代碼進行擴充完成所需任務。

        linux操作系統文章專題:linux操作系統詳解(linux不再難懂)


        評論


        相關推薦

        技術專區

        關閉
        主站蜘蛛池模板: 奉新县| 贺兰县| 尉犁县| 上蔡县| 兖州市| 侯马市| 宁晋县| 赣榆县| 兰考县| 天全县| 新竹市| 平罗县| 财经| 财经| 昌平区| 沙坪坝区| 昌吉市| 乌兰县| 德昌县| 武夷山市| 灵丘县| 同仁县| 山阳县| 邵阳市| 普陀区| 巴南区| 锡林郭勒盟| 承德县| 铜梁县| 吉木乃县| 赞皇县| 静安区| 武宣县| 博客| 巴里| 黄山市| 嘉善县| 江西省| 句容市| 怀远县| 东港市|