Partager via


Comment utiliser le lecteur continu pour lire des données à partir d’un canal USB

Cette rubrique décrit l’objet lecteur continu fourni par WDF. Les procédures décrites dans cette rubrique fournissent des instructions pas à pas sur la configuration de l’objet et son utilisation pour lire des données à partir d’un canal USB.

Windows Driver Framework (WDF) fournit un objet spécialisé appelé lecteur continu. Cet objet permet à un pilote client USB de lire des données à partir de points de terminaison en bloc et d’interruption en continu, tant qu’il existe des données disponibles. Pour utiliser le lecteur, le pilote client doit disposer d’un handle vers un objet de canal cible USB associé au point de terminaison à partir duquel le pilote lit les données. Le point de terminaison doit se trouver dans la configuration active. Vous pouvez activer une configuration de deux façons : en sélectionnant une configuration USB ou en modifiant le paramètre de remplacement dans la configuration actuelle. Pour plus d’informations sur ces opérations, consultez Comment sélectionner une configuration pour un périphérique USB et comment sélectionner un autre paramètre dans une interface USB.

Après avoir créé le lecteur continu, le pilote client peut démarrer et arrêter le lecteur en fonction des besoins. Lecteur continu qui garantit qu’une demande de lecture est toujours disponible sur l’objet de canal cible et que le pilote client est toujours prêt à recevoir des données du point de terminaison.

Le lecteur continu n’est pas automatiquement géré par l’infrastructure. Cela signifie que le pilote client doit arrêter le lecteur lorsque l’appareil entre dans un état d’alimentation inférieur et redémarre le lecteur lorsque l’appareil entre en état de fonctionnement.

Cet article utilise :

Avant de commencer

Avant que le pilote client puisse utiliser le lecteur continu, vérifiez que ces exigences sont remplies :

  • Votre périphérique USB doit avoir un point de terminaison IN. Vérifiez la configuration de l’appareil dans USBView. Usbview.exe est une application qui vous permet de parcourir tous les contrôleurs USB et les périphériques USB connectés. En règle générale, USBView est installé dans le dossier Débogueur dans le Kit de pilotes Windows (WDK).

  • Le pilote client doit avoir créé l’objet périphérique cible USB framework.

    Si vous utilisez les modèles USB fournis avec Microsoft Visual Studio Professional 2012, le code de modèle effectue ces tâches. Le code de modèle obtient le handle sur l’objet d’appareil cible et stocke dans le contexte de l’appareil.

    Pilote client KMDF :

    Un pilote client KMDF doit obtenir un handle WDFUSBDEVICE en appelant la méthode WdfUsbTargetDeviceCreateWithParameters. Pour plus d’informations, consultez « Code source de l’appareil » dans Présentation de la structure de code du pilote client USB (KMDF).

    Pilote client UMDF :

    Un pilote client UMDF doit obtenir un pointeur IWDFUsbTargetDevice en interrogeant l’objet d’appareil cible du framework. Pour plus d’informations, consultez « Implémentation IPnpCallbackHardware et tâches spécifiques à USB » dans Présentation de la structure de code du pilote client USB (UMDF).

  • L’appareil doit avoir une configuration active.

    Si vous utilisez des modèles USB, le code sélectionne la première configuration et le paramètre de remplacement par défaut dans chaque interface. Pour plus d’informations sur la modification du paramètre de remplacement, consultez Comment sélectionner un autre paramètre dans une interface USB.

    Pilote client KMDF :

    Un pilote client KMDF doit appeler la méthode WdfUsbTargetDeviceSelectConfig.

    Pilote client UMDF :

    Pour un pilote client UMDF, l’infrastructure sélectionne la première configuration et le paramètre de remplacement par défaut pour chaque interface de cette configuration.

  • Le pilote client doit avoir un handle pour l’objet de canal cible du framework pour le point de terminaison IN. Pour plus d’informations, consultez Comment énumérer les canaux USB.

Utiliser le lecteur continu dans un pilote client KMDF

Avant de commencer à utiliser le lecteur continu, vous devez le configurer en initialisant une structure WDF_USB_CONTINUOUS_READER_CONFIG.

Configurer le lecteur continu dans un pilote client KMDF

  1. Initialisez une structure WDF_USB_CONTINUOUS_READER_CONFIG en appelant la macro WDF_USB_CONTINUOUS_READER_CONFIG_INIT.

  2. Spécifiez ses options de configuration dans la structure WDF_USB_CONTINUOUS_READER_CONFIG.

  3. Appelez la méthode WdfUsbTargetPipeConfigContinuousReader.

    L’exemple de code suivant configure le lecteur continu pour l’objet de canal cible spécifié.

    NTSTATUS FX3ConfigureContinuousReader(
        _In_ WDFDEVICE Device,
        _In_ WDFUSBPIPE Pipe)
    {
        NTSTATUS status;
        PDEVICE_CONTEXT                     pDeviceContext;
        WDF_USB_CONTINUOUS_READER_CONFIG    readerConfig;
        PPIPE_CONTEXT                       pipeContext;
    
        PAGED_CODE();
    
        pDeviceContext = WdfObjectGet_DEVICE_CONTEXT(Device);
        pipeContext = GetPipeContext (Pipe);
    
        WDF_USB_CONTINUOUS_READER_CONFIG_INIT(
            &readerConfig,
            FX3EvtReadComplete,
            pDeviceContext,
            pipeContext->MaxPacketSize);
    
        readerConfig.EvtUsbTargetPipeReadersFailed=FX3EvtReadFailed;
    
        status = WdfUsbTargetPipeConfigContinuousReader(
            Pipe,
            &readerConfig);
    
        if (!NT_SUCCESS (status))
        {
            TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE,
                "%!FUNC! WdfUsbTargetPipeConfigContinuousReader failed 0x%x", status);
    
            goto Exit;
        }
    
    Exit:
        return status;
    }
    

En règle générale, le pilote client configure le lecteur continu dans la fonction de rappel EvtDevicePrepareHardware après avoir énuméré les objets de canal cible dans le paramètre actif.

Dans l’exemple précédent, le pilote client spécifie ses options de configuration de deux manières. Tout d’abord, en appelant WDF_USB_CONTINUOUS_READER_CONFIG_INIT, puis en définissant WDF_USB_CONTINUOUS_READER_CONFIG membres. Notez les paramètres de WDF_USB_CONTINUOUS_READER_CONFIG_INIT. Ces valeurs sont obligatoires. Dans cet exemple, le pilote client spécifie :

  • Pointeur vers une routine d’achèvement que le pilote implémente. L’infrastructure appelle cette routine lorsqu’elle termine une demande de lecture. Dans la routine d’achèvement, le pilote peut accéder à l’emplacement de mémoire qui contient les données lues. L’implémentation de la routine d’achèvement est abordée à l’étape 2.
  • Pointeur vers le contexte défini par le pilote.
  • Nombre d’octets pouvant être lus à partir de l’appareil dans un seul transfert. Le pilote client peut obtenir ces informations dans une structure WDF_USB_PIPE_INFORMATION en appelant WdfUsbInterfaceGetConfiguredPipe ou WdfUsbTargetPipeGetInformation. Pour plus d’informations, consultez Comment énumérer les canaux USB.

WDF_USB_CONTINUOUS_READER_CONFIG_INIT configure le lecteur continu pour utiliser la valeur par défaut pour NumPendingReads. Cette valeur détermine le nombre de demandes de lecture ajoutées par l’infrastructure à la file d’attente en attente. La valeur par défaut a été déterminée pour fournir des performances raisonnablement bonnes pour de nombreux appareils sur de nombreuses configurations de processeur.

Outre les paramètres de configuration spécifiés dans WDF_USB_CONTINUOUS_READER_CONFIG_INIT, l’exemple définit également une routine d’échec dans WDF_USB_CONTINUOUS_READER_CONFIG. Cette routine d’échec est facultative.

Outre la routine d’échec, d’autres membres sont présents dans WDF_USB_CONTINUOUS_READER_CONFIG que le pilote client peut utiliser pour spécifier la disposition de la mémoire tampon de transfert. Par exemple, considérez un pilote réseau qui utilise le lecteur continu pour recevoir des paquets réseau. Chaque paquet contient des données d’en-tête, de charge utile et de pied de page. Pour décrire le paquet, le pilote doit d’abord spécifier la taille du paquet dans son appel à WDF_USB_CONTINUOUS_READER_CONFIG_INIT. Ensuite, le pilote doit spécifier la longueur de l’en-tête et du pied de page en définissant les membres HeaderLength et TrailerLength de WDF_USB_CONTINUOUS_READER_CONFIG. L’infrastructure utilise ces valeurs pour calculer les décalages d’octets de chaque côté de la charge utile. Lorsque les données de charge utile sont lues à partir du point de terminaison, l’infrastructure stocke ces données dans la partie de la mémoire tampon entre les décalages.

Implémenter la routine d’achèvement

L’infrastructure appelle la routine d’achèvement implémentée par le pilote client chaque fois qu’une demande est terminée. L’infrastructure transmet le nombre d’octets lus et un objet WDFMEMORY dont la mémoire tampon contient les données lues à partir du canal.

L’exemple de code suivant montre l’implémentation de routine d’achèvement.

EVT_WDF_USB_READER_COMPLETION_ROUTINE FX3EvtReadComplete;

VOID FX3EvtReadComplete(
    __in  WDFUSBPIPE Pipe,
    __in  WDFMEMORY Buffer,
    __in  size_t NumBytesTransferred,
    __in  WDFCONTEXT Context
    )
{
    PDEVICE_CONTEXT  pDeviceContext;
    PVOID  requestBuffer;

    pDeviceContext = (PDEVICE_CONTEXT)Context;

    if (NumBytesTransferred == 0)
    {
        return;
    }

    requestBuffer = WdfMemoryGetBuffer(Buffer, NULL);

    if (Pipe == pDeviceContext->InterruptPipe)
    {
        KdPrintEx(( DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL,
                                "Interrupt endpoint: %s.\n",
                                requestBuffer ));
    }

    return;
}

L’infrastructure appelle la routine d’achèvement implémentée par le pilote client chaque fois qu’une demande est terminée. L’infrastructure alloue un objet mémoire pour chaque opération de lecture. Dans la routine d’achèvement, l’infrastructure transmet le nombre d’octets lus et un handle WDFMEMORY à l’objet mémoire. La mémoire tampon de l’objet mémoire contient les données lues à partir du canal. Le pilote client ne doit pas libérer l’objet mémoire. L’infrastructure libère l’objet après chaque routine d’achèvement. Si le pilote client souhaite stocker les données reçues, le pilote doit copier le contenu de la mémoire tampon dans la routine d’achèvement.

Implémenter la routine d’échec

L’infrastructure appelle la routine d’échec implémentée par le pilote client pour informer le pilote que le lecteur continu a signalé une erreur lors du traitement d’une demande de lecture. L’infrastructure transmet le pointeur à l’objet de canal cible sur lequel la requête a échoué et les valeurs du code d’erreur. En fonction de ces valeurs de code d’erreur, le pilote peut implémenter son mécanisme de récupération d’erreurs. Le pilote doit également retourner une valeur appropriée qui indique à l’infrastructure si l’infrastructure doit redémarrer le lecteur continu.

L’exemple de code suivant montre une implémentation de routine d’échec.

EVT_WDF_USB_READERS_FAILED FX3EvtReadFailed;

BOOLEAN
FX3EvtReadFailed(
    WDFUSBPIPE      Pipe,
    NTSTATUS        Status,
    USBD_STATUS     UsbdStatus
    )
{
    UNREFERENCED_PARAMETER(Status);

    TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE,
            "%!FUNC! ReadersFailedCallback failed NTSTATUS 0x%x, UsbdStatus 0x%x\n",
                    status,
                    UsbdStatus);

    return TRUE;
}

Dans l’exemple précédent, le pilote retourne TRUE. Cette valeur indique au framework qu’il doit réinitialiser le canal, puis redémarrer le lecteur continu.

Le pilote client peut également retourner FALSE et fournir un mécanisme de récupération d’erreur si une condition de blocage se produit sur le canal. Par exemple, le pilote peut case activée l’état USBD et émettre une demande de canal de réinitialisation pour effacer la condition de blocage.

Pour plus d’informations sur la récupération d’erreurs dans les canaux, consultez Comment récupérer à partir d’erreurs de canal USB.

Démarrer et arrêter le lecteur continu

Indiquez au framework de démarrer le lecteur continu lorsque l’appareil entre en état de fonctionnement ; arrêtez le lecteur lorsque l’appareil quitte l’état de fonctionnement. Appelez ces méthodes et spécifiez l’objet de canal cible comme objet cible d’E/S.

Le lecteur continu n’est pas automatiquement géré par l’infrastructure. Par conséquent, le pilote client doit démarrer ou arrêter explicitement l’objet de canal cible lorsque l’état d’alimentation de l’appareil change. Le pilote appelle WdfIoTargetStart dans l’implémentation EvtDeviceD0Entry du pilote. Cet appel garantit que la file d’attente remet les demandes uniquement lorsque l’appareil est en état de fonctionnement. À l’inverse, le pilote appelle WdfIoTargetStop dans l’implémentation evtDeviceD0Exit afin que la file d’attente arrête de remettre des demandes lorsque l’appareil entre dans un état d’alimentation inférieur.

L’exemple de code suivant configure le lecteur continu pour l’objet de canal cible spécifié.

EVT_WDF_DEVICE_D0_ENTRY FX3EvtDeviceD0Entry;

NTSTATUS FX3EvtDeviceD0Entry(
    __in  WDFDEVICE Device,
    __in  WDF_POWER_DEVICE_STATE PreviousState
    )
{
    PDEVICE_CONTEXT  pDeviceContext;
    NTSTATUS status;

    PAGED_CODE();

    pDeviceContext = WdfObjectGet_DEVICE_CONTEXT(Device);
    status = WdfIoTargetStart (WdfUsbTargetPipeGetIoTarget (pDeviceContext->InterruptPipe));

    if (!NT_SUCCESS (status))
    {
        TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE,
            "%!FUNC! Could not start interrupt pipe failed 0x%x", status);
    }
}

EVT_WDF_DEVICE_D0_EXIT FX3EvtDeviceD0Exit;

NTSTATUS FX3EvtDeviceD0Exit(
    __in  WDFDEVICE Device,
    __in  WDF_POWER_DEVICE_STATE TargetState
    )
{
    PDEVICE_CONTEXT  pDeviceContext;
    NTSTATUS status;
    PAGED_CODE();
    pDeviceContext = WdfObjectGet_DEVICE_CONTEXT(Device);
    WdfIoTargetStop (WdfUsbTargetPipeGetIoTarget (pDeviceContext->InterruptPipe), WdfIoTargetCancelSentIo));
}

L’exemple précédent montre l’implémentation des routines de rappel EvtDeviceD0Entry et EvtDeviceD0Exit. Le paramètre Action de WdfIoTargetStop permet au pilote client de décider de l’action des demandes en attente dans la file d’attente lorsque l’appareil quitte l’état de fonctionnement. Dans l’exemple, le pilote spécifie WdfIoTargetCancelSentIo. Cette option indique au framework d’annuler toutes les demandes en attente dans la file d’attente. Le pilote peut également demander à l’infrastructure d’attendre que les demandes en attente soient terminées avant d’arrêter la cible d’E/S ou de conserver les requêtes en attente et reprendre lorsque la cible d’E/S redémarre.

Utiliser le lecteur continu dans un pilote client UMDF

Avant de commencer à utiliser le lecteur continu, vous devez configurer le lecteur dans votre implémentation de la méthode IPnpCallbackHardware ::OnPrepareHardware. Après avoir obtenu un pointeur vers l’interface IWDFUsbTargetPipe de l’objet de canal cible associé au point de terminaison IN, procédez comme suit :

Configurer le lecteur continu dans un pilote client UMDF

  1. Appelez QueryInterface sur l’objet de canal cible (IWDFUsbTargetPipe) et interrogez l’interface IWDFUsbTargetPipe2.

  2. Appelez QueryInterface sur l’objet de rappel de l’appareil et interrogez l’interface IUsbTargetPipeContinuousReaderCallbackReadComplete. Pour utiliser le lecteur continu, vous devez implémenter IUsbTargetPipeContinuousReaderCallbackReadComplete. L’implémentation est décrite plus loin dans cette rubrique.

  3. Appelez QueryInterface sur l’objet de rappel de l’appareil et interrogez l’interface IUsbTargetPipeContinuousReaderCallbackReadersFailed si vous avez implémenté un rappel d’échec. L’implémentation est décrite plus loin dans cette rubrique.

  4. Appelez la méthode IWDFUsbTargetPipe2 ::ConfigureContinuousReader et spécifiez les paramètres de configuration, tels que l’en-tête, la bande-annonce, le nombre de requêtes en attente et les références aux méthodes de rappel d’achèvement et d’échec.

    La méthode configure le lecteur continu de l’objet de canal cible. Le lecteur continu crée des files d’attente qui gèrent un ensemble de demandes de lecture à mesure qu’elles sont envoyées et reçues de l’objet de canal cible.

L’exemple de code suivant configure le lecteur continu pour l’objet de canal cible spécifié. L’exemple suppose que l’objet de canal cible spécifié par l’appelant est associé à un point de terminaison IN. Le lecteur continu est configuré pour lire USBD_DEFAULT_MAXIMUM_TRANSFER_SIZE octets ; pour utiliser le nombre par défaut de requêtes en attente à l’aide de l’infrastructure ; pour appeler les méthodes de rappel d’achèvement et de rappel d’échec fournies par le pilote client. La mémoire tampon reçue ne contient aucune donnée d’en-tête ou de bande-annonce.

HRESULT CDeviceCallback::ConfigureContinuousReader (IWDFUsbTargetPipe* pFxPipe)
{
    if (!pFxPipe)
    {
        return E_INVALIDARG;
    }

    IUsbTargetPipeContinuousReaderCallbackReadComplete *pOnCompletionCallback = NULL;
    IUsbTargetPipeContinuousReaderCallbackReadersFailed *pOnFailureCallback = NULL;
    IWDFUsbTargetPipe2* pFxUsbPipe2 = NULL;

    HRESULT hr = S_OK;

    // Set up the continuous reader to read from the target pipe object.

    //Get a pointer to the target pipe2 object.
    hr = pFxPipe->QueryInterface(IID_PPV_ARGS(&pFxUsbPipe2));
    if (FAILED(hr))
    {
        goto ConfigureContinuousReaderExit;
    }

    //Get a pointer to the completion callback.
    hr = QueryInterface(IID_PPV_ARGS(&pOnCompletionCallback));
    if (FAILED(hr))
    {
        goto ConfigureContinuousReaderExit;
    }

    //Get a pointer to the failure callback.
    hr = QueryInterface(IID_PPV_ARGS(&pOnFailureCallback));
    if (FAILED(hr))
    {
        goto ConfigureContinuousReaderExit;
    }

    //Get a pointer to the target pipe2 object.
    hr = pFxUsbPipe2->ConfigureContinuousReader (
        USBD_DEFAULT_MAXIMUM_TRANSFER_SIZE, //size of data to be read
        0, //Header
        0, //Trailer
        0, // Number of pending requests queued by WDF
        NULL, // Cleanup callback. Not provided.
        pOnCompletionCallback, //Completion routine.
        NULL, //Completion routine context. Not provided.
        pOnFailureCallback); //Failure routine. Not provided

    if (FAILED(hr))
    {
        goto ConfigureContinuousReaderExit;
    }

ConfigureContinuousReaderExit:

    if (pOnFailureCallback)
    {
        pOnFailureCallback->Release();
        pOnFailureCallback = NULL;
    }

    if (pOnCompletionCallback)
    {
        pOnCompletionCallback->Release();
        pOnCompletionCallback = NULL;
    }

    if (pFxUsbPipe2)
    {
        pFxUsbPipe2->Release();
        pFxUsbPipe2 = NULL;
    }

    return hr;
}

Ensuite, spécifiez l’état de l’objet de canal cible, lorsque l’appareil entre et quitte un état de travail (D0).

Si un pilote client utilise une file d’attente gérée par l’alimentation pour envoyer des requêtes à un canal, la file d’attente remet les demandes uniquement lorsque l’appareil est dans l’état D0 . Si l’état d’alimentation de l’appareil passe de D0 à un état d’alimentation inférieur (à la sortie D0 ), l’objet de canal cible termine les requêtes en attente et la file d’attente cesse d’envoyer des demandes à l’objet de canal cible. Par conséquent, le pilote client n’est pas requis pour démarrer et arrêter l’objet de canal cible.

Le lecteur continu n’utilise pas de files d’attente gérées par l’alimentation pour envoyer des demandes. Par conséquent, vous devez démarrer ou arrêter explicitement l’objet de canal cible lorsque l’état d’alimentation de l’appareil change. Pour modifier l’état de l’objet de canal cible, vous pouvez utiliser l’interface IWDFIoTargetStateManagement implémentée par l’infrastructure. Après avoir obtenu un pointeur vers l’interface IWDFUsbTargetPipe de l’objet de canal cible associé au point de terminaison IN, procédez comme suit :

Implémenter la gestion de l’état

  1. Dans votre implémentation d’IPnpCallbackHardware ::OnPrepareHardware, appelez QueryInterface sur l’objet de canal cible (IWDFUsbTargetPipe) et interrogez l’interface IWDFIoTargetStateManagement. Stockez la référence dans une variable membre de votre classe de rappel d’appareil.

  2. Implémentez l’interface IPnpCallback sur l’objet de rappel d’appareil.

  3. Dans l’implémentation de la méthode IPnpCallback ::OnD0Entry , appelez IWDFIoTargetStateManagement ::Start pour démarrer le lecteur continu.

  4. Dans l’implémentation de la méthode IPnpCallback ::OnD0Exit , appelez IWDFIoTargetStateManagement ::Stop pour arrêter le lecteur continu.

Une fois l’appareil entré dans un état de travail (D0), l’infrastructure appelle la méthode de rappel D0 entrée fournie par le pilote client qui démarre l’objet de canal cible. Lorsque l’appareil quitte l’état D0 , l’infrastructure appelle la méthode de rappel de sortie D0. L’objet de canal cible termine le nombre de demandes de lecture en attente, configurées par le pilote client et cesse d’accepter de nouvelles demandes. L’exemple de code suivant implémente l’interface IPnpCallback sur l’objet de rappel d’appareil.

class CDeviceCallback :
    public IPnpCallbackHardware,
    public IPnpCallback,
{
public:
    CDeviceCallback();
    ~CDeviceCallback();
    virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, VOID** ppvObject);
    virtual ULONG STDMETHODCALLTYPE AddRef();
    virtual ULONG STDMETHODCALLTYPE Release();

    virtual HRESULT STDMETHODCALLTYPE OnPrepareHardware(IWDFDevice* pDevice);
    virtual HRESULT STDMETHODCALLTYPE OnReleaseHardware(IWDFDevice* pDevice);

    virtual HRESULT STDMETHODCALLTYPE OnD0Entry(IWDFDevice*  pWdfDevice, WDF_POWER_DEVICE_STATE  previousState);
    virtual HRESULT STDMETHODCALLTYPE OnD0Exit(IWDFDevice*  pWdfDevice, WDF_POWER_DEVICE_STATE  previousState);
    virtual void STDMETHODCALLTYPE OnSurpriseRemoval(IWDFDevice*  pWdfDevice);
    virtual HRESULT STDMETHODCALLTYPE OnQueryRemove(IWDFDevice*  pWdfDevice);
    virtual HRESULT STDMETHODCALLTYPE OnQueryStop(IWDFDevice*  pWdfDevice);

private:
    LONG m_cRefs;
    IWDFUsbTargetPipe* m_pFxUsbPipe;
    IWDFIoTargetStateManagement* m_pFxIoTargetInterruptPipeStateMgmt;

    HRESULT CreateUSBTargetDeviceObject (IWDFDevice* pFxDevice, IWDFUsbTargetDevice** ppUSBTargetDevice);
    HRESULT ConfigureContinuousReader (IWDFUsbTargetPipe* pFxPipe);
};

L’exemple de code suivant montre comment obtenir un pointeur vers l’interface IWDFIoTargetStateManagement de l’objet de canal cible dans la méthode IPnpCallback ::OnPrepareHardware

   //Enumerate the endpoints and get the interrupt pipe.
    for (UCHAR index = 0; index < NumEndpoints; index++)
    {
        hr = pFxInterface->RetrieveUsbPipeObject(index, &pFxPipe);

        if (SUCCEEDED (hr) && pFxPipe)
        {
            if ((pFxPipe->IsInEndPoint()) && (pFxPipe->GetType()==UsbdPipeTypeInterrupt))
            {
                //Pipe is for an interrupt IN endpoint.
                hr = pFxPipe->QueryInterface(IID_PPV_ARGS(&m_pFxIoTargetInterruptPipeStateMgmt));

                if (m_pFxIoTargetInterruptPipeStateMgmt)
                {
                    m_pFxUsbPipe = pFxPipe;
                    break;
                }

            }
            else
            {
                //Pipe is NOT for an interrupt IN endpoint.
                pFxPipe->Release();
                pFxPipe = NULL;
            }
        }
        else
        {
             //Pipe not found.
        }
    }

L’exemple de code suivant montre comment obtenir un pointeur vers l’interface IWDFIoTargetStateManagement de l’objet de canal cible dans la méthode IPnpCallbackHardware ::OnPrepareHardware.

 HRESULT CDeviceCallback::OnD0Entry(
    IWDFDevice*  pWdfDevice,
    WDF_POWER_DEVICE_STATE  previousState
    )
{

    if (!m_pFxIoTargetInterruptPipeStateMgmt)
    {
        return E_FAIL;
    }

    HRESULT hr = m_pFxIoTargetInterruptPipeStateMgmt->Start();

    if (FAILED (hr))
    {
        goto OnD0EntryExit;
    }

OnD0EntryExit:
    return hr;
}

HRESULT CDeviceCallback::OnD0Exit(
    IWDFDevice*  pWdfDevice,
    WDF_POWER_DEVICE_STATE  previousState
    )
{
    if (!m_pFxIoTargetInterruptPipeStateMgmt)
    {
        return E_FAIL;
    }

    // Stop the I/O target always succeeds.
    (void)m_pFxIoTargetInterruptPipeStateMgmt->Stop(WdfIoTargetCancelSentIo);

    return S_OK;
}

Une fois que le lecteur continu a terminé une demande de lecture, le pilote client doit fournir un moyen d’être averti lorsque la demande termine correctement une demande de lecture. Le pilote client doit ajouter ce code à l’objet de rappel d’appareil.

Fournir un rappel d’achèvement en implémentant IUsbTargetPipeContinuousReaderCallbackReadComplete

  1. Implémentez l’interface IUsbTargetPipeContinuousReaderCallbackReadComplete sur l’objet de rappel de l’appareil.

  2. Vérifiez que l’implémentation QueryInterface de l’objet de rappel d’appareil incrémente le nombre de références de l’objet de rappel, puis retourne le pointeur d’interface IUsbTargetPipeContinuousReaderCallbackReadComplete.

  3. Dans l’implémentation de la méthode IUsbTargetPipeContinuousReaderCallbackReadComplete ::OnReaderCompletion , accédez à la lecture des données lues à partir du canal. Le paramètre pMemory pointe vers la mémoire allouée par l’infrastructure qui contient les données. Vous pouvez appeler IWDFMemory ::GetDataBuffer pour obtenir la mémoire tampon qui contient les données. La mémoire tampon inclut l’en-tête, mais la longueur des données indiquées par le paramètre NumBytesTransferred d’OnReaderCompletion n’inclut pas la longueur de l’en-tête. La longueur de l’en-tête est spécifiée par le pilote client lors de la configuration du lecteur continu dans l’appel du pilote à IWDFUsbTargetPipe2 ::ConfigureContinuousReader.

  4. Fournissez un pointeur vers le rappel de saisie semi-automatique dans le paramètre pOnCompletion de la méthode IWDFUsbTargetPipe2 ::ConfigureContinuousReader.

Chaque fois que les données sont disponibles sur le point de terminaison sur l’appareil, l’objet de canal cible termine une demande de lecture. Si la demande de lecture s’est terminée correctement, l’infrastructure avertit le pilote client en appelant IUsbTargetPipeContinuousReaderCallbackReadComplete ::OnReaderCompletion. Sinon, l’infrastructure appelle un rappel d’échec fourni par le pilote client lorsque l’objet canal cible signale une erreur sur la demande de lecture.

L’exemple de code suivant implémente l’interface IUsbTargetPipeContinuousReaderCallbackReadComplete sur l’objet de rappel de l’appareil.

class CDeviceCallback :
    public IPnpCallbackHardware,
    public IPnpCallback,
    public IUsbTargetPipeContinuousReaderCallbackReadComplete

{
public:
    CDeviceCallback();
    ~CDeviceCallback();
    virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, VOID** ppvObject);
    virtual ULONG STDMETHODCALLTYPE AddRef();
    virtual ULONG STDMETHODCALLTYPE Release();

    virtual HRESULT STDMETHODCALLTYPE OnPrepareHardware(IWDFDevice* pDevice);
    virtual HRESULT STDMETHODCALLTYPE OnReleaseHardware(IWDFDevice* pDevice);

    virtual HRESULT STDMETHODCALLTYPE OnD0Entry(IWDFDevice*  pWdfDevice, WDF_POWER_DEVICE_STATE  previousState);
    virtual HRESULT STDMETHODCALLTYPE OnD0Exit(IWDFDevice*  pWdfDevice, WDF_POWER_DEVICE_STATE  previousState);
    virtual void STDMETHODCALLTYPE OnSurpriseRemoval(IWDFDevice*  pWdfDevice);
    virtual HRESULT STDMETHODCALLTYPE OnQueryRemove(IWDFDevice*  pWdfDevice);
    virtual HRESULT STDMETHODCALLTYPE OnQueryStop(IWDFDevice*  pWdfDevice);

    virtual VOID STDMETHODCALLTYPE OnReaderCompletion(IWDFUsbTargetPipe* pPipe, IWDFMemory* pMemory, SIZE_T NumBytesTransferred, PVOID Context);

private:
    LONG m_cRefs;
    IWDFUsbTargetPipe* m_pFxUsbPipe;
    IWDFIoTargetStateManagement* m_pFxIoTargetInterruptPipeStateMgmt;

    HRESULT CreateUSBTargetDeviceObject (IWDFDevice* pFxDevice, IWDFUsbTargetDevice** ppUSBTargetDevice);
    HRESULT ConfigureContinuousReader (IWDFUsbTargetPipe* pFxPipe);
};

L’exemple de code suivant montre l’implémentation QueryInterface de l’objet de rappel d’appareil.

HRESULT CDeviceCallback::QueryInterface(REFIID riid, LPVOID* ppvObject)
{
    if (ppvObject == NULL)
    {
        return E_INVALIDARG;
    }

    *ppvObject = NULL;

    HRESULT hr = E_NOINTERFACE;

    if(  IsEqualIID(riid, __uuidof(IPnpCallbackHardware))   ||  IsEqualIID(riid, __uuidof(IUnknown))  )
    {
        *ppvObject = static_cast<IPnpCallbackHardware*>(this);
        reinterpret_cast<IUnknown*>(*ppvObject)->AddRef();
        hr = S_OK;
    }

    if(  IsEqualIID(riid, __uuidof(IPnpCallback)))
    {
        *ppvObject = static_cast<IPnpCallback*>(this);
        reinterpret_cast<IUnknown*>(*ppvObject)->AddRef();
        hr = S_OK;
    }

    if(  IsEqualIID(riid, __uuidof(IUsbTargetPipeContinuousReaderCallbackReadComplete)))
    {
        *ppvObject = static_cast<IUsbTargetPipeContinuousReaderCallbackReadComplete*>(this);
        reinterpret_cast<IUnknown*>(*ppvObject)->AddRef();
        hr = S_OK;
    }

    return hr;
}

L’exemple de code suivant montre comment obtenir des données à partir de la mémoire tampon retournée par IUsbTargetPipeContinuousReaderCallbackReadComplete ::OnReaderCompletion. Chaque fois que l’objet de canal cible termine une demande de lecture, l’infrastructure appelle OnReaderCompletion. L’exemple obtient la mémoire tampon qui contient des données et imprime le contenu dans la sortie du débogueur.

 VOID CDeviceCallback::OnReaderCompletion(
    IWDFUsbTargetPipe* pPipe,
    IWDFMemory* pMemory,
    SIZE_T NumBytesTransferred,
    PVOID Context)
{
    if (pPipe != m_pFxUsbInterruptPipe)
    {
        return;
    }

    if (NumBytesTransferred == 0)
    {
        // NumBytesTransferred is zero.
        return;
    }

    PVOID pBuff = NULL;
    LONG CurrentData = 0;
    char data[20];

    pBuff = pMemory->GetDataBuffer(NULL);

    if (pBuff)
    {
        CopyMemory(&CurrentData, pBuff, sizeof(CurrentData));
        sprintf_s(data, 20, "%d\n", CurrentData);
        OutputDebugString(data);
        pBuff = NULL;
    }
    else
    {
        OutputDebugString(TEXT("Unable to get data buffer."));
    }
}

Le pilote client peut recevoir des notifications à partir de l’infrastructure lorsqu’une défaillance se produit dans l’objet de canal cible lors de la fin d’une demande de lecture. Pour recevoir des notifications, le pilote client doit implémenter un rappel d’échec et fournir un pointeur vers le rappel lors de la configuration du lecteur continu. La procédure suivante décrit comment implémenter le rappel d’échec.

Fournir un rappel d’échec en implémentant IUsbTargetPipeContinuousReaderCallbackReadersFailed

  1. Implémentez l’interface IUsbTargetPipeContinuousReaderCallbackReadersFailed sur l’objet de rappel de l’appareil.

  2. Vérifiez que l’implémentation QueryInterface de l’objet de rappel d’appareil incrémente le nombre de références de l’objet de rappel, puis retourne le pointeur d’interface IUsbTargetPipeContinuousReaderCallbackReadersFailed.

  3. Dans l’implémentation de la méthode IUsbTargetPipeContinuousReaderCallbackReadersFailed ::OnReaderFailure , fournissez la gestion des erreurs de la demande de lecture ayant échoué.

    Si le lecteur continu ne parvient pas à terminer une demande de lecture et que le pilote client fournit un rappel d’échec, l’infrastructure appelle la méthode IUsbTargetPipeContinuousReaderCallbackReadersFailed ::OnReaderFailure. L’infrastructure fournit une valeur HRESULT dans le paramètre hrStatus qui indique le code d’erreur qui s’est produit dans l’objet de canal cible. En fonction de ce code d’erreur, vous pouvez fournir une certaine gestion des erreurs. Par exemple, si vous souhaitez que l’infrastructure réinitialise le canal, puis redémarrez le lecteur continu, assurez-vous que le rappel retourne TRUE.

    Remarque : n’appelez pas IWDFIoTargetStateManagement ::Start et IWDFIoTargetStateManagement ::Stop dans le rappel d’échec.

  4. Fournissez un pointeur vers le rappel d’échec dans le paramètre pOnFailure de la méthode IWDFUsbTargetPipe2 ::ConfigureContinuousReader.

L’exemple de code suivant implémente l’interface IUsbTargetPipeContinuousReaderCallbackReadersFailed sur l’objet de rappel d’appareil.

class CDeviceCallback :
    public IPnpCallbackHardware,
    public IPnpCallback,
    public IUsbTargetPipeContinuousReaderCallbackReadComplete,
    public IUsbTargetPipeContinuousReaderCallbackReadersFailed
{
public:
    CDeviceCallback();
    ~CDeviceCallback();
    virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, VOID** ppvObject);
    virtual ULONG STDMETHODCALLTYPE AddRef();
    virtual ULONG STDMETHODCALLTYPE Release();

    virtual HRESULT STDMETHODCALLTYPE OnPrepareHardware(IWDFDevice* pDevice);
    virtual HRESULT STDMETHODCALLTYPE OnReleaseHardware(IWDFDevice* pDevice);

    virtual HRESULT STDMETHODCALLTYPE OnD0Entry(IWDFDevice*  pWdfDevice, WDF_POWER_DEVICE_STATE  previousState);
    virtual HRESULT STDMETHODCALLTYPE OnD0Exit(IWDFDevice*  pWdfDevice, WDF_POWER_DEVICE_STATE  previousState);
    virtual void STDMETHODCALLTYPE OnSurpriseRemoval(IWDFDevice*  pWdfDevice);
    virtual HRESULT STDMETHODCALLTYPE OnQueryRemove(IWDFDevice*  pWdfDevice);
    virtual HRESULT STDMETHODCALLTYPE OnQueryStop(IWDFDevice*  pWdfDevice);

    virtual VOID STDMETHODCALLTYPE OnReaderCompletion(IWDFUsbTargetPipe* pPipe, IWDFMemory* pMemory, SIZE_T NumBytesTransferred, PVOID Context);
    virtual BOOL STDMETHODCALLTYPE OnReaderFailure(IWDFUsbTargetPipe * pPipe, HRESULT hrCompletion);

private:
    LONG m_cRefs;
    IWDFUsbTargetPipe* m_pFxUsbInterruptPipe;
    IWDFIoTargetStateManagement* m_pFxIoTargetInterruptPipeStateMgmt;

    HRESULT CreateUSBTargetDeviceObject (IWDFDevice* pFxDevice, IWDFUsbTargetDevice** ppUSBTargetDevice);
    HRESULT RetrieveUSBDeviceDescriptor (IWDFUsbTargetDevice* pUSBTargetDevice, PUSB_DEVICE_DESCRIPTOR DescriptorHeader, PULONG cbDescriptor);
    HRESULT ConfigureContinuousReader (IWDFUsbTargetPipe* pFxPipe);
};

L’exemple de code suivant montre l’implémentation QueryInterface de l’objet de rappel d’appareil.

HRESULT CDeviceCallback::QueryInterface(REFIID riid, LPVOID* ppvObject)
{
    if (ppvObject == NULL)
    {
        return E_INVALIDARG;
    }

    *ppvObject = NULL;

    HRESULT hr = E_NOINTERFACE;

    if(  IsEqualIID(riid, __uuidof(IPnpCallbackHardware))   ||  IsEqualIID(riid, __uuidof(IUnknown))  )
    {
        *ppvObject = static_cast<IPnpCallbackHardware*>(this);
        reinterpret_cast<IUnknown*>(*ppvObject)->AddRef();
        hr = S_OK;

    }

    if(  IsEqualIID(riid, __uuidof(IPnpCallback)))
    {
        *ppvObject = static_cast<IPnpCallback*>(this);
        reinterpret_cast<IUnknown*>(*ppvObject)->AddRef();
        hr = S_OK;
    }

    if(  IsEqualIID(riid, __uuidof(IUsbTargetPipeContinuousReaderCallbackReadComplete)))
    {
        *ppvObject = static_cast<IUsbTargetPipeContinuousReaderCallbackReadComplete*>(this);
        reinterpret_cast<IUnknown*>(*ppvObject)->AddRef();
        hr = S_OK;
    }

    if(  IsEqualIID(riid, __uuidof(IUsbTargetPipeContinuousReaderCallbackReadersFailed)))
    {
        *ppvObject = static_cast<IUsbTargetPipeContinuousReaderCallbackReadersFailed*>(this);
        reinterpret_cast<IUnknown*>(*ppvObject)->AddRef();
        hr = S_OK;
    }

    return hr;
}

L’exemple de code suivant montre une implémentation d’un rappel d’échec. Si une demande de lecture échoue, la méthode imprime le code d’erreur signalé par l’infrastructure dans le débogueur et demande à l’infrastructure de réinitialiser le canal, puis de redémarrer le lecteur continu.

 BOOL CDeviceCallback::OnReaderFailure(
    IWDFUsbTargetPipe * pPipe,
    HRESULT hrCompletion
    )
{
    UNREFERENCED_PARAMETER(pPipe);
    UNREFERENCED_PARAMETER(hrCompletion);
    return TRUE;
}

Si le pilote client ne fournit pas de rappel d’échec et qu’une erreur se produit, l’infrastructure réinitialise le canal USB et redémarre le lecteur continu.