Cómo enviar una solicitud de transferencia de interrupción USB (aplicación para UWP)
Las transferencias de interrupción se producen cuando el host sondea el dispositivo. En este artículo se explica cómo:
- Implementación del controlador de eventos para UsbInterruptInPipe.DataReceived
- Registro y anulación del registro del controlador de eventos
API importantes
Un dispositivo USB puede admitir puntos de conexión de interrupción para que pueda enviar o recibir datos a intervalos regulares. Para ello, el host sondea el dispositivo a intervalos regulares y los datos se transmiten cada vez que el host sondea el dispositivo. Las transferencias de interrupción se usan principalmente para obtener datos de interrupción del dispositivo. En este tema se describe cómo una aplicación para UWP puede obtener datos de interrupción continua del dispositivo.
Información del punto de conexión de interrupción
En el caso de los puntos de conexión de interrupción, el descriptor expone estas propiedades. Esos valores son solo para información y no deben afectar a cómo se administra el búfer de transferencia de búfer.
¿Con qué frecuencia se pueden transmitir los datos?
Obtenga esa información del valor Interval del descriptor de punto de conexión (consulte UsbInterruptOutEndpointDescriptor.Interval o UsbInterruptInEndpointDescriptor.Interval). Ese valor indica la frecuencia con la que se envían o reciben datos desde el dispositivo en cada trama del bus.
La propiedad Interval no es el valor bInterval (definido en la especificación USB).
Ese valor indica la frecuencia con la que se transmiten los datos al dispositivo o desde él. Por ejemplo, para un dispositivo de alta velocidad, si Interval es de 125 microsegundos, los datos se transmiten cada 125 microsegundos. Si Interval es de 1000 microsegundos, los datos se transmiten cada milisegundos.
¿Cuántos datos se pueden transmitir en cada intervalo de servicio?
Obtenga el número de bytes que se pueden transmitir del tamaño máximo de paquete admitido por el descriptor de punto de conexión (consulte UsbInterruptOutEndpointDescriptor.MaxPacketSize o UsbInterruptInEndpointDescriptor.MaxPacketSize). Tamaño máximo de paquete restringido a la velocidad del dispositivo. Para dispositivos de baja velocidad de hasta 8 bytes. Para dispositivos de velocidad completa de hasta 64 bytes. Para dispositivos de alto ancho de banda y de alta velocidad, la aplicación puede enviar o recibir más de un tamaño máximo de paquete de hasta 3072 bytes por microtrama.
Los puntos de conexión de interrupción en los dispositivos SuperSpeed son capaces de transmitir aún más bytes. Ese valor se indica mediante wBytesPerInterval del USB_SUPERSPEED_ENDPOINT_COMPANION_DESCRIPTOR. Para recuperar el descriptor, obtenga el búfer del descriptor mediante la propiedad UsbEndpointDescriptor.AsByte y, a continuación, analice ese búfer mediante métodos DataReader.
Transferencias OUT de interrupción
Un dispositivo USB puede admitir los puntos de conexión OUT de interrupción que reciben datos del host a intervalos regulares. Cada vez que el host sondea el dispositivo, el host envía datos. Una aplicación para UWP puede iniciar una solicitud de transferencia OUT de interrupción que especifica los datos que se van a enviar. Esa solicitud se completa cuando el dispositivo confirma los datos del host. Una aplicación para UWP puede escribir datos en UsbInterruptOutPipe.
Transferencias IN de interrupción
Por el contrario, un dispositivo USB puede admitir puntos de conexión IN de interrupción como una manera de informar al host sobre las interrupciones de hardware generadas por el dispositivo. Normalmente, los dispositivos de interfaz humana (HID) USB, como los teclados y los dispositivos de señalización, admiten puntos de conexión OUT de interrupción. Cuando se produce una interrupción, el punto de conexión almacena los datos de interrupción, pero esos datos no llegan al host inmediatamente. El punto de conexión debe esperar a que el controlador de host sondee el dispositivo. Dado que debe haber un retraso mínimo entre el tiempo que se generan los datos y llega al host, sondea el dispositivo a intervalos regulares. Una aplicación para UWP puede obtener datos recibidos en UsbInterruptInPipe. La solicitud que se completa cuando el host recibe los datos del dispositivo.
Antes de comenzar
- Debe haber abierto el dispositivo y obtenido el objeto UsbDevice. Consulte Cómo conectarse a un dispositivo USB (aplicación para UWP).
- Puede ver el código completo que se muestra en este tema en el ejemplo CustomUsbDeviceAccess, archivos Scenario3_InterruptPipes.
Escritura en el punto de conexión OUT de interrupción
La forma en que la aplicación envía una solicitud de transferencia OUT de interrupción es idéntica a las transferencias OUT masivas, excepto que el destino es una canalización OUT de interrupción, representada por UsbInterruptOutPipe. Para obtener más información, consulte Cómo enviar una solicitud de transferencia masiva USB (aplicación para UWP).
Paso 1: Implementar el controlador de eventos de interrupción (IN de interrupción)
Cuando se reciben datos del dispositivo en la canalización de interrupción, genera el evento DataReceived. Para obtener los datos de interrupción, la aplicación debe implementar un controlador de eventos. El parámetro eventArgs del controlador apunta al búfer de datos.
Este código de ejemplo muestra una implementación sencilla del controlador de eventos. El controlador mantiene el recuento de interrupciones recibidas. Cada vez que se invoca el controlador, incrementa el recuento. El controlador obtiene el búfer de datos del parámetro eventArgs y muestra el recuento de interrupciones y la longitud de bytes recibidos.
private async void OnInterruptDataReceivedEvent(UsbInterruptInPipe sender, UsbInterruptInEventArgs eventArgs)
{
numInterruptsReceived++;
// The data from the interrupt
IBuffer buffer = eventArgs.InterruptData;
// Create a DispatchedHandler for the because we are interacting with the UI directly and the
// thread that this function is running on may not be the UI thread; if a non-UI thread modifies
// the UI, an exception is thrown
await Dispatcher.RunAsync(
CoreDispatcherPriority.Normal,
new DispatchedHandler(() =>
{
ShowData(
"Number of interrupt events received: " + numInterruptsReceived.ToString()
+ "\nReceived " + buffer.Length.ToString() + " bytes");
}));
}
void OnInterruptDataReceivedEvent(UsbInterruptInPipe^ /* sender */, UsbInterruptInEventArgs^ eventArgs )
{
numInterruptsReceived++;
// The data from the interrupt
IBuffer^ buffer = eventArgs->InterruptData;
// Create a DispatchedHandler for the because we are interracting with the UI directly and the
// thread that this function is running on may not be the UI thread; if a non-UI thread modifies
// the UI, an exception is thrown
MainPage::Current->Dispatcher->RunAsync(
CoreDispatcherPriority::Normal,
ref new DispatchedHandler([this, buffer]()
{
ShowData(
"Number of interrupt events received: " + numInterruptsReceived.ToString()
+ "\nReceived " + buffer->Length.ToString() + " bytes",
NotifyType::StatusMessage);
}));
}
Paso 2: Obtener el objeto de canalización de interrupción (IN de interrupción)
Para registrar el controlador de eventos para el evento DataReceived, obtenga una referencia a UsbInterruptInPipe mediante cualquiera de estas propiedades:
- UsbDevice.DefaultInterface.InterruptInPipes[n] si el punto de conexión de interrupción está presente en la primera interfaz USB.
- UsbDevice.Configuration.UsbInterfaces[m]. InterruptInPipes[n] para enumerar todas las canalizaciones IN de interrupción por interfaz, compatibles con el dispositivo.
- UsbInterface.InterfaceSettings[m].InterruptInEndpoints [n].Pipe para enumerar las canalizaciones IN de interrupción definidas por la configuración de una interfaz.
- UsbEndpointDescriptor.AsInterruptInEndpointDescriptor.Pipe para obtener el objeto de canalización desde el descriptor de punto de conexión para el punto de conexión IN de interrupción.
Nota Evite obtener el objeto de canalización mediante la enumeración de puntos de conexión de interrupción de una configuración de interfaz que no está seleccionada actualmente. Para transferir datos, las canalizaciones deben estar asociadas a los puntos de conexión en la configuración activa.
Paso 3: Registrar el controlador de eventos para empezar a recibir datos (IN de interrupción)
A continuación, debe registrar el controlador de eventos en el objeto UsbInterruptInPipe que genera el evento DataReceived.
En este código de ejemplo se muestra cómo registrar el controlador de eventos. En este ejemplo, la clase realiza un seguimiento del controlador de eventos, la canalización para la que está registrado el controlador de eventos y si la canalización está recibiendo datos actualmente. Toda esa información se usa para anular el registro del controlador de eventos, que se muestra en el paso siguiente.
private void RegisterForInterruptEvent(TypedEventHandler<UsbInterruptInPipe, UsbInterruptInEventArgs> eventHandler)
{
// Search for the correct pipe that has the specified endpoint number
interruptPipe = usbDevice.DefaultInterface.InterruptInPipes[0];
// Save the interrupt handler so we can use it to unregister
interruptEventHandler = eventHandler;
interruptPipe.DataReceived += interruptEventHandler;
registeredInterruptHandler = true;
}
void RegisterForInterruptEvent(TypedEventHandler<UsbInterruptInPipe, UsbInterruptInEventArgs> eventHandler)
// Search for the correct pipe that has the specified endpoint number
interruptInPipe = usbDevice.DefaultInterface.InterruptInPipes.GetAt(pipeIndex);
// Save the token so we can unregister from the event later
interruptEventHandler = interruptInPipe.DataReceived += eventHandler;
registeredInterrupt = true;
}
Una vez registrado el controlador de eventos, se invoca cada vez que se reciben datos en la canalización de interrupción asociada.
Paso 4: Anular el registro del controlador de eventos para dejar de recibir datos (IN de interrupción)
Una vez que haya terminado de recibir datos, anule el registro del controlador de eventos.
En este código de ejemplo se muestra cómo anular el registro del controlador de eventos. En este ejemplo, si la aplicación tiene un controlador de eventos registrado anteriormente, el método obtiene el controlador de eventos con seguimiento y lo anula en la canalización de interrupción.
private void UnregisterInterruptEventHandler()
{
if (registeredInterruptHandler)
{
interruptPipe.DataReceived -= interruptEventHandler;
registeredInterruptHandler = false;
}
}
void UnregisterFromInterruptEvent(void)
{
if (registeredInterrupt)
{
interruptInPipe.DataReceived -= eventHandler;
registeredInterrupt = false;
}
}
Después de anular el registro del controlador de eventos, la aplicación deja de recibir datos de la canalización de interrupción porque el controlador de eventos no se invoca en eventos de interrupción. Esto no significa que la canalización de interrupción deje de obtener datos.