Получение сведений о конфигурации устройства в 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 требуется выполнить три шага:
Отправьте запрос IRP_MN_QUERY_INTERFACE по адресу IRQL = PASSIVE_LEVEL, чтобы получить структуру интерфейса прямого вызова (BUS_INTERFACE_STANDARD) от драйвера шины PCI. Храните его в памяти пула без памяти (обычно в расширении устройства).
Вызовите подпрограммы интерфейса BUS_INTERFACE_STANDARDSetBusData и GetBusData, чтобы получить доступ к пространству конфигурации PCI в IRQL = DISPATCH_LEVEL.
Разыменуть интерфейс. Драйвер шины 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);