Comment envoyer une demande de transfert d’interruption USB (application UWP)

Les transferts d’interruption se produisent lorsque l’hôte interroge l’appareil. Cet article montre comment :

API importantes

Un périphérique USB peut prendre en charge les points de terminaison d’interruption afin qu’il puisse envoyer ou recevoir des données à intervalles réguliers. Pour ce faire, l’hôte interroge l’appareil à intervalles réguliers et les données sont transmises chaque fois que l’hôte interroge l’appareil. Les transferts d’interruption sont principalement utilisés pour obtenir des données d’interruption à partir de l’appareil. Cette rubrique décrit comment une application UWP peut obtenir des données d’interruption continue à partir de l’appareil.

Informations sur le point de terminaison d’interruption

Pour les points de terminaison d’interruption, le descripteur expose ces propriétés. Ces valeurs sont fournies à des fins d’information uniquement et ne doivent pas affecter la façon dont vous gérez la mémoire tampon de transfert de mémoire tampon.

  • À quelle fréquence les données peuvent-elles être transmises ?

    Obtenez ces informations en obtenant la valeur Interval du descripteur de point de terminaison (voir UsbInterruptOutEndpointDescriptor.Interval ou UsbInterruptInEndpointDescriptor.Interval). Cette valeur indique la fréquence à laquelle les données sont envoyées ou reçues de l’appareil dans chaque trame du bus.

    La propriété Interval n’est pas la valeur bInterval (définie dans la spécification USB).

    Cette valeur indique la fréquence à laquelle les données sont transmises vers ou depuis l’appareil. Par exemple, pour un appareil à grande vitesse, si Interval est de 125 microsecondes, les données sont transmises toutes les 125 microsecondes. Si Interval est de 1 000 microsecondes, les données sont transmises toutes les millisecondes.

  • Quelle quantité de données peut être transmise à chaque intervalle de service ?

    Obtenez le nombre d’octets qui peuvent être transmis en obtenant la taille de paquet maximale prise en charge par le descripteur de point de terminaison (voir UsbInterruptOutEndpointDescriptor.MaxPacketSize ou UsbInterruptInEndpointDescriptor.MaxPacketSize). Taille maximale des paquets limitée à la vitesse de l’appareil. Pour les appareils à faible vitesse jusqu’à 8 octets. Pour les appareils à vitesse maximale, jusqu’à 64 octets. Pour les appareils à haut débit et à bande passante élevée, l’application peut envoyer ou recevoir une taille de paquet supérieure à la taille maximale de 3 072 octets par microframe.

    Les points de terminaison d’interruption sur les appareils SuperSpeed sont capables de transmettre encore plus d’octets. Cette valeur est indiquée par le wBytesPerInterval du USB_SUPERSPEED_ENDPOINT_COMPANION_DESCRIPTOR. Pour récupérer le descripteur, récupérez la mémoire tampon de descripteur à l’aide de la propriété UsbEndpointDescriptor.AsByte , puis analysez cette mémoire tampon à l’aide des méthodes DataReader .

Interrompre les transferts OUT

Un périphérique USB peut prendre en charge les points de terminaison OUT d’interruption qui reçoivent des données de l’hôte à intervalles réguliers. Chaque fois que l’hôte interroge l’appareil, l’hôte envoie des données. Une application UWP peut lancer une demande de transfert OUT d’interruption qui spécifie les données à envoyer. Cette demande est effectuée lorsque l’appareil accuse réception des données de l’hôte. Une application UWP peut écrire des données dans usbInterruptOutPipe.

Interrompre les transferts IN

À l’inverse, un périphérique USB peut prendre en charge les points de terminaison IN d’interruption pour informer l’hôte des interruptions matérielles générées par l’appareil. En règle générale, les périphériques HID (USB Human Interface Devices), tels que les claviers et les appareils de pointage, prennent en charge les points de terminaison OUT d’interruption. Lorsqu’une interruption se produit, le point de terminaison stocke les données d’interruption, mais ces données n’atteignent pas immédiatement l’hôte. Le point de terminaison doit attendre que le contrôleur hôte interroge l’appareil. Étant donné qu’il doit y avoir un délai minimal entre le moment où les données sont générées et atteignent l’hôte, il interroge l’appareil à intervalles réguliers. Une application UWP peut obtenir des données reçues dans usbInterruptInPipe. Demande qui se termine lorsque les données de l’appareil sont reçues par l’hôte.

Avant de commencer

Écriture sur le point de terminaison OUT d’interruption

La façon dont l’application envoie une demande de transfert OUT d’interruption est identique aux transferts OUT en bloc, sauf que la cible est un canal OUT d’interruption, représenté par UsbInterruptOutPipe. Pour plus d’informations, consultez Comment envoyer une demande de transfert en bloc USB (application UWP).

Étape 1 : Implémenter le gestionnaire d’événements d’interruption (Interrompre IN)

Lorsque des données sont reçues de l’appareil dans le canal d’interruption, l’événement DataReceived s’affiche . Pour obtenir les données d’interruption, l’application doit implémenter un gestionnaire d’événements. Le paramètre eventArgs du gestionnaire pointe vers la mémoire tampon de données.

Cet exemple de code montre une implémentation simple du gestionnaire d’événements. Le gestionnaire gère le nombre d’interruptions reçues. Chaque fois que le gestionnaire est appelé, il incrémente le nombre. Le gestionnaire obtient la mémoire tampon de données à partir du paramètre eventArgs et affiche le nombre d’interruptions et la longueur des octets reçus.

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

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

Étape 2 : Obtenir l’objet de canal d’interruption (Interrompre IN)

Pour inscrire le gestionnaire d’événements pour l’événement DataReceived , obtenez une référence à UsbInterruptInPipe en utilisant les propriétés suivantes :

Note Évitez d’obtenir l’objet de canal en énumérant les points de terminaison d’interruption d’un paramètre d’interface qui n’est pas sélectionné actuellement. Pour transférer des données, les canaux doivent être associés à des points de terminaison dans le paramètre actif.

Étape 3 : Inscrire le gestionnaire d’événements pour commencer à recevoir des données (Interrompre IN)

Ensuite, vous devez inscrire le gestionnaire d’événements sur l’objet UsbInterruptInPipe qui déclenche l’événement DataReceived .

Cet exemple de code montre comment inscrire le gestionnaire d’événements. Dans cet exemple, la classe effectue le suivi du gestionnaire d’événements, du canal pour lequel le gestionnaire d’événements est inscrit et indique si le canal reçoit actuellement des données. Toutes ces informations sont utilisées pour annuler l’inscription du gestionnaire d’événements, comme indiqué à l’étape suivante.

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;    

}

Une fois le gestionnaire d’événements inscrit, il est appelé chaque fois que des données sont reçues dans le canal d’interruption associé.

Étape 4 : Annuler l’inscription du gestionnaire d’événements pour arrêter la réception de données (Interruption IN)

Une fois que vous avez fini de recevoir des données, annulez l’inscription du gestionnaire d’événements.

Cet exemple de code montre comment annuler l’inscription du gestionnaire d’événements. Dans cet exemple, si l’application possède un gestionnaire d’événements précédemment inscrit, la méthode obtient le gestionnaire d’événements suivi et l’annule sur le canal d’interruption.

private void UnregisterInterruptEventHandler()
{
    if (registeredInterruptHandler)
    {
        interruptPipe.DataReceived -= interruptEventHandler;

        registeredInterruptHandler = false;
    }
}
void UnregisterFromInterruptEvent(void)
{
    if (registeredInterrupt)
    {
        interruptInPipe.DataReceived -= eventHandler;

        registeredInterrupt = false;
    }
}

Une fois le gestionnaire d’événements désinscrit, l’application cesse de recevoir des données du canal d’interruption, car le gestionnaire d’événements n’est pas appelé sur les événements d’interruption. Cela ne signifie pas que le canal d’interruption cesse d’obtenir des données.