Come usare il lettore continuo per la lettura dei dati da una pipe USB
In questo argomento viene descritto l'oggetto lettore continuo fornito da WDF. Le procedure descritte in questo argomento forniscono istruzioni dettagliate su come configurare l'oggetto e usarlo per leggere i dati da una pipe USB.
Windows Driver Framework (WDF) fornisce un oggetto specializzato denominato lettore continuo. Questo oggetto consente a un driver client USB di leggere i dati dagli endpoint bulk ed interrupt in modo continuo, purché siano disponibili dati. Per usare il lettore, il driver client deve disporre di un handle per un oggetto della pipe di destinazione USB associato all'endpoint da cui il driver legge i dati. L'endpoint deve trovarsi nella configurazione attiva. È possibile rendere attiva una configurazione in uno dei due modi seguenti: selezionando una configurazione USB o modificando l'impostazione alternativa nella configurazione corrente. Per altre informazioni su queste operazioni, vedere Come selezionare una configurazione per un dispositivo USB e Come selezionare un'impostazione alternativa in un'interfaccia USB.
Dopo aver creato il lettore continuo, il driver client può avviare e arrestare il lettore come e quando necessario. Il lettore continuo che garantisce che una richiesta di lettura sia sempre disponibile nell'oggetto pipe di destinazione e che il driver client sia sempre pronto a ricevere dati dall'endpoint.
Il lettore continuo non viene gestito automaticamente dal framework. Ciò significa che il driver client deve arrestare il lettore quando il dispositivo entra in uno stato di alimentazione inferiore e riavviare il lettore quando il dispositivo entra nello stato di lavoro.
Questo articolo usa:
Prima di iniziare
Prima che il driver client possa usare il lettore continuo, assicurarsi che siano soddisfatti questi requisiti:
Il dispositivo USB deve avere un endpoint IN. Controllare la configurazione del dispositivo in USBView. Usbview.exe è un'applicazione che consente di esplorare tutti i controller USB e i dispositivi USB connessi. In genere, USBView viene installato nella cartella Debuggers in Windows Driver Kit (WDK).
Il driver client deve aver creato l'oggetto dispositivo di destinazione USB del framework.
Se si usano i modelli USB forniti con Microsoft Visual Studio Professional 2012, il codice del modello esegue tali attività. Il codice del modello ottiene l'handle per l'oggetto dispositivo di destinazione e archivia nel contesto del dispositivo.
Driver client KMDF:
Un driver client KMDF deve ottenere un handle WDFUSBDEVICE chiamando il metodo WdfUsbTargetDeviceCreateWithParameters. Per altre informazioni, vedere "Codice sorgente del dispositivo" in Informazioni sulla struttura del codice del driver client USB (KMDF).
Driver client UMDF:
Un driver client UMDF deve ottenere un puntatore IWDFUsbTargetDevice eseguendo una query sull'oggetto dispositivo di destinazione del framework. Per altre informazioni, vedere "Implementazione IPnpCallbackHardware e attività specifiche dell'USB" in Informazioni sulla struttura del codice del driver client USB (UMDF).
Il dispositivo deve avere una configurazione attiva.
Se si usano modelli USB, il codice seleziona la prima configurazione e l'impostazione alternativa predefinita in ogni interfaccia. Per informazioni su come modificare l'impostazione alternativa, vedere Come selezionare un'impostazione alternativa in un'interfaccia USB.
Driver client KMDF:
Un driver client KMDF deve chiamare il metodo WdfUsbTargetDeviceSelectConfig.
Driver client UMDF:
Per un driver client UMDF, il framework seleziona la prima configurazione e l'impostazione alternativa predefinita per ogni interfaccia in tale configurazione.
Il driver client deve avere un handle per l'oggetto pipe di destinazione del framework per l'endpoint IN. Per altre informazioni, vedere Come enumerare le pipe USB.
Usare il lettore continuo in un driver client KMDF
Prima di iniziare a usare il lettore continuo, è necessario configurarlo inizializzando una struttura WDF_USB_CONTINUOUS_READER_CONFIG.
Configurare il lettore continuo in un driver client KMDF
Inizializzare una struttura WDF_USB_CONTINUOUS_READER_CONFIG chiamando la macro WDF_USB_CONTINUOUS_READER_CONFIG_INIT.
Specificare le opzioni di configurazione nella struttura WDF_USB_CONTINUOUS_READER_CONFIG.
Chiamare il metodo WdfUsbTargetPipeConfigContinuousReader.
Il codice di esempio seguente configura il lettore continuo per l'oggetto pipe di destinazione specificato.
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; }
In genere il driver client configura il lettore continuo nella funzione di callback EvtDevicePrepareHardware dopo aver enumerato gli oggetti della pipe di destinazione nell'impostazione attiva.
Nell'esempio precedente, il driver client specifica le opzioni di configurazione in due modi. Per prima cosa, chiamando WDF_USB_CONTINUOUS_READER_CONFIG_INIT e quindi impostando WDF_USB_CONTINUOUS_READER_CONFIG membri. Si notino i parametri per WDF_USB_CONTINUOUS_READER_CONFIG_INIT. Questi valori sono obbligatori. In questo esempio, il driver client specifica:
- Puntatore a una routine di completamento implementata dal driver. Il framework chiama questa routine quando completa una richiesta di lettura. Nella routine di completamento, il driver può accedere alla posizione di memoria che contiene i dati letti. L'implementazione della routine di completamento è descritta nel passaggio 2.
- Puntatore al contesto definito dal driver.
- Numero di byte che possono essere letti dal dispositivo in un singolo trasferimento. Il driver client può ottenere tali informazioni in una struttura WDF_USB_PIPE_INFORMATION chiamando il metodo WdfUsbInterfaceGetConfiguredPipe o WdfUsbTargetPipeGetInformation. Per altre informazioni, vedere Come enumerare le pipe USB.
WDF_USB_CONTINUOUS_READER_CONFIG_INIT configura il lettore continuo in modo da usare il valore predefinito per NumPendingReads. Tale valore determina il numero di richieste di lettura aggiunte dal framework alla coda in sospeso. Il valore predefinito è stato determinato per offrire prestazioni ragionevolmente buone per molti dispositivi in molte configurazioni del processore.
Oltre ai parametri di configurazione specificati in WDF_USB_CONTINUOUS_READER_CONFIG_INIT, l'esempio imposta anche una routine di errore in WDF_USB_CONTINUOUS_READER_CONFIG. Questa routine di errore è facoltativa.
Oltre alla routine di errore, sono presenti altri membri in WDF_USB_CONTINUOUS_READER_CONFIG che il driver client può usare per specificare il layout del buffer di trasferimento. Si consideri, ad esempio, un driver di rete che usa il lettore continuo per ricevere pacchetti di rete. Ogni pacchetto contiene dati di intestazione, payload e piè di pagina. Per descrivere il pacchetto, il driver deve innanzitutto specificare le dimensioni del pacchetto nella chiamata a WDF_USB_CONTINUOUS_READER_CONFIG_INIT. Il driver deve quindi specificare la lunghezza dell'intestazione e del piè di pagina impostando i membri HeaderLength e TrailerLength di WDF_USB_CONTINUOUS_READER_CONFIG. Il framework usa questi valori per calcolare gli offset dei byte su entrambi i lati del payload. Quando i dati del payload vengono letti dall'endpoint, il framework archivia i dati nella parte del buffer tra gli offset.
Implementare la routine di completamento
Il framework richiama la routine di completamento implementata dal driver client ogni volta che viene completata una richiesta. Il framework passa il numero di byte letti e un oggetto WDFMEMORY il cui buffer contiene i dati letti dalla pipe.
Il codice di esempio seguente illustra l'implementazione della routine di completamento.
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;
}
Il framework richiama la routine di completamento implementata dal driver client ogni volta che viene completata una richiesta. Il framework alloca un oggetto memoria per ogni operazione di lettura. Nella routine di completamento, il framework passa il numero di byte letti e un handle WDFMEMORY all'oggetto memoria. Il buffer dell'oggetto memoria contiene i dati letti dalla pipe. Il driver client non deve liberare l'oggetto memoria. Il framework rilascia l'oggetto dopo la restituzione di ogni routine di completamento. Se il driver client vuole archiviare i dati ricevuti, il driver deve copiare il contenuto del buffer nella routine di completamento.
Implementare la routine di errore
Il framework richiama la routine di errore implementata dal driver client per informare il driver che il lettore continuo ha segnalato un errore durante l'elaborazione di una richiesta di lettura. Il framework passa il puntatore all'oggetto pipe di destinazione in cui la richiesta non è riuscita e i valori del codice di errore. In base a questi valori di codice di errore, il driver può implementare il meccanismo di ripristino degli errori. Il driver deve anche restituire un valore appropriato che indica al framework se il framework deve riavviare il lettore continuo.
Il codice di esempio seguente mostra un'implementazione di routine di errore.
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;
}
Nell'esempio precedente il driver restituisce TRUE. Questo valore indica al framework che deve reimpostare la pipe e quindi riavviare il lettore continuo.
In alternativa, il driver client può restituire FAL edizione Standard e fornire un meccanismo di ripristino degli errori se si verifica una condizione di stallo sulla pipe. Ad esempio, il driver può controllare lo stato USBD e inviare una richiesta di reimpostazione pipe per cancellare la condizione di stallo.
Per informazioni sul ripristino degli errori nelle pipe, vedere Come eseguire il ripristino da errori di pipe USB.
Avviare e arrestare il lettore continuo
Indicare al framework di avviare il lettore continuo quando il dispositivo entra nello stato di lavoro; arrestare il lettore quando il dispositivo lascia lo stato di lavoro. Chiamare questi metodi e specificare l'oggetto pipe di destinazione come oggetto di destinazione di I/O.
Il lettore continuo non viene gestito automaticamente dal framework. Pertanto, il driver client deve avviare o arrestare in modo esplicito l'oggetto pipe di destinazione quando lo stato di alimentazione del dispositivo cambia. Il driver chiama WdfIoTargetStart nell'implementazione evtDeviceD0Entry del driver. Questa chiamata garantisce che la coda fornisca le richieste solo quando il dispositivo è in stato di lavoro. Viceversa, il driver chiama WdfIoTargetStop nei driver EvtDeviceD0Exit implementazione in modo che la coda arresti il recapito delle richieste quando il dispositivo entra in uno stato di alimentazione inferiore.
Il codice di esempio seguente configura il lettore continuo per l'oggetto pipe di destinazione specificato.
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'esempio precedente mostra l'implementazione per le routine di callback EvtDeviceD0Entry e EvtDeviceD0Exit. Il parametro Action di WdfIoTargetStop consente al driver client di decidere l'azione per le richieste in sospeso nella coda quando il dispositivo lascia lo stato di lavoro. Nell'esempio il driver specifica WdfIoTargetCancelSentIo. Questa opzione indica al framework di annullare tutte le richieste in sospeso nella coda. In alternativa, il driver può indicare al framework di attendere il completamento delle richieste in sospeso prima di arrestare la destinazione di I/O o mantenere le richieste in sospeso e riprendere al riavvio della destinazione di I/O.
Usare il lettore continuo in un driver client UMDF
Prima di iniziare a usare il lettore continuo, è necessario configurare il lettore nell'implementazione del metodo IPnpCallbackHardware::OnPrepareHardware. Dopo aver visualizzato un puntatore all'interfaccia IWDFUsbTargetPipe dell'oggetto pipe di destinazione associato all'endpoint IN, seguire questa procedura:
Configurare il lettore continuo in un driver client UMDF
Chiamare QueryInterface sull'oggetto pipe di destinazione (IWDFUsbTargetPipe) e eseguire una query per l'interfaccia IWDFUsbTargetPipe2.
Chiamare QueryInterface nell'oggetto callback del dispositivo ed eseguire una query per l'interfaccia IUsbTargetPipeContinuousReaderCallbackReadComplete . Per usare il lettore continuo, è necessario implementare IUsbTargetPipeContinuousReaderCallbackReadComplete. L'implementazione è descritta più avanti in questo argomento.
Chiamare QueryInterface nell'oggetto callback del dispositivo ed eseguire una query per l'interfaccia IUsbTargetPipeContinuousReaderCallbackReadersFailed se è stato implementato un callback di errore. L'implementazione è descritta più avanti in questo argomento.
Chiamare il metodo IWDFUsbTargetPipe2::ConfigureContinuousReader e specificare i parametri di configurazione, ad esempio intestazione, trailer, numero di richieste in sospeso e riferimenti ai metodi di callback di completamento e errore.
Il metodo configura il lettore continuo per l'oggetto pipe di destinazione. Il lettore continuo crea code che gestiscono un set di richieste di lettura man mano che vengono inviate e ricevute dall'oggetto pipe di destinazione.
Il codice di esempio seguente configura il lettore continuo per l'oggetto pipe di destinazione specificato. Nell'esempio si presuppone che l'oggetto pipe di destinazione specificato dal chiamante sia associato a un endpoint IN. Il lettore continuo è configurato per leggere USBD_DEFAULT_MAXIMUM_TRANSFER_SIZE byte; per usare il numero predefinito di richieste in sospeso tramite il framework; per richiamare i metodi di callback di completamento e di errore forniti dal driver client. Il buffer ricevuto non conterrà alcun dato di intestazione o trailer.
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;
}
Specificare quindi lo stato dell'oggetto pipe di destinazione, quando il dispositivo entra e esce da uno stato di lavoro (D0).
Se un driver client usa una coda gestita dall'alimentazione per inviare richieste a una pipe, la coda recapita le richieste solo quando il dispositivo si trova nello stato D0 . Se lo stato di alimentazione del dispositivo passa da D0 a uno stato di alimentazione inferiore (all'uscita D0 ), l'oggetto pipe di destinazione completa le richieste in sospeso e la coda smette di inviare richieste all'oggetto pipe di destinazione. Pertanto, il driver client non è necessario per avviare e arrestare l'oggetto pipe di destinazione.
Il lettore continuo non usa code gestite dall'alimentazione per inviare richieste. Pertanto, è necessario avviare o arrestare in modo esplicito l'oggetto pipe di destinazione quando lo stato di alimentazione del dispositivo cambia. Per modificare lo stato dell'oggetto pipe di destinazione, è possibile usare l'interfaccia IWDFIoTargetStateManagement implementata dal framework. Dopo aver visualizzato un puntatore all'interfaccia IWDFUsbTargetPipe dell'oggetto pipe di destinazione associato all'endpoint IN, seguire questa procedura:
Implementare la gestione dello stato
Nell'implementazione di IPnpCallbackHardware::OnPrepareHardware chiamare QueryInterface sull'oggetto pipe di destinazione (IWDFUsbTargetPipe) e eseguire query per l'interfaccia IWDFIoTargetStateManagement. Archiviare il riferimento in una variabile membro della classe di callback del dispositivo.
Implementare l'interfaccia IPnpCallback nell'oggetto callback del dispositivo.
Nell'implementazione del metodo IPnpCallback::OnD0Entry chiamare IWDFIoTargetStateManagement::Start per avviare il lettore continuo.
Nell'implementazione del metodo IPnpCallback::OnD0Exit chiamare IWDFIoTargetStateManagement::Stop per arrestare il lettore continuo.
Dopo che il dispositivo entra in uno stato di lavoro (D0), il framework chiama il metodo di callback D0-entry fornito dal driver client che avvia l'oggetto pipe di destinazione. Quando il dispositivo lascia lo stato D0 , il framework chiama il metodo di callback D0-exit. L'oggetto pipe di destinazione completa il numero di richieste di lettura in sospeso, configurate dal driver client e interrompe l'accettazione di nuove richieste. Il codice di esempio seguente implementa l'interfaccia IPnpCallback nell'oggetto callback del 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);
};
Il codice di esempio seguente illustra come ottenere un puntatore all'interfaccia IWDFIoTargetStateManagement dell'oggetto pipe di destinazione nel metodo 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.
}
}
Il codice di esempio seguente illustra come ottenere un puntatore all'interfaccia IWDFIoTargetStateManagement dell'oggetto pipe di destinazione nel metodo 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;
}
Dopo che il lettore continuo completa una richiesta di lettura, il driver client deve fornire un modo per ricevere una notifica quando la richiesta completa correttamente una richiesta di lettura. Il driver client deve aggiungere questo codice all'oggetto callback del dispositivo.
Fornire un callback di completamento implementando IUsbTargetPipeContinuousReaderCallbackReadComplete
Implementare l'interfaccia IUsbTargetPipeContinuousReaderCallbackReadComplete nell'oggetto callback del dispositivo.
Assicurarsi che l'implementazione QueryInterface dell'oggetto callback del dispositivo incrementi il conteggio dei riferimenti dell'oggetto callback e quindi restituisca il puntatore all'interfaccia IUsbTargetPipeContinuousReaderCallbackReadComplete.
Nell'implementazione del metodo IUsbTargetPipeContinuousReaderCallbackReadComplete::OnReaderCompletion accedere ai dati letti dalla pipe. Il parametro pMemory punta alla memoria allocata dal framework che contiene i dati. È possibile chiamare IWDFMemory::GetDataBuffer per ottenere il buffer che contiene i dati. Il buffer include l'intestazione, tuttavia la lunghezza dei dati indicata dal parametro NumBytesTransferred di OnReaderCompletion non include la lunghezza dell'intestazione. La lunghezza dell'intestazione viene specificata dal driver client durante la configurazione del lettore continuo nella chiamata del driver a IWDFUsbTargetPipe2::ConfigureContinuousReader.
Fornire un puntatore al callback di completamento nel parametro pOnCompletion del metodo IWDFUsbTargetPipe2::ConfigureContinuousReader.
Ogni volta che i dati sono disponibili nell'endpoint nel dispositivo, l'oggetto pipe di destinazione completa una richiesta di lettura. Se la richiesta di lettura è stata completata correttamente, il framework invia una notifica al driver client chiamando IUsbTargetPipeContinuousReaderCallbackReadComplete::OnReaderCompletion. In caso contrario, il framework chiama un callback di errore fornito dal driver client quando l'oggetto pipe di destinazione segnala un errore nella richiesta di lettura.
Il codice di esempio seguente implementa l'interfaccia IUsbTargetPipeContinuousReaderCallbackReadComplete nell'oggetto callback del 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);
};
Il codice di esempio seguente illustra l'implementazione QueryInterface dell'oggetto callback del 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;
}
Il codice di esempio seguente illustra come ottenere dati dal buffer restituito da IUsbTargetPipeContinuousReaderCallbackReadComplete::OnReaderCompletion. Ogni volta che l'oggetto pipe di destinazione completa correttamente una richiesta di lettura, il framework chiama OnReaderCompletion. L'esempio ottiene il buffer che contiene dati e stampa il contenuto nell'output del debugger.
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."));
}
}
Il driver client può ricevere notifiche dal framework quando si verifica un errore nell'oggetto pipe di destinazione durante il completamento di una richiesta di lettura. Per ottenere notifiche, il driver client deve implementare un callback di errore e fornire un puntatore al callback durante la configurazione del lettore continuo. La procedura seguente descrive come implementare il callback di errore.
Fornire un callback di errore implementando IUsbTargetPipeContinuousReaderCallbackReadersFailed
Implementare l'interfaccia IUsbTargetPipeContinuousReaderCallbackReadersFailed nell'oggetto callback del dispositivo.
Assicurarsi che l'implementazione QueryInterface dell'oggetto callback del dispositivo incrementi il conteggio dei riferimenti dell'oggetto callback e quindi restituisca il puntatore all'interfaccia IUsbTargetPipeContinuousReaderCallbackReadersFailed.
Nell'implementazione del metodo IUsbTargetPipeContinuousReaderCallbackReadersFailed::OnReaderFailure , fornire la gestione degli errori della richiesta di lettura non riuscita.
Se il lettore continuo non riesce a completare una richiesta di lettura e il driver client fornisce un callback di errore, il framework richiama il metodo IUsbTargetPipeContinuousReaderCallbackReadersFailed::OnReaderFailure. Il framework fornisce un valore HRESULT nel parametro hrStatus che indica il codice di errore che si è verificato nell'oggetto pipe di destinazione. In base a tale codice di errore, è possibile fornire una determinata gestione degli errori. Ad esempio, se si vuole che il framework reimposta la pipe e quindi riavvia il lettore continuo, assicurarsi che il callback restituisca TRUE.
Nota Non chiamare IWDFIoTargetStateManagement::Start e IWDFIoTargetStateManagement::Stop all'interno del callback degli errori.
Fornire un puntatore al callback degli errori nel parametro pOnFailure del metodo IWDFUsbTargetPipe2::ConfigureContinuousReader.
Il codice di esempio seguente implementa l'interfaccia IUsbTargetPipeContinuousReaderCallbackReadersFailed nell'oggetto callback del 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);
};
Il codice di esempio seguente illustra l'implementazione QueryInterface dell'oggetto callback del 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;
}
Il codice di esempio seguente illustra un'implementazione di un callback di errore. Se una richiesta di lettura ha esito negativo, il metodo stampa il codice di errore segnalato dal framework nel debugger e indica al framework di reimpostare la pipe e quindi riavviare il lettore continuo.
BOOL CDeviceCallback::OnReaderFailure(
IWDFUsbTargetPipe * pPipe,
HRESULT hrCompletion
)
{
UNREFERENCED_PARAMETER(pPipe);
UNREFERENCED_PARAMETER(hrCompletion);
return TRUE;
}
Se il driver client non fornisce un callback di errore e si verifica un errore, il framework reimposta la pipe USB e riavvia il lettore continuo.