驱动程序(6) WDF下DMA传输的驱动程序代码详细说明之queue.c

本文深入解析queue.c中关于消息请求队列的处理,包括应用程序通信、DMA传输及MSI中断处理。重点讲解PCIe_EvtIoDeviceControl如何实现用户态与内核态的数据交换,DMA传输过程的详细步骤,以及中断回调PCIe_InterruptMessageService和PCIe_InterruptDpc的执行逻辑。此外,阐述EvtProgramDMA函数中针对特定板卡的DMA寄存器配置,并强调不同板卡可能需要调整的寄存器设置。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

这一篇主要讲queue.c里对于消息请求队列的处理,涉及到了和应用程序的通信,同时为了以后的项目需求,也加入了DMA传输和传输完成后的MSI中断处理。

queue.c

/*++
Module Name: queue.c
Abstract:This file contains the queue entry points and callbacks.And it includes DeviceIoControl function.
Environment: Kernel-mode Driver Framework
Time: 20181015
--*/

#include "driver.h"
BOOLEAN DMATransaction(IN WDFQUEUE Queue, IN WDFREQUEST Request);
//BOOLEAN EvtProgramDMA(IN WDFDMATRANSACTION Transaction, IN WDFDEVICE Device, IN WDFCONTEXT Context, IN WDF_DMA_DIRECTION Direction, IN PSCATTER_GATHER_LIST SgList);

#ifdef ALLOC_PRAGMA
#pragma alloc_text (PAGE, PCIe_EvtIoDeviceControl)
#pragma alloc_text (PAGE, PCIe_EvtIoStop)
#endif

VOID
PCIe_EvtIoDeviceControl(
    _In_ WDFQUEUE Queue,
    _In_ WDFREQUEST Request,
    _In_ size_t OutputBufferLength,
    _In_ size_t InputBufferLength,
    _In_ ULONG IoControlCode
    )
/*++
Routine Description:
    This event is invoked when the framework receives IRP_MJ_DEVICE_CONTROL request.
Arguments:
    Queue -  Handle to the framework queue object that is associated with the
             I/O request.
    Request - Handle to a framework request object.
    OutputBufferLength - Size of the output buffer in bytes
    InputBufferLength - Size of the input buffer in bytes
    IoControlCode - I/O control code.
Return Value:
    VOID
--*/
{
   
    
	//PIRP pIrp;
	//PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(pIrp);
	//ULONG code = stack->Parameters.DeviceIoControl.IoControlCode;
	PAGED_CODE();
	KdPrintEx((DPFLTR_IHVAUDIO_ID, DPFLTR_ERROR_LEVEL, "Function 'EvtIoDeviceControl' begins\n"));

	WDFDEVICE hDevice;
	PDEVICE_CONTEXT pDeviceContext;
	NTSTATUS status;

	PVOID inBuffer;
	PVOID outBuffer;
	ULONG DmaStatus, DmaControl;
	ULONG state;
	ULONG i, j;


	hDevice = WdfIoQueueGetDevice(Queue);
	pDeviceContext = GetDeviceContext(hDevice);  //获取设备上下文句柄

	switch (IoControlCode)
	{
   
   
		//基于WDF框架下的DMA传输操作
	case WDF_DMA:
		KdPrintEx((DPFLTR_IHVAUDIO_ID, DPFLTR_ERROR_LEVEL, "Begin to execute WDF_DMA!\n"));
		
		//获取从应用程序传递过来的数据,并打印出来
		status = WdfRequestRetrieveInputBuffer(Request, sizeof(ULONG), &inBuffer, NULL);
		if (!NT_SUCCESS(status))
		{
   
   
			WdfRequestComplete(Request, STATUS_UNSUCCESSFUL);
			break;
		}
		state = *(ULONG*)inBuffer;
		KdPrintEx((DPFLTR_IHVAUDIO_ID, DPFLTR_ERROR_LEVEL, "received data is %d\n\n", state));
		
		
		ULONG length;
		WDFDMATRANSACTION dmaTransaction;
		dmaTransaction = pDeviceContext->DmaTransaction;

		//测试DMA传输,先给BAR0一段空间赋值,然后将该段数据搬移
		//循环给BAR0赋值,4个Byte编址为一个数,4096字节只能存1024个数
		for (i = 0; i < 1024; i++)
		{
   
   
			WRITE_REGISTER_ULONG(WDF_PTR_ADD_OFFSET(pDeviceContext->MemBaseAddress0, i* 4), i );
			//KdPrintEx((DPFLTR_IHVAUDIO_ID, DPFLTR_ERROR_LEVEL, "%d,", READ_REGISTER_ULONG(WDF_PTR_ADD_OFFSET(pDeviceContext->MemBaseAddress0, i* 4))));
		}

	
		//初始化MDL
		PMDL BufferMdl;
		//PVOID UserModeVirtualAddress, KernelModeVirtualAddress;
		BufferMdl = IoAllocateMdl(pDeviceContext->CommonBufferBase, 4096, FALSE, FALSE, NULL);
		MmBuildMdlForNonPagedPool(BufferMdl);
		MmProbeAndLockPages(BufferMdl, KernelMode, 
### PCIe接口的C语言实现方式与示例代码 #### 1. 基础概念 PCIe(Peripheral Component Interconnect Express)是一种高速串行计算机扩展总线标准,广泛用于连接主机处理器和外围设备。在实际应用中,PCIe设备编程涉及多个复杂操作,例如中断处理、DMA传输等[^1]。 #### 2. 数据结构定义 为了更好地管理PCIe设备的数据流,在驱动程序开发中通常会定义特定的数据结构来存储队列上下文和其他相关信息。以下是基于WDF框架的一个典型数据结构示例: ```c typedef struct _QUEUE_CONTEXT { WDFQUEUE Queue; WDFDMAENABLER DmaEnabler; WDFDMATRANSACTION DmaTransaction; } QUEUE_CONTEXT, *PQUEUE_CONTEXT; ``` 上述代码片段展示了如何通过`_QUEUE_CONTEXT`结构体封装队列及其相关的DMA资源[^2]。 #### 3. 中断处理机制 在PCIe设备通信过程中,中断是一个重要的组成部分。以下是一段简单的伪代码,展示如何注册并处理中断事件: ```c NTSTATUS RegisterInterrupt(WDFDEVICE Device) { WDF_INTERRUPT_CONFIG config; NTSTATUS status; WDF_INTERRUPT_CONFIG_INIT(&config, MyInterruptIsrRoutine, MyDpcForIsr); config.PassiveHandling = FALSE; status = WdfInterruptCreate(Device, &config, NULL); return status; } BOOLEAN MyInterruptIsrRoutine(WDFINTERRUPT Interrupt, ULONG MessageID) { // 处理中断逻辑 return TRUE; } ``` 此部分实现了中断服务例程(ISR),并通过配置指定了相应的回调函数。 #### 4. DMA传输支持 直接内存访问(Direct Memory Access, DMA)允许外设绕过CPU直接存取系统RAM中的数据。下面给出了一种可能的方式来进行DMA初始化以及启动事务的操作: ```c VOID StartDmaTransfer(PQUEUE_CONTEXT Context, PHYSICAL_ADDRESS PhysicalAddress, SIZE_T Length) { NTSTATUS status; status = WdfDmaTransactionInitializeUsingRequest(Context->DmaTransaction, WdfRequestGetIoQueue(Request), WdfRequestRetrieveParameters(Request)); if (!NT_SUCCESS(status)) { DbgPrint("Failed to initialize DMA transaction\n"); return; } status = WdfDmaTransactionSetMaximumLength(Context->DmaTransaction, Length); if (!NT_SUCCESS(status)) { DbgPrint("Failed to set maximum length for DMA transfer\n"); return; } status = WdfDmaTransactionExecute(Context->DmaTransaction, &PhysicalAddress, NULL); if (!NT_SUCCESS(status)) { DbgPrint("Failed to execute DMA transaction\n"); return; } } ``` 该函数负责设置DMA交易的最大长度,并执行具体的物理地址到目标缓冲区之间的数据移动过程。 #### 5. 图像文件读写案例 对于某些特殊用途的PCIe板卡来说,可能会涉及到图像处理任务。这里提供了一个简化版的例子说明如何加载BMP图片文件至内存供后续处理使用: ```c int read_image_file(const char* filename, ImageData *imagedata) { int result = read_image_file_bmp(filename, imagedata); if(result < 0) return 0; /* 如果是损坏的BMP图像 */ if(result > 0) return 1; /* 文件成功加载 */ sprintf(rwif_errormessage, "Read Error: file '%s' is not a BMP image file.", filename); return 0; } ``` 这段代码来自一个具体项目实例,它描述了当尝试打开非BMP类型的图像时所采取的动作[^4]。 --- ###
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值