Partager via


Obtention des informations de configuration de l’appareil sur IRQL = DISPATCH_LEVEL

La méthode illustrée dans la section Obtention des informations de configuration de l’appareil à IRQL = PASSIVE_LEVEL utilise les paquets de demandes d’E/S (IRP) et est donc uniquement valide pour les pilotes s’exécutant sur IRQL = PASSIVE_LEVEL. Les pilotes s’exécutant sur IRQL = DISPATCH_LEVEL doivent utiliser une interface de bus pour obtenir des données d’espace de configuration des appareils. Pour obtenir ces données, vous pouvez utiliser une interface spécifique au bus ou l’interface de bus indépendante du bus fournie par le système , BUS_INTERFACE_STANDARD.

L’interface GUID_BUS_INTERFACE_STANDARD (définie dans wdmguid.h) permet aux pilotes de périphérique d’effectuer des appels directs aux routines de pilotes de bus parents au lieu d’utiliser des paquets de requête d’E/S (IRP) pour communiquer avec le pilote de bus. En particulier, cette interface permet aux pilotes d’accéder aux routines que le pilote de bus fournit pour les fonctions suivantes :

  • Traduction d’adresses de bus
  • Récupération d’une structure d’adaptateur DMA dans les cas où l’adaptateur de bus prend en charge DMA
  • Lecture et définition de l’espace de configuration du bus pour un appareil particulier sur le bus

Pour utiliser cette interface, envoyez un IRP IRP_MN_QUERY_INTERFACE à votre pilote de bus avec InterfaceType = GUID_BUS_INTERFACE_STANDARD. Le pilote de bus fournit un pointeur vers une structure BUS_INTERFACE_STANDARD qui contient des pointeurs vers les routines individuelles de l’interface.

Il est préférable d’utiliser BUS_INTERFACE_STANDARD dans la mesure du possible, car un numéro de bus n’est pas nécessaire pour récupérer les informations de configuration lors de l’utilisation de BUS_INTERFACE_STANDARD, alors que les conducteurs doivent souvent identifier le numéro de bus lors de la récupération d’interfaces spécifiques au bus. Les numéros de bus de certains bus, tels que PCI, peuvent changer dynamiquement. Par conséquent, les pilotes ne doivent pas dépendre du numéro de bus pour accéder directement aux ports PCI. Cela peut entraîner une défaillance du système.

Trois étapes sont requises pour accéder à l’espace de configuration d’un appareil PCI à l’adresse IRQL = DISPATCH_LEVEL :

  1. Envoyez une demande de IRP_MN_QUERY_INTERFACE à IRQL = PASSIVE_LEVEL pour obtenir la structure d’interface d’appel direct (BUS_INTERFACE_STANDARD) à partir du pilote de bus PCI. Stockez-le dans une mémoire de pool non paginé (généralement dans une extension d’appareil).

  2. Appelez les routines d’interface BUS_INTERFACE_STANDARD , SetBusData et GetBusData, pour accéder à l’espace de configuration PCI dans IRQL = DISPATCH_LEVEL.

  3. Déréférencer l’interface. Le pilote de bus PCI prend un nombre de références sur l’interface avant son retour. Par conséquent, le pilote qui accède à l’interface doit le déréférencer, une fois qu’il n’est plus nécessaire.

L’exemple de code suivant montre comment implémenter ces trois étapes :

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

L’extrait de code suivant montre comment utiliser la routine d’interface GetBusData pour obtenir les données d’espace de configuration (étape 2).

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

Lorsque le pilote a terminé avec l’interface, il peut utiliser du code similaire à l’extrait de code suivant pour déréférencer l’interface (étape 3). Les pilotes ne doivent pas appeler des routines d’interface après la déréférencement de l’interface.

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

L’interface synchronise l’accès de l’appelant au matériel de bus avec l’accès du pilote de bus PCI. L’enregistreur de pilotes n’a pas besoin de se soucier de la création de verrous de rotation pour éviter de se heurter au pilote de bus PCI pour accéder au matériel de bus.

Notez que si tout ce qui est nécessaire est des numéros de bus, de fonction et d’appareil, il n’est généralement pas nécessaire de recourir à une interface de bus pour obtenir ces informations. Ces données peuvent être récupérées indirectement en passant l’AOP de l’appareil cible à la fonction IoGetDeviceProperty comme suit :

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