IRQL = DISPATCH_LEVEL 디바이스 구성 정보 가져오기

IRQL = PASSIVE_LEVEL 디바이스 구성 정보 가져오기 섹션에 설명된 메서드는 I/O 요청 패킷(IRP)을 사용하므로 IRQL = PASSIVE_LEVEL 실행되는 드라이버에만 유효합니다. IRQL = DISPATCH_LEVEL 실행되는 드라이버는 버스 인터페이스를 사용하여 디바이스 구성 공간 데이터를 가져와야 합니다. 이 데이터를 가져오려면 버스별 인터페이스 또는 시스템 제공 버스 독립적 버스 인터페이스인 BUS_INTERFACE_STANDARD 사용할 수 있습니다.

에 정의된 GUID_BUS_INTERFACE_STANDARD 인터페이스를 사용하면 디바이스 드라이버가 wdmguid.hIRP(I/O 요청 패킷)를 사용하여 버스 드라이버와 통신하는 대신 부모 버스 드라이버 루틴을 직접 호출할 수 있습니다. 특히 이 인터페이스를 사용하면 드라이버는 버스 드라이버가 다음 함수에 대해 제공하는 루틴에 액세스할 수 있습니다.

  • 버스 주소 번역
  • 버스 어댑터가 DMA를 지원하는 경우 DMA 어댑터 구조 검색
  • 버스의 특정 디바이스에 대한 버스 구성 공간 읽기 및 설정

이 인터페이스를 사용하려면 InterfaceType = GUID_BUS_INTERFACE_STANDARD 사용하여 버스 드라이버에 IRP_MN_QUERY_INTERFACE IRP를 보냅니다. 버스 드라이버는 인터페이스의 개별 루틴에 대한 포인터를 포함하는 BUS_INTERFACE_STANDARD 구조체에 대한 포인터를 제공합니다.

버스 번호는 BUS_INTERFACE_STANDARD 사용할 때 구성 정보를 검색할 필요가 없는 반면 드라이버는 버스별 인터페이스를 검색할 때 버스 번호를 식별해야 하기 때문에 가능한 경우 BUS_INTERFACE_STANDARD 사용하는 것이 좋습니다. PCI와 같은 일부 버스의 버스 번호는 동적으로 변경 될 수 있습니다. 따라서 드라이버는 PCI 포트에 직접 액세스하기 위해 버스 번호에 의존해서는 안 됩니다. 이렇게 하면 시스템 오류가 발생할 수 있습니다.

IRQL = DISPATCH_LEVEL PCI 디바이스의 구성 공간에 액세스할 때 세 단계가 필요합니다.

  1. IRQL = PASSIVE_LEVEL IRP_MN_QUERY_INTERFACE 요청을 보내 PCI 버스 드라이버에서 직접 호출 인터페이스 구조(BUS_INTERFACE_STANDARD)를 가져옵니다. 비페이지 풀 메모리(일반적으로 디바이스 확장)에 저장합니다.

  2. BUS_INTERFACE_STANDARD 인터페이스 루틴인 SetBusDataGetBusData를 호출하여 IRQL = DISPATCH_LEVEL PCI 구성 공간에 액세스합니다.

  3. 인터페이스를 역참조합니다. PCI 버스 드라이버는 반환되기 전에 인터페이스에 대한 참조 횟수를 사용하므로 인터페이스에 액세스하는 드라이버는 더 이상 필요하지 않으면 역참조해야 합니다.

다음 코드 샘플에서는 다음 세 단계를 구현하는 방법을 보여 줍니다.

NTSTATUS
GetPCIBusInterfaceStandard(
    IN  PDEVICE_OBJECT DeviceObject,
    OUT PBUS_INTERFACE_STANDARD BusInterfaceStandard
    )
/*++
Routine Description:
    This routine gets the bus interface standard information from the PDO.
Arguments:
    DeviceObject - Device object to query for this information.
    BusInterface - Supplies a pointer to the retrieved information.
Return Value:
    NT status.
--*/ 
{
    KEVENT event;
    NTSTATUS status;
    PIRP irp;
    IO_STATUS_BLOCK ioStatusBlock;
    PIO_STACK_LOCATION irpStack;
    PDEVICE_OBJECT targetObject;

    Bus_KdPrint(("GetPciBusInterfaceStandard entered.\n"));
    KeInitializeEvent(&event, NotificationEvent, FALSE);
    targetObject = IoGetAttachedDeviceReference(DeviceObject);
    irp = IoBuildSynchronousFsdRequest(IRP_MJ_PNP,
                                       targetObject,
                                       NULL,
                                       0,
                                       NULL,
                                       &event,
                                       &ioStatusBlock);
    if (irp == NULL) {
        status = STATUS_INSUFFICIENT_RESOURCES;
        goto End;
    }
    irpStack = IoGetNextIrpStackLocation( irp );
    irpStack->MinorFunction = IRP_MN_QUERY_INTERFACE;
    irpStack->Parameters.QueryInterface.InterfaceType = (LPGUID)&GUID_BUS_INTERFACE_STANDARD;
    irpStack->Parameters.QueryInterface.Size = sizeof(BUS_INTERFACE_STANDARD);
    irpStack->Parameters.QueryInterface.Version = 1;
    irpStack->Parameters.QueryInterface.Interface = (PINTERFACE)BusInterfaceStandard;
    irpStack->Parameters.QueryInterface.InterfaceSpecificData = NULL;

    // Initialize the status to error in case the bus driver does not 
    // set it correctly.
    irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
    status = IoCallDriver(targetObject, irp);
    if (status == STATUS_PENDING) {
        KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
        status = ioStatusBlock.Status;
    }
End:
    // Done with reference
    ObDereferenceObject(targetObject);
    return status;
}

다음 코드 조각에서는 GetBusData 인터페이스 루틴을 사용하여 구성 공간 데이터를 가져오는 방법을 보여 줍니다(2단계).

 bytes = busInterfaceStandard.GetBusData(
                    busInterfaceStandard.Context,
                    PCI_WHICHSPACE_CONFIG,
                    Buffer
                    Offset,
                    Length);

드라이버가 인터페이스를 사용하여 완료되면 다음 코드 조각과 유사한 코드를 사용하여 인터페이스를 역참조할 수 있습니다(3단계). 드라이버는 인터페이스를 역참조한 후 인터페이스 루틴을 호출해서는 안 됩니다.

    (busInterfaceStandard.InterfaceDereference)(
                    (PVOID)busInterfaceStandard.Context);

인터페이스는 PCI 버스 드라이버의 액세스와 버스 하드웨어에 대한 호출자의 액세스를 동기화합니다. 드라이버 작성기는 버스 하드웨어에 액세스하기 위해 PCI 버스 드라이버와 경합하지 않도록 스핀 잠금을 만드는 것에 대해 걱정할 필요가 없습니다.

필요한 모든 것이 버스, 함수 및 디바이스 번호인 경우 일반적으로 이 정보를 얻기 위해 버스 인터페이스에 의존할 필요가 없습니다. 이 데이터는 다음과 같이 대상 디바이스의 PDO를 IoGetDeviceProperty 함수에 전달하여 간접적으로 검색할 수 있습니다.

    ULONG   propertyAddress, length;
    USHORT  FunctionNumber, DeviceNumber;

    // Get the BusNumber. Be warned that bus numbers may be
    // dynamic and therefore subject to change unpredictably!!!
    IoGetDeviceProperty(PhysicalDeviceObject,
                        DevicePropertyBusNumber,
                        sizeof(ULONG),
                        (PVOID)&BusNumber,
                        &length);

    // Get the DevicePropertyAddress
    IoGetDeviceProperty(PhysicalDeviceObject,
                        DevicePropertyAddress,
                        sizeof(ULONG),
                        (PVOID)&propertyAddress,
                        &length);

    // For PCI, the DevicePropertyAddress has device number 
    // in the high word and the function number in the low word. 
    FunctionNumber = (USHORT)((propertyAddress) & 0x0000FFFF);
    DeviceNumber = (USHORT)(((propertyAddress) >> 16) & 0x0000FFFF);