文章目录
一、程序流程图
二、头文件程序详解
在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)
其中四个参数分别表示:
- DeviceType(设备类型,高16位(16-31位),FILE_DEVICE_UNKNOWN表示自定义板卡)
- Function(功能2-13 位,从0x800开始取值,之前的已经被微软占用)
- 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方式:一般不用,应用程序缓冲区的虚拟地址传递给驱动程序)
- buffered方式:I/O管理器会创建与应用程序数据缓冲区
- 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);
详见第9节。
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 软件跟踪添加到驱动程序或应用程序的基本过程包括以下步骤(不关键):
定义一个控件 GUID
,用于唯一地将驱动程序或应用程序标识为 跟踪提供程序。 提供程序在跟踪宏的定义中指定此 GUID,WPP_CONTROL_GUIDS Tracelog 或其他跟踪控制器使用的相关控制文件中指定此 GUID。- 将所需的 WPP 相关 C 预处理器指令和 WPP 宏调用
添加到
提供程序的源文件
,如将 WPP 软件跟踪添加到 Windows 驱动程序和 WPP 软件跟踪引用中所述。 - 修改 Visual Studio 项目以运行 WPP 预处理器并生成驱动程序,如将 WPP 软件跟踪添加到 Windows 驱动程序中所述。 有关更多生成时间选项,可以参考WPP预处理器。
安装驱动程序或组件
,启动跟踪会话并记录跟踪消息。 使用 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.EngineCreateQueue函数
NTSTATUS EngineCreateQueue(WDFDEVICE device, XDMA_ENGINE* engine, WDFQUEUE* queue)
// Create a WDF IO queue for a DMA engine
{
NTSTATUS status = STATUS_SUCCESS;
WDF_IO_QUEUE_CONFIG config;
WDF_OBJECT_ATTRIBUTES attribs;
PQUEUE_CONTEXT context;
PAGED_CODE();
// engine queue is sequential
WDF_IO_QUEUE_CONFIG_INIT(&config, WdfIoQueueDispatchSequential);
ASSERTMSG("direction is neither H2C nor C2H!", (engine->dir == C2H) || (engine->dir == H2C));
if (engine->dir == H2C)
{
// callback handler for write requests
config.EvtIoWrite = EvtIoWriteDma;
TraceInfo(DBG_INIT, "EvtIoWrite=EvtIoWriteDma");
}
else if (engine->dir == C2H)
{
// callback handler for read requests
if (engine->type == EngineType_ST)
{
config.EvtIoRead = EvtIoReadEngineRing;
TraceInfo(DBG_INIT, "EvtIoRead=EvtIoReadEngineRing");
}
else
{
config.EvtIoRead = EvtIoReadDma;
TraceInfo(DBG_INIT, "EvtIoRead=EvtIoReadDma");
}
}
// serialize all callbacks related to this queue. see ref [2]
WDF_OBJECT_ATTRIBUTES_INIT(&attribs);
attribs.SynchronizationScope = WdfSynchronizationScopeQueue;
WDF_OBJECT_ATTRIBUTES_SET_CONTEXT_TYPE(&attribs, QUEUE_CONTEXT);
status = WdfIoQueueCreate(device, &config, &attribs, queue);
if (!NT_SUCCESS(status))
{
TraceError(DBG_INIT, "WdfIoQueueCreate failed %d", status);
return status;
}
// store arguments into queue context
context = GetQueueContext(*queue);
context->engine = engine;
return status;
}
10.EvtDeviceCleanup函数
VOID EvtDeviceCleanup(IN WDFOBJECT device)
{
UNREFERENCED_PARAMETER(device);
TraceInfo(DBG_INIT, "%!FUNC!");
}
2、driver.c
转载:https://blog.csdn.net/kris_paul/article/details/128019857