다음을 통해 공유


직렬 포트에 UMDF 주변 장치 드라이버 연결

SerCx2 관리 직렬 포트의 주변 디바이스에 대한 UMDF 드라이버는 디바이스를 작동하려면 특정 하드웨어 리소스가 필요합니다. 이러한 리소스에는 드라이버가 직렬 포트에 대한 논리적 연결을 여는 데 필요한 정보가 포함되어 있습니다. 추가 리소스에는 인터럽트와 하나 이상의 GPIO 입력 또는 출력 핀이 포함될 수 있습니다.

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

직렬로 연결된 주변 장치 디바이스가 초기화되지 않은 D0 디바이스 전원 상태가 된 후 드라이버 프레임워크는 드라이버의 IPnpCallbackHardware2::OnPrepareHardware 메서드를 호출하여 드라이버에 이 디바이스를 사용하도록 준비하도록 지시합니다. 이 호출 중에 드라이버는 두 개의 하드웨어 리소스 목록을 입력 매개 변수로 받습니다. pWdfResourcesRaw 매개 변수는 원시 리소스 목록을 가리키고 pWdfResourcesTranslated 매개 변수는 번역된 리소스 목록을 가리킵니다. 두 매개 변수는 모두 IWDFCmResourceList 개체에 대한 포인터입니다. 변환된 리소스에는 주변 장치 드라이버가 직렬로 연결된 주변 디바이스에 대한 논리적 연결을 설정하는 데 필요한 연결 ID가 포함됩니다.

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

UmdfDirectHardwareAccess = AllowDirectHardwareAccess 이 지시문에 대한 자세한 내용은 INF 파일에서 WDF 지시문 지정을 참조하세요. 이 지시문을 사용하는 INX 파일(해당 INF 파일을 빌드하는 데 사용)의 예제는 WDK 드라이버 샘플의 SpbAccelerometer를 참조하세요.

다음 코드 예제에서는 드라이버의 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_UART)
                    {
                        if (fConnIdFound == FALSE)
                        {
                            // Save the serial controller's 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;
    }
}

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

WCHAR szTargetPath[100];
HRESULT hres;

// Create the device path using the well-known resource hub
// path name and the connection ID.
//
hres = StringCbPrintfW(&szTargetPath[0],
                       sizeof(DevicePath),
                       L"\\\\.\\RESOURCE_HUB\\%0*I64x",
                       (size_t)(sizeof(LARGE_INTEGER) * 2),
                       connectionId.QuadPart);
if (FAILED(hres))
{
     // Error handling
     ...
}

앞의 코드 예제에서는 직렬 컨트롤러의 디바이스 경로 이름을 배열에 szTargetPath 씁니다. 다음 코드 예제에서는 이 경로 이름을 사용하여 직렬 컨트롤러에 대한 파일 핸들을 엽니다.

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 메서드에 대한 호출이 성공하면 직렬로 연결된 주변 디바이스의 드라이버는 IWDFRemoteTarget 개체를 사용하여 I/O 요청을 직렬 컨트롤러에 보낼 수 있습니다.

주변 디바이스에 읽기 또는 쓰기 요청을 보내려면 드라이버는 먼저 이 개체의 IWDFRemoteTarget::FormatRequestForRead 또는 IWDFRemoteTarget::FormatRequestForWrite 메서드를 호출하여 요청의 형식을 지정합니다. (IWDFRemoteTarget 인터페이스는 IWDFIoTarget 인터페이스에서 이러한 두 메서드를 상속합니다.)

직렬 컨트롤러에 I/O 컨트롤 요청을 보내려면 드라이버는 먼저 IWDFRemoteTarget::FormatRequestForIoctl 메서드를 호출하여 요청의 형식을 지정합니다. (IWDFRemoteTarget 인터페이스는 IWDFIoTarget 인터페이스에서 이 메서드를 상속합니다.) 다음으로 드라이버는 IWDFIoRequest::Send 메서드를 호출하여 직렬로 연결된 주변 디바이스에 I/O 제어 요청을 보냅니다.

다음 코드 예제에서 주변 장치 드라이버는 직렬 컨트롤러에 I/O 컨트롤 요청을 보냅니다.

HRESULT hres;
IWDFMemory *pInputMemory = 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
        ...
    }
}

// Format the request to be an I/O control request.
if (SUCCEEDED(hres))
{
    hres = pRemoteTarget->FormatRequestForIoctl(pWdfIoRequest,
                                                ioctlCode,
                                                NULL,
                                                pInputMemory, 
                                                NULL, 
                                                NULL,
                                                NULL);
    if (FAILED(hres))
    {
        // Error handling
        ...
    }
}

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

    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 직렬로 연결된 주변 장치를 나타내는 프레임워크 디바이스 개체의 IWDFDevice 인터페이스에 대한 포인터입니다. IWDFDevice::CreateRequest 메서드는 I/O 요청을 만들고 매개 변수가 가리키는 pWdfIoRequestIWDFIoRequest 인터페이스 instance 이 요청을 캡슐화합니다. I/O 요청은 나중에 삭제됩니다(6단계 참조). 이 구현은 전송되는 각 I/O 요청에 대한 요청 개체를 만든 다음 삭제하기 때문에 다소 비효율적입니다. 보다 효율적인 방법은 일련의 I/O 요청에 동일한 요청 개체를 다시 사용하는 것입니다. 자세한 내용은 프레임워크 요청 개체 재사용을 참조하세요.

  2. 변수는 pWdfDriver 주변 드라이버를 나타내는 프레임워크 드라이버 개체의 IWDFDriver 인터페이스에 대한 포인터입니다. 및 inBufferSize 변수는 pInBuffer I/O 컨트롤 요청에 대한 입력 버퍼의 주소와 크기를 지정합니다. IWDFDriver::CreatePreallocatedWdfMemory 메서드는 입력 버퍼에 대한 프레임워크 메모리 개체를 만들고 가 가리키는 IWDFIoRequest 개체를 pWdfIoRequest 메모리 개체의 부모 개체로 지정합니다.

  3. 변수는 pWdfRemoteTarget 이전 코드 예제의 OpenFileByName 호출에서 가져온 원격 대상 포인터입니다. IWDFRemoteTarget::FormatRequestForIoctl 메서드는 I/O 컨트롤 작업에 대한 요청의 형식을 지정합니다. ioctlCode 변수는 직렬 I/O 요청 인터페이스의 테이블에 나열된 I/O 컨트롤 코드 중 하나로 설정됩니다.

  4. fSynchronous I/O 컨트롤 요청을 동기적으로 보내려면 변수가 TRUE이고, 비동기적으로 전송될 경우 FALSE입니다. pCallback 변수는 이전에 만든 IRequestCallbackRequestCompletion 인터페이스에 대한 포인터입니다. 요청을 비동기적으로 보내려면 IWDFIoRequest::SetCompletionCallback 메서드에 대한 호출이 이 인터페이스를 등록합니다. 나중에 요청이 비동기적으로 완료되면 드라이버에 알리기 위해 IRequestCallbackRequestCompletion::OnCompletion 메서드가 호출됩니다.

  5. Send 메서드는 형식이 지정된 쓰기 요청을 직렬로 연결된 주변 장치 디바이스로 보냅니다. 변수는 Flags 쓰기 요청을 동기적으로 또는 비동기적으로 보낼지 여부를 나타냅니다.

  6. 요청이 동기적으로 전송되면 IWDFIoRequest::D eleteWdfObject 메서드는 가 가리키는 pWdfIoRequest I/O 요청 개체와 가 가리키는 자식 개체를 pInputMemory모두 삭제합니다. IWDFIoRequest 인터페이스는 IWDFObject 인터페이스에서 이 메서드를 상속합니다. 요청이 비동기적으로 전송되는 경우 나중에 드라이버의 OnCompletion 메서드에서 DeleteWdfObject 메서드에 대한 호출이 발생해야 합니다.