Obtendo informações de configuração do dispositivo em IRQL = DISPATCH_LEVEL
O método ilustrado na seção Obtendo informações de configuração do dispositivo em IRQL = PASSIVE_LEVEL usa irps (pacotes de solicitação de E/S) e, portanto, só é válido para drivers em execução em IRQL = PASSIVE_LEVEL. Os drivers em execução em IRQL = DISPATCH_LEVEL devem usar uma interface de barramento para obter dados de espaço de configuração do dispositivo. Para obter esses dados, você pode usar uma interface específica do barramento ou a interface de barramento independente de barramento fornecida pelo sistema , BUS_INTERFACE_STANDARD.
A interface de GUID_BUS_INTERFACE_STANDARD (definida em wdmguid.h
) permite que os drivers de dispositivo façam chamadas diretas para rotinas de motorista de ônibus pai em vez de usar pacotes de solicitação de E/S (IRP) para se comunicar com o motorista do ônibus. Em particular, essa interface permite que os drivers acessem rotinas que o motorista do ônibus fornece para as seguintes funções:
- Traduzindo endereços de barramento
- Recuperando uma estrutura de adaptador de DMA nos casos em que o adaptador de barramento dá suporte a DMA
- Lendo e definindo o espaço de configuração do barramento para um dispositivo específico no barramento
Para usar essa interface, envie um IRP_MN_QUERY_INTERFACE IRP para o driver de barramento com InterfaceType = GUID_BUS_INTERFACE_STANDARD. O driver de barramento fornece um ponteiro para uma estrutura BUS_INTERFACE_STANDARD que contém ponteiros para as rotinas individuais da interface.
É preferível usar BUS_INTERFACE_STANDARD sempre que possível, porque um número de ônibus não é necessário para recuperar informações de configuração ao usar BUS_INTERFACE_STANDARD, enquanto os motoristas geralmente devem identificar o número do ônibus ao recuperar interfaces específicas do ônibus. Os números de ônibus para alguns ônibus, como o PCI, podem mudar dinamicamente. Portanto, os motoristas não devem depender do número do ônibus para acessar as portas PCI diretamente. Isso pode levar à falha do sistema.
Três etapas são necessárias ao acessar o espaço de configuração de um dispositivo PCI em IRQL = DISPATCH_LEVEL:
Envie uma solicitação de IRP_MN_QUERY_INTERFACE em IRQL = PASSIVE_LEVEL para obter a estrutura de interface de chamada direta (BUS_INTERFACE_STANDARD) do driver de barramento PCI. Armazene isso em uma memória de pool nãopaged (normalmente em uma extensão de dispositivo).
Chame as rotinas da interface BUS_INTERFACE_STANDARD , SetBusData e GetBusData, para acessar o espaço de configuração da PCI em IRQL = DISPATCH_LEVEL.
Desreferenciar a interface. O driver de barramento PCI usa uma contagem de referência na interface antes de retornar, portanto, o driver que acessa a interface deve desreferi-la, uma vez que ela não é mais necessária.
O exemplo de código a seguir demonstra como implementar estas três etapas:
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;
}
O snippet de código a seguir mostra como usar a rotina de interface GetBusData para obter os dados de espaço de configuração (etapa 2).
bytes = busInterfaceStandard.GetBusData(
busInterfaceStandard.Context,
PCI_WHICHSPACE_CONFIG,
Buffer
Offset,
Length);
Quando o driver é feito com a interface , ele pode usar código semelhante ao snippet a seguir para desreferenciar a interface (etapa 3). Os drivers não devem chamar rotinas de interface depois de desreferenciar a interface.
(busInterfaceStandard.InterfaceDereference)(
(PVOID)busInterfaceStandard.Context);
A interface sincroniza o acesso do chamador ao hardware do barramento com o acesso do driver de barramento PCI. O gravador de driver não precisa se preocupar em criar bloqueios de rotação para evitar a disputa com o motorista do barramento PCI pelo acesso ao hardware do ônibus.
Observe que, se tudo o que é necessário são números de barramento, função e dispositivo, geralmente é desnecessário recorrer a uma interface de barramento para obter essas informações. Esses dados podem ser recuperados indiretamente passando o PDO do dispositivo de destino para a função IoGetDeviceProperty da seguinte maneira:
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);