Compartilhar via


Como usar o leitor contínuo para ler dados de um tubo USB

Este tópico descreve o objeto leitor contínuo fornecido pelo WDF. Os procedimentos neste tópico fornecem instruções passo a passo sobre como configurar o objeto e usá-lo para ler dados de um pipe USB.

O WDF (Windows Driver Framework) fornece um objeto especializado chamado leitor contínuo. Esse objeto permite que um driver de cliente USB leia dados de pontos de extremidade em massa e interrupção continuamente, desde que haja dados disponíveis. Para usar o leitor, o driver cliente deve ter um identificador para um objeto de pipe de destino USB que está associado ao ponto de extremidade a partir do qual o driver lê dados. O ponto de extremidade deve estar na configuração ativa. Você pode ativar uma configuração de duas maneiras: selecionando uma configuração USB ou alterando a configuração alternativa na configuração atual. Para obter mais informações sobre essas operações, consulte Como selecionar uma configuração para um dispositivo USB e Como selecionar uma configuração alternativa em uma interface USB.

Depois de criar o leitor contínuo, o driver do cliente pode iniciar e parar o leitor como e quando necessário. O leitor contínuo que garante que uma solicitação de leitura esteja sempre disponível no objeto de pipe de destino e que o driver do cliente esteja sempre pronto para receber dados do ponto de extremidade.

O leitor contínuo não é automaticamente alimentado pela estrutura. Isso significa que o driver do cliente deve parar o leitor quando o dispositivo entra em um estado de energia mais baixa e reiniciar o leitor quando o dispositivo entra em estado de funcionamento.

Este artigo utiliza:

Antes de começar

Antes que o driver cliente possa usar o leitor contínuo, certifique-se de que estes requisitos sejam atendidos:

  • O dispositivo USB deve ter um ponto de extremidade IN. Verifique a configuração do dispositivo em USBView. Usbview.exe é um aplicativo que permite que você navegue por todos os controladores USB e os dispositivos USB conectados a eles. Normalmente, USBView é instalado na pasta Depuradores no Windows Driver Kit (WDK).

  • O driver do cliente deve ter criado o objeto de dispositivo de destino USB da estrutura.

    Se você estiver usando os modelos USB fornecidos com o Microsoft Visual Studio Professional 2012, o código do modelo executará essas tarefas. O código do modelo obtém o identificador para o objeto de dispositivo de destino e armazena no contexto do dispositivo.

    Driver do cliente KMDF:

    Um driver de cliente KMDF deve obter um identificador WDFUSBDEVICE chamando o método WdfUsbTargetDeviceCreateWithParameters. Para obter mais informações, consulte "Código fonte do dispositivo" em Noções básicas sobre a estrutura de código do driver de cliente USB (KMDF).

    Driver do cliente UMDF:

    Um driver de cliente UMDF deve obter um ponteiro IWDFUsbTargetDevice consultando o objeto de dispositivo de destino da estrutura. Para obter mais informações, consulte "IPnpCallbackHardware implementation and USB-specific tasks" em Understanding the USB client driver code structure (UMDF).

  • O dispositivo deve ter uma configuração ativa.

    Se você estiver usando modelos USB, o código selecionará a primeira configuração e a configuração alternativa padrão em cada interface. Para obter informações sobre como alterar a configuração alternativa, consulte Como selecionar uma configuração alternativa em uma interface USB.

    Driver do cliente KMDF:

    Um driver de cliente KMDF deve chamar o método WdfUsbTargetDeviceSelectConfig.

    Driver do cliente UMDF:

    Para um driver de cliente UMDF, a estrutura seleciona a primeira configuração e a configuração alternativa padrão para cada interface nessa configuração.

  • O driver do cliente deve ter um identificador para o objeto de pipe de destino da estrutura para o ponto de extremidade IN. Para obter mais informações, consulte Como enumerar pipes USB.

Usar o leitor contínuo em um driver de cliente KMDF

Antes de começar a usar o leitor contínuo, você deve configurá-lo inicializando uma estrutura WDF_USB_CONTINUOUS_READER_CONFIG.

Configurar o leitor contínuo em um driver de cliente KMDF

  1. Inicialize uma estrutura WDF_USB_CONTINUOUS_READER_CONFIG chamando a macro WDF_USB_CONTINUOUS_READER_CONFIG_INIT.

  2. Especifique suas opções de configuração na estrutura WDF_USB_CONTINUOUS_READER_CONFIG.

  3. Chame o método WdfUsbTargetPipeConfigContinuousReader.

    O código de exemplo a seguir configura o leitor contínuo para o objeto de pipe de destino especificado.

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

Normalmente, o driver do cliente configura o leitor contínuo na função de retorno de chamada EvtDevicePrepareHardware depois de enumerar os objetos de pipe de destino na configuração ativa.

No exemplo anterior, o driver do cliente especifica suas opções de configuração de duas maneiras. Primeiro chamando WDF_USB_CONTINUOUS_READER_CONFIG_INIT e depois definindo WDF_USB_CONTINUOUS_READER_CONFIG membros. Observe os parâmetros para WDF_USB_CONTINUOUS_READER_CONFIG_INIT. Esses valores são obrigatórios. Neste exemplo, o driver do cliente especifica:

  • Um ponteiro para uma rotina de conclusão que o driver implementa. A estrutura chama essa rotina quando conclui uma solicitação de leitura. Na rotina de conclusão, o driver pode acessar o local da memória que contém os dados que foram lidos. A implementação da rotina de preenchimento é discutida na etapa 2.
  • Um ponteiro para o contexto definido pelo driver.
  • O número de bytes que podem ser lidos do dispositivo em uma única transferência. O driver cliente pode obter essas informações em uma estrutura WDF_USB_PIPE_INFORMATION chamando WdfUsbInterfaceGetConfiguredPipe ou WdfUsbTargetPipeGetInformation método. Para obter mais informações, consulte Como enumerar pipes USB.

WDF_USB_CONTINUOUS_READER_CONFIG_INIT configura o leitor contínuo para usar o valor padrão para NumPendingReads. Esse valor determina o número de solicitações de leitura que a estrutura adiciona à fila pendente. O valor padrão foi determinado para fornecer um desempenho razoavelmente bom para muitos dispositivos em muitas configurações de processador.

Além dos parâmetros de configuração especificados em WDF_USB_CONTINUOUS_READER_CONFIG_INIT, o exemplo também define uma rotina de falha no WDF_USB_CONTINUOUS_READER_CONFIG. Essa rotina de falha é opcional.

Além da rotina de falha, há outros membros no WDF_USB_CONTINUOUS_READER_CONFIG que o driver cliente pode usar para especificar o layout do buffer de transferência. Por exemplo, considere um driver de rede que usa o leitor contínuo para receber pacotes de rede. Cada pacote contém dados de cabeçalho, carga útil e rodapé. Para descrever o pacote, o driver deve primeiro especificar o tamanho do pacote em sua chamada para WDF_USB_CONTINUOUS_READER_CONFIG_INIT. Em seguida, o driver deve especificar o comprimento do cabeçalho e rodapé definindo os membros HeaderLength e TrailerLength de WDF_USB_CONTINUOUS_READER_CONFIG. A estrutura usa esses valores para calcular os deslocamentos de bytes em ambos os lados da carga útil. Quando os dados de carga são lidos do ponto de extremidade, a estrutura armazena esses dados na parte do buffer entre os deslocamentos.

Implementar a rotina de conclusão

A estrutura invoca a rotina de conclusão implementada pelo driver cliente sempre que uma solicitação é concluída. A estrutura passa o número de bytes lidos e um objeto WDFMEMORY cujo buffer contém os dados que são lidos do pipe.

O código de exemplo a seguir mostra a implementação da rotina de conclusão.

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

A estrutura invoca a rotina de conclusão implementada pelo driver cliente sempre que uma solicitação é concluída. A estrutura aloca um objeto de memória para cada operação de leitura. Na rotina de conclusão, a estrutura passa o número de bytes lidos e um identificador WDFMEMORY para o objeto de memória. O buffer de objeto de memória contém os dados que são lidos do pipe. O driver cliente não deve liberar o objeto de memória. A estrutura libera o objeto após cada retorno da rotina de conclusão. Se o driver cliente deseja armazenar os dados recebidos, o driver deve copiar o conteúdo do buffer na rotina de conclusão.

Implementar a rotina de falhas

A estrutura invoca a rotina de falha implementada pelo driver cliente para informar ao driver que o leitor contínuo relatou um erro ao processar uma solicitação de leitura. A estrutura passa o ponteiro para o objeto de pipe de destino no qual a solicitação falhou e os valores de código de erro. Com base nesses valores de código de erro, o driver pode implementar seu mecanismo de recuperação de erros. O driver também deve retornar um valor apropriado que indica à estrutura se a estrutura deve reiniciar o leitor contínuo.

O código de exemplo a seguir mostra uma implementação de rotina de falha.

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

No exemplo anterior, o driver retorna TRUE. Esse valor indica à estrutura que ela deve redefinir o pipe e, em seguida, reiniciar o leitor contínuo.

Como alternativa, o driver cliente pode retornar FALSE e fornecer um mecanismo de recuperação de erro se ocorrer uma condição de parada no tubo. Por exemplo, o motorista pode verificar o status USBD e emitir uma solicitação de reset-pipe para limpar a condição de estol.

Para obter informações sobre a recuperação de erros em pipes, consulte Como recuperar de erros de pipe USB.

Iniciar e parar o leitor contínuo

Instruir a estrutura para iniciar o leitor contínuo quando o dispositivo entrar em estado de funcionamento; Pare o leitor quando o dispositivo sair do estado de funcionamento. Chame esses métodos e especifique o objeto de pipe de destino como o objeto de destino de E/S.

O leitor contínuo não é automaticamente alimentado pela estrutura. Portanto, o driver cliente deve iniciar ou parar explicitamente o objeto de pipe de destino quando o estado de energia do dispositivo é alterado. O driver chama WdfIoTargetStart na implementação EvtDeviceD0Entry do driver. Essa chamada garante que a fila entregue solicitações somente quando o dispositivo estiver em estado de funcionamento. Por outro lado, o driver chama WdfIoTargetStop na implementação EvtDeviceD0Exit dos drivers para que a fila pare de entregar solicitações quando o dispositivo entrar em um estado de energia mais baixa.

O código de exemplo a seguir configura o leitor contínuo para o objeto de pipe de destino especificado.

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

O exemplo anterior mostra a implementação para as rotinas de retorno de chamada EvtDeviceD0Entry e EvtDeviceD0Exit. O parâmetro Action de WdfIoTargetStop permite que o driver cliente decida a ação para as solicitações pendentes na fila quando o dispositivo sai do estado de funcionamento. No exemplo, o driver especifica WdfIoTargetCancelSentIo. Essa opção instrui a estrutura a cancelar todas as solicitações pendentes na fila. Como alternativa, o driver pode instruir a estrutura a aguardar a conclusão das solicitações pendentes antes de parar o destino de E/S ou manter as solicitações pendentes e retomar quando o destino de E/S for reiniciado.

Usar o leitor contínuo em um driver de cliente UMDF

Antes de começar a usar o leitor contínuo, você deve configurar o leitor em sua implementação do método IPnpCallbackHardware::OnPrepareHardware. Depois de obter um ponteiro para a interface IWDFUsbTargetPipe do objeto de pipe de destino associado ao ponto de extremidade IN, execute estas etapas:

Configurar o leitor contínuo em um driver de cliente UMDF

  1. Chame QueryInterface no objeto de pipe de destino (IWDFUsbTargetPipe) e consulte a interface IWDFUsbTargetPipe2.

  2. Chame QueryInterface no objeto de retorno de chamada do dispositivo e consulte a interface IUsbTargetPipeContinuousReaderCallbackReadComplete. Para usar o leitor contínuo, você deve implementar IUsbTargetPipeContinuousReaderCallbackReadComplete. A implementação é descrita posteriormente neste tópico.

  3. Chame QueryInterface no objeto de retorno de chamada do dispositivo e consulte a interface IUsbTargetPipeContinuousReaderCallbackReadersFailed se você tiver implementado um retorno de chamada de falha. A implementação é descrita posteriormente neste tópico.

  4. Chame o método IWDFUsbTargetPipe2::ConfigureContinuousReader e especifique os parâmetros de configuração, como cabeçalho, trailer, número de solicitações pendentes e referências aos métodos de retorno de chamada de conclusão e falha.

    O método configura o leitor contínuo para o objeto de pipe de destino. O leitor contínuo cria filas que gerenciam um conjunto de solicitações de leitura à medida que são enviadas e recebidas do objeto de pipe de destino.

O código de exemplo a seguir configura o leitor contínuo para o objeto de pipe de destino especificado. O exemplo pressupõe que o objeto de pipe de destino especificado pelo chamador está associado a um ponto de extremidade IN. O leitor contínuo é configurado para ler USBD_DEFAULT_MAXIMUM_TRANSFER_SIZE bytes; usar o número padrão de solicitações pendentes usando o framework; para invocar os métodos de retorno de chamada de conclusão e falha fornecidos pelo driver do cliente. O buffer recebido não conterá nenhum dado de cabeçalho ou reboque.

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

Em seguida, especifique o estado do objeto de pipe de destino, quando o dispositivo entrar e sair de um estado de trabalho (D0).

Se um driver cliente usar uma fila gerenciada por energia para enviar solicitações a um pipe, a fila entregará solicitações somente quando o dispositivo estiver no estado D0 . Se o estado de energia do dispositivo mudar de D0 para um estado de energia mais baixa (na saída D0), o objeto de pipe de destino concluirá as solicitações pendentes e a fila interromperá o envio de solicitações para o objeto de pipe de destino. Portanto, o driver cliente não é necessário para iniciar e parar o objeto de pipe de destino.

O leitor contínuo não usa filas gerenciadas por energia para enviar solicitações. Portanto, você deve iniciar ou parar explicitamente o objeto de pipe de destino quando o estado de energia do dispositivo for alterado. Para alterar o estado do objeto de pipe de destino, você pode usar a interface IWDFIoTargetStateManagement implementada pela estrutura. Depois de obter um ponteiro para a interface IWDFUsbTargetPipe do objeto de pipe de destino associado ao ponto de extremidade IN, execute as seguintes etapas:

Implementar o gerenciamento de estado

  1. Em sua implementação de IPnpCallbackHardware::OnPrepareHardware, chame QueryInterface no objeto de pipe de destino (IWDFUsbTargetPipe) e consulte a interface IWDFIoTargetStateManagement. Armazene a referência em uma variável de membro da classe de retorno de chamada do dispositivo.

  2. Implemente a interface IPnpCallback no objeto de retorno de chamada do dispositivo.

  3. Na implementação do método IPnpCallback::OnD0Entry , chame IWDFIoTargetStateManagement::Start para iniciar o leitor contínuo.

  4. Na implementação do método IPnpCallback::OnD0Exit , chame IWDFIoTargetStateManagement::Stop para parar o leitor contínuo.

Depois que o dispositivo entra em um estado de trabalho (D0), a estrutura chama o método de retorno de chamada D0-entry fornecido pelo driver do cliente que inicia o objeto de pipe de destino. Quando o dispositivo sai do estado D0 , a estrutura chama o método de retorno de chamada D0-exit. O objeto de pipe de destino conclui o número de solicitações de leitura pendentes, configuradas pelo driver do cliente, e pára de aceitar novas solicitações. O código de exemplo a seguir implementa a interface IPnpCallback no objeto de retorno de chamada do dispositivo.

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

O código de exemplo a seguir mostra como obter um ponteiro para a interface IWDFIoTargetStateManagement do objeto de pipe de destino no método 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.
        }
    }

O código de exemplo a seguir mostra como obter um ponteiro para a interface IWDFIoTargetStateManagement do objeto de pipe de destino no método 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;
}

Depois que o leitor contínuo concluir uma solicitação de leitura, o driver cliente deve fornecer uma maneira de ser notificado quando a solicitação concluir uma solicitação de leitura com êxito. O driver cliente deve adicionar esse código ao objeto de retorno de chamada do dispositivo.

Fornecer um retorno de chamada de conclusão implementando IUsbTargetPipeContinuousReaderCallbackReadComplete

  1. Implemente a interface IUsbTargetPipeContinuousReaderCallbackReadComplete no objeto de retorno de chamada do dispositivo.

  2. Verifique se a implementação QueryInterface do objeto de retorno de chamada do dispositivo incrementa a contagem de referência do objeto de retorno de chamada e retorna o ponteiro da interface IUsbTargetPipeContinuousReaderCallbackReadComplete .

  3. Na implementação do método IUsbTargetPipeContinuousReaderCallbackReadComplete::OnReaderComplete , acesse os dados lidos que foram lidos do pipe. O parâmetro pMemory aponta para a memória alocada pela estrutura que contém os dados. Você pode chamar IWDFMemory::GetDataBuffer para obter o buffer que contém os dados. O buffer inclui o cabeçalho, no entanto, o comprimento dos dados indicado pelo parâmetro NumBytesTransferred de OnReaderCompletion não inclui o comprimento do cabeçalho. O comprimento do cabeçalho é especificado pelo driver cliente durante a configuração do leitor contínuo na chamada do driver para IWDFUsbTargetPipe2::ConfigureContinuousReader.

  4. Forneça um ponteiro para o retorno de chamada de conclusão no parâmetro pOnCompletion do método IWDFUsbTargetPipe2::ConfigureContinuousReader .

Sempre que os dados estiverem disponíveis no ponto de extremidade no dispositivo, o objeto de pipe de destino concluirá uma solicitação de leitura. Se a solicitação de leitura for concluída com êxito, a estrutura notificará o driver do cliente chamando IUsbTargetPipeContinuousReaderCallbackReadComplete::OnReaderCompletion. Caso contrário, a estrutura chama um retorno de chamada de falha fornecido pelo driver do cliente quando o objeto de pipe de destino relata um erro na solicitação de leitura.

O código de exemplo a seguir implementa a interface IUsbTargetPipeContinuousReaderCallbackReadComplete no objeto de retorno de chamada do dispositivo.

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

O código de exemplo a seguir mostra a implementação QueryInterface do objeto de retorno de chamada do dispositivo.

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

O código de exemplo a seguir mostra como obter dados do buffer retornado por IUsbTargetPipeContinuousReaderCallbackReadComplete::OnReaderCompletion. Cada vez que o objeto de pipe de destino conclui uma solicitação de leitura com êxito, a estrutura chama OnReaderCompletion. O exemplo obtém o buffer que contém dados e imprime o conteúdo na saída do depurador.

 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."));
    }
}

O driver cliente pode obter notificações da estrutura quando ocorre uma falha no objeto de pipe de destino ao concluir uma solicitação de leitura. Para obter notificações, o driver cliente deve implementar um retorno de chamada de falha e fornecer um ponteiro para o retorno de chamada durante a configuração do leitor contínuo. O procedimento a seguir descreve como implementar o retorno de chamada de falha.

Fornecer um retorno de chamada de falha implementando IUsbTargetPipeContinuousReaderCallbackReadersFailed

  1. Implemente a interface IUsbTargetPipeContinuousReaderCallbackReadersFailed no objeto de retorno de chamada do dispositivo.

  2. Verifique se a implementação QueryInterface do objeto de retorno de chamada do dispositivo incrementa a contagem de referência do objeto de retorno de chamada e retorna o ponteiro de interface IUsbTargetPipeContinuousReaderCallbackReadersFailed .

  3. Na implementação do método IUsbTargetPipeContinuousReaderCallbackReadersFailed::OnReaderFailure , forneça o tratamento de erros da solicitação de leitura com falha.

    Se o leitor contínuo não conseguir concluir uma solicitação de leitura e o driver do cliente fornecer um retorno de chamada de falha, a estrutura invocará o método IUsbTargetPipeContinuousReaderCallbackReadersFailed::OnReaderFailure. A estrutura fornece um valor HRESULT no parâmetro hrStatus que indica o código de erro que ocorreu no objeto de pipe de destino. Com base nesse código de erro, você pode fornecer um determinado tratamento de erros. Por exemplo, se você quiser que a estrutura redefina o pipe e, em seguida, reinicie o leitor contínuo, certifique-se de que o retorno de chamada retorne TRUE.

    Observação Não chame IWDFIoTargetStateManagement::Start e IWDFIoTargetStateManagement::Stop dentro do retorno de chamada de falha.

  4. Forneça um ponteiro para o retorno de chamada de falha no parâmetro pOnFailure do método IWDFUsbTargetPipe2::ConfigureContinuousReader .

O código de exemplo a seguir implementa a interface IUsbTargetPipeContinuousReaderCallbackReadersFailed no objeto de retorno de chamada do dispositivo.

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

O código de exemplo a seguir mostra a implementação QueryInterface do objeto de retorno de chamada do dispositivo.

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

O código de exemplo a seguir mostra uma implementação de um retorno de chamada de falha. Se uma solicitação de leitura falhar, o método imprimirá o código de erro relatado pela estrutura no depurador e instruirá a estrutura a redefinir o pipe e, em seguida, reiniciar o leitor contínuo.

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

Se o driver cliente não fornecer um retorno de chamada de falha e ocorrer um erro, a estrutura redefine o pipe USB e reinicia o leitor contínuo.