Come inviare una richiesta di trasferimento bulk USB (app UWP)

In questo argomento si apprenderà un trasferimento bulk USB e come avviare una richiesta di trasferimento dall'app UWP che comunica con un dispositivo USB.

I dispositivi USB full speed, high speed e SuperSpeed possono supportare endpoint bulk. Questi endpoint vengono usati per il trasferimento di grandi quantità di dati, ad esempio il trasferimento di dati da o verso un'unità flash USB. I trasferimenti bulk sono affidabili perché consentono il rilevamento degli errori e comporta un numero limitato di tentativi per assicurarsi che i dati vengano ricevuti dall'host o dal dispositivo. I trasferimenti bulk vengono usati per i dati che non sono critici per il tempo. I dati vengono trasferiti solo quando sul bus è disponibile la larghezza di banda inutilizzata. Pertanto, quando il bus è occupato con altri trasferimenti, i dati bulk possono attendere in modo indefinito.

Gli endpoint bulk sono unidirectionali e in un unico trasferimento, i dati possono essere trasferiti in una direzione IN o OUT. Per supportare la lettura e la scrittura di dati bulk, il dispositivo deve supportare endpoint IN bulk IN e BULK OUT. L'endpoint IN bulk viene usato per leggere i dati dal dispositivo all'host e l'endpoint OUT bulk viene usato per inviare dati dall'host al dispositivo.

Per avviare una richiesta di trasferimento bulk, l'app deve avere un riferimento alla pipe che rappresenta un endpoint. Una pipe è un canale di comunicazione aperto dal driver del dispositivo quando il dispositivo è configurato. Per l'app, una pipe è una rappresentazione logica di un endpoint. Per leggere i dati dall'endpoint, l'app ottiene i dati dalla pipe IN bulk associata. Per scrivere dati nell'endpoint, l'app invia i dati alla pipe OUT bulk. Per le pipe di lettura e scrittura bulk, usare le classi UsbBulkInPipe e UsbBulkOutPipe .

L'app può anche modificare il comportamento della pipe impostando determinati flag di criteri. Ad esempio, per una richiesta di lettura, è possibile impostare un flag che cancella automaticamente una condizione di stallo sulla pipe. Per informazioni su tali flag, vedere UsbReadOptions e UsbWriteOptions.

Prima di iniziare

Passaggio 1: Ottenere l'oggetto pipe bulk

Per avviare una richiesta di trasferimento, è necessario ottenere un riferimento all'oggetto pipe bulk (UsbBulkOutPipe o UsbBulkInPipe). È possibile ottenere pipe enumerando tutte le impostazioni di tutte le interfacce. Tuttavia, per i trasferimenti di dati è necessario usare solo pipe di un'impostazione attiva. Se l'oggetto pipe è Null se l'endpoint associato non si trova nell'impostazione attiva.

Per... Usare questo valore della proprietà
Inviare dati a una pipe bulk, ottenere un riferimento a UsbBulkOutPipe. UsbDevice.DefaultInterface.BulkOutPipes[n] se la configurazione del dispositivo espone un'interfaccia USB.

UsbDevice.Configuration.UsbInterfaces[m]. BulkOutPipes[n] per l'enumerazione di pipe OUT bulk in più interfacce supportate dal dispositivo.

UsbInterface.InterfaceSettings[m]. BulkOutEndpoints[n]. Pipe per l'enumerazione di pipe OUT bulk definite dalle impostazioni in un'interfaccia.

UsbEndpointDescriptor.AsBulkOutEndpointDescriptor.Pipe per ottenere l'oggetto pipe dal descrittore dell'endpoint per l'endpoint OUT bulk.
Ricevere dati da una pipe bulk, è possibile ottenere l'oggetto UsbBulkInPipe . UsbDevice.DefaultInterface.BulkInPipes[n] se la configurazione del dispositivo espone un'interfaccia USB.

UsbDevice.Configuration.UsbInterfaces[m]. BulkInPipes[n] per l'enumerazione di pipe IN bulk in più interfacce supportate dal dispositivo.

UsbInterface.InterfaceSettings[m]. BulkInEndpoints[n]. Pipe per l'enumerazione di pipe IN bulk definite dalle impostazioni in un'interfaccia.

UsbEndpointDescriptor.AsBulkInEndpointDescriptor.Pipe per ottenere l'oggetto pipe dal descrittore dell'endpoint per l'endpoint IN bulk.

Nota

Deve trovarsi nell'impostazione attiva o richiede un controllo Null.

Passaggio 2: Configurare la pipe bulk (Facoltativo)

È possibile modificare il comportamento dell'operazione di lettura o scrittura impostando determinati flag nella pipe bulk recuperata.

Per la lettura dal dispositivo, impostare la proprietà UsbBulkInPipe.ReadOptions su uno dei valori definiti in UsbReadOptions. Nel caso di scrittura, impostare la proprietà UsbBulkOutPipe.WriteOptions su uno dei valori definiti in UsbWriteOptions.

Per... Impostare questo flag
Cancellare automaticamente qualsiasi condizione di errore nell'endpoint senza arrestare il flusso di dati AutoClearStall

Per altre informazioni, vedere Cancellare le condizioni di stallo.

Questo flag si applica sia ai trasferimenti di lettura che di scrittura.
Inviare più richieste di lettura con efficienza massima. Aumentare le prestazioni ignorando il controllo degli errori. OverrideAutomaticBufferManagement

Una richiesta di dati può essere suddivisa in uno o più trasferimenti, in cui ogni trasferimento contiene un determinato numero di byte denominato dimensione massima di trasferimento. Per più trasferimenti, potrebbe verificarsi un ritardo nell'accodamento di due trasferimenti a causa del controllo degli errori eseguito dal driver. Questo flag ignora il controllo degli errori. Per ottenere le dimensioni massime del trasferimento, usare la proprietà UsbBulkInPipe.MaxTransferSizeBytes . Se le dimensioni della richiesta sono UsbBulkInPipe.MaxTransferSizeBytes, è necessario impostare questo flag.

Importante: Se si imposta questo flag, è necessario richiedere i dati in più delle dimensioni massime del pacchetto della pipe. Queste informazioni vengono archiviate nel descrittore dell'endpoint. Le dimensioni dipendono dalla velocità del bus del dispositivo. Per velocità piena, alta velocità e SuperSpeed; le dimensioni massime dei pacchetti sono rispettivamente 64, 512 e 1024 byte. Per ottenere tale valore, usare la proprietà UsbBulkInPipe.EndpointDescriptor.MaxPacketSize .

Questo flag si applica solo ai trasferimenti di lettura.
Terminare una richiesta di scrittura con un pacchetto a lunghezza zero ShortPacketTerminate

Invia un pacchetto di lunghezza zero per indicare la fine di un trasferimento OUT.

Questo flag si applica solo ai trasferimenti di scrittura.
Disabilitare la lettura di pacchetti brevi (minore delle dimensioni massime dei pacchetti supportate dall'endpoint) IgnoreShortPacket

Per impostazione predefinita, se il dispositivo invia byte inferiori alle dimensioni massime dei pacchetti, l'app li riceve. Se non si desidera ricevere pacchetti brevi, impostare questo flag.

Questo flag si applica solo ai trasferimenti di lettura.

Passaggio 3: Configurare il flusso di dati

Quando i dati bulk vengono inviati dal dispositivo, i dati vengono ricevuti come nel flusso di input nella pipe bulk. Ecco i passaggi per ottenere il flusso di input:

  1. Ottenere un riferimento al flusso di input recuperando la proprietà UsbBulkInPipe.InputStream .
  2. Creare un oggetto DataReader specificando il flusso di input nel costruttore DataReader.

Per scrivere dati nel dispositivo, l'app deve scrivere in un flusso di output nella pipe bulk. Ecco i passaggi per preparare il flusso di output:

  1. Ottenere un riferimento al flusso di output recuperando la proprietà UsbBulkOutPipe.OutputStream .
  2. Creare un oggetto DataWriter specificando il flusso di output nel costruttore DataWriter .
  3. Popolare il buffer di dati associato al flusso di output.
  4. A seconda del tipo di dati, scrivere i dati nel flusso di output chiamando metodi DataWriter, ad esempio WriteBytes.

Passaggio 4: Avviare un'operazione di trasferimento asincrona

I trasferimenti bulk vengono avviati tramite operazioni asincrone.

Per leggere i dati bulk, avviare un'operazione di lettura asincrona chiamando DataReader.LoadAsync.

Per scrivere dati bulk, avviare un'operazione di scrittura asincrona chiamando DataWriter.StoreAsync.

Passaggio 5: Ottenere risultati dell'operazione di trasferimento in lettura

Al termine dell'operazione di dati asincrona, è possibile ottenere il numero di byte letti o scritti dall'oggetto attività. Per un'operazione di lettura, chiamare metodi DataReader, ad esempio ReadBytes, per leggere i dati dal flusso di input.

Cancellare le condizioni di stallo

A volte, l'app potrebbe riscontrare trasferimenti di dati non riusciti. Un trasferimento non riuscito può essere dovuto a una condizione di stallo sull'endpoint. Purché l'endpoint sia bloccato, i dati non possono essere scritti o letti da esso. Per procedere con i trasferimenti di dati, l'app deve cancellare la condizione di stallo sulla pipe associata.

L'app può configurare la pipe per cancellare automaticamente le condizioni di stallo, quando si verificano. A tale scopo, impostare la proprietà UsbBulkInPipe.ReadOptions su UsbReadOptions.AutoClearStall o UsbBulkOutPipe.WriteOptions su UsbWriteOptions.AutoClearStall. Con questa configurazione automatica, l'app non riscontra trasferimenti non riusciti e l'esperienza di trasferimento dei dati è senza problemi.

Per cancellare manualmente una condizione di stallo, chiama UsbBulkInPipe.ClearStallAsync per una pipe IN bulk; chiamare UsbBulkOutPipe.ClearStallAsync per una pipe OUT bulk.

Nota

Una condizione di stallo non indica un endpoint vuoto. Se nell'endpoint non sono presenti dati, il trasferimento viene completato ma la lunghezza è pari a zero byte.

Per le operazioni di lettura, potrebbe essere necessario cancellare i dati in sospeso nella pipe prima di avviare una nuova richiesta di trasferimento. A tale scopo, chiamare il metodo UsbBulkInPipe.FlushBuffer .

Esempio di codice di trasferimento bulk USB

Questo esempio di codice illustra come scrivere in una pipe bulk. Nell'esempio vengono inviati dati alla prima pipe OUT bulk sull'interfaccia predefinita. Configura la pipe per inviare un pacchetto di lunghezza zero alla fine del trasferimento. Al termine del trasferimento, viene visualizzato il numero di byte.

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

Questo esempio di codice illustra come leggere da una pipe bulk. Nell'esempio vengono recuperati i dati dalla prima pipe IN bulk sull'interfaccia predefinita. Configura la pipe su per ottenere la massima efficienza e riceve i dati in blocchi di dimensioni massime dei pacchetti. Al termine del trasferimento, viene visualizzato il numero di byte.

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