Поделиться через


Получение сведений о конфигурации устройства в IRQL = DISPATCH_LEVEL

Метод, показанный в разделе Получение сведений о конфигурации устройства в IRQL = PASSIVE_LEVEL , использует пакеты запросов ввода-вывода (IRP) и поэтому действителен только для драйверов, работающих в IRQL = PASSIVE_LEVEL. Драйверы, работающие в IRQL = DISPATCH_LEVEL, должны использовать интерфейс шины для получения данных о пространстве конфигурации устройства. Чтобы получить эти данные, можно использовать интерфейс для конкретной шины или интерфейс шины, предоставляемый системой, BUS_INTERFACE_STANDARD.

Интерфейс GUID_BUS_INTERFACE_STANDARD (определенный в wdmguid.h) позволяет драйверам устройств выполнять прямые вызовы к подпрограммам родительского водителя шины вместо использования пакетов запросов ввода-вывода (IRP) для связи с водителем шины. В частности, этот интерфейс позволяет водителям получать доступ к подпрограммам, которые водитель автобуса предоставляет для следующих функций:

  • Перевод адресов шины
  • Получение структуры адаптера DMA в случаях, когда адаптер шины поддерживает DMA
  • Чтение и настройка пространства конфигурации шины для определенного устройства в шине

Чтобы использовать этот интерфейс, отправьте драйверу шины IRP_MN_QUERY_INTERFACE IRP с параметром InterfaceType = GUID_BUS_INTERFACE_STANDARD. Водитель шины предоставляет указатель на структуру BUS_INTERFACE_STANDARD, которая содержит указатели на отдельные подпрограммы интерфейса.

Предпочтительнее по возможности использовать BUS_INTERFACE_STANDARD , так как номер автобуса не требуется для получения сведений о конфигурации при использовании BUS_INTERFACE_STANDARD, в то время как водители часто должны идентифицировать номер автобуса при получении интерфейсов, относящихся к шине. Номера автобусов для некоторых автобусов, таких как PCI, могут меняться динамически. Таким образом, водители не должны зависеть от номера автобуса для прямого доступа к портам PCI. Это может привести к сбою системы.

При доступе к пространству конфигурации устройства PCI по адресу IRQL = DISPATCH_LEVEL требуется выполнить три шага:

  1. Отправьте запрос IRP_MN_QUERY_INTERFACE по адресу IRQL = PASSIVE_LEVEL, чтобы получить структуру интерфейса прямого вызова (BUS_INTERFACE_STANDARD) от драйвера шины PCI. Храните его в памяти пула без памяти (обычно в расширении устройства).

  2. Вызовите подпрограммы интерфейса BUS_INTERFACE_STANDARDSetBusData и GetBusData, чтобы получить доступ к пространству конфигурации PCI в IRQL = DISPATCH_LEVEL.

  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);