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

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

이 드라이버는 IPnpCallbackHardware2 인터페이스를 구현하고 드라이버의 IDriverEntry::OnDeviceAdd 메서드를 호출하는 동안 이 인터페이스를 UMDF에 등록합니다. 프레임워크는 IPnpCallbackHardware2 인터페이스의 메서드를 호출하여 드라이버에 디바이스의 전원 상태 변경 내용을 알립니다.

전원이 SPB에 연결된 주변 장치 디바이스로 복원되면 드라이버 프레임워크는 IPnpCallbackHardware2::OnPrepareHardware 메서드를 호출하여 이 디바이스를 사용하도록 준비해야 함을 드라이버에 알립니다. 이 호출 중에 드라이버는 두 개의 하드웨어 리소스 목록을 입력 매개 변수로 받습니다. pWdfResourcesRaw 매개 변수는 원시 리소스 목록을 가리키고 pWdfResourcesTranslated 매개 변수는 번역된 리소스 목록을 가리킵니다. 두 매개 변수는 모두 IWDFCmResourceList 개체에 대한 포인터입니다. 변환된 리소스에는 SPB 주변 장치 드라이버가 SPB 연결 주변 디바이스에 대한 논리적 연결을 설정하는 데 필요한 연결 ID 가 포함됩니다. 자세한 내용은 SPB 주변 장치용 연결 ID를 참조하세요.

UMDF 주변 장치 드라이버가 리소스 목록에서 연결 ID를 받을 수 있도록 하려면 드라이버를 설치하는 INF 파일에 WDF 관련 DDInstall 섹션에 다음 지시문을 포함해야 합니다.

UmdfDirectHardwareAccess = AllowDirectHardwareAccess 이 지시문에 대한 자세한 내용은 INF 파일에서 WDF 지시문 지정을 참조하세요.

다음 코드 예제에서는 드라이버의 OnPrepareHardware 메서드가 pWdfResourcesTranslated 매개 변수에서 연결 ID를 가져오는 방법을 보여 줍니다.

BOOLEAN fConnectIdFound = FALSE;
BOOLEAN fDuplicateFound = FALSE;
LARGE_INTEGER connectionId = 0;
ULONG resourceCount;

resourceCount = pWdfResourcesTranslated->GetCount();

// Loop through the resources and save the relevant ones.
for (ULONG ix = 0; ix < resourceCount; ix++)
{
    PCM_PARTIAL_RESOURCE_DESCRIPTOR pDescriptor;

    pDescriptor = pWdfResourcesTranslated->GetDescriptor(ix);

    if (pDescriptor == NULL)
    {
        hr = 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 (fConnIdFound == FALSE)
                    {
                        // Save the SPB connection ID.
                        connectionId.LowPart = pDescriptor->u.Connection.IdLowPart;
                        connectionId.HighPart = pDescriptor->u.Connection.IdHighPart;
                        fConnectIdFound = TRUE;
                    }
                    else
                    {
                        fDuplicateFound = TRUE;
                    }
                }
            }

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

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

    default:
        // Ignore all other resource descriptors.
        break;
    }
}

앞의 코드 예제에서는 SPB 연결 주변 디바이스의 연결 ID를 라는 connectionId변수에 복사합니다. 다음 코드 예제에서는 주변 디바이스를 식별하는 데 사용할 수 있는 디바이스 경로 이름에 연결 ID를 통합하는 방법을 보여 줍니다.

WCHAR szTargetPath[100];
HRESULT hres;

// Create the device path using the well-known resource hub
// path name and the connection ID.
//
// TODO: Replace this hardcoded string with the appropriate
//       helper method from Reshub.h when available.
hres = StringCbPrintfW(&szTargetPath[0],
                       sizeof(szTargetPath),
                       L"\\\\.\\RESOURCE_HUB\\%0*I64x",
                       (size_t)(sizeof(LARGE_INTEGER) * 2),
                       connectionId.QuadPart);
if (FAILED(hres))
{
     // Error handling
     ...
}

앞의 코드 예제에서는 SPB 연결 주변 디바이스의 경로 이름을 배열에 szTargetPath 씁니다. 다음 코드 예제에서는 이 디바이스 경로 이름을 사용하여 SPB 연결 주변 디바이스에 대한 파일 핸들을 엽니다.

UMDF_IO_TARGET_OPEN_PARAMS openParams;

openParams.dwShareMode = 0;
openParams.dwCreationDisposition = OPEN_EXISTING;
openParams.dwFlagsAndAttributes = FILE_FLAG_OVERLAPPED;
hres = pRemoteTarget->OpenFileByName(&szTargetPath[0],
                                     (GENERIC_READ | GENERIC_WRITE),
                                     &openParams);
if (FAILED(hres))
{
    // Error handling
    ...
}

앞의 코드 예제 pRemoteTarget 에서 변수는 IWDFRemoteTarget 개체에 대한 포인터입니다. IWDFRemoteTarget::OpenFileByName 메서드에 대한 호출이 성공하면 SPB 연결 주변 장치용 드라이버는 IWDFRemoteTarget 개체를 사용하여 주변 장치 디바이스에 I/O 요청을 보낼 수 있습니다. 드라이버가 주변 장치에 읽기, 쓰기 또는 IOCTL 요청을 보내기 전에 드라이버는 IWDFRemoteTarget::FormatRequestForRead, IWDFRemoteTarget::FormatRequestForWrite 또는 IWDFRemoteTarget::FormatRequestForIoctl 메서드를 호출하여 I/O 요청의 형식을 지정합니다. IWDFRemoteTarget 인터페이스는 IWDFIoTarget 인터페이스에서 이러한 세 가지 메서드를 상속합니다. 다음으로 드라이버는 IWDFIoRequest::Send 메서드를 호출하여 I/O 요청을 SPB에 연결된 주변 장치 디바이스로 보냅니다.

다음 코드 예제에서 SPB 주변 장치 드라이버는 Send 메서드를 호출하여 SPB 에 연결된 주변 디바이스에 IRP_MJ_WRITE 요청을 보냅니다.

HRESULT hres;
IWDFMemory *pInputMemory = NULL;
IWDFRemoteTarget *pWdfIoRequest = NULL;

// Create a new I/O request.
if (SUCCEEDED(hres))
{
    hres = pWdfDevice->CreateRequest(NULL, 
                                     pWdfDevice, 
                                     &pWdfIoRequest);
    if (FAILED(hres))
    {
        // Error handling
        ...
    }
}

// Allocate memory for the input buffer.
if (SUCCEEDED(hres))
{
    hres = pWdfDriver->CreatePreallocatedWdfMemory(pInBuffer, 
                                                   inBufferSize, 
                                                   NULL,
                                                   pWdfIoRequest,
                                                   &pInputMemory);
    if (FAILED(hres))
    {
        // Error handling
        ...
    }

    // After this call, the parent holds the only reference.
    pWdfMemory->Release();
}

// Format the I/O request for a write operation.
if (SUCCEEDED(hres))
{
    hres = pRemoteTarget->FormatRequestForWrite(pWdfIoRequest,
                                                NULL,
                                                pInputMemory, 
                                                NULL, 
                                                0);
    if (FAILED(hres))
    {
        // Error handling
        ...
    }
}

// Send the request to the SPB controller.
if (SUCCEEDED(hres))
{
    ULONG Flags = fSynchronous ? WDF_REQUEST_SEND_OPTION_SYNCHRONOUS : 0;

    // Set the I/O completion callback.
    if (!fSynchronous)
    {
        pWdfIoRequest->SetCompletionCallback(pCallback, NULL);
    }

    hres = pWdfIoRequest->Send(pRemoteTarget, Flags, 0);
    if (FAILED(hres))
    {
        // Error handling
        ...
    }
}

if (fSynchronous || FAILED(hres))
{
    pWdfIoRequest->DeleteWdfObject();
    SAFE_RELEASE(pWdfIoRequest);
}

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

  1. 변수는 pWdfDevice SPB 연결 주변 디바이스를 나타내는 프레임워크 디바이스 개체의 IWDFDevice 인터페이스에 대한 포인터입니다. IWDFDevice::CreateRequest 메서드는 I/O 요청을 만들고 변수가 가리키는 pWdfIoRequestIWDFIoRequest 인터페이스 instance 이 요청을 캡슐화합니다.
  2. 변수는 pWdfDriver SPB 주변 장치 드라이버를 나타내는 프레임워크 드라이버 개체의 IWDFDriver 인터페이스에 대한 포인터입니다. 및 inBufferSize 변수는 pInBuffer 쓰기 요청에 대한 데이터를 포함하는 입력 버퍼의 주소와 크기를 지정합니다. IWDFDriver::CreatePreallocatedWdfMemory 메서드는 입력 버퍼에 대한 프레임워크 메모리 개체를 만들고 에서 가리키는 pWdfIoRequestIWDFIoRequest 개체를 메모리 개체의 부모 개체로 지정합니다(부모가 해제될 때 메모리 개체가 자동으로 해제되도록). 드라이버가 Release 메서드를 호출하여 메모리 개체에 대한 로컬 참조를 해제한 후 부모는 이 개체에 대한 유일한 참조를 보유합니다.
  3. 변수는 pWdfRemoteTarget 이전 코드 예제의 OpenFileByName 호출에서 가져온 원격 대상 포인터입니다. IWDFRemoteTarget::FormatRequestForWrite 메서드는 쓰기 작업에 대한 I/O 요청의 형식을 지정합니다.
  4. fSynchronous 쓰기 요청이 동기적으로 전송될 경우 변수는 TRUE이고, 비동기적으로 전송될 경우 FALSE입니다. pCallback 변수는 이전에 만든 IRequestCallbackRequestCompletion 인터페이스에 대한 포인터입니다. 요청을 비동기적으로 보내려면 IWDFIoRequest::SetCompletionCallback 메서드에 대한 호출이 이 인터페이스를 등록합니다. 나중에 요청이 비동기적으로 완료되면 드라이버에 알리기 위해 IRequestCallbackRequestCompletion::OnCompletion 메서드가 호출됩니다.
  5. Send 메서드는 형식이 지정된 쓰기 요청을 SPB 연결 주변 장치 디바이스로 보냅니다. 변수는 Flags 쓰기 요청을 동기적으로 또는 비동기적으로 보낼지 여부를 나타냅니다.
  6. 요청이 동기적으로 전송되면 IWDFIoRequest::D eleteWdfObject 메서드는 가 가리키는 pWdfIoRequest I/O 요청 개체와 가 가리키는 자식 개체를 pInputMemory모두 삭제합니다. IWDFIoRequest 인터페이스는 IWDFObject 인터페이스에서 이 메서드를 상속합니다. 요청이 비동기적으로 전송되는 경우 나중에 드라이버의 OnCompletion 메서드에서 DeleteWdfObject 메서드에 대한 호출이 발생해야 합니다.

이전 코드 예제의 대체 구현은 드라이버 초기화 중에 IWDFIoRequestIWDFMemory 개체를 만들고 I/O 요청을 보낼 때마다 새 개체를 만들고 삭제하는 대신 이러한 동일한 개체를 반복적으로 사용할 수 있습니다. 자세한 내용은 IWDFIoRequest2::ReuseIWDFMemory::SetBuffer를 참조하세요.

또한 대체 구현은 보내기 호출이 성공하는 경우 I/O 요청에서 I/O 상태 코드를 검사할 수 있습니다. 자세한 내용은 IWDFIoRequest::GetCompletionParams를 참조하세요.