Utilisation de la structure SPB_TRANSFER_LIST pour les IOCTL personnalisés

Si votre pilote de contrôleur de bus périphérique simple (SPB) prend en charge une ou plusieurs requêtes de contrôle d’E/S personnalisées (IOCTL), utilisez la structure SPB_TRANSFER_LIST pour décrire les mémoires tampons de lecture et d’écriture dans ces requêtes. Cette structure offre un moyen uniforme de décrire les mémoires tampons dans une requête et d’éviter la surcharge de copie de mémoire tampon associée aux opérations d’E/S METHOD_BUFFERED.

Si vos requêtes IOCTL personnalisées utilisent la structure SPB_TRANSFER_LIST , votre pilote de contrôleur SPB doit appeler la méthode SpbRequestCaptureIoOtherTransferList pour capturer ces mémoires tampons dans le contexte de processus de l’originateur de requête. Votre pilote peut appeler la méthode SpbRequestGetTransferParameters pour accéder à ces mémoires tampons.

Les requêtes IOCTL_SPB_FULL_DUPLEX et IOCTL_SPB_EXECUTE_SEQUENCE , définies dans le cadre de l’interface de requête SPB I/O, utilisent la structure SPB_TRANSFER_LIST pour décrire leurs mémoires tampons de lecture et d’écriture. La structure SPB_TRANSFER_LIST d’une requête IOCTL_SPB_FULL_DUPLEX décrit à la fois la mémoire tampon d’écriture et la mémoire tampon de lecture (dans cet ordre) dans la requête. La structure SPB_TRANSFER_LIST d’une requête IOCTL_SPB_EXECUTE_SEQUENCE peut décrire une séquence arbitraire de mémoires tampons de lecture et d’écriture.

De même, vous pouvez définir vos IOCTL personnalisés pour exiger que leurs structures SPB_TRANSFER_LIST nécessitent l'utilisation d'une combinaison de mémoires tampons de lecture et d'écriture, et spécifier tout ordre des mémoires tampons dans la liste qui pourrait être nécessaire.

Le pilote Kernel-Mode Driver Foundation (KMDF) pour un périphérique SPB appelle une méthode telle que WdfIoTargetSendIoctlSynchronously pour envoyer une demande IOCTL à un contrôleur SPB. Cette méthode a des paramètres InputBuffer et OutputBuffer . Les pilotes pour certains types d’appareils peuvent utiliser ces deux paramètres pour pointer vers la mémoire tampon d’écriture et la mémoire tampon de lecture, respectivement, pour une requête IOCTL. Toutefois, pour envoyer une demande IOCTL à un contrôleur SPB, le pilote de périphérique SPB définit le paramètre InputBuffer pour qu’il pointe vers un descripteur de mémoire qui pointe vers une structure SPB_TRANSFER_LIST . Cette structure décrit les mémoires tampons de lecture ou d’écriture requises pour l’opération de contrôle d’E/S. Le pilote définit le paramètre OutputBuffer sur NULL.

De même, le pilote User-Mode Driver Foundation (UMDF) pour un périphérique SPB appelle une méthode telle que IWDFIoTarget ::FormatRequestForIoctl pour mettre en forme une demande d’E/S pour une opération de contrôle d’E/S. Cette méthode a des paramètres pInputMemory et pOutputMemory . Les pilotes pour certains types d’appareils peuvent utiliser ces deux paramètres pour pointer vers la mémoire tampon d’écriture et la mémoire tampon de lecture pour une requête IOCTL. Toutefois, pour envoyer une requête IOCTL à un contrôleur SPB, le pilote de périphérique SPB définit le paramètre pInputMemory pour qu’il pointe vers un objet mémoire qui contient une structure SPB_TRANSFER_LIST . Cette structure décrit les mémoires tampons de lecture ou d’écriture requises pour l’opération de contrôle d’E/S. Le pilote définit le paramètre pOutputMemory sur NULL.

Vérification des paramètres et capture de mémoire tampon

Lorsque l’extension de framework SPB (SpbCx) reçoit une requête IOCTL_SPB_EXECUTE_SEQUENCE , SpbCx transmet cette requête au pilote du contrôleur SPB en appelant la fonction EvtSpbControllerIoSequence du pilote. Avant cet appel, SpbCx inspecte la structure SPB_TRANSFER_LIST qui décrit les mémoires tampons dans la requête. SpbCx capture ces mémoires tampons dans le contexte de processus de l’originateur de requête. (Les mémoires tampons en mode utilisateur sont accessibles uniquement dans le processus dans lequel la mémoire est allouée.) En outre, SpbCx vérifie si les valeurs des paramètres dans la requête sont valides.

Lorsque SpbCx reçoit une demande de IOCTL_SPB_FULL_DUPLEX ou une demande IOCTL personnalisée, SpbCx transmet cette requête au pilote du contrôleur SPB en appelant la fonction de rappel EvtSpbControllerIoOther du pilote. Avant d’effectuer cet appel, SpbCx n’effectue aucune vérification de validation des valeurs de paramètre dans la requête et ne capture pas les mémoires tampons de la requête dans le contexte de l’originateur. La vérification des paramètres et la capture de mémoire tampon pour ces requêtes sont la responsabilité du pilote du contrôleur SPB.

Si un pilote de contrôleur SPB prend en charge la requête IOCTL_SPB_FULL_DUPLEX ou prend en charge une requête IOCTL personnalisée qui utilise la structure SPB_TRANSFER_LIST pour ses mémoires tampons, le pilote doit implémenter une fonction de rappel EvtIoInCallerContext . Le pilote fournit un pointeur vers cette fonction en tant que paramètre d’entrée dans l’appel à la méthode SpbControllerSetIoOtherCallback qui inscrit la fonction de rappel EvtSpbControllerIoOther du pilote. Lorsque SpbCx reçoit une demande IOCTL_SPB_FULL_DUPLEX ou une requête IOCTL personnalisée, SpbCx appelle la fonction EvtIoInCallerContext du pilote dans le contexte de l’expéditeur. Si la requête IOCTL utilise la structure SPB_TRANSFER_LIST , la fonction EvtIoInCallerContext appelle la méthode SpbRequestCaptureIoOtherTransferList pour capturer les mémoires tampons dans la requête. La fonction EvtIoInCallerContext peut également effectuer un traitement préliminaire de la demande.

L’exemple de code suivant montre une fonction EvtIoInCallerContext implémentée par un pilote de contrôleur SPB.

VOID
EvtIoInCallerContext(
    _In_  WDFDEVICE   SpbController,
    _In_  WDFREQUEST  FxRequest
    ) 
{
    NTSTATUS status = STATUS_SUCCESS;
    WDF_REQUEST_PARAMETERS fxParams;
  
    WDF_REQUEST_PARAMETERS_INIT(&fxParams);
    WdfRequestGetParameters(FxRequest, &fxParams);

    if ((fxParams.Type != WdfRequestTypeDeviceControl) &&
        (fxParams.Type != WdfRequestTypeDeviceControlInternal))
    {
        status = STATUS_NOT_SUPPORTED;
        goto exit;
    }

    //
    // The driver should check for custom IOCTLs that it handles.
    // If the IOCTL is not recognized, complete the request with a
    // status of STATUS_NOT_SUPPORTED.
    //

    switch (fxParams.Parameters.DeviceIoControl.IoControlCode)
    {
        ...

    default:
        status = STATUS_NOT_SUPPORTED;
        goto exit;
    }

    //
    // The IOCTL is recognized. Capture the buffers in the request.
    //

    status = SpbRequestCaptureIoOtherTransferList((SPBREQUEST)FxRequest);

    //
    // If the capture fails, the driver must complete the request instead
    // of placing it in the SPB controller's request queue.
    //

    if (!NT_SUCCESS(status))
    {
        goto exit;
    }

    status = WdfDeviceEnqueueRequest(SpbController, FxRequest);

    if (!NT_SUCCESS(status))
    {
        goto exit;
    }

exit:

    if (!NT_SUCCESS(status))
    {
        WdfRequestComplete(FxRequest, status);
    }
}

Dans l’exemple de code précédent, l’instruction switch vérifie que la requête contient un IOCTL que le pilote du contrôleur SPB reconnaît. (Par souci de concision, le corps de l’instruction switch n’est pas affiché.) Ensuite, l’appel à la méthode SpbRequestCaptureIoOtherTransferList capture les mémoires tampons dans la requête. Si cet appel réussit, la requête est ajoutée à la file d’E/S du contrôleur SPB. Sinon, la demande est terminée avec un code d’état d’erreur.

Pour obtenir un exemple de code montrant la vérification des paramètres par une fonction EvtSpbControllerIoOther , consultez Gestion des requêtes IOCTL_SPB_FULL_DUPLEX.