Como enviar uma solicitação de transferência em massa USB (aplicativo UWP)

Neste tópico, você aprenderá sobre uma transferência em massa USB e como iniciar uma solicitação de transferência de seu aplicativo UWP que se comunica com um dispositivo USB.

Dispositivos USB de velocidade total, alta velocidade e SuperSpeed podem dar suporte a pontos de extremidade em massa. Esses pontos de extremidade são usados para transferir grandes quantidades de dados, como transferir dados de ou para uma unidade flash USB. As transferências em massa são confiáveis porque permitem a detecção de erros e envolvem um número limitado de repetições para garantir que os dados sejam recebidos pelo host ou pelo dispositivo. As transferências em massa são usadas para dados que não são críticos para o tempo. Os dados são transferidos somente quando há largura de banda não usada disponível no barramento. Portanto, quando o ônibus está ocupado com outras transferências, os dados em massa podem esperar indefinidamente.

Os pontos de extremidade em massa são unidirecionais e, em uma transferência, os dados podem ser transferidos na direção IN ou OUT. Para dar suporte à leitura e gravação de dados em massa, o dispositivo deve dar suporte a pontos de extremidade BULK IN e BULK OUT. O ponto de extremidade BULK IN é usado para ler dados do dispositivo para o host e o ponto de extremidade bulk OUT é usado para enviar dados do host para o dispositivo.

Para iniciar uma solicitação de transferência em massa, seu aplicativo deve ter uma referência ao pipe que representa um ponto de extremidade. Um pipe é um canal de comunicação aberto pelo driver de dispositivo quando o dispositivo está configurado. Para o aplicativo, um pipe é uma representação lógica de um ponto de extremidade. Para ler dados do ponto de extremidade, o aplicativo obtém dados do pipe IN em massa associado. Para gravar dados no ponto de extremidade, o aplicativo envia dados para o pipe bulk OUT. Para pipes de leitura e gravação em massa, use as classes UsbBulkInPipe e UsbBulkOutPipe .

Seu aplicativo também pode modificar o comportamento do pipe definindo determinados sinalizadores de política. Por exemplo, para uma solicitação de leitura, você pode definir um sinalizador que limpa automaticamente uma condição de parada no pipe. Para obter informações sobre esses sinalizadores, consulte UsbReadOptions e UsbWriteOptions.

Antes de começar

Etapa 1: Obter o objeto de pipe em massa

Para iniciar uma solicitação de transferência, você deve obter uma referência ao objeto de pipe em massa (UsbBulkOutPipe ou UsbBulkInPipe. Você pode obter pipes enumerando todas as configurações de todas as interfaces. No entanto, para transferências de dados, você deve usar apenas pipes de uma configuração ativa. Se o objeto pipe for nulo se o ponto de extremidade associado não estiver na configuração ativa.

Se desejar... Usar esse valor de propriedade
Envie dados para um pipe em massa, obtenha uma referência a UsbBulkOutPipe. UsbDevice.DefaultInterface.BulkOutPipes[n] se a configuração do dispositivo expor uma interface USB.

UsbDevice.Configuration.UsbInterfaces[m]. BulkOutPipes[n] para enumerar pipes bulk OUT em várias interfaces compatíveis com o dispositivo.

UsbInterface.InterfaceSettings[m]. BulkOutEndpoints[n]. Pipe para enumerar pipes bulk OUT definidos pelas configurações em uma interface.

UsbEndpointDescriptor.AsBulkOutEndpointDescriptor.Pipe para obter o objeto pipe do descritor de ponto de extremidade para o ponto de extremidade out em massa.
Receber dados de um pipe em massa, você pode obter o objeto UsbBulkInPipe . UsbDevice.DefaultInterface.BulkInPipes[n] se a configuração do dispositivo expor uma interface USB.

UsbDevice.Configuration.UsbInterfaces[m]. BulkInPipes[n] para enumerar pipes BULK IN em várias interfaces compatíveis com o dispositivo.

UsbInterface.InterfaceSettings[m]. BulkInEndpoints[n]. Pipe para enumerar pipes BULK IN definidos pelas configurações em uma interface.

UsbEndpointDescriptor.AsBulkInEndpointDescriptor.Pipe para obter o objeto pipe do descritor de ponto de extremidade para o ponto de extremidade IN em massa.

Observação

Deve estar na configuração ativa ou requer um marcar nulo.

Etapa 2: Configurar o pipe em massa (opcional)

Você pode modificar o comportamento da operação de leitura ou gravação definindo determinados sinalizadores no pipe em massa recuperado.

Para leitura do dispositivo, defina a propriedade UsbBulkInPipe.ReadOptions como um dos valores definidos em UsbReadOptions. No caso de gravação, defina a propriedade UsbBulkOutPipe.WriteOptions como um dos valores definidos em UsbWriteOptions.

Se desejar... Definir este sinalizador
Limpar automaticamente qualquer condição de erro no ponto de extremidade sem interromper o fluxo de dados AutoClearStall

Para obter mais informações, consulte Limpando condições de parada.

Esse sinalizador se aplica a transferências de leitura e gravação.
Envie várias solicitações de leitura com máxima eficiência. Aumente o desempenho ignorando a verificação de erros. OverrideAutomaticBufferManagement

Uma solicitação de dados pode ser dividida em uma ou mais transferências, em que cada transferência contém um determinado número de bytes chamado de tamanho máximo de transferência. Para várias transferências, pode haver atraso no enfileiramento de duas transferências devido à verificação de erros executada pelo driver. Esse sinalizador ignora essa verificação de erros. Para obter o tamanho máximo de transferência, use a propriedade UsbBulkInPipe.MaxTransferSizeBytes . Se o tamanho da solicitação for UsbBulkInPipe.MaxTransferSizeBytes, você deverá definir esse sinalizador.

Importante: Se você definir esse sinalizador, deverá solicitar dados em múltiplos do tamanho máximo do pacote do pipe. Essas informações são armazenadas no descritor de ponto de extremidade. O tamanho depende da velocidade do barramento do dispositivo. Para velocidade total, alta velocidade e SuperSpeed; os tamanhos máximos de pacotes são 64, 512 e 1024 bytes, respectivamente. Para obter esse valor, use a propriedade UsbBulkInPipe.EndpointDescriptor.MaxPacketSize .

Esse sinalizador só se aplica a transferências de leitura.
Encerrar uma solicitação de gravação com um pacote de comprimento zero ShortPacketTerminate

Envia um pacote de comprimento zero para indicar o fim de uma transferência OUT.

Esse sinalizador só se aplica a transferências de gravação.
Desabilitar a leitura de pacotes curtos (menor que o tamanho máximo do pacote com suporte pelo ponto de extremidade) IgnoreShortPacket

Por padrão, se o dispositivo enviar bytes menores que o tamanho máximo do pacote, o aplicativo os receberá. Se você não quiser receber pacotes curtos, defina esse sinalizador.

Esse sinalizador só se aplica a transferências de leitura.

Etapa 3: Configurar o fluxo de dados

Quando os dados em massa são enviados pelo dispositivo, os dados são recebidos como no fluxo de entrada no pipe em massa. Estas são as etapas para obter o fluxo de entrada:

  1. Obtenha uma referência ao fluxo de entrada obtendo a propriedade UsbBulkInPipe.InputStream .
  2. Crie um objeto DataReader especificando o fluxo de entrada no construtor DataReader.

Para gravar dados no dispositivo, o aplicativo deve gravar em um fluxo de saída no pipe em massa. Estas são as etapas para preparar o fluxo de saída:

  1. Obtenha uma referência ao fluxo de saída obtendo a propriedade UsbBulkOutPipe.OutputStream .
  2. Crie um objeto DataWriter especificando o fluxo de saída no construtor DataWriter .
  3. Preencha o buffer de dados associado ao fluxo de saída.
  4. Dependendo do tipo de dados, escreva dados de transferência para o fluxo de saída chamando métodos DataWriter, como WriteBytes.

Etapa 4: Iniciar uma operação de transferência assíncrona

As transferências em massa são iniciadas por meio de operações assíncronas.

Para ler dados em massa, inicie uma operação de leitura assíncrona chamando DataReader.LoadAsync.

Para gravar dados em massa, inicie uma operação de gravação assíncrona chamando DataWriter.StoreAsync.

Etapa 5: Obter resultados da operação de transferência de leitura

Depois que a operação de dados assíncrona for concluída, você poderá obter o número de bytes lidos ou gravados do objeto de tarefa. Para uma operação de leitura, chame métodos DataReader, como ReadBytes, para ler dados do fluxo de entrada.

Limpar condições de paralisação

Às vezes, o aplicativo pode ter falhas nas transferências de dados. Uma transferência com falha pode ser devido a uma condição de paralisação no ponto de extremidade. Enquanto o ponto de extremidade estiver parado, os dados não poderão ser gravados ou lidos dele. Para continuar com transferências de dados, o aplicativo deve limpar a condição de parada no pipe associado.

Seu aplicativo pode configurar o pipe para limpar automaticamente as condições de parada, quando elas ocorrem. Para fazer isso, defina a propriedade UsbBulkInPipe.ReadOptions como UsbReadOptions.AutoClearStall ou a propriedade UsbBulkOutPipe.WriteOptions como UsbWriteOptions.AutoClearStall. Com essa configuração automática, o aplicativo não enfrenta transferências com falha e a experiência de transferência de dados é perfeita.

Para limpar uma condição de parada manualmente, chame UsbBulkInPipe.ClearStallAsync para um pipe IN em massa; chame UsbBulkOutPipe.ClearStallAsync para um pipe OUT em massa.

Observação

Uma condição de parada não indica um ponto de extremidade vazio. Se não houver dados no ponto de extremidade, a transferência será concluída, mas o comprimento será zero bytes.

Para operações de leitura, talvez seja necessário limpar os dados pendentes no pipe antes de iniciar uma nova solicitação de transferência. Para fazer isso, chame o método UsbBulkInPipe.FlushBuffer .

Exemplo de código de transferência em massa USB

Este exemplo de código mostra como gravar em um pipe em massa. O exemplo envia dados para o primeiro pipe bulk OUT na interface padrão. Ele configura o pipe para enviar um pacote de comprimento zero no final da transferência. Quando a transferência é concluída, o número de bytes é mostrado.

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

Este exemplo de código mostra como ler de um pipe em massa. O exemplo recupera dados do primeiro pipe IN em massa na interface padrão. Ele configura o pipe para para máxima eficiência e recebe dados em partes do tamanho máximo do pacote. Quando a transferência é concluída, o número de bytes é mostrado.

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