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

As transferências de interrupção ocorrem quando o host sonda o dispositivo. Este artigo demonstra como:

APIs importantes

Um dispositivo USB pode comportar pontos de extremidade de interrupção para que possa enviar ou receber dados em intervalos regulares. Para fazer isso, o host sonda o dispositivo em intervalos regulares e os dados são transmitidos cada vez que o host sonda o dispositivo. As transferências de interrupção são usadas principalmente para obter dados de interrupção do dispositivo. Este tópico descreve como um aplicativo UWP pode obter dados de interrupção contínua do dispositivo.

Informações do ponto de extremidade de extremidade de interrupção

Para pontos de extremidade de interrupção, o descritor expõe estas propriedades. Esses valores são apenas informativos e não devem afetar a forma como você gerencia o buffer de transferência de buffer.

  • Com que frequência os dados podem ser transmitidos?

    Tenha essas informações obtendo o valor Intervalo do descritor do ponto de extremidade (consulte UsbInterruptOutEndpointDescriptor.Interval ou UsbInterruptInEndpointDescriptor.Interval). Esse valor indica a frequência com que os dados são enviados ou recebidos do dispositivo em cada quadro do barramento.

    A propriedade Interval não é o valor bInterval (definido na especificação de USB).

    Esse valor indica a frequência com que os dados são transmitidos de ou para o dispositivo. Por exemplo, para um dispositivo de alta velocidade, se Intervalo 125 microssegundos, os dados serão transmitidos a cada 125 microssegundos. Se Intervalo for 1.000 microssegundos, os dados serão transmitidos a cada milissegundo.

  • Quantos dados podem ser transmitidos em cada intervalo de serviço?

    Tenha o número de bytes que podem ser transmitidos obtendo o tamanho máximo de pacote aceito pelo descritor de ponto de extremidade (consulte UsbInterruptOutEndpointDescriptor.MaxPacketSize ou UsbInterruptInEndpointDescriptor.MaxPacketSize). O tamanho máximo do pacote restrito à velocidade do dispositivo. Para dispositivos de baixa velocidade de até 8 bytes. Para dispositivos de velocidade integral, de até 64 bytes. Para dispositivos de alta velocidade e alta largura de banda, o aplicativo pode enviar ou receber mais do que o tamanho máximo do pacote de até 3.072 bytes por microquadro.

    Os pontos de extremidade de interrupção em dispositivos SuperSpeed podem transmitir ainda mais bytes. Esse valor é indicado pelo wBytesPerInterval do USB_SUPERSPEED_ENDPOINT_COMPANION_DESCRIPTOR. Para recuperar o descritor, obtenha o buffer do descritor usando a propriedade UsbEndpointDescriptor.AsByte e, em seguida, analise esse buffer usando os métodos DataReader.

Interromper transferências OUT

Um dispositivo USB pode comportar pontos de extremidade OUT de interrupção que recebam dados do host em intervalos regulares. Cada vez que o host sonda o dispositivo, ele envia dados. Um aplicativo UWP pode iniciar uma solicitação de transferência OUT de interrupção que especifica os dados a serem enviados. Essa solicitação é concluída quando o dispositivo reconhece os dados do host. Um aplicativo UWP pode gravar dados no UsbInterruptOutPipe.

Interromper transferências IN

Por outro lado, um dispositivo USB pode comportar pontos de extremidade IN de interrupção como uma forma de informar o host sobre interrupções de hardware geradas pelo dispositivo. Normalmente, os dispositivos HID (interface humana USB), como teclados e dispositivos apontadores, comportam pontos de extremidade OUT de interrupção. Quando ocorre uma interrupção, o ponto de extremidade armazena dados de interrupção, mas estes não chegam ao host imediatamente. O ponto de extremidade deve aguardar que o controlador do host sonde o dispositivo. Como deve haver um atraso mínimo entre o momento em que os dados são gerados e chegam ao host, ele sonda o dispositivo em intervalos regulares. Um aplicativo UWP pode obter dados recebidos no UsbInterruptInPipe. A solicitação concluída quando os dados do dispositivo são recebidos pelo host.

Antes de começar

Gravação no ponto de extremidade OUT de interrupção

A maneira como o aplicativo envia uma solicitação de transferência OUT de interrupção é idêntica às transferências OUT em massa, exceto que o destino é um pipe OUT de interrupção, representado por UsbInterruptOutPipe. Para obter mais informações, consulte Como enviar uma solicitação de transferência em massa USB (aplicativo UWP).

Etapa 1: Implementar o manipulador de eventos de interrupção (IN de interrupção)

Quando os dados são recebidos do dispositivo no pipe de interrupção, ele gera o evento DataReceived. Para obter os dados de interrupção, o aplicativo deve implementar um manipulador de eventos. O parâmetro eventArgs do manipulador aponta para o buffer de dados.

Este código de exemplo mostra uma implementação simples do manipulador de eventos. O manipulador mantém a contagem de interrupções recebidas. Cada vez que o manipulador é chamado, ele incrementa a contagem. O manipulador obtém o buffer de dados do parâmetro eventArgs e exibe a contagem de interrupções e o comprimento dos bytes recebidos.

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

Etapa 2: Obter o objeto de pipe de interrupção (IN de interrupção)

Para registrar o manipulador de eventos para o evento DataReceived, obtenha uma referência ao UsbInterruptInPipe usando qualquer uma destas propriedades:

Observação Evite obter o objeto de pipe enumerando pontos de extremidade de interrupção de uma configuração de interface que não esteja selecionada no momento. Para transferir dados, os pipes devem ser associados a pontos de extremidade na configuração ativa.

Etapa 3: Registrar o manipulador de eventos para começar a receber dados (IN de interrupção)

Em seguida, você precisa registrar o manipulador de eventos no objeto UsbInterruptInPipe que gera o evento DataReceived.

Este código de exemplo mostra como registrar o manipulador de eventos. Neste exemplo, a classe controla o manipulador de eventos, o pipe para o qual o manipulador de eventos está registrado e se o pipe está recebendo dados no momento. Todas essas informações são usadas para cancelar o registro do manipulador de eventos, mostrado na próxima etapa.

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;    

}

Depois que o manipulador de eventos é registrado, ele é invocado cada vez que os dados são recebidos no pipe de interrupção associado.

Etapa 4: Cancelar o registro do manipulador de eventos para interromper o recebimento de dados (IN de interrupção)

Depois de terminar de receber os dados, cancele o registro do manipulador de eventos.

Este código de exemplo mostra como cancelar o registro do manipulador de eventos. Neste exemplo, se o aplicativo tiver um manipulador de eventos registrado anteriormente, o método obterá o manipulador de eventos monitorado e cancelará o registro dele no pipe de interrupção.

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

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

        registeredInterrupt = false;
    }
}

Depois que o registro do manipulador de eventos é cancelado, o aplicativo para de receber dados do pipe de interrupção porque o manipulador de eventos não é invocado em eventos de interrupção. Isso não significa que o pipe de interrupção pare de obter dados.