다음을 통해 공유


Kernel-Mode SPB 주변 장치 드라이버용 하드웨어 리소스

이 항목의 코드 예제에서는 간단한 SPB(주변 버스)의 주변 디바이스에 대한 KMDF(커널 모드 드라이버 프레임워크) 드라이버가 디바이스를 작동하는 데 필요한 하드웨어 리소스를 가져오는 방법을 보여 줍니다. 이러한 리소스에는 드라이버가 디바이스에 대한 논리적 연결을 설정하는 데 사용하는 정보가 포함되어 있습니다. 추가 리소스에는 인터럽트 및 하나 이상의 GPIO 입력 또는 출력 핀이 포함될 수 있습니다. (GPIO 핀은 입력 또는 출력으로 구성된 범용 I/O 컨트롤러 디바이스의 핀입니다. 자세한 내용은 GPIO(범용 I/O) 드라이버를 참조하세요.) 메모리 매핑된 디바이스와 달리 SPB 연결 주변 장치는 레지스터를 매핑하기 위해 시스템 메모리 주소 블록이 필요하지 않습니다.

이 드라이버는 플러그 앤 플레이 및 전원 관리 이벤트 콜백 함수 집합을 구현합니다. KMDF에 이러한 함수를 등록하기 위해 드라이버의 EvtDriverDeviceAdd 이벤트 콜백 함수는 WdfDeviceInitSetPnpPowerEventCallbacks 메서드를 호출합니다. 프레임워크는 전원 관리 이벤트 콜백 함수를 호출하여 드라이버에 주변 디바이스의 전원 상태 변경 내용을 알립니다. 이러한 함수에는 드라이버에서 디바이스에 액세스할 수 있도록 하는 데 필요한 모든 작업을 수행하는 EvtDevicePrepareHardware 함수가 포함되어 있습니다.

전원이 주변 디바이스로 복원되면 드라이버 프레임워크는 EvtDevicePrepareHardware 함수를 호출하여 SPB 주변 장치 드라이버에 이 디바이스를 사용하도록 준비해야 함을 알립니다. 이 호출 중에 드라이버는 두 개의 하드웨어 리소스 목록을 입력 매개 변수로 받습니다. ResourcesRaw 매개 변수는 원시 리소스 목록에 대한 WDFCMRESLIST 개체 핸들이며, ResourcesTranslated 매개 변수는 번역된 리소스 목록에 대한 WDFCMRESLIST 개체 핸들입니다. 변환된 리소스에는 드라이버가 주변 디바이스에 대한 논리적 연결을 설정하는 데 필요한 연결 ID 가 포함됩니다. 자세한 내용은 SPB-Connected 주변 장치에 대한 연결 ID를 참조하세요.

다음 코드 예제에서는 EvtDevicePrepareHardware 함수가 ResourcesTranslated 매개 변수에서 연결 ID를 가져오는 방법을 보여 줍니다.

BOOLEAN fConnectionIdFound = FALSE;
LARGE_INTEGER connectionId = 0;
ULONG resourceCount;
NTSTATUS status = STATUS_SUCCESS;

resourceCount = WdfCmResourceListGetCount(ResourcesTranslated);

// Loop through the resources and save the relevant ones.

for (ULONG ix = 0; ix < resourceCount; ix++)
{
    PCM_PARTIAL_RESOURCE_DESCRIPTOR pDescriptor;

    pDescriptor = WdfCmResourceListGetDescriptor(ResourcesTranslated, ix);

    if (pDescriptor == NULL)
    {
        status = E_POINTER;
        break;
    }

    // Determine the resource type.
    switch (pDescriptor->Type)
    {
    case CmResourceTypeConnection:
        {
            // Check against the expected connection types.

            UCHAR Class = pDescriptor->u.Connection.Class;
            UCHAR Type = pDescriptor->u.Connection.Type;

            if (Class == CM_RESOURCE_CONNECTION_CLASS_SERIAL)
            {
                if (Type == CM_RESOURCE_CONNECTION_TYPE_SERIAL_I2C)
                {
                    if (fConnectionIdFound == FALSE)
                    {
                        // Save the SPB connection ID.

                        connectionId.LowPart = pDescriptor->u.Connection.IdLowPart;
                        connectionId.HighPart = pDescriptor->u.Connection.IdHighPart;
                        fConnectionIdFound = TRUE;
                    }
                }
            }

            if (Class == CM_RESOURCE_CONNECTION_CLASS_GPIO)
            {
                // Check for GPIO pin resource.
                ...
            }
        }
        break;

    case CmResourceTypeInterrupt:
        {
            // Check for interrupt resource.
            ...
        }
        break;

    default:
        // Don't care about other resource descriptors.
        break;
    }
}

앞의 코드 예제에서는 SPB 연결 주변 디바이스의 연결 ID를 라는 connectionId변수에 복사합니다.

다음 코드 예제에서는 주변 디바이스에 대한 논리적 연결을 여는 데 사용할 수 있는 디바이스 경로 이름에 이 연결 ID를 통합하는 방법을 보여 줍니다. 이 디바이스 경로 이름은 리소스 허브를 주변 디바이스에 액세스하는 데 필요한 매개 변수를 가져올 시스템 구성 요소로 식별합니다.

// Use the connection ID to create the full device path name.
 
DECLARE_UNICODE_STRING_SIZE(szDeviceName, RESOURCE_HUB_PATH_SIZE);

status = RESOURCE_HUB_CREATE_PATH_FROM_ID(&szDeviceName,
                                          connectionId.LowPart,
                                          connectionId.HighPart);

if (!NT_SUCCESS(status))
{
     // Error handling
     ...
}

앞의 코드 예제에서 DECLARE_UNICODE_STRING_SIZE 매크로는 리소스 허브에서 사용하는 형식의 디바이스 경로 이름을 포함할 수 있을 만큼 큰 버퍼가 있는 라는 szDeviceName 초기화된 UNICODE_STRING 변수의 선언을 만듭니다. 이 매크로는 Ntdef.h 헤더 파일에 정의되어 있습니다. RESOURCE_HUB_PATH_SIZE 상수는 디바이스 경로 이름의 바이트 수를 지정합니다. RESOURCE_HUB_CREATE_PATH_FROM_ID 매크로는 연결 ID에서 디바이스 경로 이름을 생성합니다. RESOURCE_HUB_PATH_SIZERESOURCE_HUB_CREATE_PATH_FROM_ID Reshub.h 헤더 파일에 정의되어 있습니다.

다음 코드 예제에서는 디바이스 경로 이름을 사용하여 SPB에 연결된 주변 디바이스에 대한 파일 핸들(명명됨 SpbIoTarget)을 엽니다.

// Open the SPB peripheral device as a remote I/O target.
 
WDF_IO_TARGET_OPEN_PARAMS openParams;
WDF_IO_TARGET_OPEN_PARAMS_INIT_OPEN_BY_NAME(&openParams,
                                            &szDeviceName,
                                            (GENERIC_READ | GENERIC_WRITE));

openParams.ShareAccess = 0;
openParams.CreateDisposition = FILE_OPEN;
openParams.FileAttributes = FILE_ATTRIBUTE_NORMAL;

status = WdfIoTargetOpen(SpbIoTarget, &openParams);

if (!NT_SUCCESS(status))
{
    // Error handling
    ...
}

앞의 코드 예제에서 WDF_IO_TARGET_OPEN_PARAMS_INIT_OPEN_BY_NAME 함수는 드라이버 가 디바이스의 이름을 지정하여 주변 디바이스에 대한 논리적 연결을 열 수 있도록 WDF_IO_TARGET_OPEN_PARAMS 구조를 초기화합니다. 변수는 SpbIoTarget 프레임워크 I/O 대상 개체에 대한 WDFIOTARGET 핸들입니다. 이 핸들은 예제에 표시되지 않는 WdfIoTargetCreate 메서드에 대한 이전 호출에서 가져옵니다. WdfIoTargetOpen 메서드에 대한 호출이 성공하면 드라이버는 핸들을 SpbIoTarget 사용하여 주변 디바이스에 I/O 요청을 보낼 수 있습니다.

EvtDriverDeviceAdd 이벤트 콜백 함수에서 SPB 주변 장치 드라이버는 WdfRequestCreate 메서드를 호출하여 드라이버에서 사용할 프레임워크 요청 개체를 할당할 수 있습니다. 나중에 개체가 더 이상 필요하지 않으면 드라이버가 WdfObjectDelete 메서드를 호출하여 개체를 삭제합니다. 드라이버는 WdfRequestCreate 호출에서 가져온 프레임워크 요청 개체를 여러 번 다시 사용하여 주변 디바이스에 I/O 요청을 보낼 수 있습니다. 읽기, 쓰기 또는 IOCTL 요청의 경우 드라이버는 WdfIoTargetSendReadSynchronously, WdfIoTargetSendWriteSynchronously 또는 WdfIoTargetSendIoctlSynchronously 메서드를 호출하여 요청을 보냅니다.

다음 코드 예제에서 드라이버는 WdfIoTargetSendWriteSynchronously 를 호출하여 SPB 에 연결된 주변 디바이스에 IRP_MJ_WRITE 요청을 동기적으로 보냅니다. 이 예제 pBuffer 의 시작 부분에 변수는 주변 디바이스에 쓸 데이터가 포함된 페이지가 없는 버퍼를 가리키고 dataSize 변수는 이 데이터의 크기를 바이트 단위로 지정합니다.

ULONG_PTR bytesWritten;
NTSTATUS status;

// Describe the input buffer.

WDF_MEMORY_DESCRIPTOR memoryDescriptor;
WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&memoryDescriptor, pBuffer, dataSize);

// Configure the write request to time out after 2 seconds.

WDF_REQUEST_SEND_OPTIONS requestOptions;
WDF_REQUEST_SEND_OPTIONS_INIT(&requestOptions, WDF_REQUEST_SEND_OPTION_TIMEOUT);
requestOptions.Timeout = WDF_REL_TIMEOUT_IN_SEC(2);

// Send the write request synchronously.

status = WdfIoTargetSendWriteSynchronously(SpbIoTarget,
                                           SpbRequest,
                                           &memoryDescriptor,
                                           NULL,
                                           &requestOptions,
                                           &bytesWritten);
if (!NT_SUCCESS(status))
{
    // Error handling
    ...
}

앞의 코드 예제에서는 다음을 수행합니다.

  1. WDF_MEMORY_DESCRIPTOR_INIT_BUFFER 함수 호출은 입력 버퍼를 memoryDescriptor 설명하는 WDF_MEMORY_DESCRIPTOR 구조체인 변수를 초기화합니다. 이전에는 드라이버가 ExAllocatePoolWithTag 와 같은 루틴을 호출하여 페이지가 없는 풀에서 버퍼를 할당하고 쓰기 데이터를 이 버퍼에 복사했습니다.
  2. WDF_REQUEST_SEND_OPTIONS_INIT 함수 호출은 쓰기 요청에 대한 선택적 설정을 포함하는 WDF_REQUEST_SEND_OPTIONS 구조체인 변수를 초기화 requestOptions 합니다. 이 예제에서 구조체는 요청이 2초 후에 완료되지 않으면 시간 초과하도록 구성합니다.
  3. WdfIoTargetSendWriteSynchronously 메서드에 대한 호출은 쓰기 요청을 SPB 연결 주변 장치로 보냅니다. 메서드는 쓰기 작업이 완료되거나 시간이 초과된 후 동기적으로 반환됩니다. 필요한 경우 다른 드라이버 스레드 가 WdfRequestCancelSentRequest 를 호출하여 요청을 취소할 수 있습니다.

WdfIoTargetSendWriteSynchronously 호출에서 드라이버는 드라이버가 이전에 만든 프레임워크 요청 개체에 대한 핸들인 이라는 SpbRequest변수를 제공합니다. WdfIoTargetSendWriteSynchronousously 호출 후 드라이버는 일반적으로 WdfRequestReuse 메서드를 호출하여 프레임워크 요청 개체를 다시 사용할 준비를 해야 합니다.