在Driver_Entry()关联了一个处理IRP_MJ_DEVICE_CONTROL类型IRP的MajorFunction,xxxControl用于处理上层与驱动通信的数据。上层应用程序可以通过DeviceIoContro()与驱动程序进行通信。
pDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] =xxxControl;
xxxControl函数的框架可定义为:
NTSTATUS xxxControl(PDEVICE_OBJECT fdo, PIRP pIrp)
{
PAGED_CODE();
PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION) fdo->DeviceExtension;
…
//得到当前堆栈
PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(pIrp);
//得到输入缓冲区大小
ULONG cbin = stack->Parameters.DeviceIoControl.InputBufferLength;
//得到输出缓冲区大小
ULONG cbout = stack->Parameters.DeviceIoControl.OutputBufferLength;
//得到IOCTL码
ULONG code = stack->Parameters.DeviceIoControl.IoControlCode;
switch (code)
{
case IOCTL_MMAP_ADDR:
…
default:
}
return CompleteRequest(pIrp, status, info);
}
首先来说说下面这一行:
ULONG code = stack->Parameters.DeviceIoControl.IoControlCode;
这一行获取IOCTL码。IOCTL码是通过DeviceIoControl()函数的第二个参数传输进来的。
IOCTL是我们遵行一定的方式自定义的,通过CTL_CODE这个宏来定义。
CTL_CODE的定义如下:
#define CTL_CODE(DeviceType, Function, Method, Access) ( ((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method) )
可通过下图的直观描述来进一步了解这个宏:
举一个实例:
#define IOCTL_MMAP_ADDR \
CTL_CODE(FILE_DEVICE_UNKNOWN,0x806,METHOD_BUFFERED,FILE_ANY_ACCESS)
第一个参数一般可设置为FILE_DEVICE_UNKNOWN,第二个参数的值须在2048-4095之间,0 – 2047由微软保留。第三个参数的功能意义是定义用于与在驱动程序中获取应用程序数据缓冲区的地址方式,一般可以设定为METHOD_BUFFERED 或者 METHOD_IN_DIRECT、METHOD_OUT_DIRECT。最后一个参数可以直接使用FILE_ANY_ACCESS。
顺便就说说DeviceIoControl():
函数定义为:
BOOL DeviceIoControl
(
HANDLE hDevice,
DWORD dwIoControlCode,
LPVOID lpInBuffer,
DWORD nInBufferSize,
LPVOID lpOutBuffer,
DWORD nOutBufferSize,
LPDWORD lpBytesReturned,
LPOVERLAPPED lpOverlapped
);
参数的含义为:
hDevice :CreateFile返回的设备句柄
dwIoControlCode :IO_CTL code
lpInBuffer:应用程序传递给驱动程序的数据缓冲区地址
nInBufferSize:应用程序传递给驱动程序的数据缓冲区大小,字节数
lpOutBuffer:驱动程序返回给应用程序的数据缓冲区地址
nOutBufferSize:驱动程序返回给应用程序的数据缓冲区大小,字节数
lpBytesReturned:驱动程序实际返回给应用程序的数据字节数地址
lpOverlapped: 一般设为NULL
映射地址:
send=*(int *)pIrp->AssociatedIrp.SystemBuffer;
//操作输出缓冲区
switch(send)
{
case 2:
OutputBuffer1=(PVOID *)pIrp->AssociatedIrp.SystemBuffer;//使OutputBuffer1与
//SystemBuffer关联相同的地址
KdPrint(("\n Mmap address of send!\n"));
pdx->pMdlDesc=IoAllocateMdl(pdx->RxDescVirBase,DESC_ADDRESS*PORT_NUM,FALSE,FALSE,NULL);
if(!pdx->pMdlDesc)
{
KdPrint(("\n Allocate DESCMDL failed!"));
}
MmBuildMdlForNonPagedPool(pdx->pMdlDesc);
pdx->pMdlDesc->MdlFlags |= MDL_PAGES_LOCKED;
pdx->UserDescAddress=MmMapLockedPages(pdx->pMdlDesc,UserMode);
*(PVOID *)OutputBuffer1=(PVOID)pdx->UserDescAddress;
//注意这里是将映射出来的地址通过输出缓冲区传回上层去
KdPrint(("\n UserDescAddress is 0x%8x\n",pdx->UserDescAddress));
info=cbout;
break;
其中IoAllocateMdl()用来分配一个足够大的内存描述符列表来映射一块缓冲区地址。定义为:
PMDL IoAllocateMdl(
__in_opt PVOID VirtualAddress,
__in ULONG Length,
__in BOOLEAN SecondaryBuffer,
__in BOOLEAN ChargeQuota,
__inout_opt PIRP Irp
);
用法如上面,一般后三个参数设为FALSE。
之后需要调用MmBuildMdlForNonPagedPool()函数来处理刚刚分配的MDL,是这块MDL能够描述实际的物理地址。
最后调用MmMapLockedPages()完成映射,该函数的返回值就是映射的虚拟地址首址。
注意保存输出缓冲区的长度:info = cbout,用于设置IRP处理完成状态。