上次讲到了,Windows驱动中的资源,今天着重讲一下,Windows资源,如何使用。关于中断,内存,IO这些,以后再讲。今天重点讲一下,总线驱动的引用。我们知道,在设备驱动中,经常会使用I2C,GPIO这些资源,但是我们如何进行操作了,当然,我们可以直接进行IO映射。直接I2C,GPIO地址进行操作,但是这种方式不安全,且MSFT不推荐。所以,MSFT在后来的CM_PARTIAL_RESOURCE_DESCRIPTOR结构体中,加上了如下结构体:
95. struct {
96. UCHAR Class;
97. UCHAR Type;
98. UCHAR Reserved1;
99. UCHAR Reserved2;
100. ULONG IdLowPart;
101. ULONG IdHighPart;
102. } Connection;
我们通过这个结构体,可以得到一个对我们配置资源总线的一个引用。即通过这个域,我们可以把它组成了一ID,通过这个ID,就可以和我们设备驱动底层的GPIO,I2C这些驱动进行通讯。
下面贴出MSDN中的一个实例。
NTSTATUS
EvtDevicePrepareHardware(
_In_ WDFDEVICE Device,
_In_ WDFCMRESLIST ResourcesRaw,
_In_ WDFCMRESLIST ResourcesTranslated
)
{
int ResourceCount, Index;
PCM_PARTIAL_RESOURCE_DESCRIPTOR Descriptor;
XYZ_DEVICE_CONTEXT *DeviceExtension;
...
DeviceExtension = XyzDrvGetDeviceExtension(Device);
ResourceCount = WdfCmResourceListGetCount(ResourcesTranslated);
for (Index = 0; Index < ResourceCount; Index += 1) {
Descriptor = WdfCmResourceListGetDescriptor(ResourcesTranslated, Index);
switch (Descriptor->Type) {
//
// GPIO I/O descriptors
//
case CmResourceTypeConnection:
//
// Check against expected connection type.
//
if ((Descriptor->u.Connection.Class == CM_RESOURCE_CONNECTION_CLASS_GPIO) &&
(Descriptor->u.Connection.Type == CM_RESOURCE_CONNECTION_TYPE_GPIO_IO)) {
DeviceExtension->ConnectionId.LowPart = Descriptor->u.Connection.IdLowPart;
DeviceExtension->ConnectionId.HighPart = Descriptor->u.Connection.IdHighPart;
...
}
我们得到这个描述符。就可以进行和目标IO进行通讯了,继续看:
NTSTATUS IoRoutine(WDFDEVICE Device)
{
XYZ_DEVICE_CONTEXT *DeviceExtension;
UNICODE_STRING ReadString;
WCHAR ReadStringBuffer[100];
WDF_OBJECT_ATTRIBUTES ObjectAttributes;
WDF_IO_TARGET_OPEN_PARAMS OpenParams;
WDFIOTARGET IoTarget;
BOOL DesiredAccess;
NTSTATUS Status;
DeviceExtension = XyzDrvGetDeviceExtension(Device);
RtlInitEmptyUnicodeString(&ReadString,
ReadStringBuffer,
sizeof(ReadStringBuffer));
Status = RESOURCE_HUB_CREATE_PATH_FROM_ID(&ReadString,
DeviceExtension->ConnectionId.LowPart,
DeviceExtension->ConnectionId.HighPart);
NT_ASSERT(NT_SUCCESS(Status));
WDF_OBJECT_ATTRIBUTES_INIT(&ObjectAttributes);
ObjectAttributes.ParentObject = Device;
Status = WdfIoTargetCreate(Device, &ObjectAttributes, &IoTarget);
if (!NT_SUCCESS(Status)) {
goto IoErrorEnd;
}
if (ReadOperation != FALSE) {
DesiredAccess = FILE_GENERIC_READ;
} else {
DesiredAccess = FILE_GENERIC_WRITE;
}
WDF_IO_TARGET_OPEN_PARAMS_INIT_OPEN_BY_NAME(&OpenParams, ReadString, DesiredAccess);
Status = WdfIoTargetOpen(IoTarget, &OpenParams);
if (!NT_SUCCESS(Status)) {
goto IoErrorEnd;
}
...
再看实际的GPIO读写状态:
WDF_OBJECT_ATTRIBUTES RequestAttributes;
WDF_OBJECT_ATTRIBUTES Attributes;
WDF_REQUEST_SEND_OPTIONS SendOptions;
WDFREQUEST IoctlRequest;
WDFIOTARGET IoTarget;
WDFMEMORY WdfMemory;
NTSTATUS Status;
WDF_OBJECT_ATTRIBUTES_INIT(&RequestAttributes);
Status = WdfRequestCreate(&RequestAttributes, IoTarget, &IoctlRequest);
if (!NT_SUCCESS(Status)) {
goto RwErrorExit;
}
//
// Set up a WDF memory object for the IOCTL request.
//
WDF_OBJECT_ATTRIBUTES_INIT(&Attributes);
Attributes.ParentObject = IoctlRequest;
Status = WdfMemoryCreatePreallocated(&Attributes, Data, Size, &WdfMemory);
if (!NT_SUCCESS(Status)) {
goto RwErrorExit;
}
//
// Format the request.
//
if (ReadOperation != FALSE) {
Status = WdfIoTargetFormatRequestForIoctl(IoTarget,
IoctlRequest,
IOCTL_GPIO_READ_PINS,
NULL,
0,
WdfMemory,
0);
} else {
Status = WdfIoTargetFormatRequestForIoctl(IoTarget,
IoctlRequest,
IOCTL_GPIO_WRITE_PINS,
WdfMemory,
0,
WdfMemory,
0);
}
if (!NT_SUCCESS(Status)) {
goto RwErrorExit;
}
//
// Send the request synchronously (with a 60-second time-out).
//
WDF_REQUEST_SEND_OPTIONS_INIT(&SendOptions,
WDF_REQUEST_SEND_OPTION_SYNCHRONOUS);
WDF_REQUEST_SEND_OPTIONS_SET_TIMEOUT(&SendOptions,
WDF_REL_TIMEOUT_IN_SEC(60));
Status = WdfRequestAllocateTimer(IoctlRequest);
if (!NT_SUCCESS(Status)) {
goto RwErrorExit;
}
if (!WdfRequestSend(IoctlRequest, IoTarget, &SendOptions)) {
Status = WdfRequestGetStatus(IoctlRequest);
}
...
我们再来看下Connection的MSDN的说明:
先看Class:
CM_RESOURCE_CONNECTION_CLASS_GPIO或者CM_RESOURCE_CONNECTION_CLASS_SERIAL
再看Type:
CM_RESOURCE_CONNECTION_TYPE_GPIO_IO或者CM_RESOURCE_CONNECTION_TYPE_SERIAL_I2C,CM_RESOURCE_CONNECTION_TYPE_SERIAL_SPI,CM_RESOURCE_CONNECTION_TYPE_SERIAL_UART
IdLowPart:64位连接ID的低32位。
IdHighPart:64位连接ID的高32位。
我们可以举一反三,在I2C, SPI中都可以这样用,当然,有使用,MSFT为我们封装了后序的IOCTL的操作,比如,直接提供I2CReadRegister,或者I2CWriteRegister.