Partager via


Comment envoyer une requête de transfert d’interruption USB (application UWP)

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

API importantes

Un dispositif USB peut prendre en charge les points d’interruption afin de pouvoir envoyer ou recevoir des données à intervalles réguliers. Pour ce faire, l’hôte interroge le dispositif à intervalles réguliers et les données sont transmises à chaque fois que l’hôte interroge le dispositif. Les transferts d’interruption sont principalement utilisés pour obtenir des données d’interruption du dispositif. Cette rubrique décrit comment une application UWP peut obtenir des données d’interruption continues du dispositif.

Informations sur le point d’interruption

Pour les points d’interruption, le descripteur expose ces propriétés. Ces valeurs sont uniquement informatives et ne doivent pas affecter la gestion de la mémoire tampon de transfert.

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

    Obtenez cette information en récupérant la valeur Interval du descripteur de point (veuillez consulter la section UsbInterruptOutEndpointDescriptor.Interval ou UsbInterruptInEndpointDescriptor.Interval). Cette valeur indique à quelle fréquence les données sont envoyées au dispositif ou reçues du dispositif dans chaque trame sur le bus.

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

    Cette valeur indique à quelle fréquence les données sont transmises vers ou depuis le dispositif. Par exemple, pour un dispositif à haute vitesse, si Interval est de 125 microsecondes, les données sont transmises toutes les 125 microsecondes. Si Interval est de 1000 microsecondes, les données sont alors transmises toutes les millisecondes.

  • Combien de données peuvent être transmises à chaque intervalle de service ?

    Obtenez le nombre d’octets pouvant être transmis en récupérant la taille maximale de paquet prise en charge par le descripteur de point (veuillez consulter la section UsbInterruptOutEndpointDescriptor.MaxPacketSize ou UsbInterruptInEndpointDescriptor.MaxPacketSize). La taille maximale de paquet est contrainte par la vitesse du dispositif. Pour les dispositifs à basse vitesse, jusqu’à 8 octets. Pour les dispositifs à pleine vitesse, jusqu’à 64 octets. Pour les dispositifs à haute vitesse et à large bande passante, l’application peut envoyer ou recevoir plus que la taille maximale de paquet, jusqu’à 3072 octets par microtrame.

    Les points d’interruption sur les dispositifs 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, obtenez la mémoire tampon du descripteur en utilisant la propriété UsbEndpointDescriptor.AsByte et analysez ensuite cette mémoire tampon en utilisant les méthodes DataReader.

Transferts d’interruption OUT

Un dispositif USB peut prendre en charge les points d’interruption OUT qui reçoivent des données de l’hôte à intervalles réguliers. Chaque fois que l’hôte interroge le dispositif, l’hôte envoie des données. Une application UWP peut initier une requête de transfert d’interruption OUT qui spécifie les données à envoyer. Cette requête est complétée lorsque le dispositif accuse réception des données de l’hôte. Une application UWP peut écrire des données dans le UsbInterruptOutPipe.

Transferts d’interruption IN

Inversement, un dispositif USB peut prendre en charge les points d’interruption IN comme moyen d’informer l’hôte des interruptions matérielles générées par le dispositif. En général, les dispositifs USB d’interface humaine (HID), tels que les claviers et les dispositifs de pointage, prennent en charge les points d’interruption OUT. Lorsqu’une interruption se produit, le point stocke les données d’interruption, mais ces données n’atteignent pas immédiatement l’hôte. Le point doit attendre que le contrôleur d’hôte interroge le dispositif. Comme il doit y avoir un délai minimal entre le moment où les données sont générées et celui où elles atteignent l’hôte, il interroge le dispositif à intervalles réguliers. Une application UWP peut obtenir les données reçues dans le UsbInterruptInPipe. La requête est complétée lorsque les données du dispositif sont reçues par l’hôte.

Avant de commencer

Écriture dans le point d’interruption OUT

La manière dont l’application envoie une requête de transfert d’interruption OUT est identique aux transferts en mode bloc OUT, sauf que la cible est un point d’interruption OUT, représenté par UsbInterruptOutPipe. Pour plus d’informations, veuillez consulter la section Comment envoyer une requête de transfert en mode bloc USB (application UWP).

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

Lorsque les données sont reçues du dispositif dans le canal d’interruption, cela déclenche l’événement DataReceived. 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 des données.

Ce code d’exemple montre une implémentation simple du gestionnaire d’événements. Le gestionnaire maintient le compte des interruptions reçues. Chaque fois que le gestionnaire est invoqué, il incrémente le compte. Le gestionnaire obtient la mémoire tampon des 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 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);
        }));
}

Étape 2 : Obtenez l’objet canal d’interruption (Interrupt IN)

Pour enregistrer le gestionnaire d’événements pour l’événement DataReceived, obtenez une référence au UsbInterruptInPipe en utilisant l’une de ces propriétés :

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

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

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

Ce code d’exemple montre comment enregistrer le gestionnaire d’événements. Dans cet exemple, la classe garde une trace du gestionnaire d’événements, du canal pour lequel le gestionnaire d’événements est enregistré et si le canal reçoit actuellement des données. Toutes ces informations sont utilisées pour désinscrire le gestionnaire d’événements, comme montré à 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 enregistré, il est invoqué chaque fois que des données sont reçues dans le canal d’interruption associé.

Étape 4 : Désinscrivez le gestionnaire d’événements pour arrêter de recevoir des données (Interrupt IN)

Une fois que vous avez terminé de recevoir des données, désinscrivez le gestionnaire d’événements.

Ce code d’exemple montre comment désinscrire le gestionnaire d’événements. Dans cet exemple, si l’application a un gestionnaire d’événements précédemment enregistré, la méthode récupère le gestionnaire d’événements suivi et le désinscrit 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 invoqué lors des événements d’interruption. Cela ne signifie pas que le canal d’interruption cesse de recevoir des données.