Comment envoyer une demande de transfert en bloc USB (application UWP)

Dans cette rubrique, vous allez découvrir un transfert USB en bloc et comment lancer une demande de transfert à partir de votre application UWP qui communique avec un périphérique USB.

Les appareils USB à pleine vitesse, haute vitesse et SuperSpeed peuvent prendre en charge les points de terminaison en bloc. Ces points de terminaison sont utilisés pour transférer de grandes quantités de données, comme le transfert de données vers ou à partir d’un lecteur flash USB. Les transferts en bloc sont fiables, car ils permettent la détection des erreurs et impliquent un nombre limité de nouvelles tentatives pour s’assurer que les données sont reçues par l’hôte ou l’appareil. Les transferts en bloc sont utilisés pour les données qui ne sont pas critiques dans le temps. Les données sont transférées uniquement quand la bande passante inutilisée est disponible sur le bus. Par conséquent, lorsque le bus est occupé avec d’autres transferts, les données en bloc peuvent attendre indéfiniment.

Les points de terminaison en bloc sont unidirectionnels et, dans un même transfert, les données peuvent être transférées dans une direction IN ou OUT. Pour prendre en charge la lecture et l’écriture de données en bloc, l’appareil doit prendre en charge les points de terminaison IN et OUT en bloc. Le point de terminaison IN en bloc est utilisé pour lire les données de l’appareil vers l’hôte et le point de terminaison OUT en bloc est utilisé pour envoyer des données de l’hôte à l’appareil.

Pour lancer une demande de transfert en bloc, votre application doit avoir une référence au canal qui représente un point de terminaison. Un canal est un canal de communication ouvert par le pilote de périphérique lorsque l’appareil est configuré. Pour l’application, un canal est une représentation logique d’un point de terminaison. Pour lire des données à partir du point de terminaison, l’application obtient les données du canal IN en bloc associé. Pour écrire des données sur le point de terminaison, l’application envoie des données au canal OUT en bloc. Pour les canaux de lecture et d’écriture en bloc, utilisez les classes UsbBulkInPipe et UsbBulkOutPipe .

Votre application peut également modifier le comportement du canal en définissant certains indicateurs de stratégie. Par exemple, pour une demande de lecture, vous pouvez définir un indicateur qui efface automatiquement une condition de décrochage sur le canal. Pour plus d’informations sur ces indicateurs, consultez UsbReadOptions et UsbWriteOptions.

Avant de commencer

Étape 1 : Obtenir l’objet de canal en bloc

Pour lancer une demande de transfert, vous devez obtenir une référence à l’objet de canal en bloc (UsbBulkOutPipe ou UsbBulkInPipe. Vous pouvez obtenir des canaux en énumérant tous les paramètres de toutes les interfaces. Toutefois, pour les transferts de données, vous devez uniquement utiliser des canaux d’un paramètre actif. Si l’objet de canal a la valeur Null si le point de terminaison associé n’est pas dans le paramètre actif.

Pour... Utiliser cette valeur de propriété
Envoyer des données à un canal en bloc, obtenir une référence à UsbBulkOutPipe. UsbDevice.DefaultInterface.BulkOutPipes[n] si la configuration de votre appareil expose une interface USB.

UsbDevice.Configuration.UsbInterfaces[m]. BulkOutPipes[n] pour l’énumération des canaux OUT en bloc dans plusieurs interfaces prises en charge par l’appareil.

UsbInterface.InterfaceSettings[m]. BulkOutEndpoints[n]. Canal pour l’énumération des canaux OUT en bloc définis par les paramètres d’une interface.

UsbEndpointDescriptor.AsBulkOutEndpointDescriptor.Pipe pour obtenir l’objet de canal à partir du descripteur de point de terminaison pour le point de terminaison OUT en bloc.
Recevez des données à partir d’un canal en bloc, vous pouvez obtenir l’objet UsbBulkInPipe . UsbDevice.DefaultInterface.BulkInPipes[n] si la configuration de votre appareil expose une interface USB.

UsbDevice.Configuration.UsbInterfaces[m]. BulkInPipes[n] pour l’énumération des canaux IN en bloc dans plusieurs interfaces prises en charge par l’appareil.

UsbInterface.InterfaceSettings[m]. BulkInEndpoints[n]. Canal d’énumération des canaux IN en bloc définis par les paramètres d’une interface.

UsbEndpointDescriptor.AsBulkInEndpointDescriptor.Pipe pour obtenir l’objet de canal à partir du descripteur de point de terminaison pour le point de terminaison IN en bloc.

Notes

Doit être dans le paramètre actif ou nécessite une case activée Null.

Étape 2 : Configurer le canal en bloc (facultatif)

Vous pouvez modifier le comportement de l’opération de lecture ou d’écriture en définissant certains indicateurs sur le canal en bloc récupéré.

Pour la lecture à partir de l’appareil, définissez la propriété UsbBulkInPipe.ReadOptions sur l’une des valeurs définies dans UsbReadOptions. En cas d’écriture, définissez la propriété UsbBulkOutPipe.WriteOptions sur l’une des valeurs définies dans UsbWriteOptions.

Pour... Définir cet indicateur
Effacer automatiquement toute condition d’erreur sur le point de terminaison sans arrêter le flux de données AutoClearStall

Pour plus d’informations, consultez Effacement des conditions de décrochage.

Cet indicateur s’applique aux transferts en lecture et en écriture.
Envoyer plusieurs demandes de lecture avec une efficacité maximale. Améliorez les performances en contournant la vérification des erreurs. OverrideAutomaticBufferManagement

Une demande de données peut être divisée en un ou plusieurs transferts, où chaque transfert contient un certain nombre d’octets appelé taille de transfert maximale. Pour plusieurs transferts, la mise en file d’attente de deux transferts peut être retardée en raison de la vérification des erreurs effectuée par le pilote. Cet indicateur contourne cette vérification des erreurs. Pour obtenir la taille de transfert maximale, utilisez la propriété UsbBulkInPipe.MaxTransferSizeBytes . Si la taille de votre requête est UsbBulkInPipe.MaxTransferSizeBytes, vous devez définir cet indicateur.

Important: Si vous définissez cet indicateur, vous devez demander des données en plusieurs de la taille maximale du paquet du canal. Ces informations sont stockées dans le descripteur de point de terminaison. La taille dépend de la vitesse du bus de l’appareil. Pour toute vitesse, vitesse élevée et SuperSpeed ; les tailles maximales de paquets sont respectivement de 64, 512 et 1 024 octets. Pour obtenir cette valeur, utilisez la propriété UsbBulkInPipe.EndpointDescriptor.MaxPacketSize .

Cet indicateur s’applique uniquement aux transferts de lecture.
Terminer une demande d’écriture avec un paquet de longueur nulle ShortPacketTerminate

Envoie un paquet de longueur nulle pour indiquer la fin d’un transfert OUT.

Cet indicateur s’applique uniquement aux transferts d’écriture.
Désactiver la lecture des paquets courts (taille de paquet inférieure à la taille maximale prise en charge par le point de terminaison) IgnoreShortPacket

Par défaut, si l’appareil envoie des octets inférieurs à la taille maximale des paquets, l’application les reçoit. Si vous ne souhaitez pas recevoir de paquets courts, définissez cet indicateur.

Cet indicateur s’applique uniquement aux transferts de lecture.

Étape 3 : Configurer le flux de données

Lorsque des données en bloc sont envoyées par l’appareil, les données sont reçues comme dans le flux d’entrée sur le canal en bloc. Voici les étapes permettant d’obtenir le flux d’entrée :

  1. Obtenez une référence au flux d’entrée en obtenant la propriété UsbBulkInPipe.InputStream .
  2. Créez un objet DataReader en spécifiant le flux d’entrée dans le constructeur DataReader.

Pour écrire des données sur l’appareil, l’application doit écrire dans un flux de sortie sur le canal en bloc. Voici les étapes de préparation du flux de sortie :

  1. Obtenez une référence au flux de sortie en obtenant la propriété UsbBulkOutPipe.OutputStream .
  2. Créez un objet DataWriter en spécifiant le flux de sortie dans le constructeur DataWriter .
  3. Remplissez la mémoire tampon de données associée au flux de sortie.
  4. Selon le type de données, l’écriture transfère les données dans le flux de sortie en appelant des méthodes DataWriter, telles que WriteBytes.

Étape 4 : Démarrer une opération de transfert asynchrone

Les transferts en bloc sont lancés par le biais d’opérations asynchrones.

Pour lire des données en bloc, démarrez une opération de lecture asynchrone en appelant DataReader.LoadAsync.

Pour écrire des données en bloc, démarrez une opération d’écriture asynchrone en appelant DataWriter.StoreAsync.

Étape 5 : Obtenir les résultats de l’opération de transfert de lecture

Une fois l’opération de données asynchrone terminée, vous pouvez obtenir le nombre d’octets lus ou écrits à partir de l’objet de tâche. Pour une opération de lecture, appelez les méthodes DataReader, telles que ReadBytes, pour lire les données du flux d’entrée.

Effacement des conditions de décrochage

Parfois, l’application peut rencontrer des échecs de transfert de données. Un transfert ayant échoué peut être dû à une condition de blocage sur le point de terminaison. Tant que le point de terminaison est bloqué, les données ne peuvent pas être écrites ou lues à partir de celui-ci. Pour poursuivre les transferts de données, l’application doit effacer la condition de décrochage sur le canal associé.

Votre application peut configurer le canal pour effacer automatiquement les conditions de décrochage, lorsqu’elles se produisent. Pour ce faire, définissez la propriété UsbBulkInPipe.ReadOptions sur UsbReadOptions.AutoClearStall ou la propriété UsbBulkOutPipe.WriteOptions sur UsbWriteOptions.AutoClearStall. Avec cette configuration automatique, l’application ne rencontre pas d’échec des transferts et l’expérience de transfert de données est transparente.

Pour effacer manuellement une condition de décrochage, appelez UsbBulkInPipe.ClearStallAsync pour un canal IN en bloc ; appelez UsbBulkOutPipe.ClearStallAsync pour un canal OUT en bloc.

Notes

Une condition de décrochage n’indique pas un point de terminaison vide. S’il n’y a pas de données dans le point de terminaison, le transfert se termine, mais la longueur est de zéro octet.

Pour les opérations de lecture, vous devrez peut-être effacer les données en attente dans le canal avant de commencer une nouvelle demande de transfert. Pour ce faire, appelez la méthode UsbBulkInPipe.FlushBuffer .

Exemple de code de transfert usb en bloc

Cet exemple de code montre comment écrire dans un canal en bloc. L’exemple envoie des données au premier canal OUT en bloc sur l’interface par défaut. Il configure le canal pour envoyer un paquet de longueur nulle à la fin du transfert. Une fois le transfert terminé, le nombre d’octets s’affiche.

    private async void BulkWrite()
    {
        String dataBuffer = "Hello World!";
        UInt32 bytesWritten = 0;

        UsbBulkOutPipe writePipe = usbDevice.DefaultInterface.BulkOutPipes[0];
        writePipe.WriteOptions |= UsbWriteOptions.ShortPacketTerminate;

        var stream = writePipe.OutputStream;

        DataWriter writer = new DataWriter(stream);

        writer.WriteString(dataBuffer);

        try
        {
            bytesWritten = await writer.StoreAsync();
        }
        catch (Exception exception)
        {
            ShowStatus(exception.Message.ToString());
        }
        finally
        {
            ShowStatus("Data written: " + bytesWritten + " bytes.");
        }
    }

Cet exemple de code montre comment lire à partir d’un canal en bloc. L’exemple récupère des données du premier canal IN en bloc sur l’interface par défaut. Il configure le canal sur pour une efficacité maximale et reçoit les données en segments de la taille maximale des paquets. Une fois le transfert terminé, le nombre d’octets s’affiche.

    private async void BulkRead()
    {
        UInt32 bytesRead = 0;

        UsbBulkInPipe readPipe = usbDevice.DefaultInterface.BulkInPipes[0];

        // Warning: Setting IgnoreShortPacket causes LoadAsync to block until you receive a number of packets >= readPipe.EndpointDescriptor.MaxPacketSize.
        // Remove the following line if you want to see messages that are less than the max transfer size, for example if you are communicating with a USBTMC device. 
        readPipe.ReadOptions |= UsbReadOptions.IgnoreShortPacket;

        var stream = readPipe.InputStream;
        DataReader reader = new DataReader(stream);

        try
        {
            bytesRead = await reader.LoadAsync(readPipe.EndpointDescriptor.MaxPacketSize);
        }
        catch (Exception exception)
        {
            ShowStatus(exception.Message.ToString());
        }
        finally
        {
            ShowStatus("Number of bytes: " + bytesRead);

            IBuffer buffer = reader.ReadBuffer(bytesRead);

            using (var dataReader = Windows.Storage.Streams.DataReader.FromBuffer(buffer))
            {
                dataReader.UnicodeEncoding = Windows.Storage.Streams.UnicodeEncoding.Utf8;
                ShowData(dataReader.ReadString(buffer.Length));
            }
        }
    }