Windows驱动PCIe DMA实例详解

以下是一个更详细的PCIe DMA驱动实现示例:

1. 首先,定义必要的结构和常量:

```c
#include <ntddk.h>
#include <wdf.h>

#define PCIE_DEVICE_ID 0x1234  // 替换为实际的设备ID
#define DMA_BUFFER_SIZE 0x1000 // 4KB

typedef struct _QUEUE_CONTEXT {
    WDFQUEUE Queue;
    WDFDMAENABLER DmaEnabler;
    WDFDMATRANSACTION DmaTransaction;
} QUEUE_CONTEXT, *PQUEUE_CONTEXT;

WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(QUEUE_CONTEXT, QueueGetContext)
```

2. 驱动入口点和设备添加函数:

```c
NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) {
    NTSTATUS status;
    WDF_DRIVER_CONFIG config;

    WDF_DRIVER_CONFIG_INIT(&config, EvtDeviceAdd);
    status = WdfDriverCreate(DriverObject, RegistryPath, WDF_NO_OBJECT_ATTRIBUTES, &config, WDF_NO_HANDLE);

    return status;
}

NTSTATUS EvtDeviceAdd(WDFDRIVER Driver, PWDFDEVICE_INIT DeviceInit) {
    NTSTATUS status;
    WDFDEVICE device;
    WDF_OBJECT_ATTRIBUTES deviceAttributes;
    WDF_PNPPOWER_EVENT_CALLBACKS pnpPowerCallbacks;

    UNREFERENCED_PARAMETER(Driver);

    WDF_PNPPOWER_EVENT_CALLBACKS_INIT(&pnpPowerCallbacks);
    pnpPowerCallbacks.EvtDevicePrepareHardware = EvtDevicePrepareHardware;
    WdfDeviceInitSetPnpPowerEventCallbacks(DeviceInit, &pnpPowerCallbacks);

    WDF_OBJECT_ATTRIBUTES_INIT(&deviceAttributes);
    status = WdfDeviceCreate(&DeviceInit, &deviceAttributes, &device);
    if (!NT_SUCCESS(status)) {
        return status;
    }

    status = QueueInitialize(device);
    return status;
}
```

3. 准备硬件和初始化DMA:

```c
NTSTATUS EvtDevicePrepareHardware(WDFDEVICE Device, WDFCMRESLIST ResourceList, WDFCMRESLIST ResourceListTranslated) {
    NTSTATUS status;
    PQUEUE_CONTEXT queueContext;
    WDF_DMA_ENABLER_CONFIG dmaConfig;
    WDFQUEUE queue;

    UNREFERENCED_PARAMETER(ResourceList);
    UNREFERENCED_PARAMETER(ResourceListTranslated);

    queue = WdfDeviceGetIOQueue(Device);
    queueContext = QueueGetContext(queue);

    WDF_DMA_ENABLER_CONFIG_INIT(&dmaConfig, WdfDmaProfileScatterGather64, DMA_BUFFER_SIZE);
    status = WdfDmaEnablerCreate(Device, &dmaConfig, WDF_NO_OBJECT_ATTRIBUTES, &queueContext->DmaEnabler);
    if (!NT_SUCCESS(status)) {
        return status;
    }

    return status;
}

NTSTATUS QueueInitialize(WDFDEVICE Device) {
    NTSTATUS status;
    WDFQUEUE queue;
    WDF_IO_QUEUE_CONFIG queueConfig;
    PQUEUE_CONTEXT queueContext;

    WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&queueConfig, WdfIoQueueDispatchSequential);
    queueConfig.EvtIoRead = EvtIoRead;

    WDF_OBJECT_ATTRIBUTES attributes;
    WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, QUEUE_CONTEXT);

    status = WdfIoQueueCreate(Device, &queueConfig, &attributes, &queue);
    if (!NT_SUCCESS(status)) {
        return status;
    }

    queueContext = QueueGetContext(queue);
    queueContext->Queue = queue;

    return status;
}
```

4. 实现DMA读取操作:

```c
VOID EvtIoRead(WDFQUEUE Queue, WDFREQUEST Request, size_t Length) {
    NTSTATUS status;
    PQUEUE_CONTEXT queueContext;
    WDFMEMORY memory;
    PVOID buffer;

    queueContext = QueueGetContext(Queue);

    status = WdfRequestRetrieveOutputMemory(Request, &memory);
    if (!NT_SUCCESS(status)) {
        WdfRequestComplete(Request, status);
        return;
    }

    buffer = WdfMemoryGetBuffer(memory, NULL);

    status = WdfDmaTransactionCreate(queueContext->DmaEnabler, WDF_NO_OBJECT_ATTRIBUTES, &queueContext->DmaTransaction);
    if (!NT_SUCCESS(status)) {
        WdfRequestComplete(Request, status);
        return;
    }

    status = WdfDmaTransactionInitialize(queueContext->DmaTransaction, EvtProgramDma, EvtDmaCompletionRoutine, EvtDmaCompletionRoutine, Queue);
    if (!NT_SUCCESS(status)) {
        WdfObjectDelete(queueContext->DmaTransaction);
        WdfRequestComplete(Request, status);
        return;
    }

    status = WdfDmaTransactionExecute(queueContext->DmaTransaction, Request);
    if (!NT_SUCCESS(status)) {
        WdfObjectDelete(queueContext->DmaTransaction);
        WdfRequestComplete(Request, status);
    }
}

BOOLEAN EvtProgramDma(WDFDMATRANSACTION Transaction, WDFDEVICE Device, WDFCONTEXT Context, WDF_DMA_DIRECTION Direction, PSCATTER_GATHER_LIST SgList) {
    UNREFERENCED_PARAMETER(Transaction);
    UNREFERENCED_PARAMETER(Device);
    UNREFERENCED_PARAMETER(Context);
    UNREFERENCED_PARAMETER(Direction);

    // 这里应该包含实际的DMA编程代码
    // 例如,设置DMA控制器寄存器,配置源和目标地址等
    // 使用 SgList 来获取物理地址信息

    // 启动DMA传输
    // StartDMATransfer();

    return TRUE;
}

VOID EvtDmaCompletionRoutine(WDFDMATRANSACTION Transaction, WDFDEVICE Device, PVOID Context, WDF_DMA_DIRECTION Direction, DMA_COMPLETION_STATUS Status) {
    WDFREQUEST request;
    NTSTATUS status;

    UNREFERENCED_PARAMETER(Device);
    UNREFERENCED_PARAMETER(Context);
    UNREFERENCED_PARAMETER(Direction);

    request = WdfDmaTransactionGetRequest(Transaction);

    if (Status == DmaComplete) {
        status = STATUS_SUCCESS;
    } else {
        status = STATUS_IO_DEVICE_ERROR;
    }

    WdfRequestCompleteWithInformation(request, status, WdfDmaTransactionGetBytesTransferred(Transaction));
    WdfObjectDelete(Transaction);
}
```

这个示例提供了一个更具体的PCIe DMA驱动实现框架。它使用了Windows Driver Frameworks (WDF),这简化了许多驱动程序开发任务。

关键点:

1. 使用WDF的DMA API来管理DMA操作。
2. 实现了基本的读取操作,使用DMA传输数据。
3. 提供了DMA编程和完成回调函数的框架。

请注意,这个示例仍然需要根据实际的PCIe设备进行定制。你需要:

1. 实现具体的DMA控制器编程逻辑。
2. 添加适当的错误处理和资源管理。
3. 可能需要实现中断处理。
4. 根据设备的具体要求调整DMA配置。

开发PCIe DMA驱动程序是一个复杂的任务,需要深入理解硬件规范和Windows驱动程序模型。建议在实际开发中参考Microsoft的文档和示例,并进行充分的测试。
 

作者:爱学习的大牛123

物联沃分享整理
物联沃-IOTWORD物联网 » Windows驱动PCIe DMA实例详解

发表回复