Uso del lector continuo para leer datos desde una canalización USB
En este tema se describe el objeto de lector continuo proporcionado por WDF. Los procedimientos de este tema proporcionan instrucciones paso a paso sobre cómo configurar el objeto y usarlos para leer datos de una canalización USB.
Windows Driver Framework (WDF) proporciona un objeto especializado denominado lector continuo. Este objeto permite a un controlador de cliente USB leer datos de puntos de conexión masivos e interrumpir continuamente, siempre y cuando haya datos disponibles. Para usar el lector, el controlador cliente debe tener un identificador para un objeto de canalización de destino USB asociado al punto de conexión desde el que el controlador lee los datos. El punto de conexión debe estar en la configuración activa. Puede activar una configuración de una de estas dos maneras: seleccionando una configuración USB o cambiando la configuración alternativa en la configuración actual. Para obtener más información sobre esas operaciones, vea Cómo seleccionar una configuración para un dispositivo USB y Cómo seleccionar una configuración alternativa en una interfaz USB.
Después de crear el lector continuo, el controlador cliente puede iniciar y detener el lector según sea necesario. Lector continuo que garantiza que una solicitud de lectura siempre está disponible en el objeto de canalización de destino y el controlador cliente siempre está listo para recibir datos del punto de conexión.
El marco no administra automáticamente el lector continuo. Esto significa que el controlador cliente debe detener el lector cuando el dispositivo entra en un estado de energía inferior y reiniciar el lector cuando el dispositivo entra en estado de trabajo.
En este artículo se usa:
Antes de comenzar
Antes de que el controlador cliente pueda usar el lector continuo, asegúrese de que se cumplen estos requisitos:
El dispositivo USB debe tener un punto de conexión IN. Compruebe la configuración del dispositivo en USBView. Usbview.exe es una aplicación que permite examinar todos los controladores USB y los dispositivos USB conectados a ellos. Normalmente, USBView se instala en la carpeta Depuradores del Kit de controladores de Windows (WDK).
El controlador cliente debe haber creado el objeto de dispositivo de destino USB del marco.
Si usa las plantillas USB que se proporcionan con Microsoft Visual Studio Professional 2012, el código de plantilla realiza esas tareas. El código de plantilla obtiene el identificador del objeto de dispositivo de destino y almacena en el contexto del dispositivo.
Controlador cliente de KMDF:
Un controlador cliente KMDF debe obtener un identificador WDFUSBDEVICE llamando al método WdfUsbTargetDeviceCreateWithParameters. Para obtener más información, vea "Código fuente del dispositivo" en Descripción de la estructura de código del controlador de cliente USB (KMDF).
Controlador de cliente UMDF:
Un controlador cliente UMDF debe obtener un puntero IWDFUsbTargetDevice consultando el objeto de dispositivo de destino de la plataforma. Para obtener más información, vea "IPnpCallbackHardware implementación y tareas específicas de USB" en Descripción de la estructura de código del controlador de cliente USB (UMDF).
El dispositivo debe tener una configuración activa.
Si usa plantillas USB, el código selecciona la primera configuración y la configuración alternativa predeterminada en cada interfaz. Para obtener información sobre cómo cambiar la configuración alternativa, vea Cómo seleccionar una configuración alternativa en una interfaz USB.
Controlador cliente de KMDF:
Un controlador cliente KMDF debe llamar al método WdfUsbTargetDeviceSelectConfig.
Controlador de cliente UMDF:
Para un controlador cliente UMDF, el marco selecciona la primera configuración y la configuración alternativa predeterminada para cada interfaz de esa configuración.
El controlador cliente debe tener un identificador para el objeto de canalización de destino del marco para el punto de conexión IN. Para obtener más información, vea Cómo enumerar canalizaciones USB.
Uso del lector continuo en un controlador cliente KMDF
Antes de empezar a usar el lector continuo, debe configurarlo inicializando una estructura de WDF_USB_CONTINUOUS_READER_CONFIG .
Configuración del lector continuo en un controlador cliente KMDF
Inicialice una estructura de WDF_USB_CONTINUOUS_READER_CONFIG llamando a la macro WDF_USB_CONTINUOUS_READER_CONFIG_INIT.
Especifique sus opciones de configuración en la estructura WDF_USB_CONTINUOUS_READER_CONFIG.
Llame al método WdfUsbTargetPipeConfigContinuousReader.
El código de ejemplo siguiente configura el lector continuo para el objeto de canalización 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, el controlador cliente configura el lector continuo en la función de devolución de llamada EvtDevicePrepareHardware después de enumerar los objetos de canalización de destino en la configuración activa.
En el ejemplo anterior, el controlador cliente especifica sus opciones de configuración de dos maneras. En primer lugar, llame a WDF_USB_CONTINUOUS_READER_CONFIG_INIT y, a continuación, establezca WDF_USB_CONTINUOUS_READER_CONFIG miembros. Observe los parámetros de WDF_USB_CONTINUOUS_READER_CONFIG_INIT. Estos valores son obligatorios. En este ejemplo, el controlador cliente especifica:
- Puntero a una rutina de finalización que implementa el controlador. El marco llama a esta rutina cuando completa una solicitud de lectura. En la rutina de finalización, el controlador puede acceder a la ubicación de memoria que contiene los datos leídos. La implementación de la rutina de finalización se describe en el paso 2.
- Puntero al contexto definido por el controlador.
- Número de bytes que se pueden leer desde el dispositivo en una sola transferencia. El controlador cliente puede obtener esa información en una estructura de WDF_USB_PIPE_INFORMATION llamando al método WdfUsbInterfaceGetConfiguredPipe o WdfUsbTargetPipeGetInformation. Para obtener más información, vea Cómo enumerar canalizaciones USB.
WDF_USB_CONTINUOUS_READER_CONFIG_INIT configura el lector continuo para usar el valor predeterminado de NumPendingReads. Ese valor determina el número de solicitudes de lectura que el marco agrega a la cola pendiente. El valor predeterminado se ha determinado para proporcionar un rendimiento razonablemente bueno para muchos dispositivos en muchas configuraciones de procesador.
Además de los parámetros de configuración especificados en WDF_USB_CONTINUOUS_READER_CONFIG_INIT, el ejemplo también establece una rutina de error en WDF_USB_CONTINUOUS_READER_CONFIG. Esta rutina de error es opcional.
Además de la rutina de error, hay otros miembros en WDF_USB_CONTINUOUS_READER_CONFIG que el controlador cliente puede usar para especificar el diseño del búfer de transferencia. Por ejemplo, considere un controlador de red que usa el lector continuo para recibir paquetes de red. Cada paquete contiene datos de encabezado, carga y pie de página. Para describir el paquete, el controlador debe especificar primero el tamaño del paquete en su llamada a WDF_USB_CONTINUOUS_READER_CONFIG_INIT. A continuación, el controlador debe especificar la longitud del encabezado y el pie de página estableciendo los miembros HeaderLength y TrailerLength de WDF_USB_CONTINUOUS_READER_CONFIG. El marco usa esos valores para calcular los desplazamientos de bytes en cualquiera de los lados de la carga. Cuando los datos de carga se leen desde el punto de conexión, el marco almacena esos datos en la parte del búfer entre los desplazamientos.
Implementar la rutina de finalización
El marco invoca la rutina de finalización implementada por el controlador cliente cada vez que se completa una solicitud. El marco pasa el número de bytes leídos y un objeto WDFMEMORY cuyo búfer contiene los datos leídos de la canalización.
En el código de ejemplo siguiente se muestra la implementación de rutina de finalización.
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;
}
El marco invoca la rutina de finalización implementada por el controlador cliente cada vez que se completa una solicitud. El marco asigna un objeto de memoria para cada operación de lectura. En la rutina de finalización, el marco pasa el número de bytes leídos y un identificador WDFMEMORY al objeto de memoria. El búfer de objetos de memoria contiene los datos leídos de la canalización. El controlador cliente no debe liberar el objeto de memoria. El marco libera el objeto después de que se devuelva cada rutina de finalización. Si el controlador cliente desea almacenar los datos recibidos, el controlador debe copiar el contenido del búfer en la rutina de finalización.
Implementar la rutina de error
El marco invoca la rutina de error implementada por el controlador cliente para informar al controlador de que el lector continuo ha notificado un error al procesar una solicitud de lectura. El marco pasa el puntero al objeto de canalización de destino en el que se produjo un error en la solicitud y los valores de código de error. En función de esos valores de código de error, el controlador puede implementar su mecanismo de recuperación de errores. El controlador también debe devolver un valor adecuado que indique al marco si el marco debe reiniciar el lector continuo.
En el código de ejemplo siguiente se muestra una implementación rutinaria de errores.
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;
}
En el ejemplo anterior, el controlador devuelve TRUE. Este valor indica al marco que debe restablecer la canalización y, a continuación, reiniciar el lector continuo.
Como alternativa, el controlador cliente puede devolver FALSE y proporcionar un mecanismo de recuperación de errores si se produce una condición de bloqueo en la canalización. Por ejemplo, el controlador puede comprobar el estado del USBD y emitir una solicitud de canalización de restablecimiento para borrar la condición de parada.
Para obtener información sobre la recuperación de errores en canalizaciones, consulte Recuperación de errores de canalización USB.
Iniciar y detener el lector continuo
Indique al marco que inicie el lector continuo cuando el dispositivo entra en estado de trabajo; detenga el lector cuando el dispositivo deje el estado de funcionamiento. Llame a estos métodos y especifique el objeto de canalización de destino como el objeto de destino de E/S.
El marco no administra automáticamente el lector continuo. Por lo tanto, el controlador cliente debe iniciar o detener explícitamente el objeto de canalización de destino cuando cambia el estado de energía del dispositivo. El controlador llama a WdfIoTargetStart en la implementación de EvtDeviceD0Entry del controlador. Esta llamada garantiza que la cola entregue solicitudes solo cuando el dispositivo está en estado de trabajo. Por el contrario, el controlador llama a WdfIoTargetStop en los controladores EvtDeviceD0Exit implementación para que la cola deje de entregar solicitudes cuando el dispositivo entre en un estado de energía inferior.
El código de ejemplo siguiente configura el lector continuo para el objeto de canalización 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));
}
En el ejemplo anterior se muestra la implementación de las rutinas de devolución de llamada EvtDeviceD0Entry y EvtDeviceD0Exit. El parámetro Action de WdfIoTargetStop permite al controlador cliente decidir la acción de las solicitudes pendientes en la cola cuando el dispositivo deja el estado de trabajo. En el ejemplo, el controlador especifica WdfIoTargetCancelSentIo. Esta opción indica al marco que cancele todas las solicitudes pendientes de la cola. Como alternativa, el controlador puede indicar al marco que espere a que se completen las solicitudes pendientes antes de detener el destino de E/S o mantener las solicitudes pendientes y reanudarse cuando se reinicie el destino de E/S.
Uso del lector continuo en un controlador cliente UMDF
Antes de empezar a usar el lector continuo, debe configurar el lector en la implementación del método IPnpCallbackHardware::OnPrepareHardware. Después de obtener un puntero a la interfaz IWDFUsbTargetPipe del objeto de canalización de destino asociado al punto de conexión IN, realice estos pasos:
Configuración del lector continuo en un controlador cliente UMDF
Llame a QueryInterface en el objeto de canalización de destino (IWDFUsbTargetPipe) y consulte la interfaz IWDFUsbTargetPipe2.
Llame a QueryInterface en el objeto de devolución de llamada del dispositivo y consulte la interfaz IUsbTargetPipeContinuousReaderCallbackReadComplete. Para poder usar el lector continuo, debe implementar IUsbTargetPipeContinuousReaderCallbackReadComplete. La implementación se describe más adelante en este tema.
Llame a QueryInterface en el objeto de devolución de llamada del dispositivo y consulte la interfaz IUsbTargetPipeContinuousReaderCallbackReadersFailed si ha implementado una devolución de llamada de error. La implementación se describe más adelante en este tema.
Llame al método IWDFUsbTargetPipe2::ConfigureContinuousReader y especifique los parámetros de configuración, como encabezado, finalizador, número de solicitudes pendientes y referencias a los métodos de devolución de llamada de finalización y error.
El método configura el lector continuo para el objeto de canalización de destino. El lector continuo crea colas que administran un conjunto de solicitudes de lectura a medida que se envían y reciben del objeto de canalización de destino.
El código de ejemplo siguiente configura el lector continuo para el objeto de canalización de destino especificado. En el ejemplo se supone que el objeto de canalización de destino especificado por el autor de la llamada está asociado a un punto de conexión IN. El lector continuo está configurado para leer USBD_DEFAULT_MAXIMUM_TRANSFER_SIZE bytes; para usar el número predeterminado de solicitudes pendientes mediante el marco de trabajo; para invocar los métodos de devolución de llamada de finalización y error proporcionados por el controlador cliente. El búfer recibido no contendrá ningún encabezado ni datos de finalizador.
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;
}
A continuación, especifique el estado del objeto de canalización de destino, cuando el dispositivo entra y sale de un estado de trabajo (D0).
Si un controlador cliente usa una cola administrada por energía para enviar solicitudes a una canalización, la cola entrega solicitudes solo cuando el dispositivo está en estado D0 . Si el estado de energía del dispositivo cambia de D0 a un estado de energía inferior (en la salida D0 ), el objeto de canalización de destino completa las solicitudes pendientes y la cola deja de enviar solicitudes al objeto de canalización de destino. Por lo tanto, el controlador cliente no es necesario para iniciar y detener el objeto de canalización de destino.
El lector continuo no usa colas administradas por energía para enviar solicitudes. Por lo tanto, debe iniciar o detener explícitamente el objeto de canalización de destino cuando cambia el estado de energía del dispositivo. Para cambiar el estado del objeto de canalización de destino, puede usar la interfaz IWDFIoTargetStateManagement implementada por el marco. Después de obtener un puntero a la interfaz IWDFUsbTargetPipe del objeto de canalización de destino asociado al punto de conexión IN, realice los pasos siguientes:
Implementación de la administración de estados
En la implementación de IPnpCallbackHardware::OnPrepareHardware, llame a QueryInterface en el objeto de canalización de destino (IWDFUsbTargetPipe) y consulte la interfaz IWDFIoTargetStateManagement. Almacene la referencia en una variable miembro de la clase de devolución de llamada del dispositivo.
Implemente la interfaz IPnpCallback en el objeto de devolución de llamada del dispositivo.
En la implementación del método IPnpCallback::OnD0Entry , llame a IWDFIoTargetStateManagement::Start para iniciar el lector continuo.
En la implementación del método IPnpCallback::OnD0Exit , llame a IWDFIoTargetStateManagement::Stop para detener el lector continuo.
Una vez que el dispositivo entra en un estado de trabajo (D0), el marco llama al método de devolución de llamada D0-entry proporcionado por el controlador cliente que inicia el objeto de canalización de destino. Cuando el dispositivo deja el estado D0 , el marco llama al método de devolución de llamada D0-exit. El objeto de canalización de destino completa el número de solicitudes de lectura pendientes, configuradas por el controlador cliente y deja de aceptar nuevas solicitudes. El código de ejemplo siguiente implementa la interfaz IPnpCallback en el objeto de devolución de llamada 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);
};
En el código de ejemplo siguiente se muestra cómo obtener un puntero a la interfaz IWDFIoTargetStateManagement del objeto de canalización de destino en el 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.
}
}
El código de ejemplo siguiente muestra cómo obtener un puntero a la interfaz IWDFIoTargetStateManagement del objeto de canalización de destino en el 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;
}
Una vez que el lector continuo completa una solicitud de lectura, el controlador cliente debe proporcionar una manera de recibir notificaciones cuando la solicitud complete correctamente una solicitud de lectura. El controlador cliente debe agregar este código al objeto de devolución de llamada del dispositivo.
Proporcionar una devolución de llamada de finalización mediante la implementación de IUsbTargetPipeContinuousReaderCallbackReadComplete
Implemente la interfaz IUsbTargetPipeContinuousReaderCallbackReadComplete en el objeto de devolución de llamada del dispositivo.
Asegúrese de que la implementación queryInterface del objeto de devolución de llamada del dispositivo incrementa el recuento de referencias del objeto de devolución de llamada y, a continuación, devuelve el puntero de interfaz IUsbTargetPipeContinuousReaderCallbackReadComplete.
En la implementación del método IUsbTargetPipeContinuousReaderCallbackReadComplete::OnReaderCompletion , obtenga acceso a los datos leídos desde la canalización. El parámetro pMemory apunta a la memoria asignada por el marco que contiene los datos. Puede llamar a IWDFMemory::GetDataBuffer para obtener el búfer que contiene los datos. El búfer incluye el encabezado sin embargo la longitud de los datos indicados por el parámetro NumBytesTransferred de OnReaderCompletion no incluye la longitud del encabezado. El controlador cliente especifica la longitud del encabezado al configurar el lector continuo en la llamada del controlador a IWDFUsbTargetPipe2::ConfigureContinuousReader.
Proporcione un puntero a la devolución de llamada de finalización en el parámetro pOnCompletion del método IWDFUsbTargetPipe2::ConfigureContinuousReader .
Cada vez que los datos están disponibles en el punto de conexión del dispositivo, el objeto de canalización de destino completa una solicitud de lectura. Si la solicitud de lectura se completó correctamente, el marco notifica al controlador cliente llamando a IUsbTargetPipeContinuousReaderCallbackReadComplete::OnReaderCompletion. De lo contrario, el marco llama a una devolución de llamada de error proporcionada por el controlador de cliente cuando el objeto de canalización de destino notifica un error en la solicitud de lectura.
El código de ejemplo siguiente implementa la interfaz IUsbTargetPipeContinuousReaderCallbackReadComplete en el objeto de devolución de llamada 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);
};
En el código de ejemplo siguiente se muestra la implementación QueryInterface del objeto de devolución de llamada 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;
}
El código de ejemplo siguiente muestra cómo obtener datos del búfer devuelto por IUsbTargetPipeContinuousReaderCallbackReadComplete::OnReaderCompletion. Cada vez que el objeto de canalización de destino completa correctamente una solicitud de lectura, el marco llama a OnReaderCompletion. En el ejemplo se obtiene el búfer que contiene datos e imprime el contenido en la salida del 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."));
}
}
El controlador cliente puede obtener notificaciones del marco cuando se produce un error en el objeto de canalización de destino mientras se completa una solicitud de lectura. Para obtener notificaciones, el controlador cliente debe implementar una devolución de llamada de error y proporcionar un puntero a la devolución de llamada al configurar el lector continuo. En el procedimiento siguiente se describe cómo implementar la devolución de llamada de error.
Proporcione una devolución de llamada de error mediante la implementación de IUsbTargetPipeContinuousReaderCallbackReadersFailed
Implemente la interfaz IUsbTargetPipeContinuousReaderCallbackReadersFailed en el objeto de devolución de llamada del dispositivo.
Asegúrese de que la implementación QueryInterface del objeto de devolución de llamada del dispositivo incrementa el recuento de referencias del objeto de devolución de llamada y, a continuación, devuelve el puntero de interfaz IUsbTargetPipeContinuousReaderCallbackReadersFailed.
En la implementación del método IUsbTargetPipeContinuousReaderCallbackReadersFailed::OnReaderFailure , proporcione el control de errores de la solicitud de lectura con errores.
Si el lector continuo no puede completar una solicitud de lectura y el controlador cliente proporciona una devolución de llamada de error, el marco invoca el método IUsbTargetPipeContinuousReaderCallbackReadersFailed::OnReaderFailure. El marco proporciona un valor HRESULT en el parámetro hrStatus que indica el código de error que se produjo en el objeto de canalización de destino. En función de ese código de error, puede proporcionar cierto control de errores. Por ejemplo, si desea que el marco restablezca la canalización y reinicie el lector continuo, asegúrese de que la devolución de llamada devuelve TRUE.
Nota No llame a IWDFIoTargetStateManagement::Start e IWDFIoTargetStateManagement::Stop dentro de la devolución de llamada de error.
Proporcione un puntero a la devolución de llamada de error en el parámetro pOnFailure del método IWDFUsbTargetPipe2::ConfigureContinuousReader .
El código de ejemplo siguiente implementa la interfaz IUsbTargetPipeContinuousReaderCallbackReadersFailed en el objeto de devolución de llamada 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);
};
En el código de ejemplo siguiente se muestra la implementación QueryInterface del objeto de devolución de llamada 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;
}
El código de ejemplo siguiente muestra una implementación de una devolución de llamada de error. Si se produce un error en una solicitud de lectura, el método imprime el código de error notificado por el marco del depurador e indica al marco que restablezca la canalización y reinicie el lector continuo.
BOOL CDeviceCallback::OnReaderFailure(
IWDFUsbTargetPipe * pPipe,
HRESULT hrCompletion
)
{
UNREFERENCED_PARAMETER(pPipe);
UNREFERENCED_PARAMETER(hrCompletion);
return TRUE;
}
Si el controlador cliente no proporciona una devolución de llamada de error y se produce un error, el marco restablece la canalización USB y reinicia el lector continuo.
Temas relacionados
- Marco de controlador en modo kernel
- Marco de controlador en modo de usuario
- Transferencias de E/S USB
- Enumeración de canalizaciones USB
- Cómo seleccionar una configuración para un dispositivo USB
- Cómo seleccionar una configuración alternativa en una interfaz USB
- Tareas comunes para controladores de cliente USB