직렬 포트에 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);
}
앞의 코드 예제에서는 다음을 수행합니다.
변수는
pWdfDevice
직렬로 연결된 주변 장치를 나타내는 프레임워크 디바이스 개체의 IWDFDevice 인터페이스에 대한 포인터입니다. IWDFDevice::CreateRequest 메서드는 I/O 요청을 만들고 매개 변수가 가리키는pWdfIoRequest
IWDFIoRequest 인터페이스 instance 이 요청을 캡슐화합니다. I/O 요청은 나중에 삭제됩니다(6단계 참조). 이 구현은 전송되는 각 I/O 요청에 대한 요청 개체를 만든 다음 삭제하기 때문에 다소 비효율적입니다. 보다 효율적인 방법은 일련의 I/O 요청에 동일한 요청 개체를 다시 사용하는 것입니다. 자세한 내용은 프레임워크 요청 개체 재사용을 참조하세요.변수는
pWdfDriver
주변 드라이버를 나타내는 프레임워크 드라이버 개체의 IWDFDriver 인터페이스에 대한 포인터입니다. 및inBufferSize
변수는pInBuffer
I/O 컨트롤 요청에 대한 입력 버퍼의 주소와 크기를 지정합니다. IWDFDriver::CreatePreallocatedWdfMemory 메서드는 입력 버퍼에 대한 프레임워크 메모리 개체를 만들고 가 가리키는 IWDFIoRequest 개체를pWdfIoRequest
메모리 개체의 부모 개체로 지정합니다.변수는
pWdfRemoteTarget
이전 코드 예제의 OpenFileByName 호출에서 가져온 원격 대상 포인터입니다. IWDFRemoteTarget::FormatRequestForIoctl 메서드는 I/O 컨트롤 작업에 대한 요청의 형식을 지정합니다.ioctlCode
변수는 직렬 I/O 요청 인터페이스의 테이블에 나열된 I/O 컨트롤 코드 중 하나로 설정됩니다.fSynchronous
I/O 컨트롤 요청을 동기적으로 보내려면 변수가 TRUE이고, 비동기적으로 전송될 경우 FALSE입니다.pCallback
변수는 이전에 만든 IRequestCallbackRequestCompletion 인터페이스에 대한 포인터입니다. 요청을 비동기적으로 보내려면 IWDFIoRequest::SetCompletionCallback 메서드에 대한 호출이 이 인터페이스를 등록합니다. 나중에 요청이 비동기적으로 완료되면 드라이버에 알리기 위해 IRequestCallbackRequestCompletion::OnCompletion 메서드가 호출됩니다.Send 메서드는 형식이 지정된 쓰기 요청을 직렬로 연결된 주변 장치 디바이스로 보냅니다. 변수는
Flags
쓰기 요청을 동기적으로 또는 비동기적으로 보낼지 여부를 나타냅니다.요청이 동기적으로 전송되면 IWDFIoRequest::D eleteWdfObject 메서드는 가 가리키는
pWdfIoRequest
I/O 요청 개체와 가 가리키는 자식 개체를pInputMemory
모두 삭제합니다. IWDFIoRequest 인터페이스는 IWDFObject 인터페이스에서 이 메서드를 상속합니다. 요청이 비동기적으로 전송되는 경우 나중에 드라이버의 OnCompletion 메서드에서 DeleteWdfObject 메서드에 대한 호출이 발생해야 합니다.