• 32、即插即用


    即插即用 

    1、即插即用(IRP_MJ_PNP)功能能够通过操作系统协调自动分配设备上的资源,如中断号,I/O地址,DMA资源,设备物理内存等。 

    WDM框架程序是分层驱动,WDM处于分层的高端,而总线驱动程序处于分层的低端。大部分即插即用IRP都会被WDM驱动发送到底层的驱动程序处理。

     wps_clip_image-2403

    图示 P35 2 

    IRP_MJ_PNP类似于Win32中的消息,而IRP_MJ_PNP的派遣函数类似于Win32编程中的窗口函数。

    所有的即插即用的事件,都会引发即插即用管理器向WDM驱动程序发送一个IRP_MJ_PNPIRP_MJ_PNP是这个IRP的主功能代码,不同的即插即用事件会有不同的子功能代码。

    图示 MSDNEnd-User I/O Requests and File Objects

    http://msdn.microsoft.com/en-us/library/ff544248%28VS.85%29.aspx#

    Handling IRPs

    http://msdn.microsoft.com/en-us/library/ff546847%28VS.85%29.aspx

    具体的子功能号讲解见MSDN

    http://msdn.microsoft.com/en-us/library/ff558807%28VS.85%29.aspx

    代码
    1 /************************************************************************
    2 * 函数名称:HelloWDMAddDevice
    3 * 功能描述:添加新设备
    4 * 参数列表:
    5 DriverObject:从I/O管理器中传进来的驱动对象
    6 PhysicalDeviceObject:从I/O管理器中传进来的物理设备对象
    7 * 返回 值:返回添加新设备状态
    8 *************************************************************************/
    9  #pragma PAGEDCODE
    10 NTSTATUS HelloWDMAddDevice(IN PDRIVER_OBJECT DriverObject,
    11 IN PDEVICE_OBJECT PhysicalDeviceObject)
    12 {
    13 PAGED_CODE();
    14 KdPrint(("Enter HelloWDMAddDevice\n"));
    15
    16 NTSTATUS status;
    17 PDEVICE_OBJECT fdo;
    18 status = IoCreateDevice(
    19 DriverObject,
    20 sizeof(DEVICE_EXTENSION),
    21 NULL,//没有指定设备名
    22   FILE_DEVICE_UNKNOWN,
    23 0,
    24 FALSE,
    25 &fdo);
    26 if( !NT_SUCCESS(status))
    27 return status;
    28 PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION)fdo->DeviceExtension;
    29 pdx->fdo = fdo;
    30 pdx->NextStackDevice = IoAttachDeviceToDeviceStack(fdo, PhysicalDeviceObject);
    31
    32 //创建设备接口
    33   status = IoRegisterDeviceInterface(PhysicalDeviceObject, &MY_WDM_DEVICE, NULL, &pdx->interfaceName);
    34 if( !NT_SUCCESS(status))
    35 {
    36 IoDeleteDevice(fdo);
    37 return status;
    38 }
    39 KdPrint(("%wZ\n",&pdx->interfaceName));
    40 IoSetDeviceInterfaceState(&pdx->interfaceName, TRUE);
    41
    42 if( !NT_SUCCESS(status))
    43 {
    44 if( !NT_SUCCESS(status))
    45 {
    46 return status;
    47 }
    48 }
    49
    50 fdo->Flags |= DO_BUFFERED_IO | DO_POWER_PAGABLE;
    51 fdo->Flags &= ~DO_DEVICE_INITIALIZING;
    52
    53 KdPrint(("Leave HelloWDMAddDevice\n"));
    54 return STATUS_SUCCESS;
    55 }
    56
    57  /************************************************************************
    58 * 函数名称:HelloWDMPnp
    59 * 功能描述:对即插即用IRP进行处理
    60 * 参数列表:
    61 fdo:功能设备对象
    62 Irp:从IO请求包
    63 * 返回 值:返回状态
    64 *************************************************************************/
    65 #pragma PAGEDCODE
    66 NTSTATUS HelloWDMPnp(IN PDEVICE_OBJECT fdo,
    67 IN PIRP Irp)
    68 {
    69 PAGED_CODE();
    70
    71 KdPrint(("Enter HelloWDMPnp\n"));
    72 NTSTATUS status = STATUS_SUCCESS;
    73 PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION) fdo->DeviceExtension;
    74 PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(Irp);
    75 static NTSTATUS (*fcntab[])(PDEVICE_EXTENSION pdx, PIRP Irp) =
    76 {
    77 HandleStartDevice, // IRP_MN_START_DEVICE
    78 DefaultPnpHandler, // IRP_MN_QUERY_REMOVE_DEVICE
    79 HandleRemoveDevice, // IRP_MN_REMOVE_DEVICE
    80 DefaultPnpHandler, // IRP_MN_CANCEL_REMOVE_DEVICE
    81 DefaultPnpHandler, // IRP_MN_STOP_DEVICE
    82 DefaultPnpHandler, // IRP_MN_QUERY_STOP_DEVICE
    83 DefaultPnpHandler, // IRP_MN_CANCEL_STOP_DEVICE
    84 DefaultPnpHandler, // IRP_MN_QUERY_DEVICE_RELATIONS
    85 DefaultPnpHandler, // IRP_MN_QUERY_INTERFACE
    86 DefaultPnpHandler, // IRP_MN_QUERY_CAPABILITIES
    87 DefaultPnpHandler, // IRP_MN_QUERY_RESOURCES
    88 DefaultPnpHandler, // IRP_MN_QUERY_RESOURCE_REQUIREMENTS
    89 DefaultPnpHandler, // IRP_MN_QUERY_DEVICE_TEXT
    90 DefaultPnpHandler, // IRP_MN_FILTER_RESOURCE_REQUIREMENTS
    91 DefaultPnpHandler, //
    92 DefaultPnpHandler, // IRP_MN_READ_CONFIG
    93 DefaultPnpHandler, // IRP_MN_WRITE_CONFIG
    94 DefaultPnpHandler, // IRP_MN_EJECT
    95 DefaultPnpHandler, // IRP_MN_SET_LOCK
    96 DefaultPnpHandler, // IRP_MN_QUERY_ID
    97 DefaultPnpHandler, // IRP_MN_QUERY_PNP_DEVICE_STATE
    98 DefaultPnpHandler, // IRP_MN_QUERY_BUS_INFORMATION
    99 DefaultPnpHandler, // IRP_MN_DEVICE_USAGE_NOTIFICATION
    100 DefaultPnpHandler, // IRP_MN_SURPRISE_REMOVAL
    101 };
    102
    103 ULONG fcn = stack->MinorFunction;
    104 if (fcn >= arraysize(fcntab))
    105 { // 未知的子功能代码
    106 status = DefaultPnpHandler(pdx, Irp); // some function we don't know about
    107 return status;
    108 }
    109
    110 #if DBG
    111 static char* fcnname[] =
    112 {
    113 "IRP_MN_START_DEVICE",
    114 "IRP_MN_QUERY_REMOVE_DEVICE",
    115 "IRP_MN_REMOVE_DEVICE",
    116 "IRP_MN_CANCEL_REMOVE_DEVICE",
    117 "IRP_MN_STOP_DEVICE",
    118 "IRP_MN_QUERY_STOP_DEVICE",
    119 "IRP_MN_CANCEL_STOP_DEVICE",
    120 "IRP_MN_QUERY_DEVICE_RELATIONS",
    121 "IRP_MN_QUERY_INTERFACE",
    122 "IRP_MN_QUERY_CAPABILITIES",
    123 "IRP_MN_QUERY_RESOURCES",
    124 "IRP_MN_QUERY_RESOURCE_REQUIREMENTS",
    125 "IRP_MN_QUERY_DEVICE_TEXT",
    126 "IRP_MN_FILTER_RESOURCE_REQUIREMENTS",
    127 "",
    128 "IRP_MN_READ_CONFIG",
    129 "IRP_MN_WRITE_CONFIG",
    130 "IRP_MN_EJECT",
    131 "IRP_MN_SET_LOCK",
    132 "IRP_MN_QUERY_ID",
    133 "IRP_MN_QUERY_PNP_DEVICE_STATE",
    134 "IRP_MN_QUERY_BUS_INFORMATION",
    135 "IRP_MN_DEVICE_USAGE_NOTIFICATION",
    136 "IRP_MN_SURPRISE_REMOVAL",
    137 };
    138
    139 KdPrint(("PNP Request (%s)\n", fcnname[fcn]));
    140 #endif // DBG
    141
    142 status = (*fcntab[fcn])(pdx, Irp);
    143 KdPrint(("Leave HelloWDMPnp\n"));
    144 return status;
    145 }
    146
    147 /************************************************************************
    148 * 函数名称:HelloWDMDispatchRoutine
    149 * 功能描述:对缺省IRP进行处理
    150 * 参数列表:
    151 fdo:功能设备对象
    152 Irp:从IO请求包
    153 * 返回 值:返回状态
    154 *************************************************************************/
    155 #pragma PAGEDCODE
    156 NTSTATUS HelloWDMDispatchRoutine(IN PDEVICE_OBJECT fdo,
    157 IN PIRP Irp)
    158 {
    159 PAGED_CODE();
    160 KdPrint(("Enter HelloWDMDispatchRoutine\n"));
    161 Irp->IoStatus.Status = STATUS_SUCCESS;
    162 Irp->IoStatus.Information = 0; // no bytes xfered
    163 IoCompleteRequest( Irp, IO_NO_INCREMENT );
    164 KdPrint(("Leave HelloWDMDispatchRoutine\n"));
    165 return STATUS_SUCCESS;
    166 }
    代码示例 P354

    2、通过设备接口寻找设备

    WDM中,一般是通过设备接口来定位一个驱动程序的。不同NT驱动通过设备名或者符号链接来定位。

    设备接口就一组全局标识,一个128位组成的数字。

    不用IoCreateSymbolicLink ,而用IoRegisterDeviceInterface为设备创建设备链接。

    见上面的示例代码中
    HelloWDMAddDevice
    示例代码 P358

    新的符号链接包括四部分组成:何种总线,类设备名称,该设备类型的第几个设备,指定设备的UUID

    A driver registers an interface instance once and then calls IoSetDeviceinterfaceState to enable and disable the interface.

    1)应用程序寻找接口

    应用程序寻找接口,是通过设备接口和设备号决定的。

    一般通过SetupDiXX系统函数来获得设备接口。

    http://msdn.microsoft.com/en-us/library/ff551015%28VS.85%29.aspx

    代码
    1 #include <windows.h>
    2 #include <stdio.h>
    3 #include <winioctl.h>
    4 #include "function.h"
    5
    6 HANDLE GetDeviceViaInterface( GUID* pGuid, DWORD instance)
    7 {
    8 // Get handle to relevant device information set
    9 HDEVINFO info = SetupDiGetClassDevs(pGuid, NULL, NULL, DIGCF_PRESENT | DIGCF_INTERFACEDEVICE);
    10 if(info==INVALID_HANDLE_VALUE)
    11 {
    12 printf("No HDEVINFO available for this GUID\n");
    13 return NULL;
    14 }
    15
    16 // Get interface data for the requested instance
    17 SP_INTERFACE_DEVICE_DATA ifdata;
    18 ifdata.cbSize = sizeof(ifdata);
    19 if(!SetupDiEnumDeviceInterfaces(info, NULL, pGuid, instance, &ifdata))
    20 {
    21 printf("No SP_INTERFACE_DEVICE_DATA available for this GUID instance\n");
    22 SetupDiDestroyDeviceInfoList(info);
    23 return NULL;
    24 }
    25
    26 // Get size of symbolic link name
    27 DWORD ReqLen;
    28 SetupDiGetDeviceInterfaceDetail(info, &ifdata, NULL, 0, &ReqLen, NULL);
    29 PSP_INTERFACE_DEVICE_DETAIL_DATA ifDetail = (PSP_INTERFACE_DEVICE_DETAIL_DATA)(new char[ReqLen]);
    30 if( ifDetail==NULL)
    31 {
    32 SetupDiDestroyDeviceInfoList(info);
    33 return NULL;
    34 }
    35
    36 // Get symbolic link name
    37 ifDetail->cbSize = sizeof(SP_INTERFACE_DEVICE_DETAIL_DATA);
    38 if( !SetupDiGetDeviceInterfaceDetail(info, &ifdata, ifDetail, ReqLen, NULL, NULL))
    39 {
    40 SetupDiDestroyDeviceInfoList(info);
    41 delete ifDetail;
    42 return NULL;
    43 }
    44
    45 printf("Symbolic link is %s\n",ifDetail->DevicePath);
    46 // Open file
    47 HANDLE rv = CreateFile( ifDetail->DevicePath,
    48 GENERIC_READ | GENERIC_WRITE,
    49 FILE_SHARE_READ | FILE_SHARE_WRITE,
    50 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    51 if( rv==INVALID_HANDLE_VALUE) rv = NULL;
    52
    53 delete ifDetail;
    54 SetupDiDestroyDeviceInfoList(info);
    55 return rv;
    56 }
    57
    58 DWORD ReadPort(HANDLE hDevice,DWORD port)
    59 {
    60 DWORD dwOutput;
    61 DWORD dwRead;
    62 DeviceIoControl(hDevice, IOCTL_READ_PORT_ULONG, &port, 4, &dwOutput, 4, &dwRead, NULL);
    63 return dwOutput;
    64 }
    65 VOID WritePort(HANDLE hDevice,DWORD port,DWORD value)
    66 {
    67 PVOID buffer[2];
    68 buffer[0] = (PVOID)port;
    69 buffer[1] = (PVOID)value;
    70 DWORD dwWrite;
    71 DeviceIoControl(hDevice, IOCTL_WRITE_PORT_ULONG, &port, 8, NULL, 0, &dwWrite, NULL);
    72 }
    73 VOID TestDriver(HANDLE hDevice)
    74 {
    75 DWORD dwOutput;
    76 DeviceIoControl(hDevice, IOCTL_TEST, NULL, 0, NULL, 0, &dwOutput, NULL);
    77 }
    78
    79
    示例代码 P359

    INI文件注意:VendorID and ProductID

    2)启动设备

    启动设备时,设备管理器向WDM驱动发一个IRP_MN_START_DEVICE的子功能代码的即插即用IRPWDM一般将这个IRP转发到底层PDO来完成。PDO根据设备的类型枚举该设备的所有资源。PDO可以认为是一个NT驱动。

    在设备管理器看到的是分配资源中的中断号,而WDM驱动中使用是翻译资源里的中断号。

    主函数
    1 #include <windows.h>
    2 #include <stdio.h>
    3 #include <winioctl.h>
    4
    5 #include "function.h"
    6
    7 #include "../MyDriver/guid.h"
    8
    9 int main()
    10 {
    11 HANDLE hDevice = GetDeviceViaInterface((LPGUID)&MY_WDM_DEVICE,0);
    12
    13 if (hDevice == INVALID_HANDLE_VALUE)
    14 {
    15 printf("Failed to obtain file handle to device: "
    16 "%s with Win32 error code: %d\n",
    17 "MyWDMDevice", GetLastError() );
    18 return 1;
    19 }
    20
    21 CloseHandle(hDevice);
    22 return 0;
    23 }
    代码
    1 #pragma LOCKEDCODE
    2 NTSTATUS OnRequestComplete(PDEVICE_OBJECT junk, PIRP Irp, PKEVENT pev)
    3 { // OnRequestComplete
    4 //在完成例程中设置等待事件
    5 KeSetEvent(pev, 0, FALSE);
    6 //标志本IRP还需要再次被完成
    7 return STATUS_MORE_PROCESSING_REQUIRED;
    8 }
    9
    10 ///////////////////////////////////////////////////////////////////////////////
    11
    12 #pragma PAGEDCODE
    13 NTSTATUS ForwardAndWait(PDEVICE_EXTENSION pdx, PIRP Irp)
    14 { // ForwardAndWait
    15 PAGED_CODE();
    16
    17 KEVENT event;
    18 //初始化事件
    19 KeInitializeEvent(&event, NotificationEvent, FALSE);
    20
    21 //将本层堆栈拷贝到下一层堆栈
    22 IoCopyCurrentIrpStackLocationToNext(Irp);
    23 //设置完成例程
    24 IoSetCompletionRoutine(Irp, (PIO_COMPLETION_ROUTINE) OnRequestComplete,
    25 (PVOID) &event, TRUE, TRUE, TRUE);
    26
    27 //调用底层驱动,即PDO
    28 IoCallDriver(pdx->NextStackDevice, Irp);
    29 //等待PDO完成
    30 KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
    31 return Irp->IoStatus.Status;
    32 }
    转发并等待示例 P366

    枚举相关资源

    PDO完成IRP_MN_START_DEVICE后,会将获得的设备资源存储在IRP的设备栈中,并且存储中stack中的AllocatedResourcesTranslated中。

    wps_clip_image-2400

    图示设备资源 P370

    代码
    1 #pragma PAGEDCODE
    2 VOID ShowResources(IN PCM_PARTIAL_RESOURCE_LIST list)
    3 {
    4 //枚举资源
    5 PCM_PARTIAL_RESOURCE_DESCRIPTOR resource = list->PartialDescriptors;
    6 ULONG nres = list->Count;
    7 ULONG i;
    8
    9 for (i = 0; i < nres; ++i, ++resource)
    10 { // for each resource
    11 ULONG type = resource->Type;
    12
    13 static char* name[] = {
    14 "CmResourceTypeNull",
    15 "CmResourceTypePort",
    16 "CmResourceTypeInterrupt",
    17 "CmResourceTypeMemory",
    18 "CmResourceTypeDma",
    19 "CmResourceTypeDeviceSpecific",
    20 "CmResourceTypeBusNumber",
    21 "CmResourceTypeDevicePrivate",
    22 "CmResourceTypeAssignedResource",
    23 "CmResourceTypeSubAllocateFrom",
    24 };
    25
    26 KdPrint((" type %s", type < arraysize(name) ? name[type] : "unknown"));
    27
    28 switch (type)
    29 { // select on resource type
    30 case CmResourceTypePort:
    31 case CmResourceTypeMemory:
    32 KdPrint((" start %8X%8.8lX length %X\n",
    33 resource->u.Port.Start.HighPart, resource->u.Port.Start.LowPart,
    34 resource->u.Port.Length));
    35 break;
    36
    37 case CmResourceTypeInterrupt:
    38 KdPrint((" level %X, vector %X, affinity %X\n",
    39 resource->u.Interrupt.Level, resource->u.Interrupt.Vector,
    40 resource->u.Interrupt.Affinity));
    41 break;
    42
    43 case CmResourceTypeDma:
    44 KdPrint((" channel %d, port %X\n",
    45 resource->u.Dma.Channel, resource->u.Dma.Port));
    46 } // select on resource type
    47 } // for each resource
    48 }
    示例代码 P371

      一个设备一般需要加载两个WDM程序,一个是总线驱动程序(它提供PDO),另一个是功能驱动程序(它提供FDO),PDO和FDO这两个设备对象形成一个设备栈。非即插即用功能的IRP一般在FDO中处理,而即插即用功能相关的IRP会被转发到PDO中处理。

      WDM驱动程序是NT式驱动程序的一个特例。

  • 相关阅读:
    jwt原理
    图书管理系统后端
    图书管理系统前端
    图书管理前端页面
    Linux多任务: exec 和fork()的联用
    CPU 字长与存储器位宽不一致处理
    关键字volatule
    linux C 中断程序:利用队列保存中断类型
    Linux下的Make与Makefile
    C :assert() 的用法
  • 原文地址:https://www.cnblogs.com/mydomain/p/1897139.html
Copyright © 2020-2023  润新知