第二讲:我的第一个驱动
Posted 朝闻道
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了第二讲:我的第一个驱动相关的知识,希望对你有一定的参考价值。
原文:http://blog.csdn.net/caperingrabbit/article/details/5285288
配置好了开发环境之后,下面就要通过具体的程序来了解驱动的开发了。下面我们以一个WDM驱动的框架来实现Windows驱动程序的HelloWorld。
作为一个驱动程序,首先应该写的是它的入口函数,这点跟MFC的WinMain或者C++中的Main函数一样,驱动的入口函数使用DriverEntry。在入口函数中主要实现的功能是一些分发例程的注册以及其他的需要初始化的事务。
DriverEntry的原型是NTSTATUS DriverEntry(IN PDRIVER_OBJECT pDriverObject,IN PUNICODE_STRING pRegistryPath),IN、OUT这只是一个宏,用来表示参数的输入输出类型,并无实质意义,也不占空间。pDriverObject是我们所要创建的驱动对象,pRegistryPath是驱动程序在注册表中的地址。至于它们的类型,PDRIVER_OBJECT是驱动开发中一个重要的数据结构,这是驱动对象,具体的我们以后再讨论,而PUNICODE_STRING则是一个宽字符的指针,在内核开发中,一般使用的字符串都是宽字符,即Unicode string。 下面是一个范例:
- extern"C" //一般用C++进行Windows驱动开发,在入口函数前面要加这个关键词,主要是为了在驱动程序中能够调用C的函数
- NTSTATUS DriverEntry(IN PDRIVER_OBJECT pDriverObject,
- IN PUNICODE_STRING pRegistryPath
- )
- {
- KdPrint(("Hello!welcome to the driver entry!/n"));//KdPrint是一个宏,类似于MFC中的Trace
- //对于通用的例程,这里也使用一个通用的处理函数进行注册
- for(ULONG i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++)
- {
- pDriverObject->MajorFunction[i] = DispatchRoutine;
- }
- //驱动的功能例程的注册,这是几个比较特殊的例程,我们分别注册,
- //而驱动开发的主要工作就是完成他们的这些例程的实现函数,为了简单方便讲解,这里使用的和上边一样
- pDriverObject->DriverExtension->AddDevice = MyDeviceAdd;
- pDriverObject->DriverUnload = DriverUnload;
- pDriverObject->MajorFunction[IRP_MJ_CREATE] = DispatchRoutine;
- pDriverObject->MajorFunction[IRP_MJ_CLOSE] = DispatchRoutine;
- pDriverObject->MajorFunction[IRP_MJ_READ] = DispatchRoutine;
- pDriverObject->MajorFunction[IRP_MJ_WRITE] = DispatchRoutine;
- return STATUS_SUCCESS;
- }
完成了入口函数之后,接下来就要实现上面注册的分发例程的功能函数了。作为一个WDM驱动程序,首先需要创建设备对象,这个任务就交给AddDevice例程,通过上面的注册,实现部分放在MyDeviceAdd。
- NTSTATUS MyDeviceAdd(IN PDRIVER_OBJECT DriverObject,
- IN PDEVICE_OBJECT PhysicalDeviceObject)
- {
- PAGED_CODE();//DDK提供的一个宏
- KdPrint(("Enter MyDrivers/n"));
- NTSTATUS status;
- PDEVICE_OBJECT fdo;
- UNICODE_STRING devName;
- RtlInitUnicodeString(&devName,L"//Device//MyDevice");
- //创建设备对象
- status = IoCreateDevice(
- DriverObject,
- sizeof(DEVICE_EXTENSION),
- &(UNICODE_STRING)devName,
- FILE_DEVICE_UNKNOWN,
- 0,
- FALSE,
- &fdo);
- if( !NT_SUCCESS(status))
- return status;
- //获得设备扩展的数据结构。这个数据结构一般是由用户自己定义,保存一些设备相关的信息
- PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION)fdo->DeviceExtension;
- //配置设备扩展的内容
- pdx->fdo = fdo;
- //设备的挂载
- pdx->NextStackDevice = IoAttachDeviceToDeviceStack(fdo, PhysicalDeviceObject);
- UNICODE_STRING symLinkName;
- RtlInitUnicodeString(&symLinkName,L"//DosDevices//MyDriver");
- pdx->ustrDeviceName = devName;
- pdx->ustrSymLinkName = symLinkName;
- status = IoCreateSymbolicLink(&(UNICODE_STRING)symLinkName,&(UNICODE_STRING)devName);
- if( !NT_SUCCESS(status))
- {
- IoDeleteSymbolicLink(&pdx->ustrSymLinkName);
- status = IoCreateSymbolicLink(&symLinkName,&devName);
- if( !NT_SUCCESS(status))
- {
- return status;
- }
- }
- //设置设备的IO状态
- fdo->Flags |= DO_BUFFERED_IO | DO_POWER_PAGABLE;
- fdo->Flags &= ~DO_DEVICE_INITIALIZING;
- KdPrint(("LeaveMyDrivers/n"));
- return STATUS_SUCCESS;
- }
生成了设备对象之后,接下来就要处理发向设备的各种请求了。这里就用一个通用的处理例程来示范一下。其实这个处理函数只是将这些请求转交给真实的设备而已,没有实际的功能。
- NTSTATUS HelloWDMDispatchRoutine(IN PDEVICE_OBJECT fdo,
- IN PIRP Irp)
- {
- PAGED_CODE();
- KdPrint(("Enter HelloWDMDispatchRoutine/n"));
- Irp->iostatus.Status = STATUS_SUCCESS;
- Irp->IoStatus.Information = 0; //
- IoCompleteRequest( Irp, IO_NO_INCREMENT );
- KdPrint(("Leave HelloWDMDispatchRoutine/n"));
- return STATUS_SUCCESS;
- }
- 执行完所有的请求之后,在最后卸载驱动。对于WDM驱动,标准的程序其实卸载例程没什么实质内容,一般都是有Pnp里边的RemoveDevice来处理的,这里只是一个演示,演示一下这个例程的功能和内容。
- VOID DriverUnload(IN PDRIVER_OBJECT pDriverObject)
- {
- PDEVICE_OBJECT pNextObj;
- KdPrint(("Enter DriverUnload/n"));
- pNextObj = pDriverObject->DeviceObject;
- while (pNextObj != NULL)
- {
- PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION)
- pNextObj->DeviceExtension;
- //删除符号链接
- UNICODE_STRING pLinkName = pDevExt->ustrSymLinkName;
- IoDeleteSymbolicLink(&pLinkName);
- pNextObj = pNextObj->NextDevice;
- IoDeleteDevice( pDevExt->pDevice );
- }
- }
这样,一个驱动的主要部分就完成了。接下来完成一些细碎的工作,编译一下,一个驱动就出现了。
头文件
- #ifdef __cplusplus
- extern "C"
- {
- #endif
- #include <wdm.h>
- #ifdef __cplusplus
- }
- #endif
- typedef struct _DEVICE_EXTENSION
- {
- PDEVICE_OBJECT fdo;
- PDEVICE_OBJECT NextStackDevice;
- UNICODE_STRING ustrDeviceName; // 设备名
- UNICODE_STRING ustrSymLinkName; // 符号链接名
- } DEVICE_EXTENSION, *PDEVICE_EXTENSION;
- #define PAGEDCODE code_seg("PAGE")
- #define LOCKEDCODE code_seg()
- #define INITCODE code_seg("INIT")
- #define PAGEDDATA data_seg("PAGE")
- #define LOCKEDDATA data_seg()
- #define INITDATA data_seg("INIT")
- #define arraysize(p) (sizeof(p)/sizeof((p)[0]))
- NTSTATUS MyDeviceAdd(IN PDRIVER_OBJECT DriverObject,
- IN PDEVICE_OBJECT PhysicalDeviceObject);
- NTSTATUS DispatchRoutine(IN PDEVICE_OBJECT fdo,
- IN PIRP Irp);
- void DriverUnload(IN PDRIVER_OBJECT DriverObject);
- extern "C"
- NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject,
- IN PUNICODE_STRING RegistryPath);
新建一个文本文件,内容如下:
- #
- # DO NOT EDIT THIS FILE!!! Edit ./sources. If you want to add a new source
- # file to this component. This file merely indirects to the real make file
- # that is shared by all the driver components of the Windows NT DDK
- #
- !INCLUDE $(NTMAKEENV)/makefile.def
重命名文件,改为Makefile,后缀没有。然后重新建立一个文本文件,写入如下内容:
- TARGETNAME=HellOWORLD
- TARGETTYPE=DRIVER
- DRIVERTYPE=WDM
- TARGETPATH=OBJ
- INCLUDES=$(BASEDIR)/inc;/
- $(BASEDIR)/inc/ddk;/
- SOURCES=MyDrivers.cpp/
保存,更改文件名为Sources,后缀没有,这样进入DDK的编译环境,已编译,一个后缀为.sys的驱动文件就出现了。
好了,一个简单的驱动就诞生了。
http://blog.csdn.net/chence19871/article/details/50697439
以上是关于第二讲:我的第一个驱动的主要内容,如果未能解决你的问题,请参考以下文章