KMDF 드라이버를 GPIO I/O 핀에 연결
GPIO I/O 리소스는 데이터 입력 또는 데이터 출력으로 구성된 하나 이상의 GPIO 핀 집합입니다. 이러한 핀에 물리적으로 연결하는 주변 장치용 드라이버는 운영 체제에서 해당 GPIO I/O 리소스를 획득합니다. 주변 장치 드라이버는 이 리소스의 GPIO 핀에 대한 연결을 열고 이 연결을 나타내는 핸들에 I/O 요청을 보냅니다.
다음 코드 예제에서는 주변 디바이스에 대한 KMDF(커널 모드 드라이버 프레임워크) 드라이버가 플러그 앤 플레이(PnP) 관리자가 드라이버에 할당한 GPIO I/O 리소스에 대한 설명을 가져오는 방법을 보여 줍니다.
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;
...
}
앞의 코드 예제 DeviceExtension
에서 변수는 주변 디바이스에 대한 디바이스 컨텍스트에 대한 포인터입니다. 이 XyzDrvGetDeviceExtension
디바이스 컨텍스트를 검색하는 함수는 주변 디바이스 드라이버에 의해 구현됩니다. 이 드라이버는 이전에 WdfDeviceInitSetPnpPowerEventCallbacks 메서드를 호출하여 EvtDevicePrepareHardware 콜백 함수를 등록했습니다.
다음 코드 예제에서는 주변 장치 드라이버가 이전 코드 예제에서 가져온 GPIO 리소스 설명을 사용하여 드라이버의 GPIO I/O 리소스에 대한 WDFIOTARGET 핸들을 여는 방법을 보여 줍니다.
NTSTATUS IoRoutine(WDFDEVICE Device, BOOLEAN ReadOperation)
{
WDFIOTARGET IoTarget;
XYZ_DEVICE_CONTEXT *DeviceExtension;
UNICODE_STRING ReadString;
WCHAR ReadStringBuffer[100];;
BOOL DesiredAccess;
NTSTATUS Status;
WDF_OBJECT_ATTRIBUTES ObjectAttributes;
WDF_IO_TARGET_OPEN_PARAMS OpenParams
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 = GENERIC_READ;
} else {
DesiredAccess = GENERIC_WRITE;
}
WDF_IO_TARGET_OPEN_PARAMS_INIT_OPEN_BY_NAME(&OpenParams, ReadString, DesiredAccess);
Status = WdfIoTargetOpen(IoTarget, &OpenParams);
if (!NT_SUCCESS(Status)) {
goto IoErrorEnd;
}
...
앞의 코드 예제 Device
에서 변수는 주변 디바이스에 대한 프레임워크 디바이스 개체에 대한 WDFDEVICE 핸들입니다.
RESOURCE_HUB_CREATE_PATH_FROM_ID 함수는 GPIO I/O 리소스의 이름을 포함하는 문자열을 만듭니다. 코드 예제에서는 이 문자열을 사용하여 이름으로 GPIO I/O 리소스를 엽니다.
주변 장치 드라이버가 GPIO I/O 리소스에 대한 핸들을 가져온 후 이 드라이버는 I/O 제어 요청을 보내 데이터를 읽거나 GPIO 핀에 데이터를 쓸 수 있습니다. 읽기용 GPIO I/O 리소스를 여는 드라이버는 IOCTL_GPIO_READ_PINS I/O 컨트롤 요청을 사용하여 리소스의 핀에서 데이터를 읽습니다. 쓰기용 GPIO I/O 리소스를 여는 드라이버는 IOCTL_GPIO_WRITE_PINS I/O 컨트롤 요청을 사용하여 리소스의 핀에 데이터를 씁니다. 다음 코드 예제에서는 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);
}
...
앞의 코드 예제에서 는 Data
데이터 버퍼에 대한 포인터이고, Size
는 이 데이터 버퍼의 크기(바이트)이며 ReadOperation
요청된 작업이 읽기(TRUE) 또는 쓰기(FALSE)인지 여부를 나타냅니다.
참조 항목
데이터 입력 핀을 요청 출력 버퍼의 비트에 매핑하는 등 IOCTL_GPIO_READ_PINS 요청에 대한 자세한 내용은 IOCTL_GPIO_READ_PINS 참조하세요. 요청 입력 버퍼의 비트를 데이터 출력 핀에 매핑하는 등 IOCTL_GPIO_WRITE_PINS 요청에 대한 자세한 내용은 IOCTL_GPIO_WRITE_PINS.
커널 모드에서 실행되는 GPIO 주변 장치 드라이버를 작성하는 방법을 보여 주는 샘플 드라이버는 GitHub의 GPIO 샘플 드라이버 컬렉션에서 SimDevice 샘플 드라이버를 참조하세요.