다음을 통해 공유


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 샘플 드라이버를 참조하세요.