驱动开发代码研读

news2024/11/6 3:17:16

文章目录

  • 一、程序流程图
  • 二、头文件程序详解
    • 1、public.h
    • 2、driver.h
    • 3、device.h
    • 4、trace.h
    • 5.XDMA.h
  • 三、C++程序详解
    • 1、driver.c
      • 1.头文件
      • 2.声明
      • 3.标记分页函数
      • 4.定义
      • 5.主函数
        • 1)参数&类型
        • 2)WPP(非必要)
        • 3)清理回调函数
        • 4)驱动初始化
        • 5)创建对象
      • 6.EvtDeviceAdd函数
        • 1)初始化:状态、分页、详细追踪
        • 2)设置传输:`I/O方式`
        • 3)PNP(即插即用)事件
        • 4) 电源管理事件
        • 5)寄存器文件
        • 6)设备初始化
        • 7)用户设备空间接口
        • 8)队列例程
      • 7.EvtDevicePrepareHardware函数
        • 1)初始化
        • 2)XDMA_DeviceOpen函数
            • 1.driver.c中应用
            • 2.入参
            • 3.用默认值初始化XDMA设备结构
            • 4.将PCIe基地址寄存器映射到主机内存
            • 5.BAR配置
            • 6.确认XDMA IP核心版本与此驱动程序匹配
            • 7.DMA启动程序
        • 3)轮询模式
        • 4)创建队列
        • 5)注册
      • 8.EvtDeviceReleaseHardware函数
      • 9.EvtDeviceCleanup函数
    • 2、driver.c

一、程序流程图

请添加图片描述

二、头文件程序详解

在WDF的PCIe驱动程序中,一般有四个.h文件:

  • Public.h
  • Driver.h
  • Device.h
  • Trace.h
    具体如下:

1、public.h

XDMA的代码中,命名为:xdma_public.h,顾名思义,public.h文件被驱动程序应用程序共同使用,其主要内容就是提供GUID接口
应用程序与驱动通信的设计过程中有两个重要的概念,即GUID值和CTL_CODE宏。

  • GUID(Globally Unique Identifier)是全局唯一标识符,通过特定算法(比如时间或地点等信息)生成一组128位二进制数,来唯一标识某个实体:
	DEFINE_GUID(GUID_DEVINTERFACE_XDMA, 0x74c7e4a9, 0x6d5d, 0x4a70, 0xbc, 0x0d, 0x20, 0x69, 0x1d, 0xff, 0x9e, 0x9d);
  • CTL_CODE是一个用于创建一个唯一的32位系统I/O控制代码的宏:
	#define XDMA_IOCTL(index) CTL_CODE(FILE_DEVICE_UNKNOWN, index, METHOD_BUFFERED, FILE_ANY_ACCESS)       

其中四个参数分别表示:

  1. DeviceType(设备类型,高16位(16-31位),FILE_DEVICE_UNKNOWN表示自定义板卡)
  2. Function(功能2-13 位,从0x800开始取值,之前的已经被微软占用)
  3. Method(I/O访问内存使用方式,有METHOD_BUFFERED、METHOD_IN_DIRECT、METHOD_OUT_DIRECT、METHOD_NEITHER四种:
    • buffered方式:I/O管理器会创建与应用程序数据缓冲区完全相同的系统缓冲区,驱动程序在这个缓冲区工作,由I/O管理器完成复制数据任务;
    • direct方式:XDMA使用,I/O管理器锁定应用程序缓冲区的物理内存页,并创建一个MDL(内存描述符表)来描述该页,驱动程序将使用MDL工作;
    • neither方式:一般不用,应用程序缓冲区的虚拟地址传递给驱动程序)
  4. Access(访问限制,14-15位,一般有:FILE_ANY_ACCESS、FILE_READ_ACCESS、FILE_WRITE_ACCESS)

其余都是一些宏定义,主要定义事件和文件,如:

	#define	XDMA_FILE_EVENT_0	L"\\event_0"
	#define	XDMA_FILE_EVENT_1	L"\\event_1"
	#define	XDMA_FILE_EVENT_2	L"\\event_2"

	#define	XDMA_FILE_H2C_0		L"\\h2c_0"
	#define	XDMA_FILE_H2C_1		L"\\h2c_1"
	#define	XDMA_FILE_H2C_2		L"\\h2c_2"

	#define	XDMA_FILE_C2H_0		L"\\c2h_0"
	#define	XDMA_FILE_C2H_1		L"\\c2h_1"
	#define	XDMA_FILE_C2H_2		L"\\c2h_2"

2、driver.h

driver文件一般用来包含PCIE所需要的头文件,并disable一些警告:

typedef struct DeviceContext_t 
{
    XDMA_DEVICE xdma;
    WDFQUEUE engineQueue[2][XDMA_MAX_NUM_CHANNELS];
    KEVENT eventSignals[XDMA_MAX_USER_IRQ];

}DeviceContext;
WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(DeviceContext, GetDeviceContext)

3、device.h

  • 设置最大传输大小:
#define XDMA_MAX_NUM_BARS (3)
typedef void(*PFN_XDMA_USER_WORK)(ULONG eventId, void* userData);
  • 定义相关事件:
typedef struct XDMA_EVENT_T 
{
    PFN_XDMA_USER_WORK work; // user callback 
    void* userData; // custom user data. will be passed into work callback function
    WDFINTERRUPT irq; //wdf interrupt handle
} XDMA_EVENT;
  • 定义设备相关变量,体现WDF中的一种“对象封装”思想
typedef struct XDMA_DEVICE_T {

    // WDF 
    WDFDEVICE wdfDevice;

    // PCIe BAR access
    UINT numBars;
    PVOID bar[XDMA_MAX_NUM_BARS]; // kernel virtual address of BAR
    ULONG barLength[XDMA_MAX_NUM_BARS];
    ULONG configBarIdx;
    LONG userBarIdx;
    LONG bypassBarIdx;
	volatile XDMA_CONFIG_REGS *configRegs;
    volatile XDMA_IRQ_REGS *interruptRegs;
    volatile XDMA_SGDMA_COMMON_REGS * sgdmaRegs;

    // DMA Engine management
    XDMA_ENGINE engines[XDMA_MAX_NUM_CHANNELS][XDMA_NUM_DIRECTIONS];
    WDFDMAENABLER dmaEnabler;   // WDF DMA Enabler for the engine queues

    // Interrupt Resources
    WDFINTERRUPT lineInterrupt;
    WDFINTERRUPT channelInterrupts[XDMA_MAX_CHAN_IRQ];
	XDMA_EVENT userEvents[XDMA_MAX_USER_IRQ];

} XDMA_DEVICE, *PXDMA_DEVICE;

4、trace.h

用来调试和跟踪,部分代码为默认生成

#define WPP_CHECK_FOR_NULL_STRING  //to prevent exceptions due to NULL strings

#define WPP_CONTROL_GUIDS \
    WPP_DEFINE_CONTROL_GUID(XdmaDrvTraceGuid,(7dd02079,3c3f,42c5,9384,c210c7cc490a), \
        WPP_DEFINE_BIT(DBG_INIT)        /* bit  0 = 0x00000001 */ \
        WPP_DEFINE_BIT(DBG_IRQ)         /* bit  1 = 0x00000002 */ \
        WPP_DEFINE_BIT(DBG_DMA)         /* bit  2 = 0x00000004 */ \
        WPP_DEFINE_BIT(DBG_DESC)        /* bit  3 = 0x00000008 */ \
        WPP_DEFINE_BIT(DBG_USER)        /* bit  4 = 0x00000010 */ \
        WPP_DEFINE_BIT(DBG_IO)          /* bit  5 = 0x00000014 */ \
        )
#define WPP_FLAG_LEVEL_LOGGER(flag, level)                                  \
    WPP_LEVEL_LOGGER(flag)

#define WPP_FLAG_LEVEL_ENABLED(flag, level)                                 \
    (WPP_LEVEL_ENABLED(flag) &&                                             \
     WPP_CONTROL(WPP_BIT_ ## flag).Level >= level)

#define WPP_LEVEL_FLAGS_LOGGER(lvl,flags) \
           WPP_LEVEL_LOGGER(flags)

#define WPP_LEVEL_FLAGS_ENABLED(lvl, flags) \
           (WPP_LEVEL_ENABLED(flags) && WPP_CONTROL(WPP_BIT_ ## flags).Level >= lvl)

#define WPP_RECORDER_FLAGS_LEVEL_ARGS(flags, lvl) WPP_RECORDER_LEVEL_FLAGS_ARGS(lvl, flags)
#define WPP_RECORDER_FLAGS_LEVEL_FILTER(flags, lvl) WPP_RECORDER_LEVEL_FLAGS_FILTER(lvl, flags)

#ifndef DBG
#define WPP_INIT_TRACING(...)  (__VA_ARGS__)
#define WPP_CLEANUP(...)       (__VA_ARGS__)
#define DBG_GENERIC         0
#define DBG_INIT            0
#define DBG_IO              0
#define DBG_IRQ             0
#define DBG_DPC             0
#define DBG_DMA             0
#define DBG_DESC            0
#define DBG_USER            0
#define TraceVerbose(...)   (__VA_ARGS__)
#define TraceInfo(...)      (__VA_ARGS__)
#define TraceWarning(...)   (__VA_ARGS__)
#define TraceError
           

5.XDMA.h


#define XDMA_MAKE_VERSION(major, minor, patch) (((major) << 24) | ((minor) << 26) | (patch))
#define XDMA_VERSION_MAJOR(version) (((uint32_t)(version) >> 24) & 0xff)
#define XDMA_VERSION_MINOR(version) (((uint32_t)(version) >> 16) & 0xff)
#define XDMA_VERSION_PATCH(version) ((uint32_t)(version) & 0xffff)

#define XDMA_LIB_VERSION XDMA_MAKE_VERSION(2017, 4, 1)
NTSTATUS XDMA_DeviceOpen(WDFDEVICE wdfDevice,
                         PXDMA_DEVICE xdma,
                         WDFCMRESLIST ResourcesRaw,
                         WDFCMRESLIST ResourcesTranslated);
void XDMA_DeviceClose(PXDMA_DEVICE xdma);

NTSTATUS XDMA_UserIsrRegister(PXDMA_DEVICE xdma,
                              ULONG index,
                              PFN_XDMA_USER_WORK handler,
                              void* userData);
NTSTATUS XDMA_UserIsrEnable(PXDMA_DEVICE xdma, ULONG eventId);
NTSTATUS XDMA_UserIsrDisable(PXDMA_DEVICE xdma, ULONG eventId);
EVT_WDF_PROGRAM_DMA XDMA_EngineProgramDma;
void XDMA_EngineSetPollMode(XDMA_ENGINE* engine, BOOLEAN pollMode);

三、C++程序详解

1、driver.c

1.头文件

#include "driver.h"
#include "file_io.h"
#include "trace.h"

2.声明

DRIVER_INITIALIZE                   DriverEntry;
DRIVER_UNLOAD                       DriverUnload;

EVT_WDF_DRIVER_DEVICE_ADD           EvtDeviceAdd;
EVT_WDF_DEVICE_CONTEXT_CLEANUP      EvtDeviceCleanup;
EVT_WDF_DEVICE_PREPARE_HARDWARE     EvtDevicePrepareHardware;
EVT_WDF_DEVICE_RELEASE_HARDWARE     EvtDeviceReleaseHardware;

其中声明的事件分别是:

  • DriverEntry 是驱动入口
  • DriverUnload 是驱动出口(卸载
  • EvtDeviceAdd 是添加驱动设备
  • EvtDeviceCleanup 是设备清理
  • EvtDevicePrepareHardware 是硬件准备
  • EvtDeviceReleaseHardware 是硬件发布

声明一个引擎队列的创建方法,用于WDF框架为DMA创建I/O队列,参数分别是:当前设备、引擎、队列:

static NTSTATUS EngineCreateQueue(WDFDEVICE device, XDMA_ENGINE* engine, WDFQUEUE* queue);

3.标记分页函数

驱动程序开发中,需要为每个函数指定位于分页内存还是非分页内存:

  • INIT表示入口函数,驱动成功加载后可以从内存删除
  • PAGE表示可以在驱动运行时被交换到硬盘上;
  • 如果不指定,将被编译器默认为非分页内存。
#ifdef ALLOC_PRAGMA
#pragma alloc_text (INIT, DriverEntry)
#pragma alloc_text (PAGE, DriverUnload)
#pragma alloc_text (PAGE, EvtDeviceAdd)
#pragma alloc_text (PAGE, EvtDevicePrepareHardware)
#pragma alloc_text (PAGE, EvtDeviceReleaseHardware)
#pragma alloc_text (PAGE, EngineCreateQueue)
#endif

4.定义

日期:

const char * const dateTimeStr = "Built " __DATE__ ", " __TIME__ ".";

从Windows注册表中获取轮询模式的驱动程序参数:

static NTSTATUS GetPollModeParameter(IN PULONG pollMode) {
    WDFDRIVER driver = WdfGetDriver();
    WDFKEY key;
    NTSTATUS status = WdfDriverOpenParametersRegistryKey(driver, STANDARD_RIGHTS_ALL,
                                                         WDF_NO_OBJECT_ATTRIBUTES, &key);
    ULONG tracepollmode;
    
    if (!NT_SUCCESS(status)) {
        TraceError(DBG_INIT, "WdfDriverOpenParametersRegistryKey failed: %!STATUS!", status);
        WdfRegistryClose(key);
        return

5.主函数

1)参数&类型

在这里插入图片描述
函数返回类型 NTSTATUS 是 WDF 中的一个宏,它实际上是一个 32 位的二进制数,不同的数值表示不同的状态,在 PCIe 设备驱动程序开发中,需要用到的状态有:

  • STATUS_SUCCESS 例程回调成功
  • STATUS_PENDING 例程回调未完成
  • STATUS_UNSUCCESSFUL 例程回调失败

DriverEntry例程类似于C语言中的main函数,在WDF中,操作系统检测到有新硬件设备插入后,会查找对应的驱动程序,其中的两个参数分别是:

  • 驱动对象DriverObject,指向驱动程序对象的指针;
  • 注册表路径RegistryPath

在传入参数里, IN、OUT是宏,IN 代表入口参数,OUT 代表出口参数。还有一种写法, 即_In_、_Out_, 两种写法对回调例程的编写都没影响。

2)WPP(非必要)

WPP软件跟踪:

    WPP_INIT_TRACING(driverObject, registryPath);
    TraceInfo(DBG_INIT, "XDMA Driver - %s", dateTimeStr);//debug初始化

WPP主要用于开发过程中调试代码,跟踪程序可以是:

  • 内核模式驱动程序
  • 用户模式驱动程序、应用程序或动态链接库 (DLL)

如果使用 WDK 中提供的Visual Studio模板创建 WDF 驱动程序,则大部分工作都由程序员完成。
将 WPP 软件跟踪添加到驱动程序或应用程序的基本过程包括以下步骤(不关键):

  1. 定义一个控件 GUID,用于唯一地将驱动程序或应用程序标识为 跟踪提供程序。 提供程序在跟踪宏的定义中指定此 GUID,WPP_CONTROL_GUIDS Tracelog 或其他跟踪控制器使用的相关控制文件中指定此 GUID。
  2. 将所需的 WPP 相关 C 预处理器指令和 WPP 宏调用添加到提供程序的源文件,如将 WPP 软件跟踪添加到 Windows 驱动程序和 WPP 软件跟踪引用中所述。
  3. 修改 Visual Studio 项目以运行 WPP 预处理器并生成驱动程序,如将 WPP 软件跟踪添加到 Windows 驱动程序中所述。 有关更多生成时间选项,可以参考WPP预处理器。
  4. 安装驱动程序或组件,启动跟踪会话并记录跟踪消息。 使用 TraceView、 Tracelog、 Tracefmt 和 Tracepdb 等软件跟踪工具配置、启动和停止跟踪会话,以及显示和筛选跟踪消息。 这些工具包含在 WDK Windows驱动程序 (工具包) 。

3)清理回调函数

暂时忽略这部分

 WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
 attributes.EvtCleanupCallback = Spw_PCIeEvtDriverContextCleanup;

4)驱动初始化

注册驱动程序的 EvtDeviceAdd 回调函数

WDF_DRIVER_CONFIG_INIT(&DriverConfig, EvtDeviceAdd);

详见第6节

5)创建对象

创建一个驱动程序对象, 向框架“注册”驱动程序

status = WdfDriverCreate(driverObject, registryPath, WDF_NO_OBJECT_ATTRIBUTES, &DriverConfig,
                             &Driver);
    if (!NT_SUCCESS(status)) {
        TraceError(DBG_INIT, "WdfDriverCreate failed: %!STATUS!", status);
        WPP_CLEANUP(driverObject);
        return status;
    }
    driverObject->DriverUnload = DriverUnload;

    return status;

6.EvtDeviceAdd函数

EvtDeviceAdd 例程的原型声明如下:

EvtDeviceAdd( IN WDFDRIVER Driver, IN PWDFDEVICE_INIT DeviceInit ) ;

每次操作系统枚举设备时, PnP 管理器就调用这个回调例程,函数与例程原型一样,函数内部:

1)初始化:状态、分页、详细追踪

    NTSTATUS status = STATUS_SUCCESS;
    PAGED_CODE();
    TraceVerbose(DBG_INIT, "(Driver=0x%p)", Driver);  

2)设置传输:I/O方式

 	WdfDeviceInitSetIoType(DeviceInit, WdfDeviceIoDirect);

其中第二个参数为枚举类,定义如下:

typedef enum _WDF_DEVICE_IO_TYPE	 		//传输类型枚举 
{									 
    WdfDeviceIoUndefined = 0,        		//默认未定义
    WdfDeviceIoNeither,				 		//两者都不					
    WdfDeviceIoBuffered, 			 		//缓冲	
    WdfDeviceIoDirect,				 		//直接
    WdfDeviceIoBufferedOrDirect = 4, 		//缓冲或直接
    WdfDeviceIoMaximum,				 		//最大值
} WDF_DEVICE_IO_TYPE, *PWDF_DEVICE_IO_TYPE;

直接I/O仅适用于延迟的缓冲区检索,不保证直接I/O实际上被使用,直接I/O仅用于全页缓冲区,缓冲I/O用于传输的其他部分.

3)PNP(即插即用)事件

初始化PnpPowerCallbacks

	WDF_PNPPOWER_EVENT_CALLBACKS PnpPowerCallbacks;

初始化即插即用例程配置结构

	WDF_PNPPOWER_EVENT_CALLBACKS_INIT(&PnpPowerCallbacks);

**设置即插即用基本例程**详见第7节

	PnpPowerCallbacks.EvtDevicePrepareHardware = EvtDevicePrepareHardware;
	PnpPowerCallbacks.EvtDeviceReleaseHardware = EvtDeviceReleaseHardware;

注册即插即用例程

	WdfDeviceInitSetPnpPowerEventCallbacks(DeviceInit, &PnpPowerCallbacks);

4) 电源管理事件

初始化powerPolicyCallbacks

	WDF_POWER_POLICY_EVENT_CALLBACKS powerPolicyCallbacks;

初始化电源管理例程配置结构

	WDF_POWER_POLICY_EVENT_CALLBACKS_INIT(&powerPolicyCallbacks);

注册电源管理例程

	WdfDeviceInitSetPowerPolicyEventCallbacks(DeviceInit, &powerPolicyCallbacks);

5)寄存器文件

寄存器文件回调

	WDF_OBJECT_ATTRIBUTES fileAttributes;
	WDF_FILEOBJECT_CONFIG fileConfig;
	WDF_FILEOBJECT_CONFIG_INIT(&fileConfig, EvtDeviceFileCreate, EvtFileClose, 	EvtFileCleanup);
	WDF_OBJECT_ATTRIBUTES_INIT(&fileAttributes);
	fileAttributes.SynchronizationScope = WdfSynchronizationScopeNone;
	WDF_OBJECT_ATTRIBUTES_SET_CONTEXT_TYPE(&fileAttributes, FILE_CONTEXT);
	WdfDeviceInitSetFileObjectConfig(DeviceInit, &fileConfig, &fileAttributes);

6)设备初始化

创建并初始化设备对象和相应的上下文区

	WDF_OBJECT_ATTRIBUTES deviceAttributes;
	WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&deviceAttributes, DeviceContext);
	deviceAttributes.EvtCleanupCallback = EvtDeviceCleanup;//有代码注释掉这部分
	WDFDEVICE device;
	status = WdfDeviceCreate(&DeviceInit, &deviceAttributes, &device);
    if (!NT_SUCCESS(status)) {
        TraceError(DBG_INIT, "WdfDeviceCreate failed: %!STATUS!", status);
        return status;
    }

7)用户设备空间接口

	status = WdfDeviceCreateDeviceInterface(device, (LPGUID)&GUID_DEVINTERFACE_XDMA, NULL);
    if (!NT_SUCCESS(status)) 
    {
        TraceError(DBG_INIT, "WdfDeviceCreateDeviceInterface failed %!STATUS!", status);
        return status;
    }

8)队列例程

初始化队列配置结构

    WDF_IO_QUEUE_CONFIG queueConfig;
    WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&queueConfig, WdfIoQueueDispatchParallel);

注册 I/O 处理例程

    queueConfig.EvtIoDeviceControl = EvtIoDeviceControl; //设备控制回调 
    queueConfig.EvtIoRead = EvtIoRead; // 读回调
    queueConfig.EvtIoWrite = EvtIoWrite; // 写回调

创建 I/O 队列

	WDFQUEUE entryQueue;
    status = WdfIoQueueCreate(device, &queueConfig, WDF_NO_OBJECT_ATTRIBUTES, &entryQueue);
    if (!NT_SUCCESS(status)) 
    {
        TraceError(DBG_INIT, "WdfIoQueueCreate failed: %!STATUS!", status);
        return status;
    }

指定要分发的 I/O 请求类型?
创建 GUID 接口?

7.EvtDevicePrepareHardware函数

设备进入工作状态后,KMDF调用EvtDevicePrepareHardware例程传递两个资源列表,驱动程序保存这两个资源列表,直到WDF框架调用了EvtDeviceReleaseHardware例程。

1)初始化

NTSTATUS EvtDevicePrepareHardware(IN WDFDEVICE device, IN WDFCMRESLIST Resources, IN WDFCMRESLIST ResourcesTranslated) 
{
	PAGED_CODE();
    UNREFERENCED_PARAMETER(Resources);
    TraceVerbose(DBG_INIT, "-->Entry");

事件上下文信息ctx

	DeviceContext* ctx = GetDeviceContext(device);
	PXDMA_DEVICE xdma = &(ctx->xdma);

其中ctx存放了xdma,队列和事件信号,结构体定义如下:

2)XDMA_DeviceOpen函数

1.driver.c中应用
 	NTSTATUS status = XDMA_DeviceOpen(device, xdma, Resources, ResourcesTranslated);
 	if (!NT_SUCCESS(status)) 
 	{
        TraceError(DBG_INIT, "XDMA_DeviceOpen failed: %!STATUS!", status);
        return status;
    }
2.入参
NTSTATUS XDMA_DeviceOpen(WDFDEVICE wdfDevice,
                         PXDMA_DEVICE xdma,
                         WDFCMRESLIST ResourcesRaw,
                         WDFCMRESLIST ResourcesTranslated)
3.用默认值初始化XDMA设备结构
 	NTSTATUS status = STATUS_INTERNAL_ERROR;
    DeviceDefaultInitialize(xdma);
    xdma->wdfDevice = wdfDevice;
4.将PCIe基地址寄存器映射到主机内存
	status = MapBARs(xdma, ResourcesTranslated);
    if (!NT_SUCCESS(status)) 
    {
        TraceError(DBG_INIT, "MapBARs() failed! %!STATUS!", status);
        return status;
    }
5.BAR配置

确定BAR配置,用户(可选)、配置、旁路(可选)

	status = IdentifyBars(xdma);
    if (!NT_SUCCESS(status))
     {
        TraceError(DBG_INIT, "IdentifyBars() failed! %!STATUS!", status);
        return status;
    }

获取BAR配置中的模块偏移量

	GetRegisterModules(xdma);
6.确认XDMA IP核心版本与此驱动程序匹配
	UINT version = GetVersion(xdma);
    if (version != v2017_1) 
    {
        TraceWarning(DBG_INIT, "Version mismatch! Expected 2017.1 (0x%x) but got (0x%x)",
                     v2017_1, version);
    }

    status = SetupInterrupts(xdma, ResourcesRaw, ResourcesTranslated);
    if (!NT_SUCCESS(status)) 
    {
        TraceError(DBG_INIT, "SetupInterrupts failed: %!STATUS!", status);
        return status;
    }
7.DMA启动程序

WDF DMA启用程序-至少8字节对齐

	WdfDeviceSetAlignmentRequirement(xdma->wdfDevice, 8 - 1); // TODO - choose correct value
    WDF_DMA_ENABLER_CONFIG dmaConfig;
    WDF_DMA_ENABLER_CONFIG_INIT(&dmaConfig, WdfDmaProfileScatterGather64Duplex, XDMA_MAX_TRANSFER_SIZE);
    status = WdfDmaEnablerCreate(xdma->wdfDevice, &dmaConfig, WDF_NO_OBJECT_ATTRIBUTES, &xdma->dmaEnabler);
    if (!NT_SUCCESS(status)) {
        TraceError(DBG_INIT, " WdfDmaEnablerCreate() failed: %!STATUS!", status);
        return status;
    }

检测并初始化硬件IP中配置的引擎

	status = ProbeEngines(xdma);
    if (!NT_SUCCESS(status)) {
        TraceError(DBG_INIT, "ProbeEngines failed: %!STATUS!", status);
        return status;
    }

    return status;

3)轮询模式

获取轮询模式参数,并根据需要将引擎配置为轮询模式

	ULONG pollMode = 0;
    status = GetPollModeParameter(&pollMode);
    if (!NT_SUCCESS(status)) 
    {
        TraceError(DBG_INIT, "GetPollModeParameter failed: %!STATUS!", status);
        return status;
    }
    for (UINT dir = H2C; dir < 2; dir++) 
    { // 0=H2C, 1=C2H
        for (ULONG ch = 0; ch < XDMA_MAX_NUM_CHANNELS; ch++) 
        {
            XDMA_ENGINE* engine = &(xdma->engines[ch][dir]);
            XDMA_EngineSetPollMode(engine, (BOOLEAN)pollMode);
        }
    }

4)创建队列

为每个引擎创建一个队列

    for (UINT dir = H2C; dir < 2; dir++) 
    { // 0=H2C, 1=C2H
        for (ULONG ch = 0; ch < XDMA_MAX_NUM_CHANNELS; ch++) 
        {
            XDMA_ENGINE* engine = &(xdma->engines[ch][dir]);
            if (engine->enabled == TRUE) 
            {
                status = EngineCreateQueue(device, engine, &(ctx->engineQueue[dir][ch]));
                if (!NT_SUCCESS(status)) 
                {
                    TraceError(DBG_INIT, "EngineCreateQueue() failed: %!STATUS!", status);
         			return status;
                }
            }
        }
    }

5)注册

部分事件初始化,用户注册

	for (UINT i = 0; i < XDMA_MAX_USER_IRQ; ++i) 
	{
        KeInitializeEvent(&ctx->eventSignals[i], NotificationEvent, FALSE);
        XDMA_UserIsrRegister(xdma, i, HandleUserEvent, &ctx->eventSignals[i]);
    }
    TraceVerbose(DBG_INIT, "<--Exit returning %!STATUS!", status);
    return status;

}

8.EvtDeviceReleaseHardware函数

本质是回调例程,其调用过程是EvtDevicePrepareHardware的逆过程,即获得虚拟地址后,利用MmUnMapIoSpace 函数将虚拟地址解映射成物理地址,然后再交给WDF框架释放。

NTSTATUS EvtDeviceReleaseHardware(IN WDFDEVICE Device, IN WDFCMRESLIST ResourcesTranslated) {

    PAGED_CODE();
    UNREFERENCED_PARAMETER(ResourcesTranslated);
    TraceVerbose(DBG_INIT, "entry");
    
    DeviceContext* ctx = GetDeviceContext(Device);
    if (ctx != NULL) {
        XDMA_DeviceClose(&ctx->xdma);
    }

    TraceVerbose(DBG_INIT, "exit");
    return STATUS_SUCCESS;
}

其中,XDMA_DeviceClose用于停止所有引擎。

void XDMA_DeviceClose(PXDMA_DEVICE xdma) {

    // todo - stop every engine?

    // reset irq vectors?
    if (xdma && xdma->interruptRegs) {
        xdma->interruptRegs->userVector[0] = 0;
        xdma->interruptRegs->userVector[1] = 0;
        xdma->interruptRegs->userVector[2] = 0;
        xdma->interruptRegs->userVector[3] = 0;
        xdma->interruptRegs->channelVector[0] = 0;
        xdma->interruptRegs->channelVector[1] = 0;
    }

    // Unmap any I/O ports. Disconnecting 

9.EvtDeviceCleanup函数

VOID EvtDeviceCleanup(IN WDFOBJECT device)
{
    UNREFERENCED_PARAMETER(device);
    TraceInfo(DBG_INIT, "%!FUNC!");
}

2、driver.c

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/54757.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【元胞自动机】元胞自动机晶体生长【含Matlab源码 232期】

⛄一、元胞自动机简介 1 元胞自动机发展历程 最初的元胞自动机是由冯 诺依曼在 1950 年代为模拟生物 细胞的自我复制而提出的. 但是并未受到学术界重视. 1970 年, 剑桥大学的约翰 何顿 康威设计了一个电脑游戏 “生命游戏” 后, 元胞自动机才吸引了科学家们的注意. 1983 年…

智工教育:军队文职报考要注意限制性条件

军队文职报考限制性条件 &#xff08;1&#xff09;曾因犯罪受过刑事处罚的人员和曾被开除公职的人员&#xff1b; &#xff08;2&#xff09;在各级公务员招考中被认定有舞弊等严重违反录用纪律行为的人员&#xff1b; &#xff08;3&#xff09;现役军人&#xff1b; &…

【多目标进化优化】MOEA 性能评价

1. 概述 \quad\quad对一个多目标进化算法的性能进行评价时&#xff0c;一方面需要有一套能够客观地反应 MOEA 优劣的评价工具或方法&#xff1b;另一方面需要选取一组比较有代表性的测试问题&#xff0c;通常选取有已知解的问 (benchmark test problem) 作为测试用例&#xff0…

C++中的回调函数再次总结(std::bind和function)

文章目录0 引入1、回调函数1.定义2.基本格式2、应用场景1.一件事需要多个独立步骤完成2.回调3、C11中的std::function和bind4、引用0 引入 最近看到一些文章说回调函数&#xff0c;再加上看到一些比较好的文章和代码&#xff0c;对于回调函数又有了重新的认识&#xff0c;在这…

react源码分析:babel如何解析jsx

同作为MVVM框架&#xff0c;React相比于Vue来讲&#xff0c;上手更需要JavaScript功底深厚一些&#xff0c;本系列将阅读React相关源码&#xff0c;从jsx -> VDom -> RDOM等一些列的过程&#xff0c;将会在本系列中一一讲解 工欲善其事&#xff0c;必先利其器 经过多年的…

性能测试 之cpu 线程 上下文切换问题分析

使用 stress-ng: 性能测试模拟线程上下文切换 上篇文章使用了stress-ng 模拟了 进程上下文切换导致的性能问题&#xff0c; 现在我们在使用 该工具模拟线程上下文切换&#xff0c;那么进程和线程有什么区别呢 抽象&#xff1a;线程&#xff08;thread&#xff09;是操作系统能…

MEMM最大熵模型

最大熵模型&#xff08;MEMM&#xff09;: 提出背景&#xff1a;解决模型三个缺点 最大熵结构&#xff1a;HMM框架加上多项的逻辑回归。 HMM缺点&#xff1a; 1.观测独立假设和齐次马尔可夫假设 解决办法&#xff1a;调转模型箭头 2.模型建模和求解不一致&#xff08;建模&am…

农产品溯源中GIS应用

农产品溯源中GIS应用 摘要 构建“从田间地头到餐桌”的农产品安全生产与溯源体系需求日益迫切。农产品的食品安全也是维持人们的生命健康重要因素之一。当前&#xff0c;农业信息化推进速度非常的迅速&#xff0c;各类型农业相关基础设施正在不断加强&#xff0c;信息技术能提…

vs2013的使用及编译中遇到的问题

目录 一、vs2013的使用 1、新建项目 2、新建源文件 3、编辑代码 但是如果每次新建文件都要加上这一句就很麻烦&#xff0c;所以这里提供一个一劳永逸的方法 二、运行代码的中的小问题 问题1&#xff1a;scanf函数不安全 解决办法 法1.用报错提示中的 scanf_s 来代替 scanf…

ShaderGraph实现序列帧动画

介绍 上篇我们介绍了ShaderLab编程实现序列帧动画,这里我们介绍一下如何使用可视化界面ShaderGraph来实现。 在使用ShaderGraph的过程中,我们可以了解ShaderGranph的一些操作,由于上篇文章已经分享了原理方面的知识,这里不再赘述。我们便开始ShaderGraph来实现序列帧动画。…

Proxyless Mesh 在 Dubbo 中的实践

作者&#xff1a;王程铭 背景 随着 Dubbo 3.1 的 release&#xff0c;Dubbo 在云原生的路上又迈出了重要的一步。在这个版本中添加了 Proxyless Mesh 的新特性&#xff0c;Dubbo Proxyless Mesh 直接实现 xDS 协议解析&#xff0c;实现 Dubbo 与 Control Plane 的直接通信&am…

深度学习训练营实现minist手写数字识别

深度学习训练营原文链接环境介绍前置工作设置GPU导入要使用的包进行归一化操作样本可视化调整图片格式构建CNN网络编译模型模型训练预测操作原文链接 &#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f366; 参考文章&#xff1a;365天深度学习训…

OpenCV图像处理——卷积操作

总目录 图像处理总目录←点击这里 二十五、卷积操作 25.1、预处理 # 指定输入图像 ap argparse.ArgumentParser() ap.add_argument("-i", "--image", requiredTrue, help"path to the input image") args vars(ap.parse_args())# 分别构建…

【数据结构趣味多】顺序表基本操作实现(Java)

目录 顺序表 1.定义顺序顺序表 2.顺序表功能 3.函数实现&#xff08;java实现&#xff09;&#xff1f; 打印顺序表display()函数 新增元素函数add() (默认在数组最后新增) 在 pos 位置新增元素add()函数&#xff08;与上方函数构成重载&#xff09; 判定是否包含某个元素…

XctNet:从单个X射线图像重建体积图像的网络

摘要 传统的计算机断层扫描&#xff08;CT&#xff09;通过使用不同角度的X射线投影计算逆氡变换来生成体积图像&#xff0c;这导致高剂量辐射、长重建时间和伪影。生物学上&#xff0c;可以利用先前的知识或经验在一定程度上从2D图像中识别体积信息。提出了一种深度学习网络Xc…

为什么要使用 kafka,为什么要使用消息队列?

总结以下两点&#xff1a; 1、缓冲和削峰&#xff1a; 上游数据时有突发流量&#xff0c;下游可能扛不住&#xff0c;或者下游没有⾜够多的机器来保证冗余&#xff0c;kafka在中间可以起到⼀个缓冲的作⽤&#xff0c;把消息暂存在kafka中&#xff0c;下游服务就可以按照⾃⼰的节…

B. Moderate Modular Mode(nmodx=ymodn )

Problem - 1603B - Codeforces 帮助他找到一个整数n&#xff0c;使得1≤n≤2⋅1018&#xff0c;并且nmodxymodn。这里&#xff0c;amodb表示a除以b后的余数。如果有多个这样的整数&#xff0c;请输出任何一个。可以证明&#xff0c;在给定的约束条件下&#xff0c;这样的整数总…

图的关键路径(含多支交叉路径分离输出)

文章目录关键路径的理解关键路径求解的图解与分析关键路径查找的代码实现多支交叉路径的分离输出总结此文代码均可在Windows与Linux操作系统下的常用编译器上运行&#xff0c;例如&#xff1a;vs、vscode、Dev-C等等。关键路径的理解 图的关键路径一般是在求从一个顶点到另一个…

RocketMQ-RocketMQ部署(Linux、docker)

文章目录一、Linux1、单机部署RocketMQ> 前置条件第一步、官网下载 并 上传至服务器第二步、配置jdk环境第三步、修改初始内存第四步、启动 NameServer第五步、启动 Broker第六步、关闭RocketMQDemo&#xff1a;发送与接收消息测试 (Linux端)2、部署可视化管理工具—rocketm…

tictoc 例子理解 13-15

tictoc13-tictoc13 子类化cMessage生成消息&#xff0c;随机目标地址tictoc 14 在13的基础上增加两变量显示于仿真界面tictoc 15 模型数据输出为直方图tictoc13 子类化cMessage生成消息&#xff0c;随机目标地址 在这一步中&#xff0c;目标地址不再是节点2——我们绘制了一个…